Unified · Split
| @@ -14,33 +14,41 @@ this.app = { · 13 unmodified lines | |||
| 14 | _$$: $$, | 14 | _$$: $$, |
| 15 | _page: page, | 15 | _page: page, |
| 16 | collections: {}, | 16 | collections: {}, |
| 17 | models: {}, | 17 | models: {}, |
| 18 | templates: {}, | 18 | templates: {}, |
| 19 | views: {}, | 19 | views: {}, |
| 20 | 20 | ||
| 21 | init() { | 21 | init() { |
| 22 | try { this.initErrorTracking(); } catch (error) {} | 22 | try { |
| 23 | if (!this.browserCheck()) { return; } | 23 | this.initErrorTracking(); |
| 24 | } catch (error) {} | ||
| 25 | if (!this.browserCheck()) { | ||
| 26 | return; | ||
| 27 | } | ||
| 24 | 28 | ||
| 25 | this.el = $('._app'); | 29 | this.el = $("._app"); |
| 26 | this.localStorage = new LocalStorageStore; | 30 | this.localStorage = new LocalStorageStore(); |
| 27 | if (app.ServiceWorker.isEnabled()) { this.serviceWorker = new app.ServiceWorker; } | 31 | if (app.ServiceWorker.isEnabled()) { |
| 28 | this.settings = new app.Settings; | 32 | this.serviceWorker = new app.ServiceWorker(); |
| 33 | } | ||
| 34 | this.settings = new app.Settings(); | ||
| 29 | this.db = new app.DB(); | 35 | this.db = new app.DB(); |
| 30 | 36 | ||
| 31 | this.settings.initLayout(); | 37 | this.settings.initLayout(); |
| 32 | 38 | ||
| 33 | this.docs = new app.collections.Docs; | 39 | this.docs = new app.collections.Docs(); |
| 34 | this.disabledDocs = new app.collections.Docs; | 40 | this.disabledDocs = new app.collections.Docs(); |
| 35 | this.entries = new app.collections.Entries; | 41 | this.entries = new app.collections.Entries(); |
| 36 | 42 | ||
| 37 | this.router = new app.Router; | 43 | this.router = new app.Router(); |
| 38 | this.shortcuts = new app.Shortcuts; | 44 | this.shortcuts = new app.Shortcuts(); |
| 39 | this.document = new app.views.Document; | 45 | this.document = new app.views.Document(); |
| 40 | if (this.isMobile()) { this.mobile = new app.views.Mobile; } | 46 | if (this.isMobile()) { |
| 47 | this.mobile = new app.views.Mobile(); | ||
| 48 | } | ||
| 41 | 49 | ||
| 42 | if (document.body.hasAttribute('data-doc')) { | 50 | if (document.body.hasAttribute("data-doc")) { |
| 43 | this.DOC = JSON.parse(document.body.getAttribute('data-doc')); | 51 | this.DOC = JSON.parse(document.body.getAttribute("data-doc")); |
| 44 | this.bootOne(); | 52 | this.bootOne(); |
| 45 | } else if (this.DOCS) { | 53 | } else if (this.DOCS) { |
| 46 | this.bootAll(); | 54 | this.bootAll(); |
| @@ -50,7 +58,9 @@ this.app = { · 3 unmodified lines | |||
| 50 | }, | 58 | }, |
| 51 | 59 | ||
| 52 | browserCheck() { | 60 | browserCheck() { |
| 53 | if (this.isSupportedBrowser()) { return true; } | 61 | if (this.isSupportedBrowser()) { |
| 62 | return true; | ||
| 63 | } | ||
| 54 | document.body.innerHTML = app.templates.unsupportedBrowser; | 64 | document.body.innerHTML = app.templates.unsupportedBrowser; |
| 55 | this.hideLoadingScreen(); | 65 | this.hideLoadingScreen(); |
| 56 | return false; | 66 | return false; |
| @@ -61,7 +71,7 @@ this.app = { · 4 unmodified lines | |||
| 61 | // from a domain other than our own, because things are likely to break. | 71 | // from a domain other than our own, because things are likely to break. |
| 62 | // (e.g. cross-domain requests) | 72 | // (e.g. cross-domain requests) |
| 63 | if (this.isInvalidLocation()) { | 73 | if (this.isInvalidLocation()) { |
| 64 | new app.views.Notif('InvalidLocation'); | 74 | new app.views.Notif("InvalidLocation"); |
| 65 | } else { | 75 | } else { |
| 66 | if (this.config.sentry_dsn) { | 76 | if (this.config.sentry_dsn) { |
| 67 | Raven.config(this.config.sentry_dsn, { | 77 | Raven.config(this.config.sentry_dsn, { |
| @@ -70,9 +80,12 @@ this.app = { · 2 unmodified lines | |||
| 70 | includePaths: [/devdocs/], | 80 | includePaths: [/devdocs/], |
| 71 | ignoreErrors: [/NPObject/, /NS_ERROR/, /^null$/, /EvalError/], | 81 | ignoreErrors: [/NPObject/, /NS_ERROR/, /^null$/, /EvalError/], |
| 72 | tags: { | 82 | tags: { |
| 73 | mode: this.isSingleDoc() ? 'single' : 'full', | 83 | mode: this.isSingleDoc() ? "single" : "full", |
| 74 | iframe: (window.top !== window).toString(), | 84 | iframe: (window.top !== window).toString(), |
| 75 | electron: (!!__guard__(window.process != null ? window.process.versions : undefined, x => x.electron)).toString() | 85 | electron: (!!__guard__( |
| 86 | window.process != null ? window.process.versions : undefined, | ||
| 87 | (x) => x.electron, | ||
| 88 | )).toString(), | ||
| 76 | }, | 89 | }, |
| 77 | shouldSendCallback: () => { | 90 | shouldSendCallback: () => { |
| 78 | try { | 91 | try { |
| @@ -89,12 +102,16 @@ this.app = { · 10 unmodified lines | |||
| 89 | dataCallback(data) { | 102 | dataCallback(data) { |
| 90 | try { | 103 | try { |
| 91 | $.extend(data.user || (data.user = {}), app.settings.dump()); | 104 | $.extend(data.user || (data.user = {}), app.settings.dump()); |
| 92 | if (data.user.docs) { data.user.docs = data.user.docs.split('/'); } | 105 | if (data.user.docs) { |
| 93 | if (app.lastIDBTransaction) { data.user.lastIDBTransaction = app.lastIDBTransaction; } | 106 | data.user.docs = data.user.docs.split("/"); |
| 107 | } | ||
| 108 | if (app.lastIDBTransaction) { | ||
| 109 | data.user.lastIDBTransaction = app.lastIDBTransaction; | ||
| 110 | } | ||
| 94 | data.tags.scriptCount = document.scripts.length; | 111 | data.tags.scriptCount = document.scripts.length; |
| 95 | } catch (error) {} | 112 | } catch (error) {} |
| 96 | return data; | 113 | return data; |
| 97 | } | 114 | }, |
| 98 | }).install(); | 115 | }).install(); |
| 99 | } | 116 | } |
| 100 | this.previousErrorHandler = onerror; | 117 | this.previousErrorHandler = onerror; |
| @@ -106,8 +123,10 @@ this.app = { · 5 unmodified lines | |||
| 106 | bootOne() { | 123 | bootOne() { |
| 107 | this.doc = new app.models.Doc(this.DOC); | 124 | this.doc = new app.models.Doc(this.DOC); |
| 108 | this.docs.reset([this.doc]); | 125 | this.docs.reset([this.doc]); |
| 109 | this.doc.load(this.start.bind(this), this.onBootError.bind(this), {readCache: true}); | 126 | this.doc.load(this.start.bind(this), this.onBootError.bind(this), { |
| 110 | new app.views.Notice('singleDoc', this.doc); | 127 | readCache: true, |
| 128 | }); | ||
| 129 | new app.views.Notice("singleDoc", this.doc); | ||
| 111 | delete this.DOC; | 130 | delete this.DOC; |
| 112 | }, | 131 | }, |
| 113 | 132 | ||
| @@ -117,40 +136,61 @@ this.app = { · 3 unmodified lines | |||
| 117 | (docs.indexOf(doc.slug) >= 0 ? this.docs : this.disabledDocs).add(doc); | 136 | (docs.indexOf(doc.slug) >= 0 ? this.docs : this.disabledDocs).add(doc); |
| 118 | } | 137 | } |
| 119 | this.migrateDocs(); | 138 | this.migrateDocs(); |
| 120 | this.docs.load(this.start.bind(this), this.onBootError.bind(this), {readCache: true, writeCache: true}); | 139 | this.docs.load(this.start.bind(this), this.onBootError.bind(this), { |
| 140 | readCache: true, | ||
| 141 | writeCache: true, | ||
| 142 | }); | ||
| 121 | delete this.DOCS; | 143 | delete this.DOCS; |
| 122 | }, | 144 | }, |
| 123 | 145 | ||
| 124 | start() { | 146 | start() { |
| 125 | let doc; | 147 | let doc; |
| 126 | for (doc of Array.from(this.docs.all())) { this.entries.add(doc.toEntry()); } | 148 | for (doc of Array.from(this.docs.all())) { |
| 127 | for (doc of Array.from(this.disabledDocs.all())) { this.entries.add(doc.toEntry()); } | 149 | this.entries.add(doc.toEntry()); |
| 128 | for (doc of Array.from(this.docs.all())) { this.initDoc(doc); } | 150 | } |
| 129 | this.trigger('ready'); | 151 | for (doc of Array.from(this.disabledDocs.all())) { |
| 152 | this.entries.add(doc.toEntry()); | ||
| 153 | } | ||
| 154 | for (doc of Array.from(this.docs.all())) { | ||
| 155 | this.initDoc(doc); | ||
| 156 | } | ||
| 157 | this.trigger("ready"); | ||
| 130 | this.router.start(); | 158 | this.router.start(); |
| 131 | this.hideLoadingScreen(); | 159 | this.hideLoadingScreen(); |
| 132 | setTimeout(() => { | 160 | setTimeout(() => { |
| 133 | if (!this.doc) { this.welcomeBack(); } | 161 | if (!this.doc) { |
| 134 | return this.removeEvent('ready bootError'); | 162 | this.welcomeBack(); |
| 135 | } | 163 | } |
| 136 | , 50); | 164 | return this.removeEvent("ready bootError"); |
| 165 | }, 50); | ||
| 137 | }, | 166 | }, |
| 138 | 167 | ||
| 139 | initDoc(doc) { | 168 | initDoc(doc) { |
| 140 | for (var type of Array.from(doc.types.all())) { doc.entries.add(type.toEntry()); } | 169 | for (var type of Array.from(doc.types.all())) { |
| 170 | doc.entries.add(type.toEntry()); | ||
| 171 | } | ||
| 141 | this.entries.add(doc.entries.all()); | 172 | this.entries.add(doc.entries.all()); |
| 142 | }, | 173 | }, |
| 143 | 174 | ||
| 144 | migrateDocs() { | 175 | migrateDocs() { |
| 145 | let needsSaving; | 176 | let needsSaving; |
| 146 | for (var slug of Array.from(this.settings.getDocs())) { | 177 | for (var slug of Array.from(this.settings.getDocs())) { |
| 147 | if (!this.docs.findBy('slug', slug)) {var doc; | 178 | if (!this.docs.findBy("slug", slug)) { |
| 148 | 179 | var doc; | |
| 180 | |||
| 149 | needsSaving = true; | 181 | needsSaving = true; |
| 150 | if (slug === 'webpack~2') { doc = this.disabledDocs.findBy('slug', 'webpack'); } | 182 | if (slug === "webpack~2") { |
| 151 | if (slug === 'angular~4_typescript') { doc = this.disabledDocs.findBy('slug', 'angular'); } | 183 | doc = this.disabledDocs.findBy("slug", "webpack"); |
| 152 | if (slug === 'angular~2_typescript') { doc = this.disabledDocs.findBy('slug', 'angular~2'); } | 184 | } |
| 153 | if (!doc) { doc = this.disabledDocs.findBy('slug_without_version', slug); } | 185 | if (slug === "angular~4_typescript") { |
| 186 | doc = this.disabledDocs.findBy("slug", "angular"); | ||
| 187 | } | ||
| 188 | if (slug === "angular~2_typescript") { | ||
| 189 | doc = this.disabledDocs.findBy("slug", "angular~2"); | ||
| 190 | } | ||
| 191 | if (!doc) { | ||
| 192 | doc = this.disabledDocs.findBy("slug_without_version", slug); | ||
| 193 | } | ||
| 154 | if (doc) { | 194 | if (doc) { |
| 155 | this.disabledDocs.remove(doc); | 195 | this.disabledDocs.remove(doc); |
| 156 | this.docs.add(doc); | 196 | this.docs.add(doc); |
| @@ -158,56 +198,70 @@ this.app = { · 1 unmodified line | |||
| 158 | } | 198 | } |
| 159 | } | 199 | } |
| 160 | 200 | ||
| 161 | if (needsSaving) { this.saveDocs(); } | 201 | if (needsSaving) { |
| 202 | this.saveDocs(); | ||
| 203 | } | ||
| 162 | }, | 204 | }, |
| 163 | 205 | ||
| 164 | enableDoc(doc, _onSuccess, onError) { | 206 | enableDoc(doc, _onSuccess, onError) { |
| 165 | if (this.docs.contains(doc)) { return; } | 207 | if (this.docs.contains(doc)) { |
| 208 | return; | ||
| 209 | } | ||
| 166 | 210 | ||
| 167 | const onSuccess = () => { | 211 | const onSuccess = () => { |
| 168 | if (this.docs.contains(doc)) { return; } | 212 | if (this.docs.contains(doc)) { |
| 213 | return; | ||
| 214 | } | ||
| 169 | this.disabledDocs.remove(doc); | 215 | this.disabledDocs.remove(doc); |
| 170 | this.docs.add(doc); | 216 | this.docs.add(doc); |
| 171 | this.docs.sort(); | 217 | this.docs.sort(); |
| 172 | this.initDoc(doc); | 218 | this.initDoc(doc); |
| 173 | this.saveDocs(); | 219 | this.saveDocs(); |
| 174 | if (app.settings.get('autoInstall')) { | 220 | if (app.settings.get("autoInstall")) { |
| 175 | doc.install(_onSuccess, onError); | 221 | doc.install(_onSuccess, onError); |
| 176 | } else { | 222 | } else { |
| 177 | _onSuccess(); | 223 | _onSuccess(); |
| 178 | } | 224 | } |
| 179 | }; | 225 | }; |
| 180 | 226 | ||
| 181 | doc.load(onSuccess, onError, {writeCache: true}); | 227 | doc.load(onSuccess, onError, { writeCache: true }); |
| 182 | }, | 228 | }, |
| 183 | 229 | ||
| 184 | saveDocs() { | 230 | saveDocs() { |
| 185 | this.settings.setDocs(Array.from(this.docs.all()).map((doc) => doc.slug)); | 231 | this.settings.setDocs(Array.from(this.docs.all()).map((doc) => doc.slug)); |
| 186 | this.db.migrate(); | 232 | this.db.migrate(); |
| 187 | return (this.serviceWorker != null ? this.serviceWorker.updateInBackground() : undefined); | 233 | return this.serviceWorker != null |
| 234 | ? this.serviceWorker.updateInBackground() | ||
| 235 | : undefined; | ||
| 188 | }, | 236 | }, |
| 189 | 237 | ||
| 190 | welcomeBack() { | 238 | welcomeBack() { |
| 191 | let visitCount = this.settings.get('count'); | 239 | let visitCount = this.settings.get("count"); |
| 192 | this.settings.set('count', ++visitCount); | 240 | this.settings.set("count", ++visitCount); |
| 193 | if (visitCount === 5) { new app.views.Notif('Share', {autoHide: null}); } | 241 | if (visitCount === 5) { |
| 242 | new app.views.Notif("Share", { autoHide: null }); | ||
| 243 | } | ||
| 194 | new app.views.News(); | 244 | new app.views.News(); |
| 195 | new app.views.Updates(); | 245 | new app.views.Updates(); |
| 196 | return this.updateChecker = new app.UpdateChecker(); | 246 | return (this.updateChecker = new app.UpdateChecker()); |
| 197 | }, | 247 | }, |
| 198 | 248 | ||
| 199 | reboot() { | 249 | reboot() { |
| 200 | if ((location.pathname !== '/') && (location.pathname !== '/settings')) { | 250 | if (location.pathname !== "/" && location.pathname !== "/settings") { |
| 201 | window.location = `/#${location.pathname}`; | 251 | window.location = `/#${location.pathname}`; |
| 202 | } else { | 252 | } else { |
| 203 | window.location = '/'; | 253 | window.location = "/"; |
| 204 | } | 254 | } |
| 205 | }, | 255 | }, |
| 206 | 256 | ||
| 207 | reload() { | 257 | reload() { |
| 208 | this.docs.clearCache(); | 258 | this.docs.clearCache(); |
| 209 | this.disabledDocs.clearCache(); | 259 | this.disabledDocs.clearCache(); |
| 210 | if (this.serviceWorker) { this.serviceWorker.reload(); } else { this.reboot(); } | 260 | if (this.serviceWorker) { |
| 261 | this.serviceWorker.reload(); | ||
| 262 | } else { | ||
| 263 | this.reboot(); | ||
| 264 | } | ||
| 211 | }, | 265 | }, |
| 212 | 266 | ||
| 213 | reset() { | 267 | reset() { |
| @@ -219,11 +273,13 @@ this.app = { · 5 unmodified lines | |||
| 219 | if (this.serviceWorker != null) { | 273 | if (this.serviceWorker != null) { |
| 220 | this.serviceWorker.update(); | 274 | this.serviceWorker.update(); |
| 221 | } | 275 | } |
| 222 | window.location = '/'; | 276 | window.location = "/"; |
| 223 | }, | 277 | }, |
| 224 | 278 | ||
| 225 | showTip(tip) { | 279 | showTip(tip) { |
| 226 | if (this.isSingleDoc()) { return; } | 280 | if (this.isSingleDoc()) { |
| 281 | return; | ||
| 282 | } | ||
| 227 | const tips = this.settings.getTips(); | 283 | const tips = this.settings.getTips(); |
| 228 | if (tips.indexOf(tip) === -1) { | 284 | if (tips.indexOf(tip) === -1) { |
| 229 | tips.push(tip); | 285 | tips.push(tip); |
| @@ -233,44 +289,61 @@ this.app = { · 3 unmodified lines | |||
| 233 | }, | 289 | }, |
| 234 | 290 | ||
| 235 | hideLoadingScreen() { | 291 | hideLoadingScreen() { |
| 236 | if ($.overlayScrollbarsEnabled()) { document.body.classList.add('_overlay-scrollbars'); } | 292 | if ($.overlayScrollbarsEnabled()) { |
| 237 | document.documentElement.classList.remove('_booting'); | 293 | document.body.classList.add("_overlay-scrollbars"); |
| 294 | } | ||
| 295 | document.documentElement.classList.remove("_booting"); | ||
| 238 | }, | 296 | }, |
| 239 | 297 | ||
| 240 | indexHost() { | 298 | indexHost() { |
| 241 | // Can't load the index files from the host/CDN when service worker is | 299 | // Can't load the index files from the host/CDN when service worker is |
| 242 | // enabled because it doesn't support caching URLs that use CORS. | 300 | // enabled because it doesn't support caching URLs that use CORS. |
| 243 | return this.config[this.serviceWorker && this.settings.hasDocs() ? 'index_path' : 'docs_origin']; | 301 | return this.config[ |
| 302 | this.serviceWorker && this.settings.hasDocs() | ||
| 303 | ? "index_path" | ||
| 304 | : "docs_origin" | ||
| 305 | ]; | ||
| 244 | }, | 306 | }, |
| 245 | 307 | ||
| 246 | onBootError(...args) { | 308 | onBootError(...args) { |
| 247 | this.trigger('bootError'); | 309 | this.trigger("bootError"); |
| 248 | this.hideLoadingScreen(); | 310 | this.hideLoadingScreen(); |
| 249 | }, | 311 | }, |
| 250 | 312 | ||
| 251 | onQuotaExceeded() { | 313 | onQuotaExceeded() { |
| 252 | if (this.quotaExceeded) { return; } | 314 | if (this.quotaExceeded) { |
| 315 | return; | ||
| 316 | } | ||
| 253 | this.quotaExceeded = true; | 317 | this.quotaExceeded = true; |
| 254 | new app.views.Notif('QuotaExceeded', {autoHide: null}); | 318 | new app.views.Notif("QuotaExceeded", { autoHide: null }); |
| 255 | }, | 319 | }, |
| 256 | 320 | ||
| 257 | onCookieBlocked(key, value, actual) { | 321 | onCookieBlocked(key, value, actual) { |
| 258 | if (this.cookieBlocked) { return; } | 322 | if (this.cookieBlocked) { |
| 323 | return; | ||
| 324 | } | ||
| 259 | this.cookieBlocked = true; | 325 | this.cookieBlocked = true; |
| 260 | new app.views.Notif('CookieBlocked', {autoHide: null}); | 326 | new app.views.Notif("CookieBlocked", { autoHide: null }); |
| 261 | Raven.captureMessage(`CookieBlocked/${key}`, {level: 'warning', extra: {value, actual}}); | 327 | Raven.captureMessage(`CookieBlocked/${key}`, { |
| 328 | level: "warning", | ||
| 329 | extra: { value, actual }, | ||
| 330 | }); | ||
| 262 | }, | 331 | }, |
| 263 | 332 | ||
| 264 | onWindowError(...args) { | 333 | onWindowError(...args) { |
| 265 | if (this.cookieBlocked) { return; } | 334 | if (this.cookieBlocked) { |
| 335 | return; | ||
| 336 | } | ||
| 266 | if (this.isInjectionError(...Array.from(args || []))) { | 337 | if (this.isInjectionError(...Array.from(args || []))) { |
| 267 | this.onInjectionError(); | 338 | this.onInjectionError(); |
| 268 | } else if (this.isAppError(...Array.from(args || []))) { | 339 | } else if (this.isAppError(...Array.from(args || []))) { |
| 269 | if (typeof this.previousErrorHandler === 'function') { | 340 | if (typeof this.previousErrorHandler === "function") { |
| 270 | this.previousErrorHandler(...Array.from(args || [])); | 341 | this.previousErrorHandler(...Array.from(args || [])); |
| 271 | } | 342 | } |
| 272 | this.hideLoadingScreen(); | 343 | this.hideLoadingScreen(); |
| 273 | if (!this.errorNotif) { this.errorNotif = new app.views.Notif('Error'); } | 344 | if (!this.errorNotif) { |
| 345 | this.errorNotif = new app.views.Notif("Error"); | ||
| 346 | } | ||
| 274 | this.errorNotif.show(); | 347 | this.errorNotif.show(); |
| 275 | } | 348 | } |
| 276 | }, | 349 | }, |
| @@ -280,75 +353,103 @@ this.app = { · 3 unmodified lines | |||
| 280 | this.injectionError = true; | 353 | this.injectionError = true; |
| 281 | alert(`\ | 354 | alert(`\ |
| 282 | JavaScript code has been injected in the page which prevents DevDocs from running correctly. | 355 | JavaScript code has been injected in the page which prevents DevDocs from running correctly. |
| 283 | Please check your browser extensions/addons. ` | 356 | Please check your browser extensions/addons. `); |
| 284 | ); | 357 | Raven.captureMessage("injection error", { level: "info" }); |
| 285 | Raven.captureMessage('injection error', {level: 'info'}); | ||
| 286 | } | 358 | } |
| 287 | }, | 359 | }, |
| 288 | 360 | ||
| 289 | isInjectionError() { | 361 | isInjectionError() { |
| 290 | // Some browser extensions expect the entire web to use jQuery. | 362 | // Some browser extensions expect the entire web to use jQuery. |
| 291 | // I gave up trying to fight back. | 363 | // I gave up trying to fight back. |
| 292 | return (window.$ !== app._$) || (window.$$ !== app._$$) || (window.page !== app._page) || (typeof $.empty !== 'function') || (typeof page.show !== 'function'); | 364 | return ( |
| 365 | window.$ !== app._$ || | ||
| 366 | window.$$ !== app._$$ || | ||
| 367 | window.page !== app._page || | ||
| 368 | typeof $.empty !== "function" || | ||
| 369 | typeof page.show !== "function" | ||
| 370 | ); | ||
| 293 | }, | 371 | }, |
| 294 | 372 | ||
| 295 | isAppError(error, file) { | 373 | isAppError(error, file) { |
| 296 | // Ignore errors from external scripts. | 374 | // Ignore errors from external scripts. |
| 297 | return file && (file.indexOf('devdocs') !== -1) && (file.indexOf('.js') === (file.length - 3)); | 375 | return ( |
| 376 | file && | ||
| 377 | file.indexOf("devdocs") !== -1 && | ||
| 378 | file.indexOf(".js") === file.length - 3 | ||
| 379 | ); | ||
| 298 | }, | 380 | }, |
| 299 | 381 | ||
| 300 | isSupportedBrowser() { | 382 | isSupportedBrowser() { |
| 301 | try { | 383 | try { |
| 302 | const features = { | 384 | const features = { |
| 303 | bind: !!Function.prototype.bind, | 385 | bind: !!Function.prototype.bind, |
| 304 | pushState: !!history.pushState, | 386 | pushState: !!history.pushState, |
| 305 | matchMedia: !!window.matchMedia, | 387 | matchMedia: !!window.matchMedia, |
| 306 | insertAdjacentHTML: !!document.body.insertAdjacentHTML, | 388 | insertAdjacentHTML: !!document.body.insertAdjacentHTML, |
| 307 | defaultPrevented: document.createEvent('CustomEvent').defaultPrevented === false, | 389 | defaultPrevented: |
| 308 | cssVariables: !!__guardMethod__(CSS, 'supports', o => o.supports('(--t: 0)')) | 390 | document.createEvent("CustomEvent").defaultPrevented === false, |
| 391 | cssVariables: !!__guardMethod__(CSS, "supports", (o) => | ||
| 392 | o.supports("(--t: 0)"), | ||
| 393 | ), | ||
| 309 | }; | 394 | }; |
| 310 | 395 | ||
| 311 | for (var key in features) { | 396 | for (var key in features) { |
| 312 | var value = features[key]; | 397 | var value = features[key]; |
| 313 | if (!value) { | 398 | if (!value) { |
| 314 | Raven.captureMessage(`unsupported/${key}`, {level: 'info'}); | 399 | Raven.captureMessage(`unsupported/${key}`, { level: "info" }); |
| 315 | return false; | 400 | return false; |
| 316 | } | 401 | } |
| 317 | } | 402 | } |
| 318 | 403 | ||
| 319 | return true; | 404 | return true; |
| 320 | } catch (error) { | 405 | } catch (error) { |
| 321 | Raven.captureMessage('unsupported/exception', {level: 'info', extra: { error }}); | 406 | Raven.captureMessage("unsupported/exception", { |
| 407 | level: "info", | ||
| 408 | extra: { error }, | ||
| 409 | }); | ||
| 322 | return false; | 410 | return false; |
| 323 | } | 411 | } |
| 324 | }, | 412 | }, |
| 325 | 413 | ||
| 326 | isSingleDoc() { | 414 | isSingleDoc() { |
| 327 | return document.body.hasAttribute('data-doc'); | 415 | return document.body.hasAttribute("data-doc"); |
| 328 | }, | 416 | }, |
| 329 | 417 | ||
| 330 | isMobile() { | 418 | isMobile() { |
| 331 | return this._isMobile != null ? this._isMobile : (this._isMobile = app.views.Mobile.detect()); | 419 | return this._isMobile != null |
| 420 | ? this._isMobile | ||
| 421 | : (this._isMobile = app.views.Mobile.detect()); | ||
| 332 | }, | 422 | }, |
| 333 | 423 | ||
| 334 | isAndroidWebview() { | 424 | isAndroidWebview() { |
| 335 | return this._isAndroidWebview != null ? this._isAndroidWebview : (this._isAndroidWebview = app.views.Mobile.detectAndroidWebview()); | 425 | return this._isAndroidWebview != null |
| 426 | ? this._isAndroidWebview | ||
| 427 | : (this._isAndroidWebview = app.views.Mobile.detectAndroidWebview()); | ||
| 336 | }, | 428 | }, |
| 337 | 429 | ||
| 338 | isInvalidLocation() { | 430 | isInvalidLocation() { |
| 339 | return (this.config.env === 'production') && (location.host.indexOf(app.config.production_host) !== 0); | 431 | return ( |
| 340 | } | 432 | this.config.env === "production" && |
| 433 | location.host.indexOf(app.config.production_host) !== 0 | ||
| 434 | ); | ||
| 435 | }, | ||
| 341 | }; | 436 | }; |
| 342 | 437 | ||
| 343 | $.extend(app, Events); | 438 | $.extend(app, Events); |
| 344 | 439 | ||
| 345 | function __guard__(value, transform) { | 440 | function __guard__(value, transform) { |
| 346 | return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; | 441 | return typeof value !== "undefined" && value !== null |
| 442 | ? transform(value) | ||
| 443 | : undefined; | ||
| 347 | } | 444 | } |
| 348 | function __guardMethod__(obj, methodName, transform) { | 445 | function __guardMethod__(obj, methodName, transform) { |
| 349 | if (typeof obj !== 'undefined' && obj !== null && typeof obj[methodName] === 'function') { | 446 | if ( |
| 447 | typeof obj !== "undefined" && | ||
| 448 | obj !== null && | ||
| 449 | typeof obj[methodName] === "function" | ||
| 450 | ) { | ||
| 350 | return transform(obj, methodName); | 451 | return transform(obj, methodName); |
| 351 | } else { | 452 | } else { |
| 352 | return undefined; | 453 | return undefined; |
| 353 | } | 454 | } |
| 354 | } | 455 | } |
| @@ -9,12 +9,12 @@ · 8 unmodified lines | |||
| 9 | * DS207: Consider shorter variations of null checks | 9 | * DS207: Consider shorter variations of null checks |
| 10 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 10 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 11 | */ | 11 | */ |
| 12 | (function() { | 12 | (function () { |
| 13 | let NAME = undefined; | 13 | let NAME = undefined; |
| 14 | let VERSION = undefined; | 14 | let VERSION = undefined; |
| 15 | const Cls = (app.DB = class DB { | 15 | const Cls = (app.DB = class DB { |
| 16 | static initClass() { | 16 | static initClass() { |
| 17 | NAME = 'docs'; | 17 | NAME = "docs"; |
| 18 | VERSION = 15; | 18 | VERSION = 15; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| @@ -29,18 +29,27 @@ · 8 unmodified lines | |||
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | db(fn) { | 31 | db(fn) { |
| 32 | if (!this.useIndexedDB) { return fn(); } | 32 | if (!this.useIndexedDB) { |
| 33 | if (fn) { this.callbacks.push(fn); } | 33 | return fn(); |
| 34 | if (this.open) { return; } | 34 | } |
| 35 | if (fn) { | ||
| 36 | this.callbacks.push(fn); | ||
| 37 | } | ||
| 38 | if (this.open) { | ||
| 39 | return; | ||
| 40 | } | ||
| 35 | 41 | ||
| 36 | try { | 42 | try { |
| 37 | this.open = true; | 43 | this.open = true; |
| 38 | const req = indexedDB.open(NAME, (VERSION * this.versionMultipler) + this.userVersion()); | 44 | const req = indexedDB.open( |
| 45 | NAME, | ||
| 46 | VERSION * this.versionMultipler + this.userVersion(), | ||
| 47 | ); | ||
| 39 | req.onsuccess = this.onOpenSuccess; | 48 | req.onsuccess = this.onOpenSuccess; |
| 40 | req.onerror = this.onOpenError; | 49 | req.onerror = this.onOpenError; |
| 41 | req.onupgradeneeded = this.onUpgradeNeeded; | 50 | req.onupgradeneeded = this.onUpgradeNeeded; |
| 42 | } catch (error) { | 51 | } catch (error) { |
| 43 | this.fail('exception', error); | 52 | this.fail("exception", error); |
| 44 | } | 53 | } |
| 45 | } | 54 | } |
| 46 | 55 | ||
| @@ -49,13 +58,17 @@ · 2 unmodified lines | |||
| 49 | const db = event.target.result; | 58 | const db = event.target.result; |
| 50 | 59 | ||
| 51 | if (db.objectStoreNames.length === 0) { | 60 | if (db.objectStoreNames.length === 0) { |
| 52 | try { db.close(); } catch (error1) {} | 61 | try { |
| 62 | db.close(); | ||
| 63 | } catch (error1) {} | ||
| 53 | this.open = false; | 64 | this.open = false; |
| 54 | this.fail('empty'); | 65 | this.fail("empty"); |
| 55 | } else if (error = this.buggyIDB(db)) { | 66 | } else if ((error = this.buggyIDB(db))) { |
| 56 | try { db.close(); } catch (error2) {} | 67 | try { |
| 68 | db.close(); | ||
| 69 | } catch (error2) {} | ||
| 57 | this.open = false; | 70 | this.open = false; |
| 58 | this.fail('buggy', error); | 71 | this.fail("buggy", error); |
| 59 | } else { | 72 | } else { |
| 60 | this.runCallbacks(db); | 73 | this.runCallbacks(db); |
| 61 | this.open = false; | 74 | this.open = false; |
| @@ -66,36 +79,43 @@ · 4 unmodified lines | |||
| 66 | onOpenError(event) { | 79 | onOpenError(event) { |
| 67 | event.preventDefault(); | 80 | event.preventDefault(); |
| 68 | this.open = false; | 81 | this.open = false; |
| 69 | const { | 82 | const { error } = event.target; |
| 70 | error | ||
| 71 | } = event.target; | ||
| 72 | 83 | ||
| 73 | switch (error.name) { | 84 | switch (error.name) { |
| 74 | case 'QuotaExceededError': | 85 | case "QuotaExceededError": |
| 75 | this.onQuotaExceededError(); | 86 | this.onQuotaExceededError(); |
| 76 | break; | 87 | break; |
| 77 | case 'VersionError': | 88 | case "VersionError": |
| 78 | this.onVersionError(); | 89 | this.onVersionError(); |
| 79 | break; | 90 | break; |
| 80 | case 'InvalidStateError': | 91 | case "InvalidStateError": |
| 81 | this.fail('private_mode'); | 92 | this.fail("private_mode"); |
| 82 | break; | 93 | break; |
| 83 | default: | 94 | default: |
| 84 | this.fail('cant_open', error); | 95 | this.fail("cant_open", error); |
| 85 | } | 96 | } |
| 86 | } | 97 | } |
| 87 | 98 | ||
| 88 | fail(reason, error) { | 99 | fail(reason, error) { |
| 89 | this.cachedDocs = null; | 100 | this.cachedDocs = null; |
| 90 | this.useIndexedDB = false; | 101 | this.useIndexedDB = false; |
| 91 | if (!this.reason) { this.reason = reason; } | 102 | if (!this.reason) { |
| 92 | if (!this.error) { this.error = error; } | 103 | this.reason = reason; |
| 93 | if (error) { if (typeof console.error === 'function') { | 104 | } |
| 94 | console.error('IDB error', error); | 105 | if (!this.error) { |
| 95 | } } | 106 | this.error = error; |
| 107 | } | ||
| 108 | if (error) { | ||
| 109 | if (typeof console.error === "function") { | ||
| 110 | console.error("IDB error", error); | ||
| 111 | } | ||
| 112 | } | ||
| 96 | this.runCallbacks(); | 113 | this.runCallbacks(); |
| 97 | if (error && (reason === 'cant_open')) { | 114 | if (error && reason === "cant_open") { |
| 98 | Raven.captureMessage(`${error.name}: ${error.message}`, {level: 'warning', fingerprint: [error.name]}); | 115 | Raven.captureMessage(`${error.name}: ${error.message}`, { |
| 116 | level: "warning", | ||
| 117 | fingerprint: [error.name], | ||
| 118 | }); | ||
| 99 | } | 119 | } |
| 100 | } | 120 | } |
| 101 | 121 | ||
| @@ -103,34 +123,39 @@ · 1 unmodified line | |||
| 103 | this.reset(); | 123 | this.reset(); |
| 104 | this.db(); | 124 | this.db(); |
| 105 | app.onQuotaExceeded(); | 125 | app.onQuotaExceeded(); |
| 106 | Raven.captureMessage('QuotaExceededError', {level: 'warning'}); | 126 | Raven.captureMessage("QuotaExceededError", { level: "warning" }); |
| 107 | } | 127 | } |
| 108 | 128 | ||
| 109 | onVersionError() { | 129 | onVersionError() { |
| 110 | const req = indexedDB.open(NAME); | 130 | const req = indexedDB.open(NAME); |
| 111 | req.onsuccess = event => { | 131 | req.onsuccess = (event) => { |
| 112 | return this.handleVersionMismatch(event.target.result.version); | 132 | return this.handleVersionMismatch(event.target.result.version); |
| 113 | }; | 133 | }; |
| 114 | req.onerror = function(event) { | 134 | req.onerror = function (event) { |
| 115 | event.preventDefault(); | 135 | event.preventDefault(); |
| 116 | return this.fail('cant_open', error); | 136 | return this.fail("cant_open", error); |
| 117 | }; | 137 | }; |
| 118 | } | 138 | } |
| 119 | 139 | ||
| 120 | handleVersionMismatch(actualVersion) { | 140 | handleVersionMismatch(actualVersion) { |
| 121 | if (Math.floor(actualVersion / this.versionMultipler) !== VERSION) { | 141 | if (Math.floor(actualVersion / this.versionMultipler) !== VERSION) { |
| 122 | this.fail('version'); | 142 | this.fail("version"); |
| 123 | } else { | 143 | } else { |
| 124 | this.setUserVersion(actualVersion - (VERSION * this.versionMultipler)); | 144 | this.setUserVersion(actualVersion - VERSION * this.versionMultipler); |
| 125 | this.db(); | 145 | this.db(); |
| 126 | } | 146 | } |
| 127 | } | 147 | } |
| 128 | 148 | ||
| 129 | buggyIDB(db) { | 149 | buggyIDB(db) { |
| 130 | if (this.checkedBuggyIDB) { return; } | 150 | if (this.checkedBuggyIDB) { |
| 151 | return; | ||
| 152 | } | ||
| 131 | this.checkedBuggyIDB = true; | 153 | this.checkedBuggyIDB = true; |
| 132 | try { | 154 | try { |
| 133 | this.idbTransaction(db, {stores: $.makeArray(db.objectStoreNames).slice(0, 2), mode: 'readwrite'}).abort(); // https://bugs.webkit.org/show_bug.cgi?id=136937 | 155 | this.idbTransaction(db, { |
| 156 | stores: $.makeArray(db.objectStoreNames).slice(0, 2), | ||
| 157 | mode: "readwrite", | ||
| 158 | }).abort(); // https://bugs.webkit.org/show_bug.cgi?id=136937 | ||
| 134 | return; | 159 | return; |
| 135 | } catch (error) { | 160 | } catch (error) { |
| 136 | return error; | 161 | return error; |
| @@ -139,53 +164,72 @@ · 2 unmodified lines | |||
| 139 | 164 | ||
| 140 | runCallbacks(db) { | 165 | runCallbacks(db) { |
| 141 | let fn; | 166 | let fn; |
| 142 | while ((fn = this.callbacks.shift())) { fn(db); } | 167 | while ((fn = this.callbacks.shift())) { |
| 168 | fn(db); | ||
| 169 | } | ||
| 143 | } | 170 | } |
| 144 | 171 | ||
| 145 | onUpgradeNeeded(event) { | 172 | onUpgradeNeeded(event) { |
| 146 | let db; | 173 | let db; |
| 147 | if (!(db = event.target.result)) { return; } | 174 | if (!(db = event.target.result)) { |
| 175 | return; | ||
| 176 | } | ||
| 148 | 177 | ||
| 149 | const objectStoreNames = $.makeArray(db.objectStoreNames); | 178 | const objectStoreNames = $.makeArray(db.objectStoreNames); |
| 150 | 179 | ||
| 151 | if (!$.arrayDelete(objectStoreNames, 'docs')) { | 180 | if (!$.arrayDelete(objectStoreNames, "docs")) { |
| 152 | try { db.createObjectStore('docs'); } catch (error) {} | 181 | try { |
| 182 | db.createObjectStore("docs"); | ||
| 183 | } catch (error) {} | ||
| 153 | } | 184 | } |
| 154 | 185 | ||
| 155 | for (var doc of Array.from(app.docs.all())) { | 186 | for (var doc of Array.from(app.docs.all())) { |
| 156 | if (!$.arrayDelete(objectStoreNames, doc.slug)) { | 187 | if (!$.arrayDelete(objectStoreNames, doc.slug)) { |
| 157 | try { db.createObjectStore(doc.slug); } catch (error1) {} | 188 | try { |
| 189 | db.createObjectStore(doc.slug); | ||
| 190 | } catch (error1) {} | ||
| 158 | } | 191 | } |
| 159 | } | 192 | } |
| 160 | 193 | ||
| 161 | for (var name of Array.from(objectStoreNames)) { | 194 | for (var name of Array.from(objectStoreNames)) { |
| 162 | try { db.deleteObjectStore(name); } catch (error2) {} | 195 | try { |
| 196 | db.deleteObjectStore(name); | ||
| 197 | } catch (error2) {} | ||
| 163 | } | 198 | } |
| 164 | } | 199 | } |
| 165 | 200 | ||
| 166 | store(doc, data, onSuccess, onError, _retry) { | 201 | store(doc, data, onSuccess, onError, _retry) { |
| 167 | if (_retry == null) { _retry = true; } | 202 | if (_retry == null) { |
| 168 | this.db(db => { | 203 | _retry = true; |
| 204 | } | ||
| 205 | this.db((db) => { | ||
| 169 | if (!db) { | 206 | if (!db) { |
| 170 | onError(); | 207 | onError(); |
| 171 | return; | 208 | return; |
| 172 | } | 209 | } |
| 173 | 210 | ||
| 174 | const txn = this.idbTransaction(db, {stores: ['docs', doc.slug], mode: 'readwrite', ignoreError: false}); | 211 | const txn = this.idbTransaction(db, { |
| 212 | stores: ["docs", doc.slug], | ||
| 213 | mode: "readwrite", | ||
| 214 | ignoreError: false, | ||
| 215 | }); | ||
| 175 | txn.oncomplete = () => { | 216 | txn.oncomplete = () => { |
| 176 | if (this.cachedDocs != null) { | 217 | if (this.cachedDocs != null) { |
| 177 | this.cachedDocs[doc.slug] = doc.mtime; | 218 | this.cachedDocs[doc.slug] = doc.mtime; |
| 178 | } | 219 | } |
| 179 | onSuccess(); | 220 | onSuccess(); |
| 180 | }; | 221 | }; |
| 181 | txn.onerror = event => { | 222 | txn.onerror = (event) => { |
| 182 | event.preventDefault(); | 223 | event.preventDefault(); |
| 183 | if (((txn.error != null ? txn.error.name : undefined) === 'NotFoundError') && _retry) { | 224 | if ( |
| 225 | (txn.error != null ? txn.error.name : undefined) === | ||
| 226 | "NotFoundError" && | ||
| 227 | _retry | ||
| 228 | ) { | ||
| 184 | this.migrate(); | 229 | this.migrate(); |
| 185 | setTimeout(() => { | 230 | setTimeout(() => { |
| 186 | return this.store(doc, data, onSuccess, onError, false); | 231 | return this.store(doc, data, onSuccess, onError, false); |
| 187 | } | 232 | }, 0); |
| 188 | , 0); | ||
| 189 | } else { | 233 | } else { |
| 190 | onError(event); | 234 | onError(event); |
| 191 | } | 235 | } |
| @@ -193,42 +237,54 @@ · 1 unmodified line | |||
| 193 | 237 | ||
| 194 | let store = txn.objectStore(doc.slug); | 238 | let store = txn.objectStore(doc.slug); |
| 195 | store.clear(); | 239 | store.clear(); |
| 196 | for (var path in data) { var content = data[path]; store.add(content, path); } | 240 | for (var path in data) { |
| 241 | var content = data[path]; | ||
| 242 | store.add(content, path); | ||
| 243 | } | ||
| 197 | 244 | ||
| 198 | store = txn.objectStore('docs'); | 245 | store = txn.objectStore("docs"); |
| 199 | store.put(doc.mtime, doc.slug); | 246 | store.put(doc.mtime, doc.slug); |
| 200 | }); | 247 | }); |
| 201 | } | 248 | } |
| 202 | 249 | ||
| 203 | unstore(doc, onSuccess, onError, _retry) { | 250 | unstore(doc, onSuccess, onError, _retry) { |
| 204 | if (_retry == null) { _retry = true; } | 251 | if (_retry == null) { |
| 205 | this.db(db => { | 252 | _retry = true; |
| 253 | } | ||
| 254 | this.db((db) => { | ||
| 206 | if (!db) { | 255 | if (!db) { |
| 207 | onError(); | 256 | onError(); |
| 208 | return; | 257 | return; |
| 209 | } | 258 | } |
| 210 | 259 | ||
| 211 | const txn = this.idbTransaction(db, {stores: ['docs', doc.slug], mode: 'readwrite', ignoreError: false}); | 260 | const txn = this.idbTransaction(db, { |
| 261 | stores: ["docs", doc.slug], | ||
| 262 | mode: "readwrite", | ||
| 263 | ignoreError: false, | ||
| 264 | }); | ||
| 212 | txn.oncomplete = () => { | 265 | txn.oncomplete = () => { |
| 213 | if (this.cachedDocs != null) { | 266 | if (this.cachedDocs != null) { |
| 214 | delete this.cachedDocs[doc.slug]; | 267 | delete this.cachedDocs[doc.slug]; |
| 215 | } | 268 | } |
| 216 | onSuccess(); | 269 | onSuccess(); |
| 217 | }; | 270 | }; |
| 218 | txn.onerror = function(event) { | 271 | txn.onerror = function (event) { |
| 219 | event.preventDefault(); | 272 | event.preventDefault(); |
| 220 | if (((txn.error != null ? txn.error.name : undefined) === 'NotFoundError') && _retry) { | 273 | if ( |
| 274 | (txn.error != null ? txn.error.name : undefined) === | ||
| 275 | "NotFoundError" && | ||
| 276 | _retry | ||
| 277 | ) { | ||
| 221 | this.migrate(); | 278 | this.migrate(); |
| 222 | setTimeout(() => { | 279 | setTimeout(() => { |
| 223 | return this.unstore(doc, onSuccess, onError, false); | 280 | return this.unstore(doc, onSuccess, onError, false); |
| 224 | } | 281 | }, 0); |
| 225 | , 0); | ||
| 226 | } else { | 282 | } else { |
| 227 | onError(event); | 283 | onError(event); |
| 228 | } | 284 | } |
| 229 | }; | 285 | }; |
| 230 | 286 | ||
| 231 | let store = txn.objectStore('docs'); | 287 | let store = txn.objectStore("docs"); |
| 232 | store.delete(doc.slug); | 288 | store.delete(doc.slug); |
| 233 | 289 | ||
| 234 | store = txn.objectStore(doc.slug); | 290 | store = txn.objectStore(doc.slug); |
| @@ -243,20 +299,23 @@ · 8 unmodified lines | |||
| 243 | return; | 299 | return; |
| 244 | } | 300 | } |
| 245 | 301 | ||
| 246 | this.db(db => { | 302 | this.db((db) => { |
| 247 | if (!db) { | 303 | if (!db) { |
| 248 | fn(false); | 304 | fn(false); |
| 249 | return; | 305 | return; |
| 250 | } | 306 | } |
| 251 | 307 | ||
| 252 | const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'}); | 308 | const txn = this.idbTransaction(db, { |
| 253 | const store = txn.objectStore('docs'); | 309 | stores: ["docs"], |
| 310 | mode: "readonly", | ||
| 311 | }); | ||
| 312 | const store = txn.objectStore("docs"); | ||
| 254 | 313 | ||
| 255 | const req = store.get(doc.slug); | 314 | const req = store.get(doc.slug); |
| 256 | req.onsuccess = function() { | 315 | req.onsuccess = function () { |
| 257 | fn(req.result); | 316 | fn(req.result); |
| 258 | }; | 317 | }; |
| 259 | req.onerror = function(event) { | 318 | req.onerror = function (event) { |
| 260 | event.preventDefault(); | 319 | event.preventDefault(); |
| 261 | fn(false); | 320 | fn(false); |
| 262 | }; | 321 | }; |
| @@ -264,36 +323,41 @@ · 1 unmodified line | |||
| 264 | } | 323 | } |
| 265 | 324 | ||
| 266 | cachedVersion(doc) { | 325 | cachedVersion(doc) { |
| 267 | if (!this.cachedDocs) { return; } | 326 | if (!this.cachedDocs) { |
| 327 | return; | ||
| 328 | } | ||
| 268 | return this.cachedDocs[doc.slug] || false; | 329 | return this.cachedDocs[doc.slug] || false; |
| 269 | } | 330 | } |
| 270 | 331 | ||
| 271 | versions(docs, fn) { | 332 | versions(docs, fn) { |
| 272 | let versions; | 333 | let versions; |
| 273 | if (versions = this.cachedVersions(docs)) { | 334 | if ((versions = this.cachedVersions(docs))) { |
| 274 | fn(versions); | 335 | fn(versions); |
| 275 | return; | 336 | return; |
| 276 | } | 337 | } |
| 277 | 338 | ||
| 278 | return this.db(db => { | 339 | return this.db((db) => { |
| 279 | if (!db) { | 340 | if (!db) { |
| 280 | fn(false); | 341 | fn(false); |
| 281 | return; | 342 | return; |
| 282 | } | 343 | } |
| 283 | 344 | ||
| 284 | const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'}); | 345 | const txn = this.idbTransaction(db, { |
| 285 | txn.oncomplete = function() { | 346 | stores: ["docs"], |
| 347 | mode: "readonly", | ||
| 348 | }); | ||
| 349 | txn.oncomplete = function () { | ||
| 286 | fn(result); | 350 | fn(result); |
| 287 | }; | 351 | }; |
| 288 | const store = txn.objectStore('docs'); | 352 | const store = txn.objectStore("docs"); |
| 289 | var result = {}; | 353 | var result = {}; |
| 290 | 354 | ||
| 291 | docs.forEach(function(doc) { | 355 | docs.forEach(function (doc) { |
| 292 | const req = store.get(doc.slug); | 356 | const req = store.get(doc.slug); |
| 293 | req.onsuccess = function() { | 357 | req.onsuccess = function () { |
| 294 | result[doc.slug] = req.result; | 358 | result[doc.slug] = req.result; |
| 295 | }; | 359 | }; |
| 296 | req.onerror = function(event) { | 360 | req.onerror = function (event) { |
| 297 | event.preventDefault(); | 361 | event.preventDefault(); |
| 298 | result[doc.slug] = false; | 362 | result[doc.slug] = false; |
| 299 | }; | 363 | }; |
| @@ -302,9 +366,13 @@ · 2 unmodified lines | |||
| 302 | } | 366 | } |
| 303 | 367 | ||
| 304 | cachedVersions(docs) { | 368 | cachedVersions(docs) { |
| 305 | if (!this.cachedDocs) { return; } | 369 | if (!this.cachedDocs) { |
| 370 | return; | ||
| 371 | } | ||
| 306 | const result = {}; | 372 | const result = {}; |
| 307 | for (var doc of Array.from(docs)) { result[doc.slug] = this.cachedVersion(doc); } | 373 | for (var doc of Array.from(docs)) { |
| 374 | result[doc.slug] = this.cachedVersion(doc); | ||
| 375 | } | ||
| 308 | return result; | 376 | return result; |
| 309 | } | 377 | } |
| 310 | 378 | ||
| @@ -320,14 +388,14 @@ · 9 unmodified lines | |||
| 320 | loadWithXHR(entry, onSuccess, onError) { | 388 | loadWithXHR(entry, onSuccess, onError) { |
| 321 | return ajax({ | 389 | return ajax({ |
| 322 | url: entry.fileUrl(), | 390 | url: entry.fileUrl(), |
| 323 | dataType: 'html', | 391 | dataType: "html", |
| 324 | success: onSuccess, | 392 | success: onSuccess, |
| 325 | error: onError | 393 | error: onError, |
| 326 | }); | 394 | }); |
| 327 | } | 395 | } |
| 328 | 396 | ||
| 329 | loadWithIDB(entry, onSuccess, onError) { | 397 | loadWithIDB(entry, onSuccess, onError) { |
| 330 | return this.db(db => { | 398 | return this.db((db) => { |
| 331 | if (!db) { | 399 | if (!db) { |
| 332 | onError(); | 400 | onError(); |
| 333 | return; | 401 | return; |
| @@ -339,14 +407,21 @@ · 5 unmodified lines | |||
| 339 | return; | 407 | return; |
| 340 | } | 408 | } |
| 341 | 409 | ||
| 342 | const txn = this.idbTransaction(db, {stores: [entry.doc.slug], mode: 'readonly'}); | 410 | const txn = this.idbTransaction(db, { |
| 411 | stores: [entry.doc.slug], | ||
| 412 | mode: "readonly", | ||
| 413 | }); | ||
| 343 | const store = txn.objectStore(entry.doc.slug); | 414 | const store = txn.objectStore(entry.doc.slug); |
| 344 | 415 | ||
| 345 | const req = store.get(entry.dbPath()); | 416 | const req = store.get(entry.dbPath()); |
| 346 | req.onsuccess = function() { | 417 | req.onsuccess = function () { |
| 347 | if (req.result) { onSuccess(req.result); } else { onError(); } | 418 | if (req.result) { |
| 419 | onSuccess(req.result); | ||
| 420 | } else { | ||
| 421 | onError(); | ||
| 422 | } | ||
| 348 | }; | 423 | }; |
| 349 | req.onerror = function(event) { | 424 | req.onerror = function (event) { |
| 350 | event.preventDefault(); | 425 | event.preventDefault(); |
| 351 | onError(); | 426 | onError(); |
| 352 | }; | 427 | }; |
| @@ -355,31 +430,38 @@ · 2 unmodified lines | |||
| 355 | } | 430 | } |
| 356 | 431 | ||
| 357 | loadDocsCache(db) { | 432 | loadDocsCache(db) { |
| 358 | if (this.cachedDocs) { return; } | 433 | if (this.cachedDocs) { |
| 434 | return; | ||
| 435 | } | ||
| 359 | this.cachedDocs = {}; | 436 | this.cachedDocs = {}; |
| 360 | 437 | ||
| 361 | const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'}); | 438 | const txn = this.idbTransaction(db, { |
| 439 | stores: ["docs"], | ||
| 440 | mode: "readonly", | ||
| 441 | }); | ||
| 362 | txn.oncomplete = () => { | 442 | txn.oncomplete = () => { |
| 363 | setTimeout(this.checkForCorruptedDocs, 50); | 443 | setTimeout(this.checkForCorruptedDocs, 50); |
| 364 | }; | 444 | }; |
| 365 | 445 | ||
| 366 | const req = txn.objectStore('docs').openCursor(); | 446 | const req = txn.objectStore("docs").openCursor(); |
| 367 | req.onsuccess = event => { | 447 | req.onsuccess = (event) => { |
| 368 | let cursor; | 448 | let cursor; |
| 369 | if (!(cursor = event.target.result)) { return; } | 449 | if (!(cursor = event.target.result)) { |
| 450 | return; | ||
| 451 | } | ||
| 370 | this.cachedDocs[cursor.key] = cursor.value; | 452 | this.cachedDocs[cursor.key] = cursor.value; |
| 371 | cursor.continue(); | 453 | cursor.continue(); |
| 372 | }; | 454 | }; |
| 373 | req.onerror = function(event) { | 455 | req.onerror = function (event) { |
| 374 | event.preventDefault(); | 456 | event.preventDefault(); |
| 375 | }; | 457 | }; |
| 376 | } | 458 | } |
| 377 | 459 | ||
| 378 | checkForCorruptedDocs() { | 460 | checkForCorruptedDocs() { |
| 379 | this.db(db => { | 461 | this.db((db) => { |
| 380 | let slug; | 462 | let slug; |
| 381 | this.corruptedDocs = []; | 463 | this.corruptedDocs = []; |
| 382 | const docs = ((() => { | 464 | const docs = (() => { |
| 383 | const result = []; | 465 | const result = []; |
| 384 | for (var key in this.cachedDocs) { | 466 | for (var key in this.cachedDocs) { |
| 385 | var value = this.cachedDocs[key]; | 467 | var value = this.cachedDocs[key]; |
| @@ -388,11 +470,13 @@ · 2 unmodified lines | |||
| 388 | } | 470 | } |
| 389 | } | 471 | } |
| 390 | return result; | 472 | return result; |
| 391 | })()); | 473 | })(); |
| 392 | if (docs.length === 0) { return; } | 474 | if (docs.length === 0) { |
| 475 | return; | ||
| 476 | } | ||
| 393 | 477 | ||
| 394 | for (slug of Array.from(docs)) { | 478 | for (slug of Array.from(docs)) { |
| 395 | if (!app.docs.findBy('slug', slug)) { | 479 | if (!app.docs.findBy("slug", slug)) { |
| 396 | this.corruptedDocs.push(slug); | 480 | this.corruptedDocs.push(slug); |
| 397 | } | 481 | } |
| 398 | } | 482 | } |
| @@ -406,46 +490,64 @@ · 7 unmodified lines | |||
| 406 | return; | 490 | return; |
| 407 | } | 491 | } |
| 408 | 492 | ||
| 409 | const txn = this.idbTransaction(db, {stores: docs, mode: 'readonly', ignoreError: false}); | 493 | const txn = this.idbTransaction(db, { |
| 494 | stores: docs, | ||
| 495 | mode: "readonly", | ||
| 496 | ignoreError: false, | ||
| 497 | }); | ||
| 410 | txn.oncomplete = () => { | 498 | txn.oncomplete = () => { |
| 411 | if (this.corruptedDocs.length > 0) { setTimeout(this.deleteCorruptedDocs, 0); } | 499 | if (this.corruptedDocs.length > 0) { |
| 500 | setTimeout(this.deleteCorruptedDocs, 0); | ||
| 501 | } | ||
| 412 | }; | 502 | }; |
| 413 | 503 | ||
| 414 | for (var doc of Array.from(docs)) { | 504 | for (var doc of Array.from(docs)) { |
| 415 | txn.objectStore(doc).get('index').onsuccess = event => { | 505 | txn.objectStore(doc).get("index").onsuccess = (event) => { |
| 416 | if (!event.target.result) { this.corruptedDocs.push(event.target.source.name); } | 506 | if (!event.target.result) { |
| 507 | this.corruptedDocs.push(event.target.source.name); | ||
| 508 | } | ||
| 417 | }; | 509 | }; |
| 418 | } | 510 | } |
| 419 | }); | 511 | }); |
| 420 | } | 512 | } |
| 421 | 513 | ||
| 422 | deleteCorruptedDocs() { | 514 | deleteCorruptedDocs() { |
| 423 | this.db(db => { | 515 | this.db((db) => { |
| 424 | let doc; | 516 | let doc; |
| 425 | const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readwrite', ignoreError: false}); | 517 | const txn = this.idbTransaction(db, { |
| 426 | const store = txn.objectStore('docs'); | 518 | stores: ["docs"], |
| 519 | mode: "readwrite", | ||
| 520 | ignoreError: false, | ||
| 521 | }); | ||
| 522 | const store = txn.objectStore("docs"); | ||
| 427 | while ((doc = this.corruptedDocs.pop())) { | 523 | while ((doc = this.corruptedDocs.pop())) { |
| 428 | this.cachedDocs[doc] = false; | 524 | this.cachedDocs[doc] = false; |
| 429 | store.delete(doc); | 525 | store.delete(doc); |
| 430 | } | 526 | } |
| 431 | }); | 527 | }); |
| 432 | Raven.captureMessage('corruptedDocs', {level: 'info', extra: { docs: this.corruptedDocs.join(',') }}); | 528 | Raven.captureMessage("corruptedDocs", { |
| 529 | level: "info", | ||
| 530 | extra: { docs: this.corruptedDocs.join(",") }, | ||
| 531 | }); | ||
| 433 | } | 532 | } |
| 434 | 533 | ||
| 435 | shouldLoadWithIDB(entry) { | 534 | shouldLoadWithIDB(entry) { |
| 436 | return this.useIndexedDB && (!this.cachedDocs || this.cachedDocs[entry.doc.slug]); | 535 | return ( |
| 536 | this.useIndexedDB && | ||
| 537 | (!this.cachedDocs || this.cachedDocs[entry.doc.slug]) | ||
| 538 | ); | ||
| 437 | } | 539 | } |
| 438 | 540 | ||
| 439 | idbTransaction(db, options) { | 541 | idbTransaction(db, options) { |
| 440 | app.lastIDBTransaction = [options.stores, options.mode]; | 542 | app.lastIDBTransaction = [options.stores, options.mode]; |
| 441 | const txn = db.transaction(options.stores, options.mode); | 543 | const txn = db.transaction(options.stores, options.mode); |
| 442 | if (options.ignoreError !== false) { | 544 | if (options.ignoreError !== false) { |
| 443 | txn.onerror = function(event) { | 545 | txn.onerror = function (event) { |
| 444 | event.preventDefault(); | 546 | event.preventDefault(); |
| 445 | }; | 547 | }; |
| 446 | } | 548 | } |
| 447 | if (options.ignoreAbort !== false) { | 549 | if (options.ignoreAbort !== false) { |
| 448 | txn.onabort = function(event) { | 550 | txn.onabort = function (event) { |
| 449 | event.preventDefault(); | 551 | event.preventDefault(); |
| 450 | }; | 552 | }; |
| 451 | } | 553 | } |
| @@ -453,9 +555,11 @@ · 1 unmodified line | |||
| 453 | } | 555 | } |
| 454 | 556 | ||
| 455 | reset() { | 557 | reset() { |
| 456 | try { if (typeof indexedDB !== 'undefined' && indexedDB !== null) { | 558 | try { |
| 457 | indexedDB.deleteDatabase(NAME); | 559 | if (typeof indexedDB !== "undefined" && indexedDB !== null) { |
| 458 | } } catch (error) {} | 560 | indexedDB.deleteDatabase(NAME); |
| 561 | } | ||
| 562 | } catch (error) {} | ||
| 459 | } | 563 | } |
| 460 | 564 | ||
| 461 | useIndexedDB() { | 565 | useIndexedDB() { |
| @@ -463,7 +567,7 @@ · 1 unmodified line | |||
| 463 | if (!app.isSingleDoc() && window.indexedDB) { | 567 | if (!app.isSingleDoc() && window.indexedDB) { |
| 464 | return true; | 568 | return true; |
| 465 | } else { | 569 | } else { |
| 466 | this.reason = 'not_supported'; | 570 | this.reason = "not_supported"; |
| 467 | return false; | 571 | return false; |
| 468 | } | 572 | } |
| 469 | } catch (error) { | 573 | } catch (error) { |
| @@ -472,15 +576,15 @@ · 2 unmodified lines | |||
| 472 | } | 576 | } |
| 473 | 577 | ||
| 474 | migrate() { | 578 | migrate() { |
| 475 | app.settings.set('schema', this.userVersion() + 1); | 579 | app.settings.set("schema", this.userVersion() + 1); |
| 476 | } | 580 | } |
| 477 | 581 | ||
| 478 | setUserVersion(version) { | 582 | setUserVersion(version) { |
| 479 | app.settings.set('schema', version); | 583 | app.settings.set("schema", version); |
| 480 | } | 584 | } |
| 481 | 585 | ||
| 482 | userVersion() { | 586 | userVersion() { |
| 483 | return app.settings.get('schema'); | 587 | return app.settings.get("schema"); |
| 484 | } | 588 | } |
| 485 | }); | 589 | }); |
| 486 | Cls.initClass(); | 590 | Cls.initClass(); |
| @@ -12,19 +12,19 @@ · 11 unmodified lines | |||
| 12 | const Cls = (app.Router = class Router { | 12 | const Cls = (app.Router = class Router { |
| 13 | static initClass() { | 13 | static initClass() { |
| 14 | $.extend(this.prototype, Events); | 14 | $.extend(this.prototype, Events); |
| 15 | 15 | ||
| 16 | this.routes = [ | 16 | this.routes = [ |
| 17 | ['*', 'before' ], | 17 | ["*", "before"], |
| 18 | ['/', 'root' ], | 18 | ["/", "root"], |
| 19 | ['/settings', 'settings' ], | 19 | ["/settings", "settings"], |
| 20 | ['/offline', 'offline' ], | 20 | ["/offline", "offline"], |
| 21 | ['/about', 'about' ], | 21 | ["/about", "about"], |
| 22 | ['/news', 'news' ], | 22 | ["/news", "news"], |
| 23 | ['/help', 'help' ], | 23 | ["/help", "help"], |
| 24 | ['/:doc-:type/', 'type' ], | 24 | ["/:doc-:type/", "type"], |
| 25 | ['/:doc/', 'doc' ], | 25 | ["/:doc/", "doc"], |
| 26 | ['/:doc/:path(*)', 'entry' ], | 26 | ["/:doc/:path(*)", "entry"], |
| 27 | ['*', 'notFound' ] | 27 | ["*", "notFound"], |
| 28 | ]; | 28 | ]; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| @@ -45,16 +45,16 @@ const Cls = (app.Router = class Router { · 14 unmodified lines | |||
| 45 | 45 | ||
| 46 | triggerRoute(name) { | 46 | triggerRoute(name) { |
| 47 | this.trigger(name, this.context); | 47 | this.trigger(name, this.context); |
| 48 | this.trigger('after', name, this.context); | 48 | this.trigger("after", name, this.context); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | before(context, next) { | 51 | before(context, next) { |
| 52 | let res; | 52 | let res; |
| 53 | const previousContext = this.context; | 53 | const previousContext = this.context; |
| 54 | this.context = context; | 54 | this.context = context; |
| 55 | this.trigger('before', context); | 55 | this.trigger("before", context); |
| 56 | 56 | ||
| 57 | if (res = next()) { | 57 | if ((res = next())) { |
| 58 | this.context = previousContext; | 58 | this.context = previousContext; |
| 59 | return res; | 59 | return res; |
| 60 | } else { | 60 | } else { |
| @@ -64,10 +64,14 @@ const Cls = (app.Router = class Router { · 3 unmodified lines | |||
| 64 | 64 | ||
| 65 | doc(context, next) { | 65 | doc(context, next) { |
| 66 | let doc; | 66 | let doc; |
| 67 | if (doc = app.docs.findBySlug(context.params.doc) || app.disabledDocs.findBySlug(context.params.doc)) { | 67 | if ( |
| 68 | (doc = | ||
| 69 | app.docs.findBySlug(context.params.doc) || | ||
| 70 | app.disabledDocs.findBySlug(context.params.doc)) | ||
| 71 | ) { | ||
| 68 | context.doc = doc; | 72 | context.doc = doc; |
| 69 | context.entry = doc.toEntry(); | 73 | context.entry = doc.toEntry(); |
| 70 | this.triggerRoute('entry'); | 74 | this.triggerRoute("entry"); |
| 71 | return; | 75 | return; |
| 72 | } else { | 76 | } else { |
| 73 | return next(); | 77 | return next(); |
| @@ -78,10 +82,13 @@ const Cls = (app.Router = class Router { · 4 unmodified lines | |||
| 78 | let type; | 82 | let type; |
| 79 | const doc = app.docs.findBySlug(context.params.doc); | 83 | const doc = app.docs.findBySlug(context.params.doc); |
| 80 | 84 | ||
| 81 | if (type = doc != null ? doc.types.findBy('slug', context.params.type) : undefined) { | 85 | if ( |
| 86 | (type = | ||
| 87 | doc != null ? doc.types.findBy("slug", context.params.type) : undefined) | ||
| 88 | ) { | ||
| 82 | context.doc = doc; | 89 | context.doc = doc; |
| 83 | context.type = type; | 90 | context.type = type; |
| 84 | this.triggerRoute('type'); | 91 | this.triggerRoute("type"); |
| 85 | return; | 92 | return; |
| 86 | } else { | 93 | } else { |
| 87 | return next(); | 94 | return next(); |
| @@ -91,86 +98,110 @@ const Cls = (app.Router = class Router { · 3 unmodified lines | |||
| 91 | entry(context, next) { | 98 | entry(context, next) { |
| 92 | let entry; | 99 | let entry; |
| 93 | const doc = app.docs.findBySlug(context.params.doc); | 100 | const doc = app.docs.findBySlug(context.params.doc); |
| 94 | if (!doc) { return next(); } | 101 | if (!doc) { |
| 95 | let { | 102 | return next(); |
| 96 | path | 103 | } |
| 97 | } = context.params; | 104 | let { path } = context.params; |
| 98 | const { | 105 | const { hash } = context; |
| 99 | hash | 106 | |
| 100 | } = context; | 107 | if ((entry = doc.findEntryByPathAndHash(path, hash))) { |
| 101 | |||
| 102 | if (entry = doc.findEntryByPathAndHash(path, hash)) { | ||
| 103 | context.doc = doc; | 108 | context.doc = doc; |
| 104 | context.entry = entry; | 109 | context.entry = entry; |
| 105 | this.triggerRoute('entry'); | 110 | this.triggerRoute("entry"); |
| 106 | return; | 111 | return; |
| 107 | } else if (path.slice(-6) === '/index') { | 112 | } else if (path.slice(-6) === "/index") { |
| 108 | path = path.substr(0, path.length - 6); | 113 | path = path.substr(0, path.length - 6); |
| 109 | if (entry = doc.findEntryByPathAndHash(path, hash)) { return entry.fullPath(); } | 114 | if ((entry = doc.findEntryByPathAndHash(path, hash))) { |
| 115 | return entry.fullPath(); | ||
| 116 | } | ||
| 110 | } else { | 117 | } else { |
| 111 | path = `${path}/index`; | 118 | path = `${path}/index`; |
| 112 | if (entry = doc.findEntryByPathAndHash(path, hash)) { return entry.fullPath(); } | 119 | if ((entry = doc.findEntryByPathAndHash(path, hash))) { |
| 120 | return entry.fullPath(); | ||
| 121 | } | ||
| 113 | } | 122 | } |
| 114 | 123 | ||
| 115 | return next(); | 124 | return next(); |
| 116 | } | 125 | } |
| 117 | 126 | ||
| 118 | root() { | 127 | root() { |
| 119 | if (app.isSingleDoc()) { return '/'; } | 128 | if (app.isSingleDoc()) { |
| 120 | this.triggerRoute('root'); | 129 | return "/"; |
| 130 | } | ||
| 131 | this.triggerRoute("root"); | ||
| 121 | } | 132 | } |
| 122 | 133 | ||
| 123 | settings(context) { | 134 | settings(context) { |
| 124 | if (app.isSingleDoc()) { return `/#/${context.path}`; } | 135 | if (app.isSingleDoc()) { |
| 125 | this.triggerRoute('settings'); | 136 | return `/#/${context.path}`; |
| 137 | } | ||
| 138 | this.triggerRoute("settings"); | ||
| 126 | } | 139 | } |
| 127 | 140 | ||
| 128 | offline(context){ | 141 | offline(context) { |
| 129 | if (app.isSingleDoc()) { return `/#/${context.path}`; } | 142 | if (app.isSingleDoc()) { |
| 130 | this.triggerRoute('offline'); | 143 | return `/#/${context.path}`; |
| 144 | } | ||
| 145 | this.triggerRoute("offline"); | ||
| 131 | } | 146 | } |
| 132 | 147 | ||
| 133 | about(context) { | 148 | about(context) { |
| 134 | if (app.isSingleDoc()) { return `/#/${context.path}`; } | 149 | if (app.isSingleDoc()) { |
| 135 | context.page = 'about'; | 150 | return `/#/${context.path}`; |
| 136 | this.triggerRoute('page'); | 151 | } |
| 152 | context.page = "about"; | ||
| 153 | this.triggerRoute("page"); | ||
| 137 | } | 154 | } |
| 138 | 155 | ||
| 139 | news(context) { | 156 | news(context) { |
| 140 | if (app.isSingleDoc()) { return `/#/${context.path}`; } | 157 | if (app.isSingleDoc()) { |
| 141 | context.page = 'news'; | 158 | return `/#/${context.path}`; |
| 142 | this.triggerRoute('page'); | 159 | } |
| 160 | context.page = "news"; | ||
| 161 | this.triggerRoute("page"); | ||
| 143 | } | 162 | } |
| 144 | 163 | ||
| 145 | help(context) { | 164 | help(context) { |
| 146 | if (app.isSingleDoc()) { return `/#/${context.path}`; } | 165 | if (app.isSingleDoc()) { |
| 147 | context.page = 'help'; | 166 | return `/#/${context.path}`; |
| 148 | this.triggerRoute('page'); | 167 | } |
| 168 | context.page = "help"; | ||
| 169 | this.triggerRoute("page"); | ||
| 149 | } | 170 | } |
| 150 | 171 | ||
| 151 | notFound(context) { | 172 | notFound(context) { |
| 152 | this.triggerRoute('notFound'); | 173 | this.triggerRoute("notFound"); |
| 153 | } | 174 | } |
| 154 | 175 | ||
| 155 | isIndex() { | 176 | isIndex() { |
| 156 | return ((this.context != null ? this.context.path : undefined) === '/') || (app.isSingleDoc() && __guard__(this.context != null ? this.context.entry : undefined, x => x.isIndex())); | 177 | return ( |
| 178 | (this.context != null ? this.context.path : undefined) === "/" || | ||
| 179 | (app.isSingleDoc() && | ||
| 180 | __guard__(this.context != null ? this.context.entry : undefined, (x) => | ||
| 181 | x.isIndex(), | ||
| 182 | )) | ||
| 183 | ); | ||
| 157 | } | 184 | } |
| 158 | 185 | ||
| 159 | isSettings() { | 186 | isSettings() { |
| 160 | return (this.context != null ? this.context.path : undefined) === '/settings'; | 187 | return ( |
| 188 | (this.context != null ? this.context.path : undefined) === "/settings" | ||
| 189 | ); | ||
| 161 | } | 190 | } |
| 162 | 191 | ||
| 163 | setInitialPath() { | 192 | setInitialPath() { |
| 164 | // Remove superfluous forward slashes at the beginning of the path | 193 | // Remove superfluous forward slashes at the beginning of the path |
| 165 | let path; | 194 | let path; |
| 166 | if ((path = location.pathname.replace(/^\/{2,}/g, '/')) !== location.pathname) { | 195 | if ( |
| 196 | (path = location.pathname.replace(/^\/{2,}/g, "/")) !== location.pathname | ||
| 197 | ) { | ||
| 167 | page.replace(path + location.search + location.hash, null, true); | 198 | page.replace(path + location.search + location.hash, null, true); |
| 168 | } | 199 | } |
| 169 | 200 | ||
| 170 | if (location.pathname === '/') { | 201 | if (location.pathname === "/") { |
| 171 | if (path = this.getInitialPathFromHash()) { | 202 | if ((path = this.getInitialPathFromHash())) { |
| 172 | page.replace(path + location.search, null, true); | 203 | page.replace(path + location.search, null, true); |
| 173 | } else if (path = this.getInitialPathFromCookie()) { | 204 | } else if ((path = this.getInitialPathFromCookie())) { |
| 174 | page.replace(path + location.search + location.hash, null, true); | 205 | page.replace(path + location.search + location.hash, null, true); |
| 175 | } | 206 | } |
| 176 | } | 207 | } |
| @@ -178,24 +209,33 @@ const Cls = (app.Router = class Router { · 1 unmodified line | |||
| 178 | 209 | ||
| 179 | getInitialPathFromHash() { | 210 | getInitialPathFromHash() { |
| 180 | try { | 211 | try { |
| 181 | return __guard__((new RegExp("#/(.+)")).exec(decodeURIComponent(location.hash)), x => x[1]); | 212 | return __guard__( |
| 213 | new RegExp("#/(.+)").exec(decodeURIComponent(location.hash)), | ||
| 214 | (x) => x[1], | ||
| 215 | ); | ||
| 182 | } catch (error) {} | 216 | } catch (error) {} |
| 183 | } | 217 | } |
| 184 | 218 | ||
| 185 | getInitialPathFromCookie() { | 219 | getInitialPathFromCookie() { |
| 186 | let path; | 220 | let path; |
| 187 | if (path = Cookies.get('initial_path')) { | 221 | if ((path = Cookies.get("initial_path"))) { |
| 188 | Cookies.expire('initial_path'); | 222 | Cookies.expire("initial_path"); |
| 189 | return path; | 223 | return path; |
| 190 | } | 224 | } |
| 191 | } | 225 | } |
| 192 | 226 | ||
| 193 | replaceHash(hash) { | 227 | replaceHash(hash) { |
| 194 | page.replace(location.pathname + location.search + (hash || ''), null, true); | 228 | page.replace( |
| 229 | location.pathname + location.search + (hash || ""), | ||
| 230 | null, | ||
| 231 | true, | ||
| 232 | ); | ||
| 195 | } | 233 | } |
| 196 | }); | 234 | }); |
| 197 | Cls.initClass(); | 235 | Cls.initClass(); |
| 198 | 236 | ||
| 199 | function __guard__(value, transform) { | 237 | function __guard__(value, transform) { |
| 200 | return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; | 238 | return typeof value !== "undefined" && value !== null |
| 201 | } | 239 | ? transform(value) |
| 240 | : undefined; | ||
| 241 | } | ||
| @@ -16,116 +16,150 @@ · 15 unmodified lines | |||
| 16 | // Match functions | 16 | // Match functions |
| 17 | // | 17 | // |
| 18 | 18 | ||
| 19 | let fuzzyRegexp, i, index, lastIndex, match, matcher, matchIndex, matchLength, queryLength, score, separators, value, valueLength; | 19 | let fuzzyRegexp, |
| 20 | const SEPARATOR = '.'; | 20 | i, |
| 21 | index, | ||
| 22 | lastIndex, | ||
| 23 | match, | ||
| 24 | matcher, | ||
| 25 | matchIndex, | ||
| 26 | matchLength, | ||
| 27 | queryLength, | ||
| 28 | score, | ||
| 29 | separators, | ||
| 30 | value, | ||
| 31 | valueLength; | ||
| 32 | const SEPARATOR = "."; | ||
| 21 | 33 | ||
| 22 | let query = | 34 | let query = |
| 23 | (queryLength = | 35 | (queryLength = |
| 24 | (value = | 36 | value = |
| 25 | (valueLength = | 37 | valueLength = |
| 26 | (matcher = // current match function | 38 | matcher = // current match function |
| 27 | (fuzzyRegexp = // query fuzzy regexp | 39 | fuzzyRegexp = // query fuzzy regexp |
| 28 | (index = // position of the query in the string being matched | 40 | index = // position of the query in the string being matched |
| 29 | (lastIndex = // last position of the query in the string being matched | 41 | lastIndex = // last position of the query in the string being matched |
| 30 | (match = // regexp match data | 42 | match = // regexp match data |
| 31 | (matchIndex = | 43 | matchIndex = |
| 32 | (matchLength = | 44 | matchLength = |
| 33 | (score = // score for the current match | 45 | score = // score for the current match |
| 34 | (separators = // counter | 46 | separators = // counter |
| 35 | (i = null))))))))))))); // cursor | 47 | i = |
| 48 | null); // cursor | ||
| 36 | 49 | ||
| 37 | function exactMatch() { | 50 | function exactMatch() { |
| 38 | index = value.indexOf(query); | 51 | index = value.indexOf(query); |
| 39 | if (!(index >= 0)) { return; } | 52 | if (!(index >= 0)) { |
| 53 | return; | ||
| 54 | } | ||
| 40 | 55 | ||
| 41 | lastIndex = value.lastIndexOf(query); | 56 | lastIndex = value.lastIndexOf(query); |
| 42 | 57 | ||
| 43 | if (index !== lastIndex) { | 58 | if (index !== lastIndex) { |
| 44 | return Math.max(scoreExactMatch(), ((index = lastIndex) && scoreExactMatch()) || 0); | 59 | return Math.max( |
| 45 | } else { | 60 | scoreExactMatch(), |
| 46 | return scoreExactMatch(); | 61 | ((index = lastIndex) && scoreExactMatch()) || 0, |
| 47 | } | 62 | ); |
| 63 | } else { | ||
| 64 | return scoreExactMatch(); | ||
| 65 | } | ||
| 48 | } | 66 | } |
| 49 | 67 | ||
| 50 | function scoreExactMatch() { | 68 | function scoreExactMatch() { |
| 51 | // Remove one point for each unmatched character. | 69 | // Remove one point for each unmatched character. |
| 52 | score = 100 - (valueLength - queryLength); | 70 | score = 100 - (valueLength - queryLength); |
| 53 | 71 | ||
| 54 | if (index > 0) { | 72 | if (index > 0) { |
| 55 | // If the character preceding the query is a dot, assign the same score | 73 | // If the character preceding the query is a dot, assign the same score |
| 56 | // as if the query was found at the beginning of the string, minus one. | 74 | // as if the query was found at the beginning of the string, minus one. |
| 57 | if (value.charAt(index - 1) === SEPARATOR) { | 75 | if (value.charAt(index - 1) === SEPARATOR) { |
| 58 | score += index - 1; | 76 | score += index - 1; |
| 59 | // Don't match a single-character query unless it's found at the beginning | 77 | // Don't match a single-character query unless it's found at the beginning |
| 60 | // of the string or is preceded by a dot. | 78 | // of the string or is preceded by a dot. |
| 61 | } else if (queryLength === 1) { | 79 | } else if (queryLength === 1) { |
| 62 | return; | 80 | return; |
| 63 | // (1) Remove one point for each unmatched character up to the nearest | 81 | // (1) Remove one point for each unmatched character up to the nearest |
| 64 | // preceding dot or the beginning of the string. | 82 | // preceding dot or the beginning of the string. |
| 65 | // (2) Remove one point for each unmatched character following the query. | 83 | // (2) Remove one point for each unmatched character following the query. |
| 66 | } else { | 84 | } else { |
| 85 | i = index - 2; | ||
| 86 | while (i >= 0 && value.charAt(i) !== SEPARATOR) { | ||
| 87 | i--; | ||
| 88 | } | ||
| 89 | score -= | ||
| 90 | index - | ||
| 91 | i + // (1) | ||
| 92 | (valueLength - queryLength - index); // (2) | ||
| 93 | } | ||
| 94 | |||
| 95 | // Remove one point for each dot preceding the query, except for the one | ||
| 96 | // immediately before the query. | ||
| 97 | separators = 0; | ||
| 67 | i = index - 2; | 98 | i = index - 2; |
| 68 | while ((i >= 0) && (value.charAt(i) !== SEPARATOR)) { i--; } | 99 | while (i >= 0) { |
| 69 | score -= (index - i) + // (1) | 100 | if (value.charAt(i) === SEPARATOR) { |
| 70 | (valueLength - queryLength - index); // (2) | 101 | separators++; |
| 102 | } | ||
| 103 | i--; | ||
| 104 | } | ||
| 105 | score -= separators; | ||
| 71 | } | 106 | } |
| 72 | 107 | ||
| 73 | // Remove one point for each dot preceding the query, except for the one | 108 | // Remove five points for each dot following the query. |
| 74 | // immediately before the query. | ||
| 75 | separators = 0; | 109 | separators = 0; |
| 76 | i = index - 2; | 110 | i = valueLength - queryLength - index - 1; |
| 77 | while (i >= 0) { | 111 | while (i >= 0) { |
| 78 | if (value.charAt(i) === SEPARATOR) { separators++; } | 112 | if (value.charAt(index + queryLength + i) === SEPARATOR) { |
| 113 | separators++; | ||
| 114 | } | ||
| 79 | i--; | 115 | i--; |
| 80 | } | 116 | } |
| 81 | score -= separators; | 117 | score -= separators * 5; |
| 82 | } | ||
| 83 | |||
| 84 | // Remove five points for each dot following the query. | ||
| 85 | separators = 0; | ||
| 86 | i = valueLength - queryLength - index - 1; | ||
| 87 | while (i >= 0) { | ||
| 88 | if (value.charAt(index + queryLength + i) === SEPARATOR) { separators++; } | ||
| 89 | i--; | ||
| 90 | } | ||
| 91 | score -= separators * 5; | ||
| 92 | 118 | ||
| 93 | return Math.max(1, score); | 119 | return Math.max(1, score); |
| 94 | } | 120 | } |
| 95 | 121 | ||
| 96 | function fuzzyMatch() { | 122 | function fuzzyMatch() { |
| 97 | if ((valueLength <= queryLength) || (value.indexOf(query) >= 0)) { return; } | 123 | if (valueLength <= queryLength || value.indexOf(query) >= 0) { |
| 98 | if (!(match = fuzzyRegexp.exec(value))) { return; } | 124 | return; |
| 99 | matchIndex = match.index; | 125 | } |
| 100 | matchLength = match[0].length; | 126 | if (!(match = fuzzyRegexp.exec(value))) { |
| 101 | score = scoreFuzzyMatch(); | 127 | return; |
| 102 | if (match = fuzzyRegexp.exec(value.slice(i = value.lastIndexOf(SEPARATOR) + 1))) { | 128 | } |
| 103 | matchIndex = i + match.index; | 129 | matchIndex = match.index; |
| 104 | matchLength = match[0].length; | 130 | matchLength = match[0].length; |
| 105 | return Math.max(score, scoreFuzzyMatch()); | 131 | score = scoreFuzzyMatch(); |
| 106 | } else { | 132 | if ( |
| 107 | return score; | 133 | (match = fuzzyRegexp.exec( |
| 108 | } | 134 | value.slice((i = value.lastIndexOf(SEPARATOR) + 1)), |
| 135 | )) | ||
| 136 | ) { | ||
| 137 | matchIndex = i + match.index; | ||
| 138 | matchLength = match[0].length; | ||
| 139 | return Math.max(score, scoreFuzzyMatch()); | ||
| 140 | } else { | ||
| 141 | return score; | ||
| 142 | } | ||
| 109 | } | 143 | } |
| 110 | 144 | ||
| 111 | function scoreFuzzyMatch() { | 145 | function scoreFuzzyMatch() { |
| 112 | // When the match is at the beginning of the string or preceded by a dot. | 146 | // When the match is at the beginning of the string or preceded by a dot. |
| 113 | if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { | 147 | if (matchIndex === 0 || value.charAt(matchIndex - 1) === SEPARATOR) { |
| 114 | return Math.max(66, 100 - matchLength); | 148 | return Math.max(66, 100 - matchLength); |
| 115 | // When the match is at the end of the string. | 149 | // When the match is at the end of the string. |
| 116 | } else if ((matchIndex + matchLength) === valueLength) { | 150 | } else if (matchIndex + matchLength === valueLength) { |
| 117 | return Math.max(33, 67 - matchLength); | 151 | return Math.max(33, 67 - matchLength); |
| 118 | // When the match is in the middle of the string. | 152 | // When the match is in the middle of the string. |
| 119 | } else { | 153 | } else { |
| 120 | return Math.max(1, 34 - matchLength); | 154 | return Math.max(1, 34 - matchLength); |
| 121 | } | 155 | } |
| 122 | } | 156 | } |
| 123 | 157 | ||
| 124 | // | 158 | // |
| 125 | // Searchers | 159 | // Searchers |
| 126 | // | 160 | // |
| 127 | 161 | ||
| 128 | (function() { | 162 | (function () { |
| 129 | let CHUNK_SIZE = undefined; | 163 | let CHUNK_SIZE = undefined; |
| 130 | let DEFAULTS = undefined; | 164 | let DEFAULTS = undefined; |
| 131 | let SEPARATORS_REGEXP = undefined; | 165 | let SEPARATORS_REGEXP = undefined; |
| @@ -141,25 +175,26 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 9 unmodified lines | |||
| 141 | const Cls = (app.Searcher = class Searcher { | 175 | const Cls = (app.Searcher = class Searcher { |
| 142 | static initClass() { | 176 | static initClass() { |
| 143 | $.extend(this.prototype, Events); | 177 | $.extend(this.prototype, Events); |
| 144 | 178 | ||
| 145 | CHUNK_SIZE = 20000; | 179 | CHUNK_SIZE = 20000; |
| 146 | 180 | ||
| 147 | DEFAULTS = { | 181 | DEFAULTS = { |
| 148 | max_results: app.config.max_results, | 182 | max_results: app.config.max_results, |
| 149 | fuzzy_min_length: 3 | 183 | fuzzy_min_length: 3, |
| 150 | }; | 184 | }; |
| 151 | 185 | ||
| 152 | SEPARATORS_REGEXP = /#|::|:-|->|\$(?=\w)|\-(?=\w)|\:(?=\w)|\ [\/\-&]\ |:\ |\ /g; | 186 | SEPARATORS_REGEXP = |
| 187 | /#|::|:-|->|\$(?=\w)|\-(?=\w)|\:(?=\w)|\ [\/\-&]\ |:\ |\ /g; | ||
| 153 | EOS_SEPARATORS_REGEXP = /(\w)[\-:]$/; | 188 | EOS_SEPARATORS_REGEXP = /(\w)[\-:]$/; |
| 154 | INFO_PARANTHESES_REGEXP = /\ \(\w+?\)$/; | 189 | INFO_PARANTHESES_REGEXP = /\ \(\w+?\)$/; |
| 155 | EMPTY_PARANTHESES_REGEXP = /\(\)/; | 190 | EMPTY_PARANTHESES_REGEXP = /\(\)/; |
| 156 | EVENT_REGEXP = /\ event$/; | 191 | EVENT_REGEXP = /\ event$/; |
| 157 | DOT_REGEXP = /\.+/g; | 192 | DOT_REGEXP = /\.+/g; |
| 158 | WHITESPACE_REGEXP = /\s/g; | 193 | WHITESPACE_REGEXP = /\s/g; |
| 159 | 194 | ||
| 160 | EMPTY_STRING = ''; | 195 | EMPTY_STRING = ""; |
| 161 | ELLIPSIS = '...'; | 196 | ELLIPSIS = "..."; |
| 162 | STRING = 'string'; | 197 | STRING = "string"; |
| 163 | } | 198 | } |
| 164 | 199 | ||
| 165 | static normalizeString(string) { | 200 | static normalizeString(string) { |
| @@ -176,13 +211,15 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 10 unmodified lines | |||
| 176 | 211 | ||
| 177 | static normalizeQuery(string) { | 212 | static normalizeQuery(string) { |
| 178 | string = this.normalizeString(string); | 213 | string = this.normalizeString(string); |
| 179 | return string.replace(EOS_SEPARATORS_REGEXP, '$1.'); | 214 | return string.replace(EOS_SEPARATORS_REGEXP, "$1."); |
| 180 | } | 215 | } |
| 181 | 216 | ||
| 182 | constructor(options) { | 217 | constructor(options) { |
| 183 | this.match = this.match.bind(this); | 218 | this.match = this.match.bind(this); |
| 184 | this.matchChunks = this.matchChunks.bind(this); | 219 | this.matchChunks = this.matchChunks.bind(this); |
| 185 | if (options == null) { options = {}; } | 220 | if (options == null) { |
| 221 | options = {}; | ||
| 222 | } | ||
| 186 | this.options = $.extend({}, DEFAULTS, options); | 223 | this.options = $.extend({}, DEFAULTS, options); |
| 187 | } | 224 | } |
| 188 | 225 | ||
| @@ -194,11 +231,15 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 5 unmodified lines | |||
| 194 | this.query = q; | 231 | this.query = q; |
| 195 | this.setup(); | 232 | this.setup(); |
| 196 | 233 | ||
| 197 | if (this.isValid()) { this.match(); } else { this.end(); } | 234 | if (this.isValid()) { |
| 235 | this.match(); | ||
| 236 | } else { | ||
| 237 | this.end(); | ||
| 238 | } | ||
| 198 | } | 239 | } |
| 199 | 240 | ||
| 200 | setup() { | 241 | setup() { |
| 201 | query = (this.query = this.constructor.normalizeQuery(this.query)); | 242 | query = this.query = this.constructor.normalizeQuery(this.query); |
| 202 | queryLength = query.length; | 243 | queryLength = query.length; |
| 203 | this.dataLength = this.data.length; | 244 | this.dataLength = this.data.length; |
| 204 | this.matchers = [exactMatch]; | 245 | this.matchers = [exactMatch]; |
| @@ -216,12 +257,14 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 11 unmodified lines | |||
| 216 | } | 257 | } |
| 217 | 258 | ||
| 218 | isValid() { | 259 | isValid() { |
| 219 | return (queryLength > 0) && (query !== SEPARATOR); | 260 | return queryLength > 0 && query !== SEPARATOR; |
| 220 | } | 261 | } |
| 221 | 262 | ||
| 222 | end() { | 263 | end() { |
| 223 | if (!this.totalResults) { this.triggerResults([]); } | 264 | if (!this.totalResults) { |
| 224 | this.trigger('end'); | 265 | this.triggerResults([]); |
| 266 | } | ||
| 267 | this.trigger("end"); | ||
| 225 | this.free(); | 268 | this.free(); |
| 226 | } | 269 | } |
| 227 | 270 | ||
| @@ -233,8 +276,17 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 5 unmodified lines | |||
| 233 | } | 276 | } |
| 234 | 277 | ||
| 235 | free() { | 278 | free() { |
| 236 | this.data = (this.attr = (this.dataLength = (this.matchers = (this.matcher = (this.query = | 279 | this.data = |
| 237 | (this.totalResults = (this.scoreMap = (this.cursor = (this.timeout = null))))))))); | 280 | this.attr = |
| 281 | this.dataLength = | ||
| 282 | this.matchers = | ||
| 283 | this.matcher = | ||
| 284 | this.query = | ||
| 285 | this.totalResults = | ||
| 286 | this.scoreMap = | ||
| 287 | this.cursor = | ||
| 288 | this.timeout = | ||
| 289 | null; | ||
| 238 | } | 290 | } |
| 239 | 291 | ||
| 240 | match() { | 292 | match() { |
| @@ -254,7 +306,7 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 13 unmodified lines | |||
| 254 | matchChunks() { | 306 | matchChunks() { |
| 255 | this.matchChunk(); | 307 | this.matchChunk(); |
| 256 | 308 | ||
| 257 | if ((this.cursor === this.dataLength) || this.scoredEnough()) { | 309 | if (this.cursor === this.dataLength || this.scoredEnough()) { |
| 258 | this.delay(this.match); | 310 | this.delay(this.match); |
| 259 | this.sendResults(); | 311 | this.sendResults(); |
| 260 | } else { | 312 | } else { |
| @@ -263,28 +315,36 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 2 unmodified lines | |||
| 263 | } | 315 | } |
| 264 | 316 | ||
| 265 | matchChunk() { | 317 | matchChunk() { |
| 266 | ({ | 318 | ({ matcher } = this); |
| 267 | matcher | 319 | for ( |
| 268 | } = this); | 320 | let j = 0, end = this.chunkSize(), asc = 0 <= end; |
| 269 | for (let j = 0, end = this.chunkSize(), asc = 0 <= end; asc ? j < end : j > end; asc ? j++ : j--) { | 321 | asc ? j < end : j > end; |
| 322 | asc ? j++ : j-- | ||
| 323 | ) { | ||
| 270 | value = this.data[this.cursor][this.attr]; | 324 | value = this.data[this.cursor][this.attr]; |
| 271 | if (value.split) { // string | 325 | if (value.split) { |
| 326 | // string | ||
| 272 | valueLength = value.length; | 327 | valueLength = value.length; |
| 273 | if (score = matcher()) { this.addResult(this.data[this.cursor], score); } | 328 | if ((score = matcher())) { |
| 274 | } else { // array | 329 | this.addResult(this.data[this.cursor], score); |
| 330 | } | ||
| 331 | } else { | ||
| 332 | // array | ||
| 275 | score = 0; | 333 | score = 0; |
| 276 | for (value of Array.from(this.data[this.cursor][this.attr])) { | 334 | for (value of Array.from(this.data[this.cursor][this.attr])) { |
| 277 | valueLength = value.length; | 335 | valueLength = value.length; |
| 278 | score = Math.max(score, matcher() || 0); | 336 | score = Math.max(score, matcher() || 0); |
| 279 | } | 337 | } |
| 280 | if (score > 0) { this.addResult(this.data[this.cursor], score); } | 338 | if (score > 0) { |
| 339 | this.addResult(this.data[this.cursor], score); | ||
| 340 | } | ||
| 281 | } | 341 | } |
| 282 | this.cursor++; | 342 | this.cursor++; |
| 283 | } | 343 | } |
| 284 | } | 344 | } |
| 285 | 345 | ||
| 286 | chunkSize() { | 346 | chunkSize() { |
| 287 | if ((this.cursor + CHUNK_SIZE) > this.dataLength) { | 347 | if (this.cursor + CHUNK_SIZE > this.dataLength) { |
| 288 | return this.dataLength % CHUNK_SIZE; | 348 | return this.dataLength % CHUNK_SIZE; |
| 289 | } else { | 349 | } else { |
| 290 | return CHUNK_SIZE; | 350 | return CHUNK_SIZE; |
| @@ -292,8 +352,11 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 1 unmodified line | |||
| 292 | } | 352 | } |
| 293 | 353 | ||
| 294 | scoredEnough() { | 354 | scoredEnough() { |
| 295 | return (this.scoreMap[100] != null ? this.scoreMap[100].length : undefined) >= this.options.max_results; | 355 | return ( |
| 296 | } | 356 | (this.scoreMap[100] != null ? this.scoreMap[100].length : undefined) >= |
| 357 | this.options.max_results | ||
| 358 | ); | ||
| 359 | } | ||
| 297 | 360 | ||
| 298 | foundEnough() { | 361 | foundEnough() { |
| 299 | return this.totalResults >= this.options.max_results; | 362 | return this.totalResults >= this.options.max_results; |
| @@ -301,7 +364,9 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 1 unmodified line | |||
| 301 | 364 | ||
| 302 | addResult(object, score) { | 365 | addResult(object, score) { |
| 303 | let name; | 366 | let name; |
| 304 | (this.scoreMap[name = Math.round(score)] || (this.scoreMap[name] = [])).push(object); | 367 | ( |
| 368 | this.scoreMap[(name = Math.round(score))] || (this.scoreMap[name] = []) | ||
| 369 | ).push(object); | ||
| 305 | this.totalResults++; | 370 | this.totalResults++; |
| 306 | } | 371 | } |
| 307 | 372 | ||
| @@ -318,21 +383,26 @@ if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { · 10 unmodified lines | |||
| 318 | 383 | ||
| 319 | sendResults() { | 384 | sendResults() { |
| 320 | const results = this.getResults(); | 385 | const results = this.getResults(); |
| 321 | if (results.length) { this.triggerResults(results); } | 386 | if (results.length) { |
| 387 | this.triggerResults(results); | ||
| 388 | } | ||
| 322 | } | 389 | } |
| 323 | 390 | ||
| 324 | triggerResults(results) { | 391 | triggerResults(results) { |
| 325 | this.trigger('results', results); | 392 | this.trigger("results", results); |
| 326 | } | 393 | } |
| 327 | 394 | ||
| 328 | delay(fn) { | 395 | delay(fn) { |
| 329 | return this.timeout = setTimeout(fn, 1); | 396 | return (this.timeout = setTimeout(fn, 1)); |
| 330 | } | 397 | } |
| 331 | 398 | ||
| 332 | queryToFuzzyRegexp(string) { | 399 | queryToFuzzyRegexp(string) { |
| 333 | const chars = string.split(''); | 400 | const chars = string.split(""); |
| 334 | for (i = 0; i < chars.length; i++) { var char = chars[i]; chars[i] = $.escapeRegexp(char); } | 401 | for (i = 0; i < chars.length; i++) { |
| 335 | return new RegExp(chars.join('.*?')); | 402 | var char = chars[i]; |
| 403 | chars[i] = $.escapeRegexp(char); | ||
| 404 | } | ||
| 405 | return new RegExp(chars.join(".*?")); | ||
| 336 | } | 406 | } |
| 337 | }); | 407 | }); |
| 338 | Cls.initClass(); | 408 | Cls.initClass(); |
| @@ -347,7 +417,9 @@ app.SynchronousSearcher = class SynchronousSearcher extends app.Searcher { · 8 unmodified lines | |||
| 347 | 417 | ||
| 348 | match() { | 418 | match() { |
| 349 | if (this.matcher) { | 419 | if (this.matcher) { |
| 350 | if (!this.allResults) { this.allResults = []; } | 420 | if (!this.allResults) { |
| 421 | this.allResults = []; | ||
| 422 | } | ||
| 351 | this.allResults.push.apply(this.allResults, this.getResults()); | 423 | this.allResults.push.apply(this.allResults, this.getResults()); |
| 352 | } | 424 | } |
| 353 | return super.match(...arguments); | 425 | return super.match(...arguments); |
| @@ -21,22 +21,28 @@ const Cls = (app.ServiceWorker = class ServiceWorker { · 20 unmodified lines | |||
| 21 | this.registration = null; | 21 | this.registration = null; |
| 22 | this.notifyUpdate = true; | 22 | this.notifyUpdate = true; |
| 23 | 23 | ||
| 24 | navigator.serviceWorker.register(app.config.service_worker_path, {scope: '/'}) | 24 | navigator.serviceWorker |
| 25 | .register(app.config.service_worker_path, { scope: "/" }) | ||
| 25 | .then( | 26 | .then( |
| 26 | registration => this.updateRegistration(registration), | 27 | (registration) => this.updateRegistration(registration), |
| 27 | error => console.error('Could not register service worker:', error)); | 28 | (error) => console.error("Could not register service worker:", error), |
| 29 | ); | ||
| 28 | } | 30 | } |
| 29 | 31 | ||
| 30 | update() { | 32 | update() { |
| 31 | if (!this.registration) { return; } | 33 | if (!this.registration) { |
| 34 | return; | ||
| 35 | } | ||
| 32 | this.notifyUpdate = true; | 36 | this.notifyUpdate = true; |
| 33 | return this.registration.update().catch(function() {}); | 37 | return this.registration.update().catch(function () {}); |
| 34 | } | 38 | } |
| 35 | 39 | ||
| 36 | updateInBackground() { | 40 | updateInBackground() { |
| 37 | if (!this.registration) { return; } | 41 | if (!this.registration) { |
| 42 | return; | ||
| 43 | } | ||
| 38 | this.notifyUpdate = false; | 44 | this.notifyUpdate = false; |
| 39 | return this.registration.update().catch(function() {}); | 45 | return this.registration.update().catch(function () {}); |
| 40 | } | 46 | } |
| 41 | 47 | ||
| 42 | reload() { | 48 | reload() { |
| @@ -45,24 +51,32 @@ const Cls = (app.ServiceWorker = class ServiceWorker { · 2 unmodified lines | |||
| 45 | 51 | ||
| 46 | updateRegistration(registration) { | 52 | updateRegistration(registration) { |
| 47 | this.registration = registration; | 53 | this.registration = registration; |
| 48 | $.on(this.registration, 'updatefound', this.onUpdateFound); | 54 | $.on(this.registration, "updatefound", this.onUpdateFound); |
| 49 | } | 55 | } |
| 50 | 56 | ||
| 51 | onUpdateFound() { | 57 | onUpdateFound() { |
| 52 | if (this.installingRegistration) { $.off(this.installingRegistration, 'statechange', this.onStateChange()); } | 58 | if (this.installingRegistration) { |
| 59 | $.off(this.installingRegistration, "statechange", this.onStateChange()); | ||
| 60 | } | ||
| 53 | this.installingRegistration = this.registration.installing; | 61 | this.installingRegistration = this.registration.installing; |
| 54 | $.on(this.installingRegistration, 'statechange', this.onStateChange); | 62 | $.on(this.installingRegistration, "statechange", this.onStateChange); |
| 55 | } | 63 | } |
| 56 | 64 | ||
| 57 | onStateChange() { | 65 | onStateChange() { |
| 58 | if (this.installingRegistration && (this.installingRegistration.state === 'installed') && navigator.serviceWorker.controller) { | 66 | if ( |
| 67 | this.installingRegistration && | ||
| 68 | this.installingRegistration.state === "installed" && | ||
| 69 | navigator.serviceWorker.controller | ||
| 70 | ) { | ||
| 59 | this.installingRegistration = null; | 71 | this.installingRegistration = null; |
| 60 | this.onUpdateReady(); | 72 | this.onUpdateReady(); |
| 61 | } | 73 | } |
| 62 | } | 74 | } |
| 63 | 75 | ||
| 64 | onUpdateReady() { | 76 | onUpdateReady() { |
| 65 | if (this.notifyUpdate) { this.trigger('updateready'); } | 77 | if (this.notifyUpdate) { |
| 78 | this.trigger("updateready"); | ||
| 79 | } | ||
| 66 | } | 80 | } |
| 67 | }); | 81 | }); |
| 68 | Cls.initClass(); | 82 | Cls.initClass(); |
| @@ -10,44 +10,39 @@ · 9 unmodified lines | |||
| 10 | * DS207: Consider shorter variations of null checks | 10 | * DS207: Consider shorter variations of null checks |
| 11 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 11 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 12 | */ | 12 | */ |
| 13 | (function() { | 13 | (function () { |
| 14 | let PREFERENCE_KEYS = undefined; | 14 | let PREFERENCE_KEYS = undefined; |
| 15 | let INTERNAL_KEYS = undefined; | 15 | let INTERNAL_KEYS = undefined; |
| 16 | const Cls = (app.Settings = class Settings { | 16 | const Cls = (app.Settings = class Settings { |
| 17 | static initClass() { | 17 | static initClass() { |
| 18 | PREFERENCE_KEYS = [ | 18 | PREFERENCE_KEYS = [ |
| 19 | 'hideDisabled', | 19 | "hideDisabled", |
| 20 | 'hideIntro', | 20 | "hideIntro", |
| 21 | 'manualUpdate', | 21 | "manualUpdate", |
| 22 | 'fastScroll', | 22 | "fastScroll", |
| 23 | 'arrowScroll', | 23 | "arrowScroll", |
| 24 | 'analyticsConsent', | 24 | "analyticsConsent", |
| 25 | 'docs', | 25 | "docs", |
| 26 | 'dark', // legacy | 26 | "dark", // legacy |
| 27 | 'theme', | 27 | "theme", |
| 28 | 'layout', | 28 | "layout", |
| 29 | 'size', | 29 | "size", |
| 30 | 'tips', | 30 | "tips", |
| 31 | 'noAutofocus', | 31 | "noAutofocus", |
| 32 | 'autoInstall', | 32 | "autoInstall", |
| 33 | 'spaceScroll', | 33 | "spaceScroll", |
| 34 | 'spaceTimeout' | 34 | "spaceTimeout", |
| 35 | ]; | 35 | ]; |
| 36 | 36 | ||
| 37 | INTERNAL_KEYS = [ | 37 | INTERNAL_KEYS = ["count", "schema", "version", "news"]; |
| 38 | 'count', | 38 | |
| 39 | 'schema', | ||
| 40 | 'version', | ||
| 41 | 'news' | ||
| 42 | ]; | ||
| 43 | |||
| 44 | this.prototype.LAYOUTS = [ | 39 | this.prototype.LAYOUTS = [ |
| 45 | '_max-width', | 40 | "_max-width", |
| 46 | '_sidebar-hidden', | 41 | "_sidebar-hidden", |
| 47 | '_native-scrollbars', | 42 | "_native-scrollbars", |
| 48 | '_text-justify-hyphenate' | 43 | "_text-justify-hyphenate", |
| 49 | ]; | 44 | ]; |
| 50 | 45 | ||
| 51 | this.defaults = { | 46 | this.defaults = { |
| 52 | count: 0, | 47 | count: 0, |
| 53 | hideDisabled: false, | 48 | hideDisabled: false, |
| @@ -56,29 +51,38 @@ · 2 unmodified lines | |||
| 56 | manualUpdate: false, | 51 | manualUpdate: false, |
| 57 | schema: 1, | 52 | schema: 1, |
| 58 | analyticsConsent: false, | 53 | analyticsConsent: false, |
| 59 | theme: 'auto', | 54 | theme: "auto", |
| 60 | spaceScroll: 1, | 55 | spaceScroll: 1, |
| 61 | spaceTimeout: 0.5 | 56 | spaceTimeout: 0.5, |
| 62 | }; | 57 | }; |
| 63 | } | 58 | } |
| 64 | 59 | ||
| 65 | constructor() { | 60 | constructor() { |
| 66 | this.store = new CookiesStore; | 61 | this.store = new CookiesStore(); |
| 67 | this.cache = {}; | 62 | this.cache = {}; |
| 68 | this.autoSupported = window.matchMedia('(prefers-color-scheme)').media !== 'not all'; | 63 | this.autoSupported = |
| 64 | window.matchMedia("(prefers-color-scheme)").media !== "not all"; | ||
| 69 | if (this.autoSupported) { | 65 | if (this.autoSupported) { |
| 70 | this.darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)'); | 66 | this.darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)"); |
| 71 | this.darkModeQuery.addListener(() => this.setTheme(this.get('theme'))); | 67 | this.darkModeQuery.addListener(() => this.setTheme(this.get("theme"))); |
| 72 | } | 68 | } |
| 73 | } | 69 | } |
| 74 | 70 | ||
| 75 | |||
| 76 | get(key) { | 71 | get(key) { |
| 77 | let left; | 72 | let left; |
| 78 | if (this.cache.hasOwnProperty(key)) { return this.cache[key]; } | 73 | if (this.cache.hasOwnProperty(key)) { |
| 79 | this.cache[key] = (left = this.store.get(key)) != null ? left : this.constructor.defaults[key]; | 74 | return this.cache[key]; |
| 80 | if ((key === 'theme') && (this.cache[key] === 'auto') && !this.darkModeQuery) { | 75 | } |
| 81 | return this.cache[key] = 'default'; | 76 | this.cache[key] = |
| 77 | (left = this.store.get(key)) != null | ||
| 78 | ? left | ||
| 79 | : this.constructor.defaults[key]; | ||
| 80 | if ( | ||
| 81 | key === "theme" && | ||
| 82 | this.cache[key] === "auto" && | ||
| 83 | !this.darkModeQuery | ||
| 84 | ) { | ||
| 85 | return (this.cache[key] = "default"); | ||
| 82 | } else { | 86 | } else { |
| 83 | return this.cache[key]; | 87 | return this.cache[key]; |
| 84 | } | 88 | } |
| @@ -87,7 +91,9 @@ · 2 unmodified lines | |||
| 87 | set(key, value) { | 91 | set(key, value) { |
| 88 | this.store.set(key, value); | 92 | this.store.set(key, value); |
| 89 | delete this.cache[key]; | 93 | delete this.cache[key]; |
| 90 | if (key === 'theme') { this.setTheme(value); } | 94 | if (key === "theme") { |
| 95 | this.setTheme(value); | ||
| 96 | } | ||
| 91 | } | 97 | } |
| 92 | 98 | ||
| 93 | del(key) { | 99 | del(key) { |
| @@ -96,51 +102,58 @@ · 2 unmodified lines | |||
| 96 | } | 102 | } |
| 97 | 103 | ||
| 98 | hasDocs() { | 104 | hasDocs() { |
| 99 | try { return !!this.store.get('docs'); } catch (error) {} | 105 | try { |
| 106 | return !!this.store.get("docs"); | ||
| 107 | } catch (error) {} | ||
| 100 | } | 108 | } |
| 101 | 109 | ||
| 102 | getDocs() { | 110 | getDocs() { |
| 103 | return __guard__(this.store.get('docs'), x => x.split('/')) || app.config.default_docs; | 111 | return ( |
| 112 | __guard__(this.store.get("docs"), (x) => x.split("/")) || | ||
| 113 | app.config.default_docs | ||
| 114 | ); | ||
| 104 | } | 115 | } |
| 105 | 116 | ||
| 106 | setDocs(docs) { | 117 | setDocs(docs) { |
| 107 | this.set('docs', docs.join('/')); | 118 | this.set("docs", docs.join("/")); |
| 108 | } | 119 | } |
| 109 | 120 | ||
| 110 | getTips() { | 121 | getTips() { |
| 111 | return __guard__(this.store.get('tips'), x => x.split('/')) || []; | 122 | return __guard__(this.store.get("tips"), (x) => x.split("/")) || []; |
| 112 | } | 123 | } |
| 113 | 124 | ||
| 114 | setTips(tips) { | 125 | setTips(tips) { |
| 115 | this.set('tips', tips.join('/')); | 126 | this.set("tips", tips.join("/")); |
| 116 | } | 127 | } |
| 117 | 128 | ||
| 118 | setLayout(name, enable) { | 129 | setLayout(name, enable) { |
| 119 | this.toggleLayout(name, enable); | 130 | this.toggleLayout(name, enable); |
| 120 | 131 | ||
| 121 | const layout = (this.store.get('layout') || '').split(' '); | 132 | const layout = (this.store.get("layout") || "").split(" "); |
| 122 | $.arrayDelete(layout, ''); | 133 | $.arrayDelete(layout, ""); |
| 123 | 134 | ||
| 124 | if (enable) { | 135 | if (enable) { |
| 125 | if (layout.indexOf(name) === -1) { layout.push(name); } | 136 | if (layout.indexOf(name) === -1) { |
| 137 | layout.push(name); | ||
| 138 | } | ||
| 126 | } else { | 139 | } else { |
| 127 | $.arrayDelete(layout, name); | 140 | $.arrayDelete(layout, name); |
| 128 | } | 141 | } |
| 129 | 142 | ||
| 130 | if (layout.length > 0) { | 143 | if (layout.length > 0) { |
| 131 | this.set('layout', layout.join(' ')); | 144 | this.set("layout", layout.join(" ")); |
| 132 | } else { | 145 | } else { |
| 133 | this.del('layout'); | 146 | this.del("layout"); |
| 134 | } | 147 | } |
| 135 | } | 148 | } |
| 136 | 149 | ||
| 137 | hasLayout(name) { | 150 | hasLayout(name) { |
| 138 | const layout = (this.store.get('layout') || '').split(' '); | 151 | const layout = (this.store.get("layout") || "").split(" "); |
| 139 | return layout.indexOf(name) !== -1; | 152 | return layout.indexOf(name) !== -1; |
| 140 | } | 153 | } |
| 141 | 154 | ||
| 142 | setSize(value) { | 155 | setSize(value) { |
| 143 | this.set('size', value); | 156 | this.set("size", value); |
| 144 | } | 157 | } |
| 145 | 158 | ||
| 146 | dump() { | 159 | dump() { |
| @@ -149,7 +162,9 @@ · 2 unmodified lines | |||
| 149 | 162 | ||
| 150 | export() { | 163 | export() { |
| 151 | const data = this.dump(); | 164 | const data = this.dump(); |
| 152 | for (var key of Array.from(INTERNAL_KEYS)) { delete data[key]; } | 165 | for (var key of Array.from(INTERNAL_KEYS)) { |
| 166 | delete data[key]; | ||
| 167 | } | ||
| 153 | return data; | 168 | return data; |
| 154 | } | 169 | } |
| 155 | 170 | ||
| @@ -158,11 +173,15 @@ · 2 unmodified lines | |||
| 158 | const object = this.export(); | 173 | const object = this.export(); |
| 159 | for (key in object) { | 174 | for (key in object) { |
| 160 | value = object[key]; | 175 | value = object[key]; |
| 161 | if (!data.hasOwnProperty(key)) { this.del(key); } | 176 | if (!data.hasOwnProperty(key)) { |
| 177 | this.del(key); | ||
| 178 | } | ||
| 162 | } | 179 | } |
| 163 | for (key in data) { | 180 | for (key in data) { |
| 164 | value = data[key]; | 181 | value = data[key]; |
| 165 | if (PREFERENCE_KEYS.indexOf(key) !== -1) { this.set(key, value); } | 182 | if (PREFERENCE_KEYS.indexOf(key) !== -1) { |
| 183 | this.set(key, value); | ||
| 184 | } | ||
| 166 | } | 185 | } |
| 167 | } | 186 | } |
| 168 | 187 | ||
| @@ -172,44 +191,54 @@ · 3 unmodified lines | |||
| 172 | } | 191 | } |
| 173 | 192 | ||
| 174 | initLayout() { | 193 | initLayout() { |
| 175 | if (this.get('dark') === 1) { | 194 | if (this.get("dark") === 1) { |
| 176 | this.set('theme', 'dark'); | 195 | this.set("theme", "dark"); |
| 177 | this.del('dark'); | 196 | this.del("dark"); |
| 197 | } | ||
| 198 | this.setTheme(this.get("theme")); | ||
| 199 | for (var layout of Array.from(this.LAYOUTS)) { | ||
| 200 | this.toggleLayout(layout, this.hasLayout(layout)); | ||
| 178 | } | 201 | } |
| 179 | this.setTheme(this.get('theme')); | ||
| 180 | for (var layout of Array.from(this.LAYOUTS)) { this.toggleLayout(layout, this.hasLayout(layout)); } | ||
| 181 | this.initSidebarWidth(); | 202 | this.initSidebarWidth(); |
| 182 | } | 203 | } |
| 183 | 204 | ||
| 184 | setTheme(theme) { | 205 | setTheme(theme) { |
| 185 | if (theme === 'auto') { | 206 | if (theme === "auto") { |
| 186 | theme = this.darkModeQuery.matches ? 'dark' : 'default'; | 207 | theme = this.darkModeQuery.matches ? "dark" : "default"; |
| 187 | } | 208 | } |
| 188 | const { | 209 | const { classList } = document.documentElement; |
| 189 | classList | 210 | classList.remove("_theme-default", "_theme-dark"); |
| 190 | } = document.documentElement; | 211 | classList.add("_theme-" + theme); |
| 191 | classList.remove('_theme-default', '_theme-dark'); | ||
| 192 | classList.add('_theme-' + theme); | ||
| 193 | this.updateColorMeta(); | 212 | this.updateColorMeta(); |
| 194 | } | 213 | } |
| 195 | 214 | ||
| 196 | updateColorMeta() { | 215 | updateColorMeta() { |
| 197 | const color = getComputedStyle(document.documentElement).getPropertyValue('--headerBackground').trim(); | 216 | const color = getComputedStyle(document.documentElement) |
| 198 | $('meta[name=theme-color]').setAttribute('content', color); | 217 | .getPropertyValue("--headerBackground") |
| 218 | .trim(); | ||
| 219 | $("meta[name=theme-color]").setAttribute("content", color); | ||
| 199 | } | 220 | } |
| 200 | 221 | ||
| 201 | toggleLayout(layout, enable) { | 222 | toggleLayout(layout, enable) { |
| 202 | const { | 223 | const { classList } = document.body; |
| 203 | classList | ||
| 204 | } = document.body; | ||
| 205 | // sidebar is always shown for settings; its state is updated in app.views.Settings | 224 | // sidebar is always shown for settings; its state is updated in app.views.Settings |
| 206 | if ((layout !== '_sidebar-hidden') || !(app.router != null ? app.router.isSettings : undefined)) { classList.toggle(layout, enable); } | 225 | if ( |
| 207 | classList.toggle('_overlay-scrollbars', $.overlayScrollbarsEnabled()); | 226 | layout !== "_sidebar-hidden" || |
| 227 | !(app.router != null ? app.router.isSettings : undefined) | ||
| 228 | ) { | ||
| 229 | classList.toggle(layout, enable); | ||
| 230 | } | ||
| 231 | classList.toggle("_overlay-scrollbars", $.overlayScrollbarsEnabled()); | ||
| 208 | } | 232 | } |
| 209 | 233 | ||
| 210 | initSidebarWidth() { | 234 | initSidebarWidth() { |
| 211 | const size = this.get('size'); | 235 | const size = this.get("size"); |
| 212 | if (size) { document.documentElement.style.setProperty('--sidebarWidth', size + 'px'); } | 236 | if (size) { |
| 237 | document.documentElement.style.setProperty( | ||
| 238 | "--sidebarWidth", | ||
| 239 | size + "px", | ||
| 240 | ); | ||
| 241 | } | ||
| 213 | } | 242 | } |
| 214 | }); | 243 | }); |
| 215 | Cls.initClass(); | 244 | Cls.initClass(); |
| @@ -217,5 +246,7 @@ · 1 unmodified line | |||
| 217 | })(); | 246 | })(); |
| 218 | 247 | ||
| 219 | function __guard__(value, transform) { | 248 | function __guard__(value, transform) { |
| 220 | return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; | 249 | return typeof value !== "undefined" && value !== null |
| 221 | } | 250 | ? transform(value) |
| 251 | : undefined; | ||
| 252 | } | ||
| @@ -21,111 +21,151 @@ const Cls = (app.Shortcuts = class Shortcuts { · 20 unmodified lines | |||
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | start() { | 23 | start() { |
| 24 | $.on(document, 'keydown', this.onKeydown); | 24 | $.on(document, "keydown", this.onKeydown); |
| 25 | $.on(document, 'keypress', this.onKeypress); | 25 | $.on(document, "keypress", this.onKeypress); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | stop() { | 28 | stop() { |
| 29 | $.off(document, 'keydown', this.onKeydown); | 29 | $.off(document, "keydown", this.onKeydown); |
| 30 | $.off(document, 'keypress', this.onKeypress); | 30 | $.off(document, "keypress", this.onKeypress); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | swapArrowKeysBehavior() { | 33 | swapArrowKeysBehavior() { |
| 34 | return app.settings.get('arrowScroll'); | 34 | return app.settings.get("arrowScroll"); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | spaceScroll() { | 37 | spaceScroll() { |
| 38 | return app.settings.get('spaceScroll'); | 38 | return app.settings.get("spaceScroll"); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | showTip() { | 41 | showTip() { |
| 42 | app.showTip('KeyNav'); | 42 | app.showTip("KeyNav"); |
| 43 | return this.showTip = null; | 43 | return (this.showTip = null); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | spaceTimeout() { | 46 | spaceTimeout() { |
| 47 | return app.settings.get('spaceTimeout'); | 47 | return app.settings.get("spaceTimeout"); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | onKeydown(event) { | 50 | onKeydown(event) { |
| 51 | if (this.buggyEvent(event)) { return; } | 51 | if (this.buggyEvent(event)) { |
| 52 | return; | ||
| 53 | } | ||
| 52 | const result = (() => { | 54 | const result = (() => { |
| 53 | if (event.ctrlKey || event.metaKey) { | 55 | if (event.ctrlKey || event.metaKey) { |
| 54 | if (!event.altKey && !event.shiftKey) { return this.handleKeydownSuperEvent(event); } | 56 | if (!event.altKey && !event.shiftKey) { |
| 55 | } else if (event.shiftKey) { | 57 | return this.handleKeydownSuperEvent(event); |
| 56 | if (!event.altKey) { return this.handleKeydownShiftEvent(event); } | 58 | } |
| 57 | } else if (event.altKey) { | 59 | } else if (event.shiftKey) { |
| 58 | return this.handleKeydownAltEvent(event); | 60 | if (!event.altKey) { |
| 59 | } else { | 61 | return this.handleKeydownShiftEvent(event); |
| 60 | return this.handleKeydownEvent(event); | 62 | } |
| 61 | } | 63 | } else if (event.altKey) { |
| 64 | return this.handleKeydownAltEvent(event); | ||
| 65 | } else { | ||
| 66 | return this.handleKeydownEvent(event); | ||
| 67 | } | ||
| 62 | })(); | 68 | })(); |
| 63 | 69 | ||
| 64 | if (result === false) { event.preventDefault(); } | 70 | if (result === false) { |
| 71 | event.preventDefault(); | ||
| 72 | } | ||
| 65 | } | 73 | } |
| 66 | 74 | ||
| 67 | onKeypress(event) { | 75 | onKeypress(event) { |
| 68 | if (this.buggyEvent(event) || ((event.charCode === 63) && (document.activeElement.tagName === 'INPUT'))) { return; } | 76 | if ( |
| 77 | this.buggyEvent(event) || | ||
| 78 | (event.charCode === 63 && document.activeElement.tagName === "INPUT") | ||
| 79 | ) { | ||
| 80 | return; | ||
| 81 | } | ||
| 69 | if (!event.ctrlKey && !event.metaKey) { | 82 | if (!event.ctrlKey && !event.metaKey) { |
| 70 | const result = this.handleKeypressEvent(event); | 83 | const result = this.handleKeypressEvent(event); |
| 71 | if (result === false) { event.preventDefault(); } | 84 | if (result === false) { |
| 85 | event.preventDefault(); | ||
| 86 | } | ||
| 72 | } | 87 | } |
| 73 | } | 88 | } |
| 74 | 89 | ||
| 75 | handleKeydownEvent(event, _force) { | 90 | handleKeydownEvent(event, _force) { |
| 76 | if (!_force && [37, 38, 39, 40].includes(event.which) && this.swapArrowKeysBehavior()) { return this.handleKeydownAltEvent(event, true); } | 91 | if ( |
| 92 | !_force && | ||
| 93 | [37, 38, 39, 40].includes(event.which) && | ||
| 94 | this.swapArrowKeysBehavior() | ||
| 95 | ) { | ||
| 96 | return this.handleKeydownAltEvent(event, true); | ||
| 97 | } | ||
| 77 | 98 | ||
| 78 | if (!event.target.form && ((48 <= event.which && event.which <= 57) || (65 <= event.which && event.which <= 90))) { | 99 | if ( |
| 79 | this.trigger('typing'); | 100 | !event.target.form && |
| 101 | ((48 <= event.which && event.which <= 57) || | ||
| 102 | (65 <= event.which && event.which <= 90)) | ||
| 103 | ) { | ||
| 104 | this.trigger("typing"); | ||
| 80 | return; | 105 | return; |
| 81 | } | 106 | } |
| 82 | 107 | ||
| 83 | switch (event.which) { | 108 | switch (event.which) { |
| 84 | case 8: | 109 | case 8: |
| 85 | if (!event.target.form) { return this.trigger('typing'); } | 110 | if (!event.target.form) { |
| 111 | return this.trigger("typing"); | ||
| 112 | } | ||
| 86 | break; | 113 | break; |
| 87 | case 13: | 114 | case 13: |
| 88 | return this.trigger('enter'); | 115 | return this.trigger("enter"); |
| 89 | case 27: | 116 | case 27: |
| 90 | this.trigger('escape'); | 117 | this.trigger("escape"); |
| 91 | return false; | 118 | return false; |
| 92 | case 32: | 119 | case 32: |
| 93 | if ((event.target.type === 'search') && this.spaceScroll() && (!this.lastKeypress || (this.lastKeypress < (Date.now() - (this.spaceTimeout() * 1000))))) { | 120 | if ( |
| 94 | this.trigger('pageDown'); | 121 | event.target.type === "search" && |
| 122 | this.spaceScroll() && | ||
| 123 | (!this.lastKeypress || | ||
| 124 | this.lastKeypress < Date.now() - this.spaceTimeout() * 1000) | ||
| 125 | ) { | ||
| 126 | this.trigger("pageDown"); | ||
| 95 | return false; | 127 | return false; |
| 96 | } | 128 | } |
| 97 | break; | 129 | break; |
| 98 | case 33: | 130 | case 33: |
| 99 | return this.trigger('pageUp'); | 131 | return this.trigger("pageUp"); |
| 100 | case 34: | 132 | case 34: |
| 101 | return this.trigger('pageDown'); | 133 | return this.trigger("pageDown"); |
| 102 | case 35: | 134 | case 35: |
| 103 | if (!event.target.form) { return this.trigger('pageBottom'); } | 135 | if (!event.target.form) { |
| 136 | return this.trigger("pageBottom"); | ||
| 137 | } | ||
| 104 | break; | 138 | break; |
| 105 | case 36: | 139 | case 36: |
| 106 | if (!event.target.form) { return this.trigger('pageTop'); } | 140 | if (!event.target.form) { |
| 141 | return this.trigger("pageTop"); | ||
| 142 | } | ||
| 107 | break; | 143 | break; |
| 108 | case 37: | 144 | case 37: |
| 109 | if (!event.target.value) { return this.trigger('left'); } | 145 | if (!event.target.value) { |
| 146 | return this.trigger("left"); | ||
| 147 | } | ||
| 110 | break; | 148 | break; |
| 111 | case 38: | 149 | case 38: |
| 112 | this.trigger('up'); | 150 | this.trigger("up"); |
| 113 | if (typeof this.showTip === 'function') { | 151 | if (typeof this.showTip === "function") { |
| 114 | this.showTip(); | 152 | this.showTip(); |
| 115 | } | 153 | } |
| 116 | return false; | 154 | return false; |
| 117 | case 39: | 155 | case 39: |
| 118 | if (!event.target.value) { return this.trigger('right'); } | 156 | if (!event.target.value) { |
| 157 | return this.trigger("right"); | ||
| 158 | } | ||
| 119 | break; | 159 | break; |
| 120 | case 40: | 160 | case 40: |
| 121 | this.trigger('down'); | 161 | this.trigger("down"); |
| 122 | if (typeof this.showTip === 'function') { | 162 | if (typeof this.showTip === "function") { |
| 123 | this.showTip(); | 163 | this.showTip(); |
| 124 | } | 164 | } |
| 125 | return false; | 165 | return false; |
| 126 | case 191: | 166 | case 191: |
| 127 | if (!event.target.form) { | 167 | if (!event.target.form) { |
| 128 | this.trigger('typing'); | 168 | this.trigger("typing"); |
| 129 | return false; | 169 | return false; |
| 130 | } | 170 | } |
| 131 | break; | 171 | break; |
| @@ -135,52 +175,58 @@ const Cls = (app.Shortcuts = class Shortcuts { · 3 unmodified lines | |||
| 135 | handleKeydownSuperEvent(event) { | 175 | handleKeydownSuperEvent(event) { |
| 136 | switch (event.which) { | 176 | switch (event.which) { |
| 137 | case 13: | 177 | case 13: |
| 138 | return this.trigger('superEnter'); | 178 | return this.trigger("superEnter"); |
| 139 | case 37: | 179 | case 37: |
| 140 | if (this.isMac) { | 180 | if (this.isMac) { |
| 141 | this.trigger('superLeft'); | 181 | this.trigger("superLeft"); |
| 142 | return false; | 182 | return false; |
| 143 | } | 183 | } |
| 144 | break; | 184 | break; |
| 145 | case 38: | 185 | case 38: |
| 146 | this.trigger('pageTop'); | 186 | this.trigger("pageTop"); |
| 147 | return false; | 187 | return false; |
| 148 | case 39: | 188 | case 39: |
| 149 | if (this.isMac) { | 189 | if (this.isMac) { |
| 150 | this.trigger('superRight'); | 190 | this.trigger("superRight"); |
| 151 | return false; | 191 | return false; |
| 152 | } | 192 | } |
| 153 | break; | 193 | break; |
| 154 | case 40: | 194 | case 40: |
| 155 | this.trigger('pageBottom'); | 195 | this.trigger("pageBottom"); |
| 156 | return false; | 196 | return false; |
| 157 | case 188: | 197 | case 188: |
| 158 | this.trigger('preferences'); | 198 | this.trigger("preferences"); |
| 159 | return false; | 199 | return false; |
| 160 | } | 200 | } |
| 161 | } | 201 | } |
| 162 | 202 | ||
| 163 | handleKeydownShiftEvent(event, _force) { | 203 | handleKeydownShiftEvent(event, _force) { |
| 164 | if (!_force && [37, 38, 39, 40].includes(event.which) && this.swapArrowKeysBehavior()) { return this.handleKeydownEvent(event, true); } | 204 | if ( |
| 205 | !_force && | ||
| 206 | [37, 38, 39, 40].includes(event.which) && | ||
| 207 | this.swapArrowKeysBehavior() | ||
| 208 | ) { | ||
| 209 | return this.handleKeydownEvent(event, true); | ||
| 210 | } | ||
| 165 | 211 | ||
| 166 | if (!event.target.form && (65 <= event.which && event.which <= 90)) { | 212 | if (!event.target.form && 65 <= event.which && event.which <= 90) { |
| 167 | this.trigger('typing'); | 213 | this.trigger("typing"); |
| 168 | return; | 214 | return; |
| 169 | } | 215 | } |
| 170 | 216 | ||
| 171 | switch (event.which) { | 217 | switch (event.which) { |
| 172 | case 32: | 218 | case 32: |
| 173 | this.trigger('pageUp'); | 219 | this.trigger("pageUp"); |
| 174 | return false; | 220 | return false; |
| 175 | case 38: | 221 | case 38: |
| 176 | if (!__guard__(getSelection(), x => x.toString())) { | 222 | if (!__guard__(getSelection(), (x) => x.toString())) { |
| 177 | this.trigger('altUp'); | 223 | this.trigger("altUp"); |
| 178 | return false; | 224 | return false; |
| 179 | } | 225 | } |
| 180 | break; | 226 | break; |
| 181 | case 40: | 227 | case 40: |
| 182 | if (!__guard__(getSelection(), x1 => x1.toString())) { | 228 | if (!__guard__(getSelection(), (x1) => x1.toString())) { |
| 183 | this.trigger('altDown'); | 229 | this.trigger("altDown"); |
| 184 | return false; | 230 | return false; |
| 185 | } | 231 | } |
| 186 | break; | 232 | break; |
| @@ -188,58 +234,64 @@ const Cls = (app.Shortcuts = class Shortcuts { · 1 unmodified line | |||
| 188 | } | 234 | } |
| 189 | 235 | ||
| 190 | handleKeydownAltEvent(event, _force) { | 236 | handleKeydownAltEvent(event, _force) { |
| 191 | if (!_force && [37, 38, 39, 40].includes(event.which) && this.swapArrowKeysBehavior()) { return this.handleKeydownEvent(event, true); } | 237 | if ( |
| 238 | !_force && | ||
| 239 | [37, 38, 39, 40].includes(event.which) && | ||
| 240 | this.swapArrowKeysBehavior() | ||
| 241 | ) { | ||
| 242 | return this.handleKeydownEvent(event, true); | ||
| 243 | } | ||
| 192 | 244 | ||
| 193 | switch (event.which) { | 245 | switch (event.which) { |
| 194 | case 9: | 246 | case 9: |
| 195 | return this.trigger('altRight', event); | 247 | return this.trigger("altRight", event); |
| 196 | case 37: | 248 | case 37: |
| 197 | if (!this.isMac) { | 249 | if (!this.isMac) { |
| 198 | this.trigger('superLeft'); | 250 | this.trigger("superLeft"); |
| 199 | return false; | 251 | return false; |
| 200 | } | 252 | } |
| 201 | break; | 253 | break; |
| 202 | case 38: | 254 | case 38: |
| 203 | this.trigger('altUp'); | 255 | this.trigger("altUp"); |
| 204 | return false; | 256 | return false; |
| 205 | case 39: | 257 | case 39: |
| 206 | if (!this.isMac) { | 258 | if (!this.isMac) { |
| 207 | this.trigger('superRight'); | 259 | this.trigger("superRight"); |
| 208 | return false; | 260 | return false; |
| 209 | } | 261 | } |
| 210 | break; | 262 | break; |
| 211 | case 40: | 263 | case 40: |
| 212 | this.trigger('altDown'); | 264 | this.trigger("altDown"); |
| 213 | return false; | 265 | return false; |
| 214 | case 67: | 266 | case 67: |
| 215 | this.trigger('altC'); | 267 | this.trigger("altC"); |
| 216 | return false; | 268 | return false; |
| 217 | case 68: | 269 | case 68: |
| 218 | this.trigger('altD'); | 270 | this.trigger("altD"); |
| 219 | return false; | 271 | return false; |
| 220 | case 70: | 272 | case 70: |
| 221 | return this.trigger('altF', event); | 273 | return this.trigger("altF", event); |
| 222 | case 71: | 274 | case 71: |
| 223 | this.trigger('altG'); | 275 | this.trigger("altG"); |
| 224 | return false; | 276 | return false; |
| 225 | case 79: | 277 | case 79: |
| 226 | this.trigger('altO'); | 278 | this.trigger("altO"); |
| 227 | return false; | 279 | return false; |
| 228 | case 82: | 280 | case 82: |
| 229 | this.trigger('altR'); | 281 | this.trigger("altR"); |
| 230 | return false; | 282 | return false; |
| 231 | case 83: | 283 | case 83: |
| 232 | this.trigger('altS'); | 284 | this.trigger("altS"); |
| 233 | return false; | 285 | return false; |
| 234 | } | 286 | } |
| 235 | } | 287 | } |
| 236 | 288 | ||
| 237 | handleKeypressEvent(event) { | 289 | handleKeypressEvent(event) { |
| 238 | if ((event.which === 63) && !event.target.value) { | 290 | if (event.which === 63 && !event.target.value) { |
| 239 | this.trigger('help'); | 291 | this.trigger("help"); |
| 240 | return false; | 292 | return false; |
| 241 | } else { | 293 | } else { |
| 242 | return this.lastKeypress = Date.now(); | 294 | return (this.lastKeypress = Date.now()); |
| 243 | } | 295 | } |
| 244 | } | 296 | } |
| 245 | 297 | ||
| @@ -257,5 +309,7 @@ const Cls = (app.Shortcuts = class Shortcuts { · 11 unmodified lines | |||
| 257 | Cls.initClass(); | 309 | Cls.initClass(); |
| 258 | 310 | ||
| 259 | function __guard__(value, transform) { | 311 | function __guard__(value, transform) { |
| 260 | return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; | 312 | return typeof value !== "undefined" && value !== null |
| 261 | } | 313 | ? transform(value) |
| 314 | : undefined; | ||
| 315 | } | ||
| @@ -11,9 +11,9 @@ app.UpdateChecker = class UpdateChecker { · 10 unmodified lines | |||
| 11 | this.onFocus = this.onFocus.bind(this); | 11 | this.onFocus = this.onFocus.bind(this); |
| 12 | this.lastCheck = Date.now(); | 12 | this.lastCheck = Date.now(); |
| 13 | 13 | ||
| 14 | $.on(window, 'focus', this.onFocus); | 14 | $.on(window, "focus", this.onFocus); |
| 15 | if (app.serviceWorker != null) { | 15 | if (app.serviceWorker != null) { |
| 16 | app.serviceWorker.on('updateready', this.onUpdateReady); | 16 | app.serviceWorker.on("updateready", this.onUpdateReady); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | setTimeout(this.checkDocs, 0); | 19 | setTimeout(this.checkDocs, 0); |
| @@ -24,31 +24,39 @@ app.UpdateChecker = class UpdateChecker { · 4 unmodified lines | |||
| 24 | app.serviceWorker.update(); | 24 | app.serviceWorker.update(); |
| 25 | } else { | 25 | } else { |
| 26 | ajax({ | 26 | ajax({ |
| 27 | url: $('script[src*="application"]').getAttribute('src'), | 27 | url: $('script[src*="application"]').getAttribute("src"), |
| 28 | dataType: 'application/javascript', | 28 | dataType: "application/javascript", |
| 29 | error: (_, xhr) => { if (xhr.status === 404) { return this.onUpdateReady(); } } | 29 | error: (_, xhr) => { |
| 30 | if (xhr.status === 404) { | ||
| 31 | return this.onUpdateReady(); | ||
| 32 | } | ||
| 33 | }, | ||
| 30 | }); | 34 | }); |
| 31 | } | 35 | } |
| 32 | } | 36 | } |
| 33 | 37 | ||
| 34 | onUpdateReady() { | 38 | onUpdateReady() { |
| 35 | new app.views.Notif('UpdateReady', {autoHide: null}); | 39 | new app.views.Notif("UpdateReady", { autoHide: null }); |
| 36 | } | 40 | } |
| 37 | 41 | ||
| 38 | checkDocs() { | 42 | checkDocs() { |
| 39 | if (!app.settings.get('manualUpdate')) { | 43 | if (!app.settings.get("manualUpdate")) { |
| 40 | app.docs.updateInBackground(); | 44 | app.docs.updateInBackground(); |
| 41 | } else { | 45 | } else { |
| 42 | app.docs.checkForUpdates(i => { if (i > 0) { return this.onDocsUpdateReady(); } }); | 46 | app.docs.checkForUpdates((i) => { |
| 47 | if (i > 0) { | ||
| 48 | return this.onDocsUpdateReady(); | ||
| 49 | } | ||
| 50 | }); | ||
| 43 | } | 51 | } |
| 44 | } | 52 | } |
| 45 | 53 | ||
| 46 | onDocsUpdateReady() { | 54 | onDocsUpdateReady() { |
| 47 | new app.views.Notif('UpdateDocs', {autoHide: null}); | 55 | new app.views.Notif("UpdateDocs", { autoHide: null }); |
| 48 | } | 56 | } |
| 49 | 57 | ||
| 50 | onFocus() { | 58 | onFocus() { |
| 51 | if ((Date.now() - this.lastCheck) > 21600e3) { | 59 | if (Date.now() - this.lastCheck > 21600e3) { |
| 52 | this.lastCheck = Date.now(); | 60 | this.lastCheck = Date.now(); |
| 53 | this.check(); | 61 | this.check(); |
| 54 | } | 62 | } |
| @@ -27,8 +27,8 @@ · 26 unmodified lines | |||
| 27 | 27 | ||
| 28 | //= require tracking | 28 | //= require tracking |
| 29 | 29 | ||
| 30 | var init = function() { | 30 | var init = function () { |
| 31 | document.removeEventListener('DOMContentLoaded', init, false); | 31 | document.removeEventListener("DOMContentLoaded", init, false); |
| 32 | 32 | ||
| 33 | if (document.body) { | 33 | if (document.body) { |
| 34 | return app.init(); | 34 | return app.init(); |
| @@ -37,4 +37,4 @@ var init = function() { · 2 unmodified lines | |||
| 37 | } | 37 | } |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | document.addEventListener('DOMContentLoaded', init, false); | 40 | document.addEventListener("DOMContentLoaded", init, false); |
| @@ -9,7 +9,9 @@ · 8 unmodified lines | |||
| 9 | */ | 9 | */ |
| 10 | app.Collection = class Collection { | 10 | app.Collection = class Collection { |
| 11 | constructor(objects) { | 11 | constructor(objects) { |
| 12 | if (objects == null) { objects = []; } | 12 | if (objects == null) { |
| 13 | objects = []; | ||
| 14 | } | ||
| 13 | this.reset(objects); | 15 | this.reset(objects); |
| 14 | } | 16 | } |
| 15 | 17 | ||
| @@ -18,16 +20,22 @@ app.Collection = class Collection { · 2 unmodified lines | |||
| 18 | } | 20 | } |
| 19 | 21 | ||
| 20 | reset(objects) { | 22 | reset(objects) { |
| 21 | if (objects == null) { objects = []; } | 23 | if (objects == null) { |
| 24 | objects = []; | ||
| 25 | } | ||
| 22 | this.models = []; | 26 | this.models = []; |
| 23 | for (var object of Array.from(objects)) { this.add(object); } | 27 | for (var object of Array.from(objects)) { |
| 28 | this.add(object); | ||
| 29 | } | ||
| 24 | } | 30 | } |
| 25 | 31 | ||
| 26 | add(object) { | 32 | add(object) { |
| 27 | if (object instanceof app.Model) { | 33 | if (object instanceof app.Model) { |
| 28 | this.models.push(object); | 34 | this.models.push(object); |
| 29 | } else if (object instanceof Array) { | 35 | } else if (object instanceof Array) { |
| 30 | for (var obj of Array.from(object)) { this.add(obj); } | 36 | for (var obj of Array.from(object)) { |
| 37 | this.add(obj); | ||
| 38 | } | ||
| 31 | } else if (object instanceof app.Collection) { | 39 | } else if (object instanceof app.Collection) { |
| 32 | this.models.push(...Array.from(object.all() || [])); | 40 | this.models.push(...Array.from(object.all() || [])); |
| 33 | } else { | 41 | } else { |
| @@ -48,7 +56,9 @@ app.Collection = class Collection { · 14 unmodified lines | |||
| 48 | } | 56 | } |
| 49 | 57 | ||
| 50 | each(fn) { | 58 | each(fn) { |
| 51 | for (var model of Array.from(this.models)) { fn(model); } | 59 | for (var model of Array.from(this.models)) { |
| 60 | fn(model); | ||
| 61 | } | ||
| 52 | } | 62 | } |
| 53 | 63 | ||
| 54 | all() { | 64 | all() { |
| @@ -61,7 +71,9 @@ app.Collection = class Collection { · 6 unmodified lines | |||
| 61 | 71 | ||
| 62 | findBy(attr, value) { | 72 | findBy(attr, value) { |
| 63 | for (var model of Array.from(this.models)) { | 73 | for (var model of Array.from(this.models)) { |
| 64 | if (model[attr] === value) { return model; } | 74 | if (model[attr] === value) { |
| 75 | return model; | ||
| 76 | } | ||
| 65 | } | 77 | } |
| 66 | } | 78 | } |
| 67 | 79 | ||
| @@ -71,7 +83,11 @@ app.Collection = class Collection { · 3 unmodified lines | |||
| 71 | 83 | ||
| 72 | countAllBy(attr, value) { | 84 | countAllBy(attr, value) { |
| 73 | let i = 0; | 85 | let i = 0; |
| 74 | for (var model of Array.from(this.models)) { if (model[attr] === value) { i += 1; } } | 86 | for (var model of Array.from(this.models)) { |
| 87 | if (model[attr] === value) { | ||
| 88 | i += 1; | ||
| 89 | } | ||
| 90 | } | ||
| 75 | return i; | 91 | return i; |
| 76 | } | 92 | } |
| 77 | }; | 93 | }; |
| @@ -8,29 +8,35 @@ · 7 unmodified lines | |||
| 8 | * DS206: Consider reworking classes to avoid initClass | 8 | * DS206: Consider reworking classes to avoid initClass |
| 9 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 9 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 10 | */ | 10 | */ |
| 11 | (function() { | 11 | (function () { |
| 12 | let NORMALIZE_VERSION_RGX = undefined; | 12 | let NORMALIZE_VERSION_RGX = undefined; |
| 13 | let NORMALIZE_VERSION_SUB = undefined; | 13 | let NORMALIZE_VERSION_SUB = undefined; |
| 14 | let CONCURRENCY = undefined; | 14 | let CONCURRENCY = undefined; |
| 15 | const Cls = (app.collections.Docs = class Docs extends app.Collection { | 15 | const Cls = (app.collections.Docs = class Docs extends app.Collection { |
| 16 | static initClass() { | 16 | static initClass() { |
| 17 | this.model = 'Doc'; | 17 | this.model = "Doc"; |
| 18 | 18 | ||
| 19 | NORMALIZE_VERSION_RGX = /\.(\d)$/; | 19 | NORMALIZE_VERSION_RGX = /\.(\d)$/; |
| 20 | NORMALIZE_VERSION_SUB = '.0$1'; | 20 | NORMALIZE_VERSION_SUB = ".0$1"; |
| 21 | 21 | ||
| 22 | // Load models concurrently. | 22 | // Load models concurrently. |
| 23 | // It's not pretty but I didn't want to import a promise library only for this. | 23 | // It's not pretty but I didn't want to import a promise library only for this. |
| 24 | CONCURRENCY = 3; | 24 | CONCURRENCY = 3; |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | findBySlug(slug) { | 27 | findBySlug(slug) { |
| 28 | return this.findBy('slug', slug) || this.findBy('slug_without_version', slug); | 28 | return ( |
| 29 | this.findBy("slug", slug) || this.findBy("slug_without_version", slug) | ||
| 30 | ); | ||
| 29 | } | 31 | } |
| 30 | sort() { | 32 | sort() { |
| 31 | return this.models.sort(function(a, b) { | 33 | return this.models.sort(function (a, b) { |
| 32 | if (a.name === b.name) { | 34 | if (a.name === b.name) { |
| 33 | if (!a.version || (a.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) > b.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB))) { | 35 | if ( |
| 36 | !a.version || | ||
| 37 | a.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) > | ||
| 38 | b.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) | ||
| 39 | ) { | ||
| 34 | return -1; | 40 | return -1; |
| 35 | } else { | 41 | } else { |
| 36 | return 1; | 42 | return 1; |
| @@ -48,13 +54,13 @@ · 11 unmodified lines | |||
| 48 | var next = () => { | 54 | var next = () => { |
| 49 | if (i < this.models.length) { | 55 | if (i < this.models.length) { |
| 50 | this.models[i].load(next, fail, options); | 56 | this.models[i].load(next, fail, options); |
| 51 | } else if (i === ((this.models.length + CONCURRENCY) - 1)) { | 57 | } else if (i === this.models.length + CONCURRENCY - 1) { |
| 52 | onComplete(); | 58 | onComplete(); |
| 53 | } | 59 | } |
| 54 | i++; | 60 | i++; |
| 55 | }; | 61 | }; |
| 56 | 62 | ||
| 57 | var fail = function(...args) { | 63 | var fail = function (...args) { |
| 58 | if (onError) { | 64 | if (onError) { |
| 59 | onError(...Array.from(args || [])); | 65 | onError(...Array.from(args || [])); |
| 60 | onError = null; | 66 | onError = null; |
| @@ -62,11 +68,19 @@ · 1 unmodified line | |||
| 62 | next(); | 68 | next(); |
| 63 | }; | 69 | }; |
| 64 | 70 | ||
| 65 | for (let j = 0, end = CONCURRENCY, asc = 0 <= end; asc ? j < end : j > end; asc ? j++ : j--) { next(); } | 71 | for ( |
| 72 | let j = 0, end = CONCURRENCY, asc = 0 <= end; | ||
| 73 | asc ? j < end : j > end; | ||
| 74 | asc ? j++ : j-- | ||
| 75 | ) { | ||
| 76 | next(); | ||
| 77 | } | ||
| 66 | } | 78 | } |
| 67 | 79 | ||
| 68 | clearCache() { | 80 | clearCache() { |
| 69 | for (var doc of Array.from(this.models)) { doc.clearCache(); } | 81 | for (var doc of Array.from(this.models)) { |
| 82 | doc.clearCache(); | ||
| 83 | } | ||
| 70 | } | 84 | } |
| 71 | 85 | ||
| 72 | uninstall(callback) { | 86 | uninstall(callback) { |
| @@ -82,11 +96,11 @@ · 9 unmodified lines | |||
| 82 | } | 96 | } |
| 83 | 97 | ||
| 84 | getInstallStatuses(callback) { | 98 | getInstallStatuses(callback) { |
| 85 | app.db.versions(this.models, function(statuses) { | 99 | app.db.versions(this.models, function (statuses) { |
| 86 | if (statuses) { | 100 | if (statuses) { |
| 87 | for (var key in statuses) { | 101 | for (var key in statuses) { |
| 88 | var value = statuses[key]; | 102 | var value = statuses[key]; |
| 89 | statuses[key] = {installed: !!value, mtime: value}; | 103 | statuses[key] = { installed: !!value, mtime: value }; |
| 90 | } | 104 | } |
| 91 | } | 105 | } |
| 92 | callback(statuses); | 106 | callback(statuses); |
| @@ -94,22 +108,31 @@ · 1 unmodified line | |||
| 94 | } | 108 | } |
| 95 | 109 | ||
| 96 | checkForUpdates(callback) { | 110 | checkForUpdates(callback) { |
| 97 | this.getInstallStatuses(statuses => { | 111 | this.getInstallStatuses((statuses) => { |
| 98 | let i = 0; | 112 | let i = 0; |
| 99 | if (statuses) { | 113 | if (statuses) { |
| 100 | for (var slug in statuses) { var status = statuses[slug]; if (this.findBy('slug', slug).isOutdated(status)) { i += 1; } } | 114 | for (var slug in statuses) { |
| 115 | var status = statuses[slug]; | ||
| 116 | if (this.findBy("slug", slug).isOutdated(status)) { | ||
| 117 | i += 1; | ||
| 118 | } | ||
| 119 | } | ||
| 101 | } | 120 | } |
| 102 | callback(i); | 121 | callback(i); |
| 103 | }); | 122 | }); |
| 104 | } | 123 | } |
| 105 | 124 | ||
| 106 | updateInBackground() { | 125 | updateInBackground() { |
| 107 | this.getInstallStatuses(statuses => { | 126 | this.getInstallStatuses((statuses) => { |
| 108 | if (!statuses) { return; } | 127 | if (!statuses) { |
| 128 | return; | ||
| 129 | } | ||
| 109 | for (var slug in statuses) { | 130 | for (var slug in statuses) { |
| 110 | var status = statuses[slug]; | 131 | var status = statuses[slug]; |
| 111 | var doc = this.findBy('slug', slug); | 132 | var doc = this.findBy("slug", slug); |
| 112 | if (doc.isOutdated(status)) { doc.install($.noop, $.noop); } | 133 | if (doc.isOutdated(status)) { |
| 134 | doc.install($.noop, $.noop); | ||
| 135 | } | ||
| 113 | } | 136 | } |
| 114 | }); | 137 | }); |
| 115 | } | 138 | } |
| @@ -7,7 +7,7 @@ · 6 unmodified lines | |||
| 7 | */ | 7 | */ |
| 8 | const Cls = (app.collections.Entries = class Entries extends app.Collection { | 8 | const Cls = (app.collections.Entries = class Entries extends app.Collection { |
| 9 | static initClass() { | 9 | static initClass() { |
| 10 | this.model = 'Entry'; | 10 | this.model = "Entry"; |
| 11 | } | 11 | } |
| 12 | }); | 12 | }); |
| 13 | Cls.initClass(); | 13 | Cls.initClass(); |
| @@ -8,14 +8,15 @@ · 7 unmodified lines | |||
| 8 | * DS206: Consider reworking classes to avoid initClass | 8 | * DS206: Consider reworking classes to avoid initClass |
| 9 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 9 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 10 | */ | 10 | */ |
| 11 | (function() { | 11 | (function () { |
| 12 | let GUIDES_RGX = undefined; | 12 | let GUIDES_RGX = undefined; |
| 13 | let APPENDIX_RGX = undefined; | 13 | let APPENDIX_RGX = undefined; |
| 14 | const Cls = (app.collections.Types = class Types extends app.Collection { | 14 | const Cls = (app.collections.Types = class Types extends app.Collection { |
| 15 | static initClass() { | 15 | static initClass() { |
| 16 | this.model = 'Type'; | 16 | this.model = "Type"; |
| 17 | 17 | ||
| 18 | GUIDES_RGX = /(^|\()(guides?|tutorials?|reference|book|getting\ started|manual|examples)($|[\):])/i; | 18 | GUIDES_RGX = |
| 19 | /(^|\()(guides?|tutorials?|reference|book|getting\ started|manual|examples)($|[\):])/i; | ||
| 19 | APPENDIX_RGX = /appendix/i; | 20 | APPENDIX_RGX = /appendix/i; |
| 20 | } | 21 | } |
| 21 | 22 | ||
| @@ -23,9 +24,11 @@ · 1 unmodified line | |||
| 23 | const result = []; | 24 | const result = []; |
| 24 | for (var type of Array.from(this.models)) { | 25 | for (var type of Array.from(this.models)) { |
| 25 | var name; | 26 | var name; |
| 26 | (result[name = this._groupFor(type)] || (result[name] = [])).push(type); | 27 | (result[(name = this._groupFor(type))] || (result[name] = [])).push( |
| 28 | type, | ||
| 29 | ); | ||
| 27 | } | 30 | } |
| 28 | return result.filter(e => e.length > 0); | 31 | return result.filter((e) => e.length > 0); |
| 29 | } | 32 | } |
| 30 | 33 | ||
| 31 | _groupFor(type) { | 34 | _groupFor(type) { |
| @@ -9,26 +9,33 @@ · 8 unmodified lines | |||
| 9 | * DS209: Avoid top-level return | 9 | * DS209: Avoid top-level return |
| 10 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 10 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 11 | */ | 11 | */ |
| 12 | if (!(typeof console !== 'undefined' && console !== null ? console.time : undefined) || !console.groupCollapsed) { return; } | 12 | if ( |
| 13 | !(typeof console !== "undefined" && console !== null | ||
| 14 | ? console.time | ||
| 15 | : undefined) || | ||
| 16 | !console.groupCollapsed | ||
| 17 | ) { | ||
| 18 | return; | ||
| 19 | } | ||
| 13 | 20 | ||
| 14 | // | 21 | // |
| 15 | // App | 22 | // App |
| 16 | // | 23 | // |
| 17 | 24 | ||
| 18 | const _init = app.init; | 25 | const _init = app.init; |
| 19 | app.init = function() { | 26 | app.init = function () { |
| 20 | console.time('Init'); | 27 | console.time("Init"); |
| 21 | _init.call(app); | 28 | _init.call(app); |
| 22 | console.timeEnd('Init'); | 29 | console.timeEnd("Init"); |
| 23 | return console.time('Load'); | 30 | return console.time("Load"); |
| 24 | }; | 31 | }; |
| 25 | 32 | ||
| 26 | const _start = app.start; | 33 | const _start = app.start; |
| 27 | app.start = function() { | 34 | app.start = function () { |
| 28 | console.timeEnd('Load'); | 35 | console.timeEnd("Load"); |
| 29 | console.time('Start'); | 36 | console.time("Start"); |
| 30 | _start.call(app, ...arguments); | 37 | _start.call(app, ...arguments); |
| 31 | return console.timeEnd('Start'); | 38 | return console.timeEnd("Start"); |
| 32 | }; | 39 | }; |
| 33 | 40 | ||
| 34 | // | 41 | // |
| @@ -38,47 +45,50 @@ app.start = function() { · 3 unmodified lines | |||
| 38 | const _super = app.Searcher; | 45 | const _super = app.Searcher; |
| 39 | const _proto = app.Searcher.prototype; | 46 | const _proto = app.Searcher.prototype; |
| 40 | 47 | ||
| 41 | app.Searcher = function() { | 48 | app.Searcher = function () { |
| 42 | _super.apply(this, arguments); | 49 | _super.apply(this, arguments); |
| 43 | 50 | ||
| 44 | const _setup = this.setup.bind(this); | 51 | const _setup = this.setup.bind(this); |
| 45 | this.setup = function() { | 52 | this.setup = function () { |
| 46 | console.groupCollapsed(`Search: ${this.query}`); | 53 | console.groupCollapsed(`Search: ${this.query}`); |
| 47 | console.time('Total'); | 54 | console.time("Total"); |
| 48 | return _setup(); | 55 | return _setup(); |
| 49 | }; | 56 | }; |
| 50 | 57 | ||
| 51 | const _match = this.match.bind(this); | 58 | const _match = this.match.bind(this); |
| 52 | this.match = () => { | 59 | this.match = () => { |
| 53 | if (this.matcher) { console.timeEnd(this.matcher.name); } | 60 | if (this.matcher) { |
| 61 | console.timeEnd(this.matcher.name); | ||
| 62 | } | ||
| 54 | return _match(); | 63 | return _match(); |
| 55 | }; | 64 | }; |
| 56 | 65 | ||
| 57 | const _setupMatcher = this.setupMatcher.bind(this); | 66 | const _setupMatcher = this.setupMatcher.bind(this); |
| 58 | this.setupMatcher = function() { | 67 | this.setupMatcher = function () { |
| 59 | console.time(this.matcher.name); | 68 | console.time(this.matcher.name); |
| 60 | return _setupMatcher(); | 69 | return _setupMatcher(); |
| 61 | }; | 70 | }; |
| 62 | 71 | ||
| 63 | const _end = this.end.bind(this); | 72 | const _end = this.end.bind(this); |
| 64 | this.end = function() { | 73 | this.end = function () { |
| 65 | console.log(`Results: ${this.totalResults}`); | 74 | console.log(`Results: ${this.totalResults}`); |
| 66 | console.timeEnd('Total'); | 75 | console.timeEnd("Total"); |
| 67 | console.groupEnd(); | 76 | console.groupEnd(); |
| 68 | return _end(); | 77 | return _end(); |
| 69 | }; | 78 | }; |
| 70 | 79 | ||
| 71 | const _kill = this.kill.bind(this); | 80 | const _kill = this.kill.bind(this); |
| 72 | this.kill = function() { | 81 | this.kill = function () { |
| 73 | if (this.timeout) { | 82 | if (this.timeout) { |
| 74 | if (this.matcher) { console.timeEnd(this.matcher.name); } | 83 | if (this.matcher) { |
| 84 | console.timeEnd(this.matcher.name); | ||
| 85 | } | ||
| 75 | console.groupEnd(); | 86 | console.groupEnd(); |
| 76 | console.timeEnd('Total'); | 87 | console.timeEnd("Total"); |
| 77 | console.warn('Killed'); | 88 | console.warn("Killed"); |
| 78 | } | 89 | } |
| 79 | return _kill(); | 90 | return _kill(); |
| 80 | }; | 91 | }; |
| 81 | |||
| 82 | }; | 92 | }; |
| 83 | 93 | ||
| 84 | $.extend(app.Searcher, _super); | 94 | $.extend(app.Searcher, _super); |
| @@ -89,23 +99,40 @@ app.Searcher.prototype = _proto; · 4 unmodified lines | |||
| 89 | // View tree | 99 | // View tree |
| 90 | // | 100 | // |
| 91 | 101 | ||
| 92 | this.viewTree = function(view, level, visited) { | 102 | this.viewTree = function (view, level, visited) { |
| 93 | if (view == null) { view = app.document; } | 103 | if (view == null) { |
| 94 | if (level == null) { level = 0; } | 104 | view = app.document; |
| 95 | if (visited == null) { visited = []; } | 105 | } |
| 96 | if (visited.indexOf(view) >= 0) { return; } | 106 | if (level == null) { |
| 107 | level = 0; | ||
| 108 | } | ||
| 109 | if (visited == null) { | ||
| 110 | visited = []; | ||
| 111 | } | ||
| 112 | if (visited.indexOf(view) >= 0) { | ||
| 113 | return; | ||
| 114 | } | ||
| 97 | visited.push(view); | 115 | visited.push(view); |
| 98 | 116 | ||
| 99 | console.log(`%c ${Array(level + 1).join(' ')}${view.constructor.name}: ${!!view.activated}`, | 117 | console.log( |
| 100 | 'color:' + ((view.activated && 'green') || 'red')); | 118 | `%c ${Array(level + 1).join(" ")}${ |
| 119 | view.constructor.name | ||
| 120 | }: ${!!view.activated}`, | ||
| 121 | "color:" + ((view.activated && "green") || "red"), | ||
| 122 | ); | ||
| 101 | 123 | ||
| 102 | for (var key of Object.keys(view || {})) { | 124 | for (var key of Object.keys(view || {})) { |
| 103 | var value = view[key]; | 125 | var value = view[key]; |
| 104 | if ((key !== 'view') && value) { | 126 | if (key !== "view" && value) { |
| 105 | if ((typeof value === 'object') && value.setupElement) { | 127 | if (typeof value === "object" && value.setupElement) { |
| 106 | this.viewTree(value, level + 1, visited); | 128 | this.viewTree(value, level + 1, visited); |
| 107 | } else if (value.constructor.toString().match(/Object\(\)/)) { | 129 | } else if (value.constructor.toString().match(/Object\(\)/)) { |
| 108 | for (var k of Object.keys(value || {})) { var v = value[k]; if (v && (typeof v === 'object') && v.setupElement) { this.viewTree(v, level + 1, visited); } } | 130 | for (var k of Object.keys(value || {})) { |
| 131 | var v = value[k]; | ||
| 132 | if (v && typeof v === "object" && v.setupElement) { | ||
| 133 | this.viewTree(v, level + 1, visited); | ||
| 134 | } | ||
| 135 | } | ||
| 109 | } | 136 | } |
| 110 | } | 137 | } |
| 111 | } | 138 | } |
| @@ -9,11 +9,11 @@ · 8 unmodified lines | |||
| 9 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 9 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 10 | */ | 10 | */ |
| 11 | const MIME_TYPES = { | 11 | const MIME_TYPES = { |
| 12 | json: 'application/json', | 12 | json: "application/json", |
| 13 | html: 'text/html' | 13 | html: "text/html", |
| 14 | }; | 14 | }; |
| 15 | 15 | ||
| 16 | this.ajax = function(options) { | 16 | this.ajax = function (options) { |
| 17 | applyDefaults(options); | 17 | applyDefaults(options); |
| 18 | serializeData(options); | 18 | serializeData(options); |
| 19 | 19 | ||
| @@ -26,7 +26,7 @@ this.ajax = function(options) { · 6 unmodified lines | |||
| 26 | xhr.send(options.data); | 26 | xhr.send(options.data); |
| 27 | 27 | ||
| 28 | if (options.async) { | 28 | if (options.async) { |
| 29 | return {abort: abort.bind(undefined, xhr)}; | 29 | return { abort: abort.bind(undefined, xhr) }; |
| 30 | } else { | 30 | } else { |
| 31 | return parseResponse(xhr, options); | 31 | return parseResponse(xhr, options); |
| 32 | } | 32 | } |
| @@ -34,51 +34,63 @@ this.ajax = function(options) { · 1 unmodified line | |||
| 34 | 34 | ||
| 35 | ajax.defaults = { | 35 | ajax.defaults = { |
| 36 | async: true, | 36 | async: true, |
| 37 | dataType: 'json', | 37 | dataType: "json", |
| 38 | timeout: 30, | 38 | timeout: 30, |
| 39 | type: 'GET' | 39 | type: "GET", |
| 40 | }; | 40 | }; |
| 41 | // contentType | 41 | // contentType |
| 42 | // context | 42 | // context |
| 43 | // data | 43 | // data |
| 44 | // error | 44 | // error |
| 45 | // headers | 45 | // headers |
| 46 | // progress | 46 | // progress |
| 47 | // success | 47 | // success |
| 48 | // url | 48 | // url |
| 49 | 49 | ||
| 50 | var applyDefaults = function(options) { | 50 | var applyDefaults = function (options) { |
| 51 | for (var key in ajax.defaults) { | 51 | for (var key in ajax.defaults) { |
| 52 | if (options[key] == null) { options[key] = ajax.defaults[key]; } | 52 | if (options[key] == null) { |
| 53 | options[key] = ajax.defaults[key]; | ||
| 54 | } | ||
| 53 | } | 55 | } |
| 54 | }; | 56 | }; |
| 55 | 57 | ||
| 56 | var serializeData = function(options) { | 58 | var serializeData = function (options) { |
| 57 | if (!options.data) { return; } | 59 | if (!options.data) { |
| 60 | return; | ||
| 61 | } | ||
| 58 | 62 | ||
| 59 | if (options.type === 'GET') { | 63 | if (options.type === "GET") { |
| 60 | options.url += '?' + serializeParams(options.data); | 64 | options.url += "?" + serializeParams(options.data); |
| 61 | options.data = null; | 65 | options.data = null; |
| 62 | } else { | 66 | } else { |
| 63 | options.data = serializeParams(options.data); | 67 | options.data = serializeParams(options.data); |
| 64 | } | 68 | } |
| 65 | }; | 69 | }; |
| 66 | 70 | ||
| 67 | var serializeParams = params => ((() => { | 71 | var serializeParams = (params) => |
| 68 | const result = []; | 72 | (() => { |
| 69 | for (var key in params) { | 73 | const result = []; |
| 70 | var value = params[key]; | 74 | for (var key in params) { |
| 71 | result.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`); | 75 | var value = params[key]; |
| 72 | } | 76 | result.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`); |
| 73 | return result; | 77 | } |
| 74 | })()).join('&'); | 78 | return result; |
| 79 | })().join("&"); | ||
| 75 | 80 | ||
| 76 | var applyCallbacks = function(xhr, options) { | 81 | var applyCallbacks = function (xhr, options) { |
| 77 | if (!options.async) { return; } | 82 | if (!options.async) { |
| 83 | return; | ||
| 84 | } | ||
| 78 | 85 | ||
| 79 | xhr.timer = setTimeout(onTimeout.bind(undefined, xhr, options), options.timeout * 1000); | 86 | xhr.timer = setTimeout( |
| 80 | if (options.progress) { xhr.onprogress = options.progress; } | 87 | onTimeout.bind(undefined, xhr, options), |
| 81 | xhr.onreadystatechange = function() { | 88 | options.timeout * 1000, |
| 89 | ); | ||
| 90 | if (options.progress) { | ||
| 91 | xhr.onprogress = options.progress; | ||
| 92 | } | ||
| 93 | xhr.onreadystatechange = function () { | ||
| 82 | if (xhr.readyState === 4) { | 94 | if (xhr.readyState === 4) { |
| 83 | clearTimeout(xhr.timer); | 95 | clearTimeout(xhr.timer); |
| 84 | onComplete(xhr, options); | 96 | onComplete(xhr, options); |
| @@ -86,19 +98,26 @@ var applyCallbacks = function(xhr, options) { · 1 unmodified line | |||
| 86 | }; | 98 | }; |
| 87 | }; | 99 | }; |
| 88 | 100 | ||
| 89 | var applyHeaders = function(xhr, options) { | 101 | var applyHeaders = function (xhr, options) { |
| 90 | if (!options.headers) { options.headers = {}; } | 102 | if (!options.headers) { |
| 103 | options.headers = {}; | ||
| 104 | } | ||
| 91 | 105 | ||
| 92 | if (options.contentType) { | 106 | if (options.contentType) { |
| 93 | options.headers['Content-Type'] = options.contentType; | 107 | options.headers["Content-Type"] = options.contentType; |
| 94 | } | 108 | } |
| 95 | 109 | ||
| 96 | if (!options.headers['Content-Type'] && options.data && (options.type !== 'GET')) { | 110 | if ( |
| 97 | options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; | 111 | !options.headers["Content-Type"] && |
| 112 | options.data && | ||
| 113 | options.type !== "GET" | ||
| 114 | ) { | ||
| 115 | options.headers["Content-Type"] = "application/x-www-form-urlencoded"; | ||
| 98 | } | 116 | } |
| 99 | 117 | ||
| 100 | if (options.dataType) { | 118 | if (options.dataType) { |
| 101 | options.headers['Accept'] = MIME_TYPES[options.dataType] || options.dataType; | 119 | options.headers["Accept"] = |
| 120 | MIME_TYPES[options.dataType] || options.dataType; | ||
| 102 | } | 121 | } |
| 103 | 122 | ||
| 104 | for (var key in options.headers) { | 123 | for (var key in options.headers) { |
| @@ -107,50 +126,52 @@ var applyHeaders = function(xhr, options) { · 2 unmodified lines | |||
| 107 | } | 126 | } |
| 108 | }; | 127 | }; |
| 109 | 128 | ||
| 110 | var onComplete = function(xhr, options) { | 129 | var onComplete = function (xhr, options) { |
| 111 | if (200 <= xhr.status && xhr.status < 300) { | 130 | if (200 <= xhr.status && xhr.status < 300) { |
| 112 | let response; | 131 | let response; |
| 113 | if ((response = parseResponse(xhr, options)) != null) { | 132 | if ((response = parseResponse(xhr, options)) != null) { |
| 114 | onSuccess(response, xhr, options); | 133 | onSuccess(response, xhr, options); |
| 115 | } else { | 134 | } else { |
| 116 | onError('invalid', xhr, options); | 135 | onError("invalid", xhr, options); |
| 117 | } | 136 | } |
| 118 | } else { | 137 | } else { |
| 119 | onError('error', xhr, options); | 138 | onError("error", xhr, options); |
| 120 | } | 139 | } |
| 121 | }; | 140 | }; |
| 122 | 141 | ||
| 123 | var onSuccess = function(response, xhr, options) { | 142 | var onSuccess = function (response, xhr, options) { |
| 124 | if (options.success != null) { | 143 | if (options.success != null) { |
| 125 | options.success.call(options.context, response, xhr, options); | 144 | options.success.call(options.context, response, xhr, options); |
| 126 | } | 145 | } |
| 127 | }; | 146 | }; |
| 128 | 147 | ||
| 129 | var onError = function(type, xhr, options) { | 148 | var onError = function (type, xhr, options) { |
| 130 | if (options.error != null) { | 149 | if (options.error != null) { |
| 131 | options.error.call(options.context, type, xhr, options); | 150 | options.error.call(options.context, type, xhr, options); |
| 132 | } | 151 | } |
| 133 | }; | 152 | }; |
| 134 | 153 | ||
| 135 | var onTimeout = function(xhr, options) { | 154 | var onTimeout = function (xhr, options) { |
| 136 | xhr.abort(); | 155 | xhr.abort(); |
| 137 | onError('timeout', xhr, options); | 156 | onError("timeout", xhr, options); |
| 138 | }; | 157 | }; |
| 139 | 158 | ||
| 140 | var abort = function(xhr) { | 159 | var abort = function (xhr) { |
| 141 | clearTimeout(xhr.timer); | 160 | clearTimeout(xhr.timer); |
| 142 | xhr.onreadystatechange = null; | 161 | xhr.onreadystatechange = null; |
| 143 | xhr.abort(); | 162 | xhr.abort(); |
| 144 | }; | 163 | }; |
| 145 | 164 | ||
| 146 | var parseResponse = function(xhr, options) { | 165 | var parseResponse = function (xhr, options) { |
| 147 | if (options.dataType === 'json') { | 166 | if (options.dataType === "json") { |
| 148 | return parseJSON(xhr.responseText); | 167 | return parseJSON(xhr.responseText); |
| 149 | } else { | 168 | } else { |
| 150 | return xhr.responseText; | 169 | return xhr.responseText; |
| 151 | } | 170 | } |
| 152 | }; | 171 | }; |
| 153 | 172 | ||
| 154 | var parseJSON = function(json) { | 173 | var parseJSON = function (json) { |
| 155 | try { return JSON.parse(json); } catch (error) {} | 174 | try { |
| 175 | return JSON.parse(json); | ||
| 176 | } catch (error) {} | ||
| 156 | }; | 177 | }; |
| @@ -8,14 +8,14 @@ · 7 unmodified lines | |||
| 8 | * DS207: Consider shorter variations of null checks | 8 | * DS207: Consider shorter variations of null checks |
| 9 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 9 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 10 | */ | 10 | */ |
| 11 | (function() { | 11 | (function () { |
| 12 | let INT = undefined; | 12 | let INT = undefined; |
| 13 | const Cls = (this.CookiesStore = class CookiesStore { | 13 | const Cls = (this.CookiesStore = class CookiesStore { |
| 14 | static initClass() { | 14 | static initClass() { |
| 15 | // Intentionally called CookiesStore instead of CookieStore | 15 | // Intentionally called CookiesStore instead of CookieStore |
| 16 | // Calling it CookieStore causes issues when the Experimental Web Platform features flag is enabled in Chrome | 16 | // Calling it CookieStore causes issues when the Experimental Web Platform features flag is enabled in Chrome |
| 17 | // Related issue: https://github.com/freeCodeCamp/devdocs/issues/932 | 17 | // Related issue: https://github.com/freeCodeCamp/devdocs/issues/932 |
| 18 | 18 | ||
| 19 | INT = /^\d+$/; | 19 | INT = /^\d+$/; |
| 20 | } | 20 | } |
| 21 | 21 | ||
| @@ -23,7 +23,9 @@ · 1 unmodified line | |||
| 23 | 23 | ||
| 24 | get(key) { | 24 | get(key) { |
| 25 | let value = Cookies.get(key); | 25 | let value = Cookies.get(key); |
| 26 | if ((value != null) && INT.test(value)) { value = parseInt(value, 10); } | 26 | if (value != null && INT.test(value)) { |
| 27 | value = parseInt(value, 10); | ||
| 28 | } | ||
| 27 | return value; | 29 | return value; |
| 28 | } | 30 | } |
| 29 | 31 | ||
| @@ -33,10 +35,19 @@ · 3 unmodified lines | |||
| 33 | return; | 35 | return; |
| 34 | } | 36 | } |
| 35 | 37 | ||
| 36 | if (value === true) { value = 1; } | 38 | if (value === true) { |
| 37 | if (value && (typeof INT.test === 'function' ? INT.test(value) : undefined)) { value = parseInt(value, 10); } | 39 | value = 1; |
| 38 | Cookies.set(key, '' + value, {path: '/', expires: 1e8}); | 40 | } |
| 39 | if (this.get(key) !== value) { this.constructor.onBlocked(key, value, this.get(key)); } | 41 | if ( |
| 42 | value && | ||
| 43 | (typeof INT.test === "function" ? INT.test(value) : undefined) | ||
| 44 | ) { | ||
| 45 | value = parseInt(value, 10); | ||
| 46 | } | ||
| 47 | Cookies.set(key, "" + value, { path: "/", expires: 1e8 }); | ||
| 48 | if (this.get(key) !== value) { | ||
| 49 | this.constructor.onBlocked(key, value, this.get(key)); | ||
| 50 | } | ||
| 40 | } | 51 | } |
| 41 | 52 | ||
| 42 | del(key) { | 53 | del(key) { |
| @@ -46,7 +57,7 @@ · 3 unmodified lines | |||
| 46 | reset() { | 57 | reset() { |
| 47 | try { | 58 | try { |
| 48 | for (var cookie of Array.from(document.cookie.split(/;\s?/))) { | 59 | for (var cookie of Array.from(document.cookie.split(/;\s?/))) { |
| 49 | Cookies.expire(cookie.split('=')[0]); | 60 | Cookies.expire(cookie.split("=")[0]); |
| 50 | } | 61 | } |
| 51 | return; | 62 | return; |
| 52 | } catch (error) {} | 63 | } catch (error) {} |
| @@ -55,8 +66,8 @@ · 2 unmodified lines | |||
| 55 | dump() { | 66 | dump() { |
| 56 | const result = {}; | 67 | const result = {}; |
| 57 | for (var cookie of Array.from(document.cookie.split(/;\s?/))) { | 68 | for (var cookie of Array.from(document.cookie.split(/;\s?/))) { |
| 58 | if (cookie[0] !== '_') { | 69 | if (cookie[0] !== "_") { |
| 59 | cookie = cookie.split('='); | 70 | cookie = cookie.split("="); |
| 60 | result[cookie[0]] = cookie[1]; | 71 | result[cookie[0]] = cookie[1]; |
| 61 | } | 72 | } |
| 62 | } | 73 | } |
| @@ -11,22 +11,38 @@ · 10 unmodified lines | |||
| 11 | */ | 11 | */ |
| 12 | this.Events = { | 12 | this.Events = { |
| 13 | on(event, callback) { | 13 | on(event, callback) { |
| 14 | if (event.indexOf(' ') >= 0) { | 14 | if (event.indexOf(" ") >= 0) { |
| 15 | for (var name of Array.from(event.split(' '))) { this.on(name, callback); } | 15 | for (var name of Array.from(event.split(" "))) { |
| 16 | this.on(name, callback); | ||
| 17 | } | ||
| 16 | } else { | 18 | } else { |
| 17 | let base; | 19 | let base; |
| 18 | (((base = this._callbacks != null ? this._callbacks : (this._callbacks = {})))[event] != null ? base[event] : (base[event] = [])).push(callback); | 20 | ((base = |
| 21 | this._callbacks != null ? this._callbacks : (this._callbacks = {}))[ | ||
| 22 | event | ||
| 23 | ] != null | ||
| 24 | ? base[event] | ||
| 25 | : (base[event] = []) | ||
| 26 | ).push(callback); | ||
| 19 | } | 27 | } |
| 20 | return this; | 28 | return this; |
| 21 | }, | 29 | }, |
| 22 | 30 | ||
| 23 | off(event, callback) { | 31 | off(event, callback) { |
| 24 | let callbacks, index; | 32 | let callbacks, index; |
| 25 | if (event.indexOf(' ') >= 0) { | 33 | if (event.indexOf(" ") >= 0) { |
| 26 | for (var name of Array.from(event.split(' '))) { this.off(name, callback); } | 34 | for (var name of Array.from(event.split(" "))) { |
| 27 | } else if ((callbacks = this._callbacks != null ? this._callbacks[event] : undefined) && ((index = callbacks.indexOf(callback)) >= 0)) { | 35 | this.off(name, callback); |
| 36 | } | ||
| 37 | } else if ( | ||
| 38 | (callbacks = | ||
| 39 | this._callbacks != null ? this._callbacks[event] : undefined) && | ||
| 40 | (index = callbacks.indexOf(callback)) >= 0 | ||
| 41 | ) { | ||
| 28 | callbacks.splice(index, 1); | 42 | callbacks.splice(index, 1); |
| 29 | if (!callbacks.length) { delete this._callbacks[event]; } | 43 | if (!callbacks.length) { |
| 44 | delete this._callbacks[event]; | ||
| 45 | } | ||
| 30 | } | 46 | } |
| 31 | return this; | 47 | return this; |
| 32 | }, | 48 | }, |
| @@ -34,20 +50,28 @@ this.Events = { · 1 unmodified line | |||
| 34 | trigger(event, ...args) { | 50 | trigger(event, ...args) { |
| 35 | let callbacks; | 51 | let callbacks; |
| 36 | this.eventInProgress = { name: event, args }; | 52 | this.eventInProgress = { name: event, args }; |
| 37 | if (callbacks = this._callbacks != null ? this._callbacks[event] : undefined) { | 53 | if ( |
| 38 | for (var callback of Array.from(callbacks.slice(0))) { if (typeof callback === 'function') { | 54 | (callbacks = this._callbacks != null ? this._callbacks[event] : undefined) |
| 39 | callback(...Array.from(args || [])); | 55 | ) { |
| 40 | } } | 56 | for (var callback of Array.from(callbacks.slice(0))) { |
| 57 | if (typeof callback === "function") { | ||
| 58 | callback(...Array.from(args || [])); | ||
| 59 | } | ||
| 60 | } | ||
| 41 | } | 61 | } |
| 42 | this.eventInProgress = null; | 62 | this.eventInProgress = null; |
| 43 | if (event !== 'all') { this.trigger('all', event, ...Array.from(args)); } | 63 | if (event !== "all") { |
| 64 | this.trigger("all", event, ...Array.from(args)); | ||
| 65 | } | ||
| 44 | return this; | 66 | return this; |
| 45 | }, | 67 | }, |
| 46 | 68 | ||
| 47 | removeEvent(event) { | 69 | removeEvent(event) { |
| 48 | if (this._callbacks != null) { | 70 | if (this._callbacks != null) { |
| 49 | for (var name of Array.from(event.split(' '))) { delete this._callbacks[name]; } | 71 | for (var name of Array.from(event.split(" "))) { |
| 72 | delete this._callbacks[name]; | ||
| 73 | } | ||
| 50 | } | 74 | } |
| 51 | return this; | 75 | return this; |
| 52 | } | 76 | }, |
| 53 | }; | 77 | }; |
| @@ -12,22 +12,24 @@ let currentSlug = null; · 11 unmodified lines | |||
| 12 | const imageCache = {}; | 12 | const imageCache = {}; |
| 13 | const urlCache = {}; | 13 | const urlCache = {}; |
| 14 | 14 | ||
| 15 | const withImage = function(url, action) { | 15 | const withImage = function (url, action) { |
| 16 | if (imageCache[url]) { | 16 | if (imageCache[url]) { |
| 17 | return action(imageCache[url]); | 17 | return action(imageCache[url]); |
| 18 | } else { | 18 | } else { |
| 19 | const img = new Image(); | 19 | const img = new Image(); |
| 20 | img.crossOrigin = 'anonymous'; | 20 | img.crossOrigin = "anonymous"; |
| 21 | img.src = url; | 21 | img.src = url; |
| 22 | return img.onload = () => { | 22 | return (img.onload = () => { |
| 23 | imageCache[url] = img; | 23 | imageCache[url] = img; |
| 24 | return action(img); | 24 | return action(img); |
| 25 | }; | 25 | }); |
| 26 | } | 26 | } |
| 27 | }; | 27 | }; |
| 28 | 28 | ||
| 29 | this.setFaviconForDoc = function(doc) { | 29 | this.setFaviconForDoc = function (doc) { |
| 30 | if (currentSlug === doc.slug) { return; } | 30 | if (currentSlug === doc.slug) { |
| 31 | return; | ||
| 32 | } | ||
| 31 | 33 | ||
| 32 | const favicon = $('link[rel="icon"]'); | 34 | const favicon = $('link[rel="icon"]'); |
| 33 | 35 | ||
| @@ -41,51 +43,67 @@ this.setFaviconForDoc = function(doc) { · 7 unmodified lines | |||
| 41 | return; | 43 | return; |
| 42 | } | 44 | } |
| 43 | 45 | ||
| 44 | const iconEl = $(`._icon-${doc.slug.split('~')[0]}`); | 46 | const iconEl = $(`._icon-${doc.slug.split("~")[0]}`); |
| 45 | if (iconEl === null) { return; } | 47 | if (iconEl === null) { |
| 48 | return; | ||
| 49 | } | ||
| 46 | 50 | ||
| 47 | const styles = window.getComputedStyle(iconEl, ':before'); | 51 | const styles = window.getComputedStyle(iconEl, ":before"); |
| 48 | 52 | ||
| 49 | const backgroundPositionX = styles['background-position-x']; | 53 | const backgroundPositionX = styles["background-position-x"]; |
| 50 | const backgroundPositionY = styles['background-position-y']; | 54 | const backgroundPositionY = styles["background-position-y"]; |
| 51 | if ((backgroundPositionX === undefined) || (backgroundPositionY === undefined)) { return; } | 55 | if (backgroundPositionX === undefined || backgroundPositionY === undefined) { |
| 56 | return; | ||
| 57 | } | ||
| 52 | 58 | ||
| 53 | const bgUrl = app.config.favicon_spritesheet; | 59 | const bgUrl = app.config.favicon_spritesheet; |
| 54 | const sourceSize = 16; | 60 | const sourceSize = 16; |
| 55 | const sourceX = Math.abs(parseInt(backgroundPositionX.slice(0, -2))); | 61 | const sourceX = Math.abs(parseInt(backgroundPositionX.slice(0, -2))); |
| 56 | const sourceY = Math.abs(parseInt(backgroundPositionY.slice(0, -2))); | 62 | const sourceY = Math.abs(parseInt(backgroundPositionY.slice(0, -2))); |
| 57 | 63 | ||
| 58 | return withImage(bgUrl, docImg => withImage(defaultUrl, function(defaultImg) { | 64 | return withImage(bgUrl, (docImg) => |
| 59 | const size = defaultImg.width; | 65 | withImage(defaultUrl, function (defaultImg) { |
| 60 | 66 | const size = defaultImg.width; | |
| 61 | const canvas = document.createElement('canvas'); | 67 | |
| 62 | const ctx = canvas.getContext('2d'); | 68 | const canvas = document.createElement("canvas"); |
| 63 | 69 | const ctx = canvas.getContext("2d"); | |
| 64 | canvas.width = size; | 70 | |
| 65 | canvas.height = size; | 71 | canvas.width = size; |
| 66 | ctx.drawImage(defaultImg, 0, 0); | 72 | canvas.height = size; |
| 67 | 73 | ctx.drawImage(defaultImg, 0, 0); | |
| 68 | const docIconPercentage = 65; | 74 | |
| 69 | const destinationCoords = (size / 100) * (100 - docIconPercentage); | 75 | const docIconPercentage = 65; |
| 70 | const destinationSize = (size / 100) * docIconPercentage; | 76 | const destinationCoords = (size / 100) * (100 - docIconPercentage); |
| 71 | 77 | const destinationSize = (size / 100) * docIconPercentage; | |
| 72 | ctx.drawImage(docImg, sourceX, sourceY, sourceSize, sourceSize, destinationCoords, destinationCoords, destinationSize, destinationSize); | 78 | |
| 73 | 79 | ctx.drawImage( | |
| 74 | try { | 80 | docImg, |
| 75 | urlCache[doc.slug] = canvas.toDataURL(); | 81 | sourceX, |
| 76 | favicon.href = urlCache[doc.slug]; | 82 | sourceY, |
| 77 | 83 | sourceSize, | |
| 78 | return currentSlug = doc.slug; | 84 | sourceSize, |
| 79 | } catch (error) { | 85 | destinationCoords, |
| 80 | Raven.captureException(error, { level: 'info' }); | 86 | destinationCoords, |
| 81 | return this.resetFavicon(); | 87 | destinationSize, |
| 82 | } | 88 | destinationSize, |
| 83 | })); | 89 | ); |
| 90 | |||
| 91 | try { | ||
| 92 | urlCache[doc.slug] = canvas.toDataURL(); | ||
| 93 | favicon.href = urlCache[doc.slug]; | ||
| 94 | |||
| 95 | return (currentSlug = doc.slug); | ||
| 96 | } catch (error) { | ||
| 97 | Raven.captureException(error, { level: "info" }); | ||
| 98 | return this.resetFavicon(); | ||
| 99 | } | ||
| 100 | }), | ||
| 101 | ); | ||
| 84 | }; | 102 | }; |
| 85 | 103 | ||
| 86 | this.resetFavicon = function() { | 104 | this.resetFavicon = function () { |
| 87 | if ((defaultUrl !== null) && (currentSlug !== null)) { | 105 | if (defaultUrl !== null && currentSlug !== null) { |
| 88 | $('link[rel="icon"]').href = defaultUrl; | 106 | $('link[rel="icon"]').href = defaultUrl; |
| 89 | return currentSlug = null; | 107 | return (currentSlug = null); |
| 90 | } | 108 | } |
| 91 | }; | 109 | }; |
| @@ -6,4 +6,4 @@ · 5 unmodified lines | |||
| 6 | * This source code is licensed under the terms of the Mozilla | 6 | * This source code is licensed under the terms of the Mozilla |
| 7 | * Public License, v. 2.0, a copy of which may be obtained at: | 7 | * Public License, v. 2.0, a copy of which may be obtained at: |
| 8 | * http://mozilla.org/MPL/2.0/ | 8 | * http://mozilla.org/MPL/2.0/ |
| 9 | */ | 9 | */ |
| @@ -13,50 +13,54 @@ · 12 unmodified lines | |||
| 13 | * Based on github.com/visionmedia/page.js | 13 | * Based on github.com/visionmedia/page.js |
| 14 | * Licensed under the MIT license | 14 | * Licensed under the MIT license |
| 15 | * Copyright 2012 TJ Holowaychuk <tj@vision-media.ca> | 15 | * Copyright 2012 TJ Holowaychuk <tj@vision-media.ca> |
| 16 | */ | 16 | */ |
| 17 | 17 | ||
| 18 | let running = false; | 18 | let running = false; |
| 19 | let currentState = null; | 19 | let currentState = null; |
| 20 | const callbacks = []; | 20 | const callbacks = []; |
| 21 | 21 | ||
| 22 | this.page = function(value, fn) { | 22 | this.page = function (value, fn) { |
| 23 | if (typeof value === 'function') { | 23 | if (typeof value === "function") { |
| 24 | page('*', value); | 24 | page("*", value); |
| 25 | } else if (typeof fn === 'function') { | 25 | } else if (typeof fn === "function") { |
| 26 | const route = new Route(value); | 26 | const route = new Route(value); |
| 27 | callbacks.push(route.middleware(fn)); | 27 | callbacks.push(route.middleware(fn)); |
| 28 | } else if (typeof value === 'string') { | 28 | } else if (typeof value === "string") { |
| 29 | page.show(value, fn); | 29 | page.show(value, fn); |
| 30 | } else { | 30 | } else { |
| 31 | page.start(value); | 31 | page.start(value); |
| 32 | } | 32 | } |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | page.start = function(options) { | 35 | page.start = function (options) { |
| 36 | if (options == null) { options = {}; } | 36 | if (options == null) { |
| 37 | options = {}; | ||
| 38 | } | ||
| 37 | if (!running) { | 39 | if (!running) { |
| 38 | running = true; | 40 | running = true; |
| 39 | addEventListener('popstate', onpopstate); | 41 | addEventListener("popstate", onpopstate); |
| 40 | addEventListener('click', onclick); | 42 | addEventListener("click", onclick); |
| 41 | page.replace(currentPath(), null, null, true); | 43 | page.replace(currentPath(), null, null, true); |
| 42 | } | 44 | } |
| 43 | }; | 45 | }; |
| 44 | 46 | ||
| 45 | page.stop = function() { | 47 | page.stop = function () { |
| 46 | if (running) { | 48 | if (running) { |
| 47 | running = false; | 49 | running = false; |
| 48 | removeEventListener('click', onclick); | 50 | removeEventListener("click", onclick); |
| 49 | removeEventListener('popstate', onpopstate); | 51 | removeEventListener("popstate", onpopstate); |
| 50 | } | 52 | } |
| 51 | }; | 53 | }; |
| 52 | 54 | ||
| 53 | page.show = function(path, state) { | 55 | page.show = function (path, state) { |
| 54 | let res; | 56 | let res; |
| 55 | if (path === (currentState != null ? currentState.path : undefined)) { return; } | 57 | if (path === (currentState != null ? currentState.path : undefined)) { |
| 58 | return; | ||
| 59 | } | ||
| 56 | const context = new Context(path, state); | 60 | const context = new Context(path, state); |
| 57 | const previousState = currentState; | 61 | const previousState = currentState; |
| 58 | currentState = context.state; | 62 | currentState = context.state; |
| 59 | if (res = page.dispatch(context)) { | 63 | if ((res = page.dispatch(context))) { |
| 60 | currentState = previousState; | 64 | currentState = previousState; |
| 61 | location.assign(res); | 65 | location.assign(res); |
| 62 | } else { | 66 | } else { |
| @@ -67,12 +71,14 @@ page.show = function(path, state) { · 4 unmodified lines | |||
| 67 | return context; | 71 | return context; |
| 68 | }; | 72 | }; |
| 69 | 73 | ||
| 70 | page.replace = function(path, state, skipDispatch, init) { | 74 | page.replace = function (path, state, skipDispatch, init) { |
| 71 | let result; | 75 | let result; |
| 72 | let context = new Context(path, state || currentState); | 76 | let context = new Context(path, state || currentState); |
| 73 | context.init = init; | 77 | context.init = init; |
| 74 | currentState = context.state; | 78 | currentState = context.state; |
| 75 | if (!skipDispatch) { result = page.dispatch(context); } | 79 | if (!skipDispatch) { |
| 80 | result = page.dispatch(context); | ||
| 81 | } | ||
| 76 | if (result) { | 82 | if (result) { |
| 77 | context = new Context(result); | 83 | context = new Context(result); |
| 78 | context.init = init; | 84 | context.init = init; |
| @@ -81,15 +87,19 @@ page.replace = function(path, state, skipDispatch, init) { · 2 unmodified lines | |||
| 81 | } | 87 | } |
| 82 | context.replaceState(); | 88 | context.replaceState(); |
| 83 | updateCanonicalLink(); | 89 | updateCanonicalLink(); |
| 84 | if (!skipDispatch) { track(); } | 90 | if (!skipDispatch) { |
| 91 | track(); | ||
| 92 | } | ||
| 85 | return context; | 93 | return context; |
| 86 | }; | 94 | }; |
| 87 | 95 | ||
| 88 | page.dispatch = function(context) { | 96 | page.dispatch = function (context) { |
| 89 | let i = 0; | 97 | let i = 0; |
| 90 | var next = function() { | 98 | var next = function () { |
| 91 | let fn, res; | 99 | let fn, res; |
| 92 | if (fn = callbacks[i++]) { res = fn(context, next); } | 100 | if ((fn = callbacks[i++])) { |
| 101 | res = fn(context, next); | ||
| 102 | } | ||
| 93 | return res; | 103 | return res; |
| 94 | }; | 104 | }; |
| 95 | return next(); | 105 | return next(); |
| @@ -113,11 +123,11 @@ class Context { · 17 unmodified lines | |||
| 113 | } | 123 | } |
| 114 | 124 | ||
| 115 | static isLastState(state) { | 125 | static isLastState(state) { |
| 116 | return state.id === (this.stateId - 1); | 126 | return state.id === this.stateId - 1; |
| 117 | } | 127 | } |
| 118 | 128 | ||
| 119 | static isInitialPopState(state) { | 129 | static isInitialPopState(state) { |
| 120 | return (state.path === this.initialPath) && (this.stateId === 1); | 130 | return state.path === this.initialPath && this.stateId === 1; |
| 121 | } | 131 | } |
| 122 | 132 | ||
| 123 | static isSameSession(state) { | 133 | static isSameSession(state) { |
| @@ -125,27 +135,40 @@ class Context { · 1 unmodified line | |||
| 125 | } | 135 | } |
| 126 | 136 | ||
| 127 | constructor(path, state) { | 137 | constructor(path, state) { |
| 128 | if (path == null) { path = '/'; } | 138 | if (path == null) { |
| 139 | path = "/"; | ||
| 140 | } | ||
| 129 | this.path = path; | 141 | this.path = path; |
| 130 | if (state == null) { state = {}; } | 142 | if (state == null) { |
| 143 | state = {}; | ||
| 144 | } | ||
| 131 | this.state = state; | 145 | this.state = state; |
| 132 | this.pathname = this.path.replace(/(?:\?([^#]*))?(?:#(.*))?$/, (_, query, hash) => { | 146 | this.pathname = this.path.replace( |
| 133 | this.query = query; | 147 | /(?:\?([^#]*))?(?:#(.*))?$/, |
| 134 | this.hash = hash; | 148 | (_, query, hash) => { |
| 135 | return ''; | 149 | this.query = query; |
| 136 | }); | 150 | this.hash = hash; |
| 137 | 151 | return ""; | |
| 138 | if (this.state.id == null) { this.state.id = this.constructor.stateId++; } | 152 | }, |
| 139 | if (this.state.sessionId == null) { this.state.sessionId = this.constructor.sessionId; } | 153 | ); |
| 154 | |||
| 155 | if (this.state.id == null) { | ||
| 156 | this.state.id = this.constructor.stateId++; | ||
| 157 | } | ||
| 158 | if (this.state.sessionId == null) { | ||
| 159 | this.state.sessionId = this.constructor.sessionId; | ||
| 160 | } | ||
| 140 | this.state.path = this.path; | 161 | this.state.path = this.path; |
| 141 | } | 162 | } |
| 142 | 163 | ||
| 143 | pushState() { | 164 | pushState() { |
| 144 | history.pushState(this.state, '', this.path); | 165 | history.pushState(this.state, "", this.path); |
| 145 | } | 166 | } |
| 146 | 167 | ||
| 147 | replaceState() { | 168 | replaceState() { |
| 148 | try { history.replaceState(this.state, '', this.path); } catch (error) {} // NS_ERROR_FAILURE in Firefox | 169 | try { |
| 170 | history.replaceState(this.state, "", this.path); | ||
| 171 | } catch (error) {} // NS_ERROR_FAILURE in Firefox | ||
| 149 | } | 172 | } |
| 150 | } | 173 | } |
| 151 | Context.initClass(); | 174 | Context.initClass(); |
| @@ -153,7 +176,9 @@ Context.initClass(); · 1 unmodified line | |||
| 153 | class Route { | 176 | class Route { |
| 154 | constructor(path, options) { | 177 | constructor(path, options) { |
| 155 | this.path = path; | 178 | this.path = path; |
| 156 | if (options == null) { options = {}; } | 179 | if (options == null) { |
| 180 | options = {}; | ||
| 181 | } | ||
| 157 | this.keys = []; | 182 | this.keys = []; |
| 158 | this.regexp = pathtoRegexp(this.path, this.keys); | 183 | this.regexp = pathtoRegexp(this.path, this.keys); |
| 159 | } | 184 | } |
| @@ -172,13 +197,17 @@ class Route { · 12 unmodified lines | |||
| 172 | 197 | ||
| 173 | match(path, params) { | 198 | match(path, params) { |
| 174 | let matchData; | 199 | let matchData; |
| 175 | if (!(matchData = this.regexp.exec(path))) { return; } | 200 | if (!(matchData = this.regexp.exec(path))) { |
| 201 | return; | ||
| 202 | } | ||
| 176 | 203 | ||
| 177 | const iterable = matchData.slice(1); | 204 | const iterable = matchData.slice(1); |
| 178 | for (let i = 0; i < iterable.length; i++) { | 205 | for (let i = 0; i < iterable.length; i++) { |
| 179 | var key; | 206 | var key; |
| 180 | var value = iterable[i]; | 207 | var value = iterable[i]; |
| 181 | if (typeof value === 'string') { value = decodeURIComponent(value); } | 208 | if (typeof value === "string") { |
| 209 | value = decodeURIComponent(value); | ||
| 210 | } | ||
| 182 | if ((key = this.keys[i])) { | 211 | if ((key = this.keys[i])) { |
| 183 | params[key.name] = value; | 212 | params[key.name] = value; |
| 184 | } else { | 213 | } else { |
| @@ -189,32 +218,50 @@ class Route { · 4 unmodified lines | |||
| 189 | } | 218 | } |
| 190 | } | 219 | } |
| 191 | 220 | ||
| 192 | var pathtoRegexp = function(path, keys) { | 221 | var pathtoRegexp = function (path, keys) { |
| 193 | if (path instanceof RegExp) { return path; } | 222 | if (path instanceof RegExp) { |
| 223 | return path; | ||
| 224 | } | ||
| 194 | 225 | ||
| 195 | if (path instanceof Array) { path = `(${path.join('|')})`; } | 226 | if (path instanceof Array) { |
| 227 | path = `(${path.join("|")})`; | ||
| 228 | } | ||
| 196 | path = path | 229 | path = path |
| 197 | .replace(/\/\(/g, '(?:/') | 230 | .replace(/\/\(/g, "(?:/") |
| 198 | .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional) { | 231 | .replace( |
| 199 | if (slash == null) { slash = ''; } | 232 | /(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, |
| 200 | if (format == null) { format = ''; } | 233 | function (_, slash, format, key, capture, optional) { |
| 201 | keys.push({name: key, optional: !!optional}); | 234 | if (slash == null) { |
| 202 | let str = optional ? '' : slash; | 235 | slash = ""; |
| 203 | str += '(?:'; | 236 | } |
| 204 | if (optional) { str += slash; } | 237 | if (format == null) { |
| 205 | str += format; | 238 | format = ""; |
| 206 | str += capture || (format ? '([^/.]+?)' : '([^/]+?)'); | 239 | } |
| 207 | str += ')'; | 240 | keys.push({ name: key, optional: !!optional }); |
| 208 | if (optional) { str += optional; } | 241 | let str = optional ? "" : slash; |
| 209 | return str; | 242 | str += "(?:"; |
| 210 | }).replace(/([\/.])/g, '\\$1') | 243 | if (optional) { |
| 211 | .replace(/\*/g, '(.*)'); | 244 | str += slash; |
| 245 | } | ||
| 246 | str += format; | ||
| 247 | str += capture || (format ? "([^/.]+?)" : "([^/]+?)"); | ||
| 248 | str += ")"; | ||
| 249 | if (optional) { | ||
| 250 | str += optional; | ||
| 251 | } | ||
| 252 | return str; | ||
| 253 | }, | ||
| 254 | ) | ||
| 255 | .replace(/([\/.])/g, "\\$1") | ||
| 256 | .replace(/\*/g, "(.*)"); | ||
| 212 | 257 | ||
| 213 | return new RegExp(`^${path}$`); | 258 | return new RegExp(`^${path}$`); |
| 214 | }; | 259 | }; |
| 215 | 260 | ||
| 216 | var onpopstate = function(event) { | 261 | var onpopstate = function (event) { |
| 217 | if (!event.state || Context.isInitialPopState(event.state)) { return; } | 262 | if (!event.state || Context.isInitialPopState(event.state)) { |
| 263 | return; | ||
| 264 | } | ||
| 218 | 265 | ||
| 219 | if (Context.isSameSession(event.state)) { | 266 | if (Context.isSameSession(event.state)) { |
| 220 | page.replace(event.state.path, event.state); | 267 | page.replace(event.state.path, event.state); |
| @@ -223,59 +270,83 @@ var onpopstate = function(event) { · 2 unmodified lines | |||
| 223 | } | 270 | } |
| 224 | }; | 271 | }; |
| 225 | 272 | ||
| 226 | var onclick = function(event) { | 273 | var onclick = function (event) { |
| 227 | try { | 274 | try { |
| 228 | if ((event.which !== 1) || event.metaKey || event.ctrlKey || event.shiftKey || event.defaultPrevented) { return; } | 275 | if ( |
| 276 | event.which !== 1 || | ||
| 277 | event.metaKey || | ||
| 278 | event.ctrlKey || | ||
| 279 | event.shiftKey || | ||
| 280 | event.defaultPrevented | ||
| 281 | ) { | ||
| 282 | return; | ||
| 283 | } | ||
| 229 | } catch (error) { | 284 | } catch (error) { |
| 230 | return; | 285 | return; |
| 231 | } | 286 | } |
| 232 | 287 | ||
| 233 | let link = $.eventTarget(event); | 288 | let link = $.eventTarget(event); |
| 234 | while (link && (link.tagName !== 'A')) { link = link.parentNode; } | 289 | while (link && link.tagName !== "A") { |
| 290 | link = link.parentNode; | ||
| 291 | } | ||
| 235 | 292 | ||
| 236 | if (link && !link.target && isSameOrigin(link.href)) { | 293 | if (link && !link.target && isSameOrigin(link.href)) { |
| 237 | event.preventDefault(); | 294 | event.preventDefault(); |
| 238 | let path = link.pathname + link.search + link.hash; | 295 | let path = link.pathname + link.search + link.hash; |
| 239 | path = path.replace(/^\/\/+/, '/'); // IE11 bug | 296 | path = path.replace(/^\/\/+/, "/"); // IE11 bug |
| 240 | page.show(path); | 297 | page.show(path); |
| 241 | } | 298 | } |
| 242 | }; | 299 | }; |
| 243 | 300 | ||
| 244 | var isSameOrigin = url => url.indexOf(`${location.protocol}//${location.hostname}`) === 0; | 301 | var isSameOrigin = (url) => |
| 302 | url.indexOf(`${location.protocol}//${location.hostname}`) === 0; | ||
| 245 | 303 | ||
| 246 | var updateCanonicalLink = function() { | 304 | var updateCanonicalLink = function () { |
| 247 | if (!this.canonicalLink) { this.canonicalLink = document.head.querySelector('link[rel="canonical"]'); } | 305 | if (!this.canonicalLink) { |
| 248 | return this.canonicalLink.setAttribute('href', `https://${location.host}${location.pathname}`); | 306 | this.canonicalLink = document.head.querySelector('link[rel="canonical"]'); |
| 307 | } | ||
| 308 | return this.canonicalLink.setAttribute( | ||
| 309 | "href", | ||
| 310 | `https://${location.host}${location.pathname}`, | ||
| 311 | ); | ||
| 249 | }; | 312 | }; |
| 250 | 313 | ||
| 251 | const trackers = []; | 314 | const trackers = []; |
| 252 | 315 | ||
| 253 | page.track = function(fn) { | 316 | page.track = function (fn) { |
| 254 | trackers.push(fn); | 317 | trackers.push(fn); |
| 255 | }; | 318 | }; |
| 256 | 319 | ||
| 257 | var track = function() { | 320 | var track = function () { |
| 258 | if (app.config.env !== 'production') { return; } | 321 | if (app.config.env !== "production") { |
| 259 | if (navigator.doNotTrack === '1') { return; } | 322 | return; |
| 260 | if (navigator.globalPrivacyControl) { return; } | 323 | } |
| 324 | if (navigator.doNotTrack === "1") { | ||
| 325 | return; | ||
| 326 | } | ||
| 327 | if (navigator.globalPrivacyControl) { | ||
| 328 | return; | ||
| 329 | } | ||
| 261 | 330 | ||
| 262 | const consentGiven = Cookies.get('analyticsConsent'); | 331 | const consentGiven = Cookies.get("analyticsConsent"); |
| 263 | const consentAsked = Cookies.get('analyticsConsentAsked'); | 332 | const consentAsked = Cookies.get("analyticsConsentAsked"); |
| 264 | 333 | ||
| 265 | if (consentGiven === '1') { | 334 | if (consentGiven === "1") { |
| 266 | for (var tracker of Array.from(trackers)) { tracker.call(); } | 335 | for (var tracker of Array.from(trackers)) { |
| 267 | } else if ((consentGiven === undefined) && (consentAsked === undefined)) { | 336 | tracker.call(); |
| 337 | } | ||
| 338 | } else if (consentGiven === undefined && consentAsked === undefined) { | ||
| 268 | // Only ask for consent once per browser session | 339 | // Only ask for consent once per browser session |
| 269 | Cookies.set('analyticsConsentAsked', '1'); | 340 | Cookies.set("analyticsConsentAsked", "1"); |
| 270 | 341 | ||
| 271 | new app.views.Notif('AnalyticsConsent', {autoHide: null}); | 342 | new app.views.Notif("AnalyticsConsent", { autoHide: null }); |
| 272 | } | 343 | } |
| 273 | }; | 344 | }; |
| 274 | 345 | ||
| 275 | this.resetAnalytics = function() { | 346 | this.resetAnalytics = function () { |
| 276 | for (var cookie of Array.from(document.cookie.split(/;\s?/))) { | 347 | for (var cookie of Array.from(document.cookie.split(/;\s?/))) { |
| 277 | var name = cookie.split('=')[0]; | 348 | var name = cookie.split("=")[0]; |
| 278 | if ((name[0] === '_') && (name[1] !== '_')) { | 349 | if (name[0] === "_" && name[1] !== "_") { |
| 279 | Cookies.expire(name); | 350 | Cookies.expire(name); |
| 280 | } | 351 | } |
| 281 | } | 352 | } |
| @@ -16,32 +16,52 @@ · 15 unmodified lines | |||
| 16 | // | 16 | // |
| 17 | 17 | ||
| 18 | let smoothDistance, smoothDuration, smoothEnd, smoothStart; | 18 | let smoothDistance, smoothDuration, smoothEnd, smoothStart; |
| 19 | this.$ = function(selector, el) { | 19 | this.$ = function (selector, el) { |
| 20 | if (el == null) { el = document; } | 20 | if (el == null) { |
| 21 | try { return el.querySelector(selector); } catch (error) {} | 21 | el = document; |
| 22 | } | ||
| 23 | try { | ||
| 24 | return el.querySelector(selector); | ||
| 25 | } catch (error) {} | ||
| 22 | }; | 26 | }; |
| 23 | 27 | ||
| 24 | this.$$ = function(selector, el) { | 28 | this.$$ = function (selector, el) { |
| 25 | if (el == null) { el = document; } | 29 | if (el == null) { |
| 26 | try { return el.querySelectorAll(selector); } catch (error) {} | 30 | el = document; |
| 31 | } | ||
| 32 | try { | ||
| 33 | return el.querySelectorAll(selector); | ||
| 34 | } catch (error) {} | ||
| 27 | }; | 35 | }; |
| 28 | 36 | ||
| 29 | $.id = id => document.getElementById(id); | 37 | $.id = (id) => document.getElementById(id); |
| 30 | 38 | ||
| 31 | $.hasChild = function(parent, el) { | 39 | $.hasChild = function (parent, el) { |
| 32 | if (!parent) { return; } | 40 | if (!parent) { |
| 41 | return; | ||
| 42 | } | ||
| 33 | while (el) { | 43 | while (el) { |
| 34 | if (el === parent) { return true; } | 44 | if (el === parent) { |
| 35 | if (el === document.body) { return; } | 45 | return true; |
| 46 | } | ||
| 47 | if (el === document.body) { | ||
| 48 | return; | ||
| 49 | } | ||
| 36 | el = el.parentNode; | 50 | el = el.parentNode; |
| 37 | } | 51 | } |
| 38 | }; | 52 | }; |
| 39 | 53 | ||
| 40 | $.closestLink = function(el, parent) { | 54 | $.closestLink = function (el, parent) { |
| 41 | if (parent == null) { parent = document.body; } | 55 | if (parent == null) { |
| 56 | parent = document.body; | ||
| 57 | } | ||
| 42 | while (el) { | 58 | while (el) { |
| 43 | if (el.tagName === 'A') { return el; } | 59 | if (el.tagName === "A") { |
| 44 | if (el === parent) { return; } | 60 | return el; |
| 61 | } | ||
| 62 | if (el === parent) { | ||
| 63 | return; | ||
| 64 | } | ||
| 45 | el = el.parentNode; | 65 | el = el.parentNode; |
| 46 | } | 66 | } |
| 47 | }; | 67 | }; |
| @@ -50,55 +70,85 @@ $.closestLink = function(el, parent) { · 2 unmodified lines | |||
| 50 | // Events | 70 | // Events |
| 51 | // | 71 | // |
| 52 | 72 | ||
| 53 | $.on = function(el, event, callback, useCapture) { | 73 | $.on = function (el, event, callback, useCapture) { |
| 54 | if (useCapture == null) { useCapture = false; } | 74 | if (useCapture == null) { |
| 55 | if (event.indexOf(' ') >= 0) { | 75 | useCapture = false; |
| 56 | for (var name of Array.from(event.split(' '))) { $.on(el, name, callback); } | 76 | } |
| 77 | if (event.indexOf(" ") >= 0) { | ||
| 78 | for (var name of Array.from(event.split(" "))) { | ||
| 79 | $.on(el, name, callback); | ||
| 80 | } | ||
| 57 | } else { | 81 | } else { |
| 58 | el.addEventListener(event, callback, useCapture); | 82 | el.addEventListener(event, callback, useCapture); |
| 59 | } | 83 | } |
| 60 | }; | 84 | }; |
| 61 | 85 | ||
| 62 | $.off = function(el, event, callback, useCapture) { | 86 | $.off = function (el, event, callback, useCapture) { |
| 63 | if (useCapture == null) { useCapture = false; } | 87 | if (useCapture == null) { |
| 64 | if (event.indexOf(' ') >= 0) { | 88 | useCapture = false; |
| 65 | for (var name of Array.from(event.split(' '))) { $.off(el, name, callback); } | 89 | } |
| 90 | if (event.indexOf(" ") >= 0) { | ||
| 91 | for (var name of Array.from(event.split(" "))) { | ||
| 92 | $.off(el, name, callback); | ||
| 93 | } | ||
| 66 | } else { | 94 | } else { |
| 67 | el.removeEventListener(event, callback, useCapture); | 95 | el.removeEventListener(event, callback, useCapture); |
| 68 | } | 96 | } |
| 69 | }; | 97 | }; |
| 70 | 98 | ||
| 71 | $.trigger = function(el, type, canBubble, cancelable) { | 99 | $.trigger = function (el, type, canBubble, cancelable) { |
| 72 | if (canBubble == null) { canBubble = true; } | 100 | if (canBubble == null) { |
| 73 | if (cancelable == null) { cancelable = true; } | 101 | canBubble = true; |
| 74 | const event = document.createEvent('Event'); | 102 | } |
| 103 | if (cancelable == null) { | ||
| 104 | cancelable = true; | ||
| 105 | } | ||
| 106 | const event = document.createEvent("Event"); | ||
| 75 | event.initEvent(type, canBubble, cancelable); | 107 | event.initEvent(type, canBubble, cancelable); |
| 76 | el.dispatchEvent(event); | 108 | el.dispatchEvent(event); |
| 77 | }; | 109 | }; |
| 78 | 110 | ||
| 79 | $.click = function(el) { | 111 | $.click = function (el) { |
| 80 | const event = document.createEvent('MouseEvent'); | 112 | const event = document.createEvent("MouseEvent"); |
| 81 | event.initMouseEvent('click', true, true, window, null, 0, 0, 0, 0, false, false, false, false, 0, null); | 113 | event.initMouseEvent( |
| 114 | "click", | ||
| 115 | true, | ||
| 116 | true, | ||
| 117 | window, | ||
| 118 | null, | ||
| 119 | 0, | ||
| 120 | 0, | ||
| 121 | 0, | ||
| 122 | 0, | ||
| 123 | false, | ||
| 124 | false, | ||
| 125 | false, | ||
| 126 | false, | ||
| 127 | 0, | ||
| 128 | null, | ||
| 129 | ); | ||
| 82 | el.dispatchEvent(event); | 130 | el.dispatchEvent(event); |
| 83 | }; | 131 | }; |
| 84 | 132 | ||
| 85 | $.stopEvent = function(event) { | 133 | $.stopEvent = function (event) { |
| 86 | event.preventDefault(); | 134 | event.preventDefault(); |
| 87 | event.stopPropagation(); | 135 | event.stopPropagation(); |
| 88 | event.stopImmediatePropagation(); | 136 | event.stopImmediatePropagation(); |
| 89 | }; | 137 | }; |
| 90 | 138 | ||
| 91 | $.eventTarget = event => event.target.correspondingUseElement || event.target; | 139 | $.eventTarget = (event) => event.target.correspondingUseElement || event.target; |
| 92 | 140 | ||
| 93 | // | 141 | // |
| 94 | // Manipulation | 142 | // Manipulation |
| 95 | // | 143 | // |
| 96 | 144 | ||
| 97 | const buildFragment = function(value) { | 145 | const buildFragment = function (value) { |
| 98 | const fragment = document.createDocumentFragment(); | 146 | const fragment = document.createDocumentFragment(); |
| 99 | 147 | ||
| 100 | if ($.isCollection(value)) { | 148 | if ($.isCollection(value)) { |
| 101 | for (var child of Array.from($.makeArray(value))) { fragment.appendChild(child); } | 149 | for (var child of Array.from($.makeArray(value))) { |
| 150 | fragment.appendChild(child); | ||
| 151 | } | ||
| 102 | } else { | 152 | } else { |
| 103 | fragment.innerHTML = value; | 153 | fragment.innerHTML = value; |
| 104 | } | 154 | } |
| @@ -106,36 +156,40 @@ const buildFragment = function(value) { · 1 unmodified line | |||
| 106 | return fragment; | 156 | return fragment; |
| 107 | }; | 157 | }; |
| 108 | 158 | ||
| 109 | $.append = function(el, value) { | 159 | $.append = function (el, value) { |
| 110 | if (typeof value === 'string') { | 160 | if (typeof value === "string") { |
| 111 | el.insertAdjacentHTML('beforeend', value); | 161 | el.insertAdjacentHTML("beforeend", value); |
| 112 | } else { | 162 | } else { |
| 113 | if ($.isCollection(value)) { value = buildFragment(value); } | 163 | if ($.isCollection(value)) { |
| 164 | value = buildFragment(value); | ||
| 165 | } | ||
| 114 | el.appendChild(value); | 166 | el.appendChild(value); |
| 115 | } | 167 | } |
| 116 | }; | 168 | }; |
| 117 | 169 | ||
| 118 | $.prepend = function(el, value) { | 170 | $.prepend = function (el, value) { |
| 119 | if (!el.firstChild) { | 171 | if (!el.firstChild) { |
| 120 | $.append(value); | 172 | $.append(value); |
| 121 | } else if (typeof value === 'string') { | 173 | } else if (typeof value === "string") { |
| 122 | el.insertAdjacentHTML('afterbegin', value); | 174 | el.insertAdjacentHTML("afterbegin", value); |
| 123 | } else { | 175 | } else { |
| 124 | if ($.isCollection(value)) { value = buildFragment(value); } | 176 | if ($.isCollection(value)) { |
| 177 | value = buildFragment(value); | ||
| 178 | } | ||
| 125 | el.insertBefore(value, el.firstChild); | 179 | el.insertBefore(value, el.firstChild); |
| 126 | } | 180 | } |
| 127 | }; | 181 | }; |
| 128 | 182 | ||
| 129 | $.before = function(el, value) { | 183 | $.before = function (el, value) { |
| 130 | if ((typeof value === 'string') || $.isCollection(value)) { | 184 | if (typeof value === "string" || $.isCollection(value)) { |
| 131 | value = buildFragment(value); | 185 | value = buildFragment(value); |
| 132 | } | 186 | } |
| 133 | 187 | ||
| 134 | el.parentNode.insertBefore(value, el); | 188 | el.parentNode.insertBefore(value, el); |
| 135 | }; | 189 | }; |
| 136 | 190 | ||
| 137 | $.after = function(el, value) { | 191 | $.after = function (el, value) { |
| 138 | if ((typeof value === 'string') || $.isCollection(value)) { | 192 | if (typeof value === "string" || $.isCollection(value)) { |
| 139 | value = buildFragment(value); | 193 | value = buildFragment(value); |
| 140 | } | 194 | } |
| 141 | 195 | ||
| @@ -146,11 +200,13 @@ $.after = function(el, value) { · 4 unmodified lines | |||
| 146 | } | 200 | } |
| 147 | }; | 201 | }; |
| 148 | 202 | ||
| 149 | $.remove = function(value) { | 203 | $.remove = function (value) { |
| 150 | if ($.isCollection(value)) { | 204 | if ($.isCollection(value)) { |
| 151 | for (var el of Array.from($.makeArray(value))) { if (el.parentNode != null) { | 205 | for (var el of Array.from($.makeArray(value))) { |
| 152 | el.parentNode.removeChild(el); | 206 | if (el.parentNode != null) { |
| 153 | } } | 207 | el.parentNode.removeChild(el); |
| 208 | } | ||
| 209 | } | ||
| 154 | } else { | 210 | } else { |
| 155 | if (value.parentNode != null) { | 211 | if (value.parentNode != null) { |
| 156 | value.parentNode.removeChild(value); | 212 | value.parentNode.removeChild(value); |
| @@ -158,13 +214,15 @@ $.remove = function(value) { · 1 unmodified line | |||
| 158 | } | 214 | } |
| 159 | }; | 215 | }; |
| 160 | 216 | ||
| 161 | $.empty = function(el) { | 217 | $.empty = function (el) { |
| 162 | while (el.firstChild) { el.removeChild(el.firstChild); } | 218 | while (el.firstChild) { |
| 219 | el.removeChild(el.firstChild); | ||
| 220 | } | ||
| 163 | }; | 221 | }; |
| 164 | 222 | ||
| 165 | // Calls the function while the element is off the DOM to avoid triggering | 223 | // Calls the function while the element is off the DOM to avoid triggering |
| 166 | // unnecessary reflows and repaints. | 224 | // unnecessary reflows and repaints. |
| 167 | $.batchUpdate = function(el, fn) { | 225 | $.batchUpdate = function (el, fn) { |
| 168 | const parent = el.parentNode; | 226 | const parent = el.parentNode; |
| 169 | const sibling = el.nextSibling; | 227 | const sibling = el.nextSibling; |
| 170 | parent.removeChild(el); | 228 | parent.removeChild(el); |
| @@ -182,14 +240,16 @@ $.batchUpdate = function(el, fn) { · 11 unmodified lines | |||
| 182 | // Offset | 240 | // Offset |
| 183 | // | 241 | // |
| 184 | 242 | ||
| 185 | $.rect = el => el.getBoundingClientRect(); | 243 | $.rect = (el) => el.getBoundingClientRect(); |
| 186 | 244 | ||
| 187 | $.offset = function(el, container) { | 245 | $.offset = function (el, container) { |
| 188 | if (container == null) { container = document.body; } | 246 | if (container == null) { |
| 247 | container = document.body; | ||
| 248 | } | ||
| 189 | let top = 0; | 249 | let top = 0; |
| 190 | let left = 0; | 250 | let left = 0; |
| 191 | 251 | ||
| 192 | while (el && (el !== container)) { | 252 | while (el && el !== container) { |
| 193 | top += el.offsetTop; | 253 | top += el.offsetTop; |
| 194 | left += el.offsetLeft; | 254 | left += el.offsetLeft; |
| 195 | el = el.offsetParent; | 255 | el = el.offsetParent; |
| @@ -197,102 +257,131 @@ $.offset = function(el, container) { · 1 unmodified line | |||
| 197 | 257 | ||
| 198 | return { | 258 | return { |
| 199 | top, | 259 | top, |
| 200 | left | 260 | left, |
| 201 | }; | 261 | }; |
| 202 | }; | 262 | }; |
| 203 | 263 | ||
| 204 | $.scrollParent = function(el) { | 264 | $.scrollParent = function (el) { |
| 205 | while ((el = el.parentNode) && (el.nodeType === 1)) { | 265 | while ((el = el.parentNode) && el.nodeType === 1) { |
| 206 | var needle; | 266 | var needle; |
| 207 | if (el.scrollTop > 0) { break; } | 267 | if (el.scrollTop > 0) { |
| 208 | if ((needle = __guard__(getComputedStyle(el), x => x.overflowY), ['auto', 'scroll'].includes(needle))) { break; } | 268 | break; |
| 269 | } | ||
| 270 | if ( | ||
| 271 | ((needle = __guard__(getComputedStyle(el), (x) => x.overflowY)), | ||
| 272 | ["auto", "scroll"].includes(needle)) | ||
| 273 | ) { | ||
| 274 | break; | ||
| 275 | } | ||
| 209 | } | 276 | } |
| 210 | return el; | 277 | return el; |
| 211 | }; | 278 | }; |
| 212 | 279 | ||
| 213 | $.scrollTo = function(el, parent, position, options) { | 280 | $.scrollTo = function (el, parent, position, options) { |
| 214 | if (position == null) { position = 'center'; } | 281 | if (position == null) { |
| 215 | if (options == null) { options = {}; } | 282 | position = "center"; |
| 216 | if (!el) { return; } | 283 | } |
| 284 | if (options == null) { | ||
| 285 | options = {}; | ||
| 286 | } | ||
| 287 | if (!el) { | ||
| 288 | return; | ||
| 289 | } | ||
| 217 | 290 | ||
| 218 | if (parent == null) { parent = $.scrollParent(el); } | 291 | if (parent == null) { |
| 219 | if (!parent) { return; } | 292 | parent = $.scrollParent(el); |
| 293 | } | ||
| 294 | if (!parent) { | ||
| 295 | return; | ||
| 296 | } | ||
| 220 | 297 | ||
| 221 | const parentHeight = parent.clientHeight; | 298 | const parentHeight = parent.clientHeight; |
| 222 | const parentScrollHeight = parent.scrollHeight; | 299 | const parentScrollHeight = parent.scrollHeight; |
| 223 | if (!(parentScrollHeight > parentHeight)) { return; } | 300 | if (!(parentScrollHeight > parentHeight)) { |
| 301 | return; | ||
| 302 | } | ||
| 224 | 303 | ||
| 225 | const { | 304 | const { top } = $.offset(el, parent); |
| 226 | top | 305 | const { offsetTop } = parent.firstElementChild; |
| 227 | } = $.offset(el, parent); | ||
| 228 | const { | ||
| 229 | offsetTop | ||
| 230 | } = parent.firstElementChild; | ||
| 231 | 306 | ||
| 232 | switch (position) { | 307 | switch (position) { |
| 233 | case 'top': | 308 | case "top": |
| 234 | parent.scrollTop = top - offsetTop - ((options.margin != null) ? options.margin : 0); | 309 | parent.scrollTop = |
| 310 | top - offsetTop - (options.margin != null ? options.margin : 0); | ||
| 235 | break; | 311 | break; |
| 236 | case 'center': | 312 | case "center": |
| 237 | parent.scrollTop = top - Math.round((parentHeight / 2) - (el.offsetHeight / 2)); | 313 | parent.scrollTop = |
| 314 | top - Math.round(parentHeight / 2 - el.offsetHeight / 2); | ||
| 238 | break; | 315 | break; |
| 239 | case 'continuous': | 316 | case "continuous": |
| 240 | var { | 317 | var { scrollTop } = parent; |
| 241 | scrollTop | ||
| 242 | } = parent; | ||
| 243 | var height = el.offsetHeight; | 318 | var height = el.offsetHeight; |
| 244 | 319 | ||
| 245 | var lastElementOffset = parent.lastElementChild.offsetTop + parent.lastElementChild.offsetHeight; | 320 | var lastElementOffset = |
| 246 | var offsetBottom = lastElementOffset > 0 ? parentScrollHeight - lastElementOffset : 0; | 321 | parent.lastElementChild.offsetTop + |
| 322 | parent.lastElementChild.offsetHeight; | ||
| 323 | var offsetBottom = | ||
| 324 | lastElementOffset > 0 ? parentScrollHeight - lastElementOffset : 0; | ||
| 247 | 325 | ||
| 248 | // If the target element is above the visible portion of its scrollable | 326 | // If the target element is above the visible portion of its scrollable |
| 249 | // ancestor, move it near the top with a gap = options.topGap * target's height. | 327 | // ancestor, move it near the top with a gap = options.topGap * target's height. |
| 250 | if ((top - offsetTop) <= (scrollTop + (height * (options.topGap || 1)))) { | 328 | if (top - offsetTop <= scrollTop + height * (options.topGap || 1)) { |
| 251 | parent.scrollTop = top - offsetTop - (height * (options.topGap || 1)); | 329 | parent.scrollTop = top - offsetTop - height * (options.topGap || 1); |
| 252 | // If the target element is below the visible portion of its scrollable | 330 | // If the target element is below the visible portion of its scrollable |
| 253 | // ancestor, move it near the bottom with a gap = options.bottomGap * target's height. | 331 | // ancestor, move it near the bottom with a gap = options.bottomGap * target's height. |
| 254 | } else if ((top + offsetBottom) >= ((scrollTop + parentHeight) - (height * ((options.bottomGap || 1) + 1)))) { | 332 | } else if ( |
| 255 | parent.scrollTop = ((top + offsetBottom) - parentHeight) + (height * ((options.bottomGap || 1) + 1)); | 333 | top + offsetBottom >= |
| 334 | scrollTop + parentHeight - height * ((options.bottomGap || 1) + 1) | ||
| 335 | ) { | ||
| 336 | parent.scrollTop = | ||
| 337 | top + | ||
| 338 | offsetBottom - | ||
| 339 | parentHeight + | ||
| 340 | height * ((options.bottomGap || 1) + 1); | ||
| 256 | } | 341 | } |
| 257 | break; | 342 | break; |
| 258 | } | 343 | } |
| 259 | }; | 344 | }; |
| 260 | 345 | ||
| 261 | $.scrollToWithImageLock = function(el, parent, ...args) { | 346 | $.scrollToWithImageLock = function (el, parent, ...args) { |
| 262 | if (parent == null) { parent = $.scrollParent(el); } | 347 | if (parent == null) { |
| 263 | if (!parent) { return; } | 348 | parent = $.scrollParent(el); |
| 349 | } | ||
| 350 | if (!parent) { | ||
| 351 | return; | ||
| 352 | } | ||
| 264 | 353 | ||
| 265 | $.scrollTo(el, parent, ...Array.from(args)); | 354 | $.scrollTo(el, parent, ...Array.from(args)); |
| 266 | 355 | ||
| 267 | // Lock the scroll position on the target element for up to 3 seconds while | 356 | // Lock the scroll position on the target element for up to 3 seconds while |
| 268 | // nearby images are loaded and rendered. | 357 | // nearby images are loaded and rendered. |
| 269 | for (var image of Array.from(parent.getElementsByTagName('img'))) { | 358 | for (var image of Array.from(parent.getElementsByTagName("img"))) { |
| 270 | if (!image.complete) { | 359 | if (!image.complete) { |
| 271 | (function() { | 360 | (function () { |
| 272 | let timeout; | 361 | let timeout; |
| 273 | const onLoad = function(event) { | 362 | const onLoad = function (event) { |
| 274 | clearTimeout(timeout); | 363 | clearTimeout(timeout); |
| 275 | unbind(event.target); | 364 | unbind(event.target); |
| 276 | return $.scrollTo(el, parent, ...Array.from(args)); | 365 | return $.scrollTo(el, parent, ...Array.from(args)); |
| 277 | }; | 366 | }; |
| 278 | 367 | ||
| 279 | var unbind = target => $.off(target, 'load', onLoad); | 368 | var unbind = (target) => $.off(target, "load", onLoad); |
| 280 | 369 | ||
| 281 | $.on(image, 'load', onLoad); | 370 | $.on(image, "load", onLoad); |
| 282 | return timeout = setTimeout(unbind.bind(null, image), 3000); | 371 | return (timeout = setTimeout(unbind.bind(null, image), 3000)); |
| 283 | })(); | 372 | })(); |
| 284 | } | 373 | } |
| 285 | } | 374 | } |
| 286 | }; | 375 | }; |
| 287 | 376 | ||
| 288 | // Calls the function while locking the element's position relative to the window. | 377 | // Calls the function while locking the element's position relative to the window. |
| 289 | $.lockScroll = function(el, fn) { | 378 | $.lockScroll = function (el, fn) { |
| 290 | let parent; | 379 | let parent; |
| 291 | if (parent = $.scrollParent(el)) { | 380 | if ((parent = $.scrollParent(el))) { |
| 292 | let { | 381 | let { top } = $.rect(el); |
| 293 | top | 382 | if (![document.body, document.documentElement].includes(parent)) { |
| 294 | } = $.rect(el); | 383 | top -= $.rect(parent).top; |
| 295 | if (![document.body, document.documentElement].includes(parent)) { top -= $.rect(parent).top; } | 384 | } |
| 296 | fn(); | 385 | fn(); |
| 297 | parent.scrollTop = $.offset(el, parent).top - top; | 386 | parent.scrollTop = $.offset(el, parent).top - top; |
| 298 | } else { | 387 | } else { |
| @@ -300,9 +389,14 @@ $.lockScroll = function(el, fn) { · 1 unmodified line | |||
| 300 | } | 389 | } |
| 301 | }; | 390 | }; |
| 302 | 391 | ||
| 303 | let smoothScroll = (smoothStart = (smoothEnd = (smoothDistance = (smoothDuration = null)))); | 392 | let smoothScroll = |
| 393 | (smoothStart = | ||
| 394 | smoothEnd = | ||
| 395 | smoothDistance = | ||
| 396 | smoothDuration = | ||
| 397 | null); | ||
| 304 | 398 | ||
| 305 | $.smoothScroll = function(el, end) { | 399 | $.smoothScroll = function (el, end) { |
| 306 | if (!window.requestAnimationFrame) { | 400 | if (!window.requestAnimationFrame) { |
| 307 | el.scrollTop = end; | 401 | el.scrollTop = end; |
| 308 | return; | 402 | return; |
| @@ -322,12 +416,18 @@ $.smoothScroll = function(el, end) { · 13 unmodified lines | |||
| 322 | smoothDuration = Math.min(300, Math.abs(smoothDistance)); | 416 | smoothDuration = Math.min(300, Math.abs(smoothDistance)); |
| 323 | const startTime = Date.now(); | 417 | const startTime = Date.now(); |
| 324 | 418 | ||
| 325 | smoothScroll = function() { | 419 | smoothScroll = function () { |
| 326 | const p = Math.min(1, (Date.now() - startTime) / smoothDuration); | 420 | const p = Math.min(1, (Date.now() - startTime) / smoothDuration); |
| 327 | const y = Math.max(0, Math.floor(smoothStart + (smoothDistance * (p < 0.5 ? 2 * p * p : (p * (4 - (p * 2))) - 1)))); | 421 | const y = Math.max( |
| 422 | 0, | ||
| 423 | Math.floor( | ||
| 424 | smoothStart + | ||
| 425 | smoothDistance * (p < 0.5 ? 2 * p * p : p * (4 - p * 2) - 1), | ||
| 426 | ), | ||
| 427 | ); | ||
| 328 | el.scrollTop = y; | 428 | el.scrollTop = y; |
| 329 | if (p === 1) { | 429 | if (p === 1) { |
| 330 | return smoothScroll = null; | 430 | return (smoothScroll = null); |
| 331 | } else { | 431 | } else { |
| 332 | return requestAnimationFrame(smoothScroll); | 432 | return requestAnimationFrame(smoothScroll); |
| 333 | } | 433 | } |
| @@ -339,7 +439,7 @@ $.smoothScroll = function(el, end) { · 5 unmodified lines | |||
| 339 | // Utilities | 439 | // Utilities |
| 340 | // | 440 | // |
| 341 | 441 | ||
| 342 | $.extend = function(target, ...objects) { | 442 | $.extend = function (target, ...objects) { |
| 343 | for (var object of Array.from(objects)) { | 443 | for (var object of Array.from(objects)) { |
| 344 | if (object) { | 444 | if (object) { |
| 345 | for (var key in object) { | 445 | for (var key in object) { |
| @@ -351,7 +451,7 @@ $.extend = function(target, ...objects) { · 5 unmodified lines | |||
| 351 | return target; | 451 | return target; |
| 352 | }; | 452 | }; |
| 353 | 453 | ||
| 354 | $.makeArray = function(object) { | 454 | $.makeArray = function (object) { |
| 355 | if (Array.isArray(object)) { | 455 | if (Array.isArray(object)) { |
| 356 | return object; | 456 | return object; |
| 357 | } else { | 457 | } else { |
| @@ -359,7 +459,7 @@ $.makeArray = function(object) { · 1 unmodified line | |||
| 359 | } | 459 | } |
| 360 | }; | 460 | }; |
| 361 | 461 | ||
| 362 | $.arrayDelete = function(array, object) { | 462 | $.arrayDelete = function (array, object) { |
| 363 | const index = array.indexOf(object); | 463 | const index = array.indexOf(object); |
| 364 | if (index >= 0) { | 464 | if (index >= 0) { |
| 365 | array.splice(index, 1); | 465 | array.splice(index, 1); |
| @@ -370,45 +470,49 @@ $.arrayDelete = function(array, object) { · 4 unmodified lines | |||
| 370 | }; | 470 | }; |
| 371 | 471 | ||
| 372 | // Returns true if the object is an array or a collection of DOM elements. | 472 | // Returns true if the object is an array or a collection of DOM elements. |
| 373 | $.isCollection = object => Array.isArray(object) || (typeof (object != null ? object.item : undefined) === 'function'); | 473 | $.isCollection = (object) => |
| 474 | Array.isArray(object) || | ||
| 475 | typeof (object != null ? object.item : undefined) === "function"; | ||
| 374 | 476 | ||
| 375 | const ESCAPE_HTML_MAP = { | 477 | const ESCAPE_HTML_MAP = { |
| 376 | '&': '&', | 478 | "&": "&", |
| 377 | '<': '<', | 479 | "<": "<", |
| 378 | '>': '>', | 480 | ">": ">", |
| 379 | '"': '"', | 481 | '"': """, |
| 380 | "'": ''', | 482 | "'": "'", |
| 381 | '/': '/' | 483 | "/": "/", |
| 382 | }; | 484 | }; |
| 383 | 485 | ||
| 384 | const ESCAPE_HTML_REGEXP = /[&<>"'\/]/g; | 486 | const ESCAPE_HTML_REGEXP = /[&<>"'\/]/g; |
| 385 | 487 | ||
| 386 | $.escape = string => string.replace(ESCAPE_HTML_REGEXP, match => ESCAPE_HTML_MAP[match]); | 488 | $.escape = (string) => |
| 489 | string.replace(ESCAPE_HTML_REGEXP, (match) => ESCAPE_HTML_MAP[match]); | ||
| 387 | 490 | ||
| 388 | const ESCAPE_REGEXP = /([.*+?^=!:${}()|\[\]\/\\])/g; | 491 | const ESCAPE_REGEXP = /([.*+?^=!:${}()|\[\]\/\\])/g; |
| 389 | 492 | ||
| 390 | $.escapeRegexp = string => string.replace(ESCAPE_REGEXP, "\\$1"); | 493 | $.escapeRegexp = (string) => string.replace(ESCAPE_REGEXP, "\\$1"); |
| 391 | 494 | ||
| 392 | $.urlDecode = string => decodeURIComponent(string.replace(/\+/g, '%20')); | 495 | $.urlDecode = (string) => decodeURIComponent(string.replace(/\+/g, "%20")); |
| 393 | 496 | ||
| 394 | $.classify = function(string) { | 497 | $.classify = function (string) { |
| 395 | string = string.split('_'); | 498 | string = string.split("_"); |
| 396 | for (let i = 0; i < string.length; i++) { | 499 | for (let i = 0; i < string.length; i++) { |
| 397 | var substr = string[i]; | 500 | var substr = string[i]; |
| 398 | string[i] = substr[0].toUpperCase() + substr.slice(1); | 501 | string[i] = substr[0].toUpperCase() + substr.slice(1); |
| 399 | } | 502 | } |
| 400 | return string.join(''); | 503 | return string.join(""); |
| 401 | }; | 504 | }; |
| 402 | 505 | ||
| 403 | $.framify = function(fn, obj) { | 506 | $.framify = function (fn, obj) { |
| 404 | if (window.requestAnimationFrame) { | 507 | if (window.requestAnimationFrame) { |
| 405 | return (...args) => requestAnimationFrame(fn.bind(obj, ...Array.from(args))); | 508 | return (...args) => |
| 509 | requestAnimationFrame(fn.bind(obj, ...Array.from(args))); | ||
| 406 | } else { | 510 | } else { |
| 407 | return fn; | 511 | return fn; |
| 408 | } | 512 | } |
| 409 | }; | 513 | }; |
| 410 | 514 | ||
| 411 | $.requestAnimationFrame = function(fn) { | 515 | $.requestAnimationFrame = function (fn) { |
| 412 | if (window.requestAnimationFrame) { | 516 | if (window.requestAnimationFrame) { |
| 413 | requestAnimationFrame(fn); | 517 | requestAnimationFrame(fn); |
| 414 | } else { | 518 | } else { |
| @@ -420,37 +524,81 @@ $.requestAnimationFrame = function(fn) { · 5 unmodified lines | |||
| 420 | // Miscellaneous | 524 | // Miscellaneous |
| 421 | // | 525 | // |
| 422 | 526 | ||
| 423 | $.noop = function() {}; | 527 | $.noop = function () {}; |
| 424 | 528 | ||
| 425 | $.popup = function(value) { | 529 | $.popup = function (value) { |
| 426 | try { | 530 | try { |
| 427 | const win = window.open(); | 531 | const win = window.open(); |
| 428 | if (win.opener) { win.opener = null; } | 532 | if (win.opener) { |
| 533 | win.opener = null; | ||
| 534 | } | ||
| 429 | win.location = value.href || value; | 535 | win.location = value.href || value; |
| 430 | } catch (error) { | 536 | } catch (error) { |
| 431 | window.open(value.href || value, '_blank'); | 537 | window.open(value.href || value, "_blank"); |
| 432 | } | 538 | } |
| 433 | }; | 539 | }; |
| 434 | 540 | ||
| 435 | let isMac = null; | 541 | let isMac = null; |
| 436 | $.isMac = () => isMac != null ? isMac : (isMac = (navigator.userAgent != null ? navigator.userAgent.indexOf('Mac') : undefined) >= 0); | 542 | $.isMac = () => |
| 543 | isMac != null | ||
| 544 | ? isMac | ||
| 545 | : (isMac = | ||
| 546 | (navigator.userAgent != null | ||
| 547 | ? navigator.userAgent.indexOf("Mac") | ||
| 548 | : undefined) >= 0); | ||
| 437 | 549 | ||
| 438 | let isIE = null; | 550 | let isIE = null; |
| 439 | $.isIE = () => isIE != null ? isIE : (isIE = ((navigator.userAgent != null ? navigator.userAgent.indexOf('MSIE') : undefined) >= 0) || ((navigator.userAgent != null ? navigator.userAgent.indexOf('rv:11.0') : undefined) >= 0)); | 551 | $.isIE = () => |
| 552 | isIE != null | ||
| 553 | ? isIE | ||
| 554 | : (isIE = | ||
| 555 | (navigator.userAgent != null | ||
| 556 | ? navigator.userAgent.indexOf("MSIE") | ||
| 557 | : undefined) >= 0 || | ||
| 558 | (navigator.userAgent != null | ||
| 559 | ? navigator.userAgent.indexOf("rv:11.0") | ||
| 560 | : undefined) >= 0); | ||
| 440 | 561 | ||
| 441 | let isChromeForAndroid = null; | 562 | let isChromeForAndroid = null; |
| 442 | $.isChromeForAndroid = () => isChromeForAndroid != null ? isChromeForAndroid : (isChromeForAndroid = ((navigator.userAgent != null ? navigator.userAgent.indexOf('Android') : undefined) >= 0) && /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent)); | 563 | $.isChromeForAndroid = () => |
| 564 | isChromeForAndroid != null | ||
| 565 | ? isChromeForAndroid | ||
| 566 | : (isChromeForAndroid = | ||
| 567 | (navigator.userAgent != null | ||
| 568 | ? navigator.userAgent.indexOf("Android") | ||
| 569 | : undefined) >= 0 && | ||
| 570 | /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent)); | ||
| 443 | 571 | ||
| 444 | let isAndroid = null; | 572 | let isAndroid = null; |
| 445 | $.isAndroid = () => isAndroid != null ? isAndroid : (isAndroid = (navigator.userAgent != null ? navigator.userAgent.indexOf('Android') : undefined) >= 0); | 573 | $.isAndroid = () => |
| 574 | isAndroid != null | ||
| 575 | ? isAndroid | ||
| 576 | : (isAndroid = | ||
| 577 | (navigator.userAgent != null | ||
| 578 | ? navigator.userAgent.indexOf("Android") | ||
| 579 | : undefined) >= 0); | ||
| 446 | 580 | ||
| 447 | let isIOS = null; | 581 | let isIOS = null; |
| 448 | $.isIOS = () => isIOS != null ? isIOS : (isIOS = ((navigator.userAgent != null ? navigator.userAgent.indexOf('iPhone') : undefined) >= 0) || ((navigator.userAgent != null ? navigator.userAgent.indexOf('iPad') : undefined) >= 0)); | 582 | $.isIOS = () => |
| 449 | 583 | isIOS != null | |
| 450 | $.overlayScrollbarsEnabled = function() { | 584 | ? isIOS |
| 451 | if (!$.isMac()) { return false; } | 585 | : (isIOS = |
| 452 | const div = document.createElement('div'); | 586 | (navigator.userAgent != null |
| 453 | div.setAttribute('style', 'width: 100px; height: 100px; overflow: scroll; position: absolute'); | 587 | ? navigator.userAgent.indexOf("iPhone") |
| 588 | : undefined) >= 0 || | ||
| 589 | (navigator.userAgent != null | ||
| 590 | ? navigator.userAgent.indexOf("iPad") | ||
| 591 | : undefined) >= 0); | ||
| 592 | |||
| 593 | $.overlayScrollbarsEnabled = function () { | ||
| 594 | if (!$.isMac()) { | ||
| 595 | return false; | ||
| 596 | } | ||
| 597 | const div = document.createElement("div"); | ||
| 598 | div.setAttribute( | ||
| 599 | "style", | ||
| 600 | "width: 100px; height: 100px; overflow: scroll; position: absolute", | ||
| 601 | ); | ||
| 454 | document.body.appendChild(div); | 602 | document.body.appendChild(div); |
| 455 | const result = div.offsetWidth === div.clientWidth; | 603 | const result = div.offsetWidth === div.clientWidth; |
| 456 | document.body.removeChild(div); | 604 | document.body.removeChild(div); |
| @@ -458,36 +606,39 @@ $.overlayScrollbarsEnabled = function() { · 1 unmodified line | |||
| 458 | }; | 606 | }; |
| 459 | 607 | ||
| 460 | const HIGHLIGHT_DEFAULTS = { | 608 | const HIGHLIGHT_DEFAULTS = { |
| 461 | className: 'highlight', | 609 | className: "highlight", |
| 462 | delay: 1000 | 610 | delay: 1000, |
| 463 | }; | 611 | }; |
| 464 | 612 | ||
| 465 | $.highlight = function(el, options) { | 613 | $.highlight = function (el, options) { |
| 466 | if (options == null) { options = {}; } | 614 | if (options == null) { |
| 615 | options = {}; | ||
| 616 | } | ||
| 467 | options = $.extend({}, HIGHLIGHT_DEFAULTS, options); | 617 | options = $.extend({}, HIGHLIGHT_DEFAULTS, options); |
| 468 | el.classList.add(options.className); | 618 | el.classList.add(options.className); |
| 469 | setTimeout((() => el.classList.remove(options.className)), options.delay); | 619 | setTimeout(() => el.classList.remove(options.className), options.delay); |
| 470 | }; | 620 | }; |
| 471 | 621 | ||
| 472 | $.copyToClipboard = function(string) { | 622 | $.copyToClipboard = function (string) { |
| 473 | let result; | 623 | let result; |
| 474 | const textarea = document.createElement('textarea'); | 624 | const textarea = document.createElement("textarea"); |
| 475 | textarea.style.position = 'fixed'; | 625 | textarea.style.position = "fixed"; |
| 476 | textarea.style.opacity = 0; | 626 | textarea.style.opacity = 0; |
| 477 | textarea.value = string; | 627 | textarea.value = string; |
| 478 | document.body.appendChild(textarea); | 628 | document.body.appendChild(textarea); |
| 479 | try { | 629 | try { |
| 480 | textarea.select(); | 630 | textarea.select(); |
| 481 | result = !!document.execCommand('copy'); | 631 | result = !!document.execCommand("copy"); |
| 482 | } catch (error) { | 632 | } catch (error) { |
| 483 | result = false; | 633 | result = false; |
| 484 | } | 634 | } finally { |
| 485 | finally { | ||
| 486 | document.body.removeChild(textarea); | 635 | document.body.removeChild(textarea); |
| 487 | } | 636 | } |
| 488 | return result; | 637 | return result; |
| 489 | }; | 638 | }; |
| 490 | 639 | ||
| 491 | function __guard__(value, transform) { | 640 | function __guard__(value, transform) { |
| 492 | return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; | 641 | return typeof value !== "undefined" && value !== null |
| 493 | } | 642 | ? transform(value) |
| 643 | : undefined; | ||
| 644 | } | ||
| @@ -12,10 +12,12 @@ app.models.Doc = class Doc extends app.Model { · 11 unmodified lines | |||
| 12 | constructor() { | 12 | constructor() { |
| 13 | super(...arguments); | 13 | super(...arguments); |
| 14 | this.reset(this); | 14 | this.reset(this); |
| 15 | this.slug_without_version = this.slug.split('~')[0]; | 15 | this.slug_without_version = this.slug.split("~")[0]; |
| 16 | this.fullName = `${this.name}` + (this.version ? ` ${this.version}` : ''); | 16 | this.fullName = `${this.name}` + (this.version ? ` ${this.version}` : ""); |
| 17 | this.icon = this.slug_without_version; | 17 | this.icon = this.slug_without_version; |
| 18 | if (this.version) { this.short_version = this.version.split(' ')[0]; } | 18 | if (this.version) { |
| 19 | this.short_version = this.version.split(" ")[0]; | ||
| 20 | } | ||
| 19 | this.text = this.toEntry().text; | 21 | this.text = this.toEntry().text; |
| 20 | } | 22 | } |
| 21 | 23 | ||
| @@ -26,17 +28,25 @@ app.models.Doc = class Doc extends app.Model { · 4 unmodified lines | |||
| 26 | 28 | ||
| 27 | resetEntries(entries) { | 29 | resetEntries(entries) { |
| 28 | this.entries = new app.collections.Entries(entries); | 30 | this.entries = new app.collections.Entries(entries); |
| 29 | this.entries.each(entry => { return entry.doc = this; }); | 31 | this.entries.each((entry) => { |
| 32 | return (entry.doc = this); | ||
| 33 | }); | ||
| 30 | } | 34 | } |
| 31 | 35 | ||
| 32 | resetTypes(types) { | 36 | resetTypes(types) { |
| 33 | this.types = new app.collections.Types(types); | 37 | this.types = new app.collections.Types(types); |
| 34 | this.types.each(type => { return type.doc = this; }); | 38 | this.types.each((type) => { |
| 39 | return (type.doc = this); | ||
| 40 | }); | ||
| 35 | } | 41 | } |
| 36 | 42 | ||
| 37 | fullPath(path) { | 43 | fullPath(path) { |
| 38 | if (path == null) { path = ''; } | 44 | if (path == null) { |
| 39 | if (path[0] !== '/') { path = `/${path}`; } | 45 | path = ""; |
| 46 | } | ||
| 47 | if (path[0] !== "/") { | ||
| 48 | path = `/${path}`; | ||
| 49 | } | ||
| 40 | return `/${this.slug}${path}`; | 50 | return `/${this.slug}${path}`; |
| 41 | } | 51 | } |
| 42 | 52 | ||
| @@ -49,45 +59,57 @@ app.models.Doc = class Doc extends app.Model { · 6 unmodified lines | |||
| 49 | } | 59 | } |
| 50 | 60 | ||
| 51 | indexUrl() { | 61 | indexUrl() { |
| 52 | return `${app.indexHost()}/${this.slug}/${app.config.index_filename}?${this.mtime}`; | 62 | return `${app.indexHost()}/${this.slug}/${app.config.index_filename}?${ |
| 63 | this.mtime | ||
| 64 | }`; | ||
| 53 | } | 65 | } |
| 54 | 66 | ||
| 55 | toEntry() { | 67 | toEntry() { |
| 56 | if (this.entry) { return this.entry; } | 68 | if (this.entry) { |
| 69 | return this.entry; | ||
| 70 | } | ||
| 57 | this.entry = new app.models.Entry({ | 71 | this.entry = new app.models.Entry({ |
| 58 | doc: this, | 72 | doc: this, |
| 59 | name: this.fullName, | 73 | name: this.fullName, |
| 60 | path: 'index' | 74 | path: "index", |
| 61 | }); | 75 | }); |
| 62 | if (this.version) { this.entry.addAlias(this.name); } | 76 | if (this.version) { |
| 77 | this.entry.addAlias(this.name); | ||
| 78 | } | ||
| 63 | return this.entry; | 79 | return this.entry; |
| 64 | } | 80 | } |
| 65 | 81 | ||
| 66 | findEntryByPathAndHash(path, hash) { | 82 | findEntryByPathAndHash(path, hash) { |
| 67 | let entry; | 83 | let entry; |
| 68 | if (hash && (entry = this.entries.findBy('path', `${path}#${hash}`))) { | 84 | if (hash && (entry = this.entries.findBy("path", `${path}#${hash}`))) { |
| 69 | return entry; | 85 | return entry; |
| 70 | } else if (path === 'index') { | 86 | } else if (path === "index") { |
| 71 | return this.toEntry(); | 87 | return this.toEntry(); |
| 72 | } else { | 88 | } else { |
| 73 | return this.entries.findBy('path', path); | 89 | return this.entries.findBy("path", path); |
| 74 | } | 90 | } |
| 75 | } | 91 | } |
| 76 | 92 | ||
| 77 | load(onSuccess, onError, options) { | 93 | load(onSuccess, onError, options) { |
| 78 | if (options == null) { options = {}; } | 94 | if (options == null) { |
| 79 | if (options.readCache && this._loadFromCache(onSuccess)) { return; } | 95 | options = {}; |
| 96 | } | ||
| 97 | if (options.readCache && this._loadFromCache(onSuccess)) { | ||
| 98 | return; | ||
| 99 | } | ||
| 80 | 100 | ||
| 81 | const callback = data => { | 101 | const callback = (data) => { |
| 82 | this.reset(data); | 102 | this.reset(data); |
| 83 | onSuccess(); | 103 | onSuccess(); |
| 84 | if (options.writeCache) { this._setCache(data); } | 104 | if (options.writeCache) { |
| 105 | this._setCache(data); | ||
| 106 | } | ||
| 85 | }; | 107 | }; |
| 86 | 108 | ||
| 87 | return ajax({ | 109 | return ajax({ |
| 88 | url: this.indexUrl(), | 110 | url: this.indexUrl(), |
| 89 | success: callback, | 111 | success: callback, |
| 90 | error: onError | 112 | error: onError, |
| 91 | }); | 113 | }); |
| 92 | } | 114 | } |
| 93 | 115 | ||
| @@ -97,7 +119,9 @@ app.models.Doc = class Doc extends app.Model { · 3 unmodified lines | |||
| 97 | 119 | ||
| 98 | _loadFromCache(onSuccess) { | 120 | _loadFromCache(onSuccess) { |
| 99 | let data; | 121 | let data; |
| 100 | if (!(data = this._getCache())) { return; } | 122 | if (!(data = this._getCache())) { |
| 123 | return; | ||
| 124 | } | ||
| 101 | 125 | ||
| 102 | const callback = () => { | 126 | const callback = () => { |
| 103 | this.reset(data); | 127 | this.reset(data); |
| @@ -110,7 +134,9 @@ app.models.Doc = class Doc extends app.Model { · 6 unmodified lines | |||
| 110 | 134 | ||
| 111 | _getCache() { | 135 | _getCache() { |
| 112 | let data; | 136 | let data; |
| 113 | if (!(data = app.localStorage.get(this.slug))) { return; } | 137 | if (!(data = app.localStorage.get(this.slug))) { |
| 138 | return; | ||
| 139 | } | ||
| 114 | 140 | ||
| 115 | if (data[0] === this.mtime) { | 141 | if (data[0] === this.mtime) { |
| 116 | return data[1]; | 142 | return data[1]; |
| @@ -125,7 +151,9 @@ app.models.Doc = class Doc extends app.Model { · 8 unmodified lines | |||
| 125 | } | 151 | } |
| 126 | 152 | ||
| 127 | install(onSuccess, onError, onProgress) { | 153 | install(onSuccess, onError, onProgress) { |
| 128 | if (this.installing) { return; } | 154 | if (this.installing) { |
| 155 | return; | ||
| 156 | } | ||
| 129 | this.installing = true; | 157 | this.installing = true; |
| 130 | 158 | ||
| 131 | const error = () => { | 159 | const error = () => { |
| @@ -133,7 +161,7 @@ app.models.Doc = class Doc extends app.Model { · 1 unmodified line | |||
| 133 | onError(); | 161 | onError(); |
| 134 | }; | 162 | }; |
| 135 | 163 | ||
| 136 | const success = data => { | 164 | const success = (data) => { |
| 137 | this.installing = null; | 165 | this.installing = null; |
| 138 | app.db.store(this, data, onSuccess, error); | 166 | app.db.store(this, data, onSuccess, error); |
| 139 | }; | 167 | }; |
| @@ -143,12 +171,14 @@ app.models.Doc = class Doc extends app.Model { · 3 unmodified lines | |||
| 143 | success, | 171 | success, |
| 144 | error, | 172 | error, |
| 145 | progress: onProgress, | 173 | progress: onProgress, |
| 146 | timeout: 3600 | 174 | timeout: 3600, |
| 147 | }); | 175 | }); |
| 148 | } | 176 | } |
| 149 | 177 | ||
| 150 | uninstall(onSuccess, onError) { | 178 | uninstall(onSuccess, onError) { |
| 151 | if (this.installing) { return; } | 179 | if (this.installing) { |
| 180 | return; | ||
| 181 | } | ||
| 152 | this.installing = true; | 182 | this.installing = true; |
| 153 | 183 | ||
| 154 | const success = () => { | 184 | const success = () => { |
| @@ -165,12 +195,16 @@ app.models.Doc = class Doc extends app.Model { · 10 unmodified lines | |||
| 165 | } | 195 | } |
| 166 | 196 | ||
| 167 | getInstallStatus(callback) { | 197 | getInstallStatus(callback) { |
| 168 | app.db.version(this, value => callback({installed: !!value, mtime: value})); | 198 | app.db.version(this, (value) => |
| 199 | callback({ installed: !!value, mtime: value }), | ||
| 200 | ); | ||
| 169 | } | 201 | } |
| 170 | 202 | ||
| 171 | isOutdated(status) { | 203 | isOutdated(status) { |
| 172 | if (!status) { return false; } | 204 | if (!status) { |
| 173 | const isInstalled = status.installed || app.settings.get('autoInstall'); | 205 | return false; |
| 174 | return isInstalled && (this.mtime !== status.mtime); | 206 | } |
| 207 | const isInstalled = status.installed || app.settings.get("autoInstall"); | ||
| 208 | return isInstalled && this.mtime !== status.mtime; | ||
| 175 | } | 209 | } |
| 176 | }; | 210 | }; |
| @@ -8,63 +8,62 @@ · 7 unmodified lines | |||
| 8 | */ | 8 | */ |
| 9 | //= require app/searcher | 9 | //= require app/searcher |
| 10 | 10 | ||
| 11 | (function() { | 11 | (function () { |
| 12 | let applyAliases = undefined; | 12 | let applyAliases = undefined; |
| 13 | const Cls = (app.models.Entry = class Entry extends app.Model { | 13 | const Cls = (app.models.Entry = class Entry extends app.Model { |
| 14 | static initClass() { | 14 | static initClass() { |
| 15 | |||
| 16 | let ALIASES; | 15 | let ALIASES; |
| 17 | applyAliases = function(string) { | 16 | applyAliases = function (string) { |
| 18 | if (ALIASES.hasOwnProperty(string)) { | 17 | if (ALIASES.hasOwnProperty(string)) { |
| 19 | return [string, ALIASES[string]]; | 18 | return [string, ALIASES[string]]; |
| 20 | } else { | 19 | } else { |
| 21 | const words = string.split('.'); | 20 | const words = string.split("."); |
| 22 | for (let i = 0; i < words.length; i++) { | 21 | for (let i = 0; i < words.length; i++) { |
| 23 | var word = words[i]; | 22 | var word = words[i]; |
| 24 | if (ALIASES.hasOwnProperty(word)) { | 23 | if (ALIASES.hasOwnProperty(word)) { |
| 25 | words[i] = ALIASES[word]; | 24 | words[i] = ALIASES[word]; |
| 26 | return [string, words.join('.')]; | 25 | return [string, words.join(".")]; |
| 27 | } | 26 | } |
| 28 | } | 27 | } |
| 29 | } | 28 | } |
| 30 | return string; | 29 | return string; |
| 31 | }; | 30 | }; |
| 32 | 31 | ||
| 33 | this.ALIASES = (ALIASES = { | 32 | this.ALIASES = ALIASES = { |
| 34 | 'angular': 'ng', | 33 | angular: "ng", |
| 35 | 'angular.js': 'ng', | 34 | "angular.js": "ng", |
| 36 | 'backbone.js': 'bb', | 35 | "backbone.js": "bb", |
| 37 | 'c++': 'cpp', | 36 | "c++": "cpp", |
| 38 | 'coffeescript': 'cs', | 37 | coffeescript: "cs", |
| 39 | 'crystal': 'cr', | 38 | crystal: "cr", |
| 40 | 'elixir': 'ex', | 39 | elixir: "ex", |
| 41 | 'javascript': 'js', | 40 | javascript: "js", |
| 42 | 'julia': 'jl', | 41 | julia: "jl", |
| 43 | 'jquery': '$', | 42 | jquery: "$", |
| 44 | 'knockout.js': 'ko', | 43 | "knockout.js": "ko", |
| 45 | 'kubernetes': 'k8s', | 44 | kubernetes: "k8s", |
| 46 | 'less': 'ls', | 45 | less: "ls", |
| 47 | 'lodash': '_', | 46 | lodash: "_", |
| 48 | 'löve': 'love', | 47 | löve: "love", |
| 49 | 'marionette': 'mn', | 48 | marionette: "mn", |
| 50 | 'markdown': 'md', | 49 | markdown: "md", |
| 51 | 'matplotlib': 'mpl', | 50 | matplotlib: "mpl", |
| 52 | 'modernizr': 'mdr', | 51 | modernizr: "mdr", |
| 53 | 'moment.js': 'mt', | 52 | "moment.js": "mt", |
| 54 | 'openjdk': 'java', | 53 | openjdk: "java", |
| 55 | 'nginx': 'ngx', | 54 | nginx: "ngx", |
| 56 | 'numpy': 'np', | 55 | numpy: "np", |
| 57 | 'pandas': 'pd', | 56 | pandas: "pd", |
| 58 | 'postgresql': 'pg', | 57 | postgresql: "pg", |
| 59 | 'python': 'py', | 58 | python: "py", |
| 60 | 'ruby.on.rails': 'ror', | 59 | "ruby.on.rails": "ror", |
| 61 | 'ruby': 'rb', | 60 | ruby: "rb", |
| 62 | 'rust': 'rs', | 61 | rust: "rs", |
| 63 | 'sass': 'scss', | 62 | sass: "scss", |
| 64 | 'tensorflow': 'tf', | 63 | tensorflow: "tf", |
| 65 | 'typescript': 'ts', | 64 | typescript: "ts", |
| 66 | 'underscore.js': '_' | 65 | "underscore.js": "_", |
| 67 | }); | 66 | }; |
| 68 | } | 67 | } |
| 69 | // Attributes: name, type, path | 68 | // Attributes: name, type, path |
| 70 | 69 | ||
| @@ -75,16 +74,18 @@ · 4 unmodified lines | |||
| 75 | 74 | ||
| 76 | addAlias(name) { | 75 | addAlias(name) { |
| 77 | const text = applyAliases(app.Searcher.normalizeString(name)); | 76 | const text = applyAliases(app.Searcher.normalizeString(name)); |
| 78 | if (!Array.isArray(this.text)) { this.text = [this.text]; } | 77 | if (!Array.isArray(this.text)) { |
| 78 | this.text = [this.text]; | ||
| 79 | } | ||
| 79 | this.text.push(Array.isArray(text) ? text[1] : text); | 80 | this.text.push(Array.isArray(text) ? text[1] : text); |
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | fullPath() { | 83 | fullPath() { |
| 83 | return this.doc.fullPath(this.isIndex() ? '' : this.path); | 84 | return this.doc.fullPath(this.isIndex() ? "" : this.path); |
| 84 | } | 85 | } |
| 85 | 86 | ||
| 86 | dbPath() { | 87 | dbPath() { |
| 87 | return this.path.replace(/#.*/, ''); | 88 | return this.path.replace(/#.*/, ""); |
| 88 | } | 89 | } |
| 89 | 90 | ||
| 90 | filePath() { | 91 | filePath() { |
| @@ -96,17 +97,19 @@ · 5 unmodified lines | |||
| 96 | } | 97 | } |
| 97 | 98 | ||
| 98 | _filePath() { | 99 | _filePath() { |
| 99 | let result = this.path.replace(/#.*/, ''); | 100 | let result = this.path.replace(/#.*/, ""); |
| 100 | if (result.slice(-5) !== '.html') { result += '.html'; } | 101 | if (result.slice(-5) !== ".html") { |
| 102 | result += ".html"; | ||
| 103 | } | ||
| 101 | return result; | 104 | return result; |
| 102 | } | 105 | } |
| 103 | 106 | ||
| 104 | isIndex() { | 107 | isIndex() { |
| 105 | return this.path === 'index'; | 108 | return this.path === "index"; |
| 106 | } | 109 | } |
| 107 | 110 | ||
| 108 | getType() { | 111 | getType() { |
| 109 | return this.doc.types.findBy('name', this.type); | 112 | return this.doc.types.findBy("name", this.type); |
| 110 | } | 113 | } |
| 111 | 114 | ||
| 112 | loadFile(onSuccess, onError) { | 115 | loadFile(onSuccess, onError) { |
| @@ -2,6 +2,9 @@ · 1 unmodified line | |||
| 2 | // Sanity-check the conversion and remove this comment. | 2 | // Sanity-check the conversion and remove this comment. |
| 3 | app.Model = class Model { | 3 | app.Model = class Model { |
| 4 | constructor(attributes) { | 4 | constructor(attributes) { |
| 5 | for (var key in attributes) { var value = attributes[key]; this[key] = value; } | 5 | for (var key in attributes) { |
| 6 | var value = attributes[key]; | ||
| 7 | this[key] = value; | ||
| 8 | } | ||
| 6 | } | 9 | } |
| 7 | }; | 10 | }; |
| @@ -13,14 +13,14 @@ app.models.Type = class Type extends app.Model { · 12 unmodified lines | |||
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | entries() { | 15 | entries() { |
| 16 | return this.doc.entries.findAllBy('type', this.name); | 16 | return this.doc.entries.findAllBy("type", this.name); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | toEntry() { | 19 | toEntry() { |
| 20 | return new app.models.Entry({ | 20 | return new app.models.Entry({ |
| 21 | doc: this.doc, | 21 | doc: this.doc, |
| 22 | name: `${this.doc.name} / ${this.name}`, | 22 | name: `${this.doc.name} / ${this.name}`, |
| 23 | path: '..' + this.fullPath() | 23 | path: ".." + this.fullPath(), |
| 24 | }); | 24 | }); |
| 25 | } | 25 | } |
| 26 | }; | 26 | }; |
| @@ -6,14 +6,16 @@ · 5 unmodified lines | |||
| 6 | * DS102: Remove unnecessary code created because of implicit returns | 6 | * DS102: Remove unnecessary code created because of implicit returns |
| 7 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 7 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 8 | */ | 8 | */ |
| 9 | app.templates.render = function(name, value, ...args) { | 9 | app.templates.render = function (name, value, ...args) { |
| 10 | const template = app.templates[name]; | 10 | const template = app.templates[name]; |
| 11 | 11 | ||
| 12 | if (Array.isArray(value)) { | 12 | if (Array.isArray(value)) { |
| 13 | let result = ''; | 13 | let result = ""; |
| 14 | for (var val of Array.from(value)) { result += template(val, ...Array.from(args)); } | 14 | for (var val of Array.from(value)) { |
| 15 | result += template(val, ...Array.from(args)); | ||
| 16 | } | ||
| 15 | return result; | 17 | return result; |
| 16 | } else if (typeof template === 'function') { | 18 | } else if (typeof template === "function") { |
| 17 | return template(value, ...Array.from(args)); | 19 | return template(value, ...Array.from(args)); |
| 18 | } else { | 20 | } else { |
| 19 | return template; | 21 | return template; |
| @@ -7,62 +7,79 @@ · 6 unmodified lines | |||
| 7 | * DS207: Consider shorter variations of null checks | 7 | * DS207: Consider shorter variations of null checks |
| 8 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 8 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 9 | */ | 9 | */ |
| 10 | const error = function(title, text, links) { | 10 | const error = function (title, text, links) { |
| 11 | if (text == null) { text = ''; } | 11 | if (text == null) { |
| 12 | if (links == null) { links = ''; } | 12 | text = ""; |
| 13 | if (text) { text = `<p class="_error-text">${text}</p>`; } | 13 | } |
| 14 | if (links) { links = `<p class="_error-links">${links}</p>`; } | 14 | if (links == null) { |
| 15 | links = ""; | ||
| 16 | } | ||
| 17 | if (text) { | ||
| 18 | text = `<p class="_error-text">${text}</p>`; | ||
| 19 | } | ||
| 20 | if (links) { | ||
| 21 | links = `<p class="_error-links">${links}</p>`; | ||
| 22 | } | ||
| 15 | return `<div class="_error"><h1 class="_error-title">${title}</h1>${text}${links}</div>`; | 23 | return `<div class="_error"><h1 class="_error-title">${title}</h1>${text}${links}</div>`; |
| 16 | }; | 24 | }; |
| 17 | 25 | ||
| 18 | const back = '<a href="#" data-behavior="back" class="_error-link">Go back</a>'; | 26 | const back = '<a href="#" data-behavior="back" class="_error-link">Go back</a>'; |
| 19 | 27 | ||
| 20 | app.templates.notFoundPage = () => error(" Page not found. ", | 28 | app.templates.notFoundPage = () => |
| 21 | " It may be missing from the source documentation or this could be a bug. ", | 29 | error( |
| 22 | back); | 30 | " Page not found. ", |
| 31 | " It may be missing from the source documentation or this could be a bug. ", | ||
| 32 | back, | ||
| 33 | ); | ||
| 23 | 34 | ||
| 24 | app.templates.pageLoadError = () => error(" The page failed to load. ", | 35 | app.templates.pageLoadError = () => |
| 25 | ` It may be missing from the server (try reloading the app) or you could be offline (try <a href="/offline">installing the documentation for offline usage</a> when online again).<br> | 36 | error( |
| 37 | " The page failed to load. ", | ||
| 38 | ` It may be missing from the server (try reloading the app) or you could be offline (try <a href="/offline">installing the documentation for offline usage</a> when online again).<br> | ||
| 26 | If you're online and you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. `, | 39 | If you're online and you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. `, |
| 27 | ` ${back} · <a href="/#${location.pathname}" target="_top" class="_error-link">Reload</a> | 40 | ` ${back} · <a href="/#${location.pathname}" target="_top" class="_error-link">Reload</a> |
| 28 | · <a href="#" class="_error-link" data-retry>Retry</a> ` | 41 | · <a href="#" class="_error-link" data-retry>Retry</a> `, |
| 29 | ); | 42 | ); |
| 30 | 43 | ||
| 31 | app.templates.bootError = () => error(" The app failed to load. ", | 44 | app.templates.bootError = () => |
| 32 | ` Check your Internet connection and try <a href="#" data-behavior="reload">reloading</a>.<br> | 45 | error( |
| 33 | If you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. ` | 46 | " The app failed to load. ", |
| 34 | ); | 47 | ` Check your Internet connection and try <a href="#" data-behavior="reload">reloading</a>.<br> |
| 48 | If you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. `, | ||
| 49 | ); | ||
| 35 | 50 | ||
| 36 | app.templates.offlineError = function(reason, exception) { | 51 | app.templates.offlineError = function (reason, exception) { |
| 37 | if (reason === 'cookie_blocked') { | 52 | if (reason === "cookie_blocked") { |
| 38 | return error(" Cookies must be enabled to use offline mode. "); | 53 | return error(" Cookies must be enabled to use offline mode. "); |
| 39 | } | 54 | } |
| 40 | 55 | ||
| 41 | reason = (() => { switch (reason) { | 56 | reason = (() => { |
| 42 | case 'not_supported': | 57 | switch (reason) { |
| 43 | return ` DevDocs requires IndexedDB to cache documentations for offline access.<br> | 58 | case "not_supported": |
| 59 | return ` DevDocs requires IndexedDB to cache documentations for offline access.<br> | ||
| 44 | Unfortunately your browser either doesn't support IndexedDB or doesn't make it available. `; | 60 | Unfortunately your browser either doesn't support IndexedDB or doesn't make it available. `; |
| 45 | case 'buggy': | 61 | case "buggy": |
| 46 | return ` DevDocs requires IndexedDB to cache documentations for offline access.<br> | 62 | return ` DevDocs requires IndexedDB to cache documentations for offline access.<br> |
| 47 | Unfortunately your browser's implementation of IndexedDB contains bugs that prevent DevDocs from using it. `; | 63 | Unfortunately your browser's implementation of IndexedDB contains bugs that prevent DevDocs from using it. `; |
| 48 | case 'private_mode': | 64 | case "private_mode": |
| 49 | return ` Your browser appears to be running in private mode.<br> | 65 | return ` Your browser appears to be running in private mode.<br> |
| 50 | This prevents DevDocs from caching documentations for offline access.`; | 66 | This prevents DevDocs from caching documentations for offline access.`; |
| 51 | case 'exception': | 67 | case "exception": |
| 52 | return ` An error occurred when trying to open the IndexedDB database:<br> | 68 | return ` An error occurred when trying to open the IndexedDB database:<br> |
| 53 | <code class="_label">${exception.name}: ${exception.message}</code> `; | 69 | <code class="_label">${exception.name}: ${exception.message}</code> `; |
| 54 | case 'cant_open': | 70 | case "cant_open": |
| 55 | return ` An error occurred when trying to open the IndexedDB database:<br> | 71 | return ` An error occurred when trying to open the IndexedDB database:<br> |
| 56 | <code class="_label">${exception.name}: ${exception.message}</code><br> | 72 | <code class="_label">${exception.name}: ${exception.message}</code><br> |
| 57 | This could be because you're browsing in private mode or have disallowed offline storage on the domain. `; | 73 | This could be because you're browsing in private mode or have disallowed offline storage on the domain. `; |
| 58 | case 'version': | 74 | case "version": |
| 59 | return ` The IndexedDB database was modified with a newer version of the app.<br> | 75 | return ` The IndexedDB database was modified with a newer version of the app.<br> |
| 60 | <a href="#" data-behavior="reload">Reload the page</a> to use offline mode. `; | 76 | <a href="#" data-behavior="reload">Reload the page</a> to use offline mode. `; |
| 61 | case 'empty': | 77 | case "empty": |
| 62 | return " The IndexedDB database appears to be corrupted. Try <a href=\"#\" data-behavior=\"reset\">resetting the app</a>. "; | 78 | return ' The IndexedDB database appears to be corrupted. Try <a href="#" data-behavior="reset">resetting the app</a>. '; |
| 63 | } })(); | 79 | } |
| 80 | })(); | ||
| 64 | 81 | ||
| 65 | return error('Offline mode is unavailable.', reason); | 82 | return error("Offline mode is unavailable.", reason); |
| 66 | }; | 83 | }; |
| 67 | 84 | ||
| 68 | app.templates.unsupportedBrowser = `\ | 85 | app.templates.unsupportedBrowser = `\ |
| @@ -5,12 +5,12 @@ · 4 unmodified lines | |||
| 5 | * DS102: Remove unnecessary code created because of implicit returns | 5 | * DS102: Remove unnecessary code created because of implicit returns |
| 6 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 6 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 7 | */ | 7 | */ |
| 8 | const notice = text => `<p class="_notice-text">${text}</p>`; | 8 | const notice = (text) => `<p class="_notice-text">${text}</p>`; |
| 9 | 9 | ||
| 10 | app.templates.singleDocNotice = doc => notice(` You're browsing the ${doc.fullName} documentation. To browse all docs, go to | 10 | app.templates.singleDocNotice = (doc) => |
| 11 | <a href="//${app.config.production_host}" target="_top">${app.config.production_host}</a> (or press <code>esc</code>). ` | 11 | notice(` You're browsing the ${doc.fullName} documentation. To browse all docs, go to |
| 12 | ); | 12 | <a href="//${app.config.production_host}" target="_top">${app.config.production_host}</a> (or press <code>esc</code>). `); |
| 13 | 13 | ||
| 14 | app.templates.disabledDocNotice = () => notice(` <strong>This documentation is disabled.</strong> | 14 | app.templates.disabledDocNotice = () => |
| 15 | To enable it, go to <a href="/settings" class="_notice-link">Preferences</a>. ` | 15 | notice(` <strong>This documentation is disabled.</strong> |
| 16 | ); | 16 | To enable it, go to <a href="/settings" class="_notice-link">Preferences</a>. `); |
| @@ -6,7 +6,7 @@ · 5 unmodified lines | |||
| 6 | * DS102: Remove unnecessary code created because of implicit returns | 6 | * DS102: Remove unnecessary code created because of implicit returns |
| 7 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 7 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 8 | */ | 8 | */ |
| 9 | const notif = function(title, html) { | 9 | const notif = function (title, html) { |
| 10 | html = html.replace(/<a /g, '<a class="_notif-link" '); | 10 | html = html.replace(/<a /g, '<a class="_notif-link" '); |
| 11 | return ` <h5 class="_notif-title">${title}</h5> | 11 | return ` <h5 class="_notif-title">${title}</h5> |
| 12 | ${html} | 12 | ${html} |
| @@ -14,32 +14,56 @@ ${html} · 1 unmodified line | |||
| 14 | `; | 14 | `; |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
| 17 | const textNotif = (title, message) => notif(title, `<p class="_notif-text">${message}`); | 17 | const textNotif = (title, message) => |
| 18 | notif(title, `<p class="_notif-text">${message}`); | ||
| 18 | 19 | ||
| 19 | app.templates.notifUpdateReady = () => textNotif("<span data-behavior=\"reboot\">DevDocs has been updated.</span>", | 20 | app.templates.notifUpdateReady = () => |
| 20 | "<span data-behavior=\"reboot\"><a href=\"#\" data-behavior=\"reboot\">Reload the page</a> to use the new version.</span>"); | 21 | textNotif( |
| 22 | '<span data-behavior="reboot">DevDocs has been updated.</span>', | ||
| 23 | '<span data-behavior="reboot"><a href="#" data-behavior="reboot">Reload the page</a> to use the new version.</span>', | ||
| 24 | ); | ||
| 21 | 25 | ||
| 22 | app.templates.notifError = () => textNotif(" Oops, an error occurred. ", | 26 | app.templates.notifError = () => |
| 23 | ` Try <a href="#" data-behavior="hard-reload">reloading</a>, and if the problem persists, | 27 | textNotif( |
| 28 | " Oops, an error occurred. ", | ||
| 29 | ` Try <a href="#" data-behavior="hard-reload">reloading</a>, and if the problem persists, | ||
| 24 | <a href="#" data-behavior="reset">resetting the app</a>.<br> | 30 | <a href="#" data-behavior="reset">resetting the app</a>.<br> |
| 25 | You can also report this issue on <a href="https://github.com/freeCodeCamp/devdocs/issues/new" target="_blank" rel="noopener">GitHub</a>. ` | 31 | You can also report this issue on <a href="https://github.com/freeCodeCamp/devdocs/issues/new" target="_blank" rel="noopener">GitHub</a>. `, |
| 26 | ); | 32 | ); |
| 27 | 33 | ||
| 28 | app.templates.notifQuotaExceeded = () => textNotif(" The offline database has exceeded its size limitation. ", | 34 | app.templates.notifQuotaExceeded = () => |
| 29 | " Unfortunately this quota can't be detected programmatically, and the database can't be opened while over the quota, so it had to be reset. "); | 35 | textNotif( |
| 36 | " The offline database has exceeded its size limitation. ", | ||
| 37 | " Unfortunately this quota can't be detected programmatically, and the database can't be opened while over the quota, so it had to be reset. ", | ||
| 38 | ); | ||
| 30 | 39 | ||
| 31 | app.templates.notifCookieBlocked = () => textNotif(" Please enable cookies. ", | 40 | app.templates.notifCookieBlocked = () => |
| 32 | " DevDocs will not work properly if cookies are disabled. "); | 41 | textNotif( |
| 42 | " Please enable cookies. ", | ||
| 43 | " DevDocs will not work properly if cookies are disabled. ", | ||
| 44 | ); | ||
| 33 | 45 | ||
| 34 | app.templates.notifInvalidLocation = () => textNotif(` DevDocs must be loaded from ${app.config.production_host} `, | 46 | app.templates.notifInvalidLocation = () => |
| 35 | " Otherwise things are likely to break. "); | 47 | textNotif( |
| 48 | ` DevDocs must be loaded from ${app.config.production_host} `, | ||
| 49 | " Otherwise things are likely to break. ", | ||
| 50 | ); | ||
| 36 | 51 | ||
| 37 | app.templates.notifImportInvalid = () => textNotif(" Oops, an error occurred. ", | 52 | app.templates.notifImportInvalid = () => |
| 38 | " The file you selected is invalid. "); | 53 | textNotif( |
| 54 | " Oops, an error occurred. ", | ||
| 55 | " The file you selected is invalid. ", | ||
| 56 | ); | ||
| 39 | 57 | ||
| 40 | app.templates.notifNews = news => notif('Changelog', `<div class="_notif-content _notif-news">${app.templates.newsList(news, {years: false})}</div>`); | 58 | app.templates.notifNews = (news) => |
| 59 | notif( | ||
| 60 | "Changelog", | ||
| 61 | `<div class="_notif-content _notif-news">${app.templates.newsList(news, { | ||
| 62 | years: false, | ||
| 63 | })}</div>`, | ||
| 64 | ); | ||
| 41 | 65 | ||
| 42 | app.templates.notifUpdates = function(docs, disabledDocs) { | 66 | app.templates.notifUpdates = function (docs, disabledDocs) { |
| 43 | let doc; | 67 | let doc; |
| 44 | let html = '<div class="_notif-content _notif-news">'; | 68 | let html = '<div class="_notif-content _notif-news">'; |
| 45 | 69 | ||
| @@ -48,9 +72,11 @@ app.templates.notifUpdates = function(docs, disabledDocs) { · 2 unmodified lines | |||
| 48 | html += '<ul class="_notif-list">'; | 72 | html += '<ul class="_notif-list">'; |
| 49 | for (doc of Array.from(docs)) { | 73 | for (doc of Array.from(docs)) { |
| 50 | html += `<li>${doc.name}`; | 74 | html += `<li>${doc.name}`; |
| 51 | if (doc.release) { html += ` <code>→</code> ${doc.release}`; } | 75 | if (doc.release) { |
| 76 | html += ` <code>→</code> ${doc.release}`; | ||
| 77 | } | ||
| 52 | } | 78 | } |
| 53 | html += '</ul></div>'; | 79 | html += "</ul></div>"; |
| 54 | } | 80 | } |
| 55 | 81 | ||
| 56 | if (disabledDocs.length > 0) { | 82 | if (disabledDocs.length > 0) { |
| @@ -58,26 +84,35 @@ app.templates.notifUpdates = function(docs, disabledDocs) { · 1 unmodified line | |||
| 58 | html += '<ul class="_notif-list">'; | 84 | html += '<ul class="_notif-list">'; |
| 59 | for (doc of Array.from(disabledDocs)) { | 85 | for (doc of Array.from(disabledDocs)) { |
| 60 | html += `<li>${doc.name}`; | 86 | html += `<li>${doc.name}`; |
| 61 | if (doc.release) { html += ` <code>→</code> ${doc.release}`; } | 87 | if (doc.release) { |
| 62 | html += "<span class=\"_notif-info\"><a href=\"/settings\">Enable</a></span>"; | 88 | html += ` <code>→</code> ${doc.release}`; |
| 89 | } | ||
| 90 | html += '<span class="_notif-info"><a href="/settings">Enable</a></span>'; | ||
| 63 | } | 91 | } |
| 64 | html += '</ul></div>'; | 92 | html += "</ul></div>"; |
| 65 | } | 93 | } |
| 66 | 94 | ||
| 67 | return notif('Updates', `${html}</div>`); | 95 | return notif("Updates", `${html}</div>`); |
| 68 | }; | 96 | }; |
| 69 | 97 | ||
| 70 | app.templates.notifShare = () => textNotif(" Hi there! ", | 98 | app.templates.notifShare = () => |
| 71 | ` Like DevDocs? Help us reach more developers by sharing the link with your friends on | 99 | textNotif( |
| 100 | " Hi there! ", | ||
| 101 | ` Like DevDocs? Help us reach more developers by sharing the link with your friends on | ||
| 72 | <a href="https://out.devdocs.io/s/tw" target="_blank" rel="noopener">Twitter</a>, <a href="https://out.devdocs.io/s/fb" target="_blank" rel="noopener">Facebook</a>, | 102 | <a href="https://out.devdocs.io/s/tw" target="_blank" rel="noopener">Twitter</a>, <a href="https://out.devdocs.io/s/fb" target="_blank" rel="noopener">Facebook</a>, |
| 73 | <a href="https://out.devdocs.io/s/re" target="_blank" rel="noopener">Reddit</a>, etc.<br>Thanks :) ` | 103 | <a href="https://out.devdocs.io/s/re" target="_blank" rel="noopener">Reddit</a>, etc.<br>Thanks :) `, |
| 74 | ); | 104 | ); |
| 75 | 105 | ||
| 76 | app.templates.notifUpdateDocs = () => textNotif(" Documentation updates available. ", | 106 | app.templates.notifUpdateDocs = () => |
| 77 | " <a href=\"/offline\">Install them</a> as soon as possible to avoid broken pages. "); | 107 | textNotif( |
| 108 | " Documentation updates available. ", | ||
| 109 | ' <a href="/offline">Install them</a> as soon as possible to avoid broken pages. ', | ||
| 110 | ); | ||
| 78 | 111 | ||
| 79 | app.templates.notifAnalyticsConsent = () => textNotif(" Tracking cookies ", | 112 | app.templates.notifAnalyticsConsent = () => |
| 80 | ` We would like to gather usage data about how DevDocs is used through Google Analytics and Gauges. We only collect anonymous traffic information. | 113 | textNotif( |
| 114 | " Tracking cookies ", | ||
| 115 | ` We would like to gather usage data about how DevDocs is used through Google Analytics and Gauges. We only collect anonymous traffic information. | ||
| 81 | Please confirm if you accept our tracking cookies. You can always change your decision in the settings. | 116 | Please confirm if you accept our tracking cookies. You can always change your decision in the settings. |
| 82 | <br><span class="_notif-right"><a href="#" data-behavior="accept-analytics">Accept</a> or <a href="#" data-behavior="decline-analytics">Decline</a></span> ` | 117 | <br><span class="_notif-right"><a href="#" data-behavior="accept-analytics">Accept</a> or <a href="#" data-behavior="decline-analytics">Decline</a></span> `, |
| 83 | ); | 118 | ); |
| @@ -8,12 +8,18 @@ · 7 unmodified lines | |||
| 8 | * DS207: Consider shorter variations of null checks | 8 | * DS207: Consider shorter variations of null checks |
| 9 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 9 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 10 | */ | 10 | */ |
| 11 | app.templates.aboutPage = function() { | 11 | app.templates.aboutPage = function () { |
| 12 | let doc; | 12 | let doc; |
| 13 | const all_docs = app.docs.all().concat(...Array.from(app.disabledDocs.all() || [])); | 13 | const all_docs = app.docs |
| 14 | .all() | ||
| 15 | .concat(...Array.from(app.disabledDocs.all() || [])); | ||
| 14 | // de-duplicate docs by doc.name | 16 | // de-duplicate docs by doc.name |
| 15 | const docs = []; | 17 | const docs = []; |
| 16 | for (doc of Array.from(all_docs)) { if (!(docs.find(d => d.name === doc.name))) { docs.push(doc); } } | 18 | for (doc of Array.from(all_docs)) { |
| 19 | if (!docs.find((d) => d.name === doc.name)) { | ||
| 20 | docs.push(doc); | ||
| 21 | } | ||
| 22 | } | ||
| 17 | return `\ | 23 | return `\ |
| 18 | <nav class="_toc" role="directory"> | 24 | <nav class="_toc" role="directory"> |
| 19 | <h3 class="_toc-title">Table of Contents</h3> | 25 | <h3 class="_toc-title">Table of Contents</h3> |
| @@ -79,18 +85,23 @@ app.templates.aboutPage = function() { · 59 unmodified lines | |||
| 79 | <th>Documentation | 85 | <th>Documentation |
| 80 | <th>Copyright/License | 86 | <th>Copyright/License |
| 81 | <th>Source code | 87 | <th>Source code |
| 82 | ${((() => { | 88 | ${(() => { |
| 83 | const result = []; | 89 | const result = []; |
| 84 | 90 | ||
| 85 | for (doc of Array.from(docs)) { result.push(`<tr> \ | 91 | for (doc of Array.from(docs)) { |
| 86 | <td><a href=\"${(doc.links != null ? doc.links.home : undefined)}\">${doc.name}</a></td> \ | 92 | result.push(`<tr> \ |
| 93 | <td><a href=\"${doc.links != null ? doc.links.home : undefined}\">${ | ||
| 94 | doc.name | ||
| 95 | }</a></td> \ | ||
| 87 | <td>${doc.attribution}</td> \ | 96 | <td>${doc.attribution}</td> \ |
| 88 | <td><a href=\"${(doc.links != null ? doc.links.code : undefined)}\">Source code</a></td> \ | 97 | <td><a href=\"${ |
| 98 | doc.links != null ? doc.links.code : undefined | ||
| 99 | }\">Source code</a></td> \ | ||
| 89 | </tr>`); | 100 | </tr>`); |
| 90 | } | 101 | } |
| 91 | 102 | ||
| 92 | return result; | 103 | return result; |
| 93 | })()).join('')} | 104 | })().join("")} |
| 94 | </table> | 105 | </table> |
| 95 | </div> | 106 | </div> |
| 96 | 107 | ||
| @@ -6,11 +6,11 @@ · 5 unmodified lines | |||
| 6 | * DS205: Consider reworking code to avoid use of IIFEs | 6 | * DS205: Consider reworking code to avoid use of IIFEs |
| 7 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 7 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 8 | */ | 8 | */ |
| 9 | app.templates.helpPage = function() { | 9 | app.templates.helpPage = function () { |
| 10 | let key, value; | 10 | let key, value; |
| 11 | const ctrlKey = $.isMac() ? 'cmd' : 'ctrl'; | 11 | const ctrlKey = $.isMac() ? "cmd" : "ctrl"; |
| 12 | const navKey = $.isMac() ? 'cmd' : 'alt'; | 12 | const navKey = $.isMac() ? "cmd" : "alt"; |
| 13 | const arrowScroll = app.settings.get('arrowScroll'); | 13 | const arrowScroll = app.settings.get("arrowScroll"); |
| 14 | 14 | ||
| 15 | const aliases_one = {}; | 15 | const aliases_one = {}; |
| 16 | const aliases_two = {}; | 16 | const aliases_two = {}; |
| @@ -18,7 +18,8 @@ app.templates.helpPage = function() { · 1 unmodified line | |||
| 18 | const middle = Math.ceil(keys.length / 2) - 1; | 18 | const middle = Math.ceil(keys.length / 2) - 1; |
| 19 | for (let i = 0; i < keys.length; i++) { | 19 | for (let i = 0; i < keys.length; i++) { |
| 20 | key = keys[i]; | 20 | key = keys[i]; |
| 21 | (i > middle ? aliases_two : aliases_one)[key] = app.models.Entry.ALIASES[key]; | 21 | (i > middle ? aliases_two : aliases_one)[key] = |
| 22 | app.models.Entry.ALIASES[key]; | ||
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | return `\ | 25 | return `\ |
| @@ -79,12 +80,12 @@ app.templates.helpPage = function() { · 54 unmodified lines | |||
| 79 | <h3 class="_shortcuts-title">Sidebar</h3> | 80 | <h3 class="_shortcuts-title">Sidebar</h3> |
| 80 | <dl class="_shortcuts-dl"> | 81 | <dl class="_shortcuts-dl"> |
| 81 | <dt class="_shortcuts-dt"> | 82 | <dt class="_shortcuts-dt"> |
| 82 | ${arrowScroll ? '<code class="_shortcut-code">shift</code> + ' : ''} | 83 | ${arrowScroll ? '<code class="_shortcut-code">shift</code> + ' : ""} |
| 83 | <code class="_shortcut-code">↓</code> | 84 | <code class="_shortcut-code">↓</code> |
| 84 | <code class="_shortcut-code">↑</code> | 85 | <code class="_shortcut-code">↑</code> |
| 85 | <dd class="_shortcuts-dd">Move selection | 86 | <dd class="_shortcuts-dd">Move selection |
| 86 | <dt class="_shortcuts-dt"> | 87 | <dt class="_shortcuts-dt"> |
| 87 | ${arrowScroll ? '<code class="_shortcut-code">shift</code> + ' : ''} | 88 | ${arrowScroll ? '<code class="_shortcut-code">shift</code> + ' : ""} |
| 88 | <code class="_shortcut-code">→</code> | 89 | <code class="_shortcut-code">→</code> |
| 89 | <code class="_shortcut-code">←</code> | 90 | <code class="_shortcut-code">←</code> |
| 90 | <dd class="_shortcuts-dd">Show/hide sub-list | 91 | <dd class="_shortcuts-dd">Show/hide sub-list |
| @@ -105,15 +106,16 @@ app.templates.helpPage = function() { · 14 unmodified lines | |||
| 105 | <code class="_shortcut-code">${navKey} + →</code> | 106 | <code class="_shortcut-code">${navKey} + →</code> |
| 106 | <dd class="_shortcuts-dd">Go back/forward | 107 | <dd class="_shortcuts-dd">Go back/forward |
| 107 | <dt class="_shortcuts-dt"> | 108 | <dt class="_shortcuts-dt"> |
| 108 | ${arrowScroll ? | 109 | ${ |
| 109 | '<code class="_shortcut-code">↓</code> ' + | 110 | arrowScroll |
| 111 | ? '<code class="_shortcut-code">↓</code> ' + | ||
| 110 | '<code class="_shortcut-code">↑</code>' | 112 | '<code class="_shortcut-code">↑</code>' |
| 111 | : | 113 | : '<code class="_shortcut-code">alt + ↓</code> ' + |
| 112 | '<code class="_shortcut-code">alt + ↓</code> ' + | ||
| 113 | '<code class="_shortcut-code">alt + ↑</code>' + | 114 | '<code class="_shortcut-code">alt + ↑</code>' + |
| 114 | '<br>' + | 115 | "<br>" + |
| 115 | '<code class="_shortcut-code">shift + ↓</code> ' + | 116 | '<code class="_shortcut-code">shift + ↓</code> ' + |
| 116 | '<code class="_shortcut-code">shift + ↑</code>'} | 117 | '<code class="_shortcut-code">shift + ↑</code>' |
| 118 | } | ||
| 117 | <dd class="_shortcuts-dd">Scroll step by step<br><br> | 119 | <dd class="_shortcuts-dd">Scroll step by step<br><br> |
| 118 | <dt class="_shortcuts-dt"> | 120 | <dt class="_shortcuts-dt"> |
| 119 | <code class="_shortcut-code">space</code> | 121 | <code class="_shortcut-code">space</code> |
| @@ -167,27 +169,31 @@ app.templates.helpPage = function() { · 47 unmodified lines | |||
| 167 | <tr> | 169 | <tr> |
| 168 | <th>Word | 170 | <th>Word |
| 169 | <th>Alias | 171 | <th>Alias |
| 170 | ${((() => { | 172 | ${(() => { |
| 171 | const result = []; | 173 | const result = []; |
| 172 | for (key in aliases_one) { | 174 | for (key in aliases_one) { |
| 173 | value = aliases_one[key]; | 175 | value = aliases_one[key]; |
| 174 | result.push(`<tr><td class=\"_code\">${key}<td class=\"_code\">${value}`); | 176 | result.push( |
| 175 | } | 177 | `<tr><td class=\"_code\">${key}<td class=\"_code\">${value}`, |
| 176 | return result; | 178 | ); |
| 177 | })()).join('')} | 179 | } |
| 180 | return result; | ||
| 181 | })().join("")} | ||
| 178 | </table> | 182 | </table> |
| 179 | <table> | 183 | <table> |
| 180 | <tr> | 184 | <tr> |
| 181 | <th>Word | 185 | <th>Word |
| 182 | <th>Alias | 186 | <th>Alias |
| 183 | ${((() => { | 187 | ${(() => { |
| 184 | const result1 = []; | 188 | const result1 = []; |
| 185 | for (key in aliases_two) { | 189 | for (key in aliases_two) { |
| 186 | value = aliases_two[key]; | 190 | value = aliases_two[key]; |
| 187 | result1.push(`<tr><td class=\"_code\">${key}<td class=\"_code\">${value}`); | 191 | result1.push( |
| 188 | } | 192 | `<tr><td class=\"_code\">${key}<td class=\"_code\">${value}`, |
| 189 | return result1; | 193 | ); |
| 190 | })()).join('')} | 194 | } |
| 195 | return result1; | ||
| 196 | })().join("")} | ||
| 191 | </table> | 197 | </table> |
| 192 | </div> | 198 | </div> |
| 193 | <p>Feel free to suggest new aliases on <a href="https://github.com/freeCodeCamp/devdocs/issues/new">GitHub</a>.\ | 199 | <p>Feel free to suggest new aliases on <a href="https://github.com/freeCodeCamp/devdocs/issues/new">GitHub</a>.\ |
| @@ -5,12 +5,14 @@ · 4 unmodified lines | |||
| 5 | * DS102: Remove unnecessary code created because of implicit returns | 5 | * DS102: Remove unnecessary code created because of implicit returns |
| 6 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 6 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 7 | */ | 7 | */ |
| 8 | app.templates.offlinePage = docs => `\ | 8 | app.templates.offlinePage = (docs) => `\ |
| 9 | <h1 class="_lined-heading">Offline Documentation</h1> | 9 | <h1 class="_lined-heading">Offline Documentation</h1> |
| 10 | 10 | ||
| 11 | <div class="_docs-tools"> | 11 | <div class="_docs-tools"> |
| 12 | <label> | 12 | <label> |
| 13 | <input type="checkbox" name="autoUpdate" value="1" ${app.settings.get('manualUpdate') ? '' : 'checked'}>Install updates automatically | 13 | <input type="checkbox" name="autoUpdate" value="1" ${ |
| 14 | app.settings.get("manualUpdate") ? "" : "checked" | ||
| 15 | }>Install updates automatically | ||
| 14 | </label> | 16 | </label> |
| 15 | <div class="_docs-links"> | 17 | <div class="_docs-links"> |
| 16 | <button type="button" class="_btn-link" data-action-all="install">Install all</button><button type="button" class="_btn-link" data-action-all="update"><strong>Update all</strong></button><button type="button" class="_btn-link" data-action-all="uninstall">Uninstall all</button> | 18 | <button type="button" class="_btn-link" data-action-all="install">Install all</button><button type="button" class="_btn-link" data-action-all="update"><strong>Update all</strong></button><button type="button" class="_btn-link" data-action-all="uninstall">Uninstall all</button> |
| @@ -47,14 +49,15 @@ app.templates.offlinePage = docs => `\ · 30 unmodified lines | |||
| 47 | </dl>\ | 49 | </dl>\ |
| 48 | `; | 50 | `; |
| 49 | 51 | ||
| 50 | var canICloseTheTab = function() { | 52 | var canICloseTheTab = function () { |
| 51 | if (app.ServiceWorker.isEnabled()) { | 53 | if (app.ServiceWorker.isEnabled()) { |
| 52 | return " Yes! Even offline, you can open a new tab, go to <a href=\"//devdocs.io\">devdocs.io</a>, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). "; | 54 | return ' Yes! Even offline, you can open a new tab, go to <a href="//devdocs.io">devdocs.io</a>, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). '; |
| 53 | } else { | 55 | } else { |
| 54 | let reason = "aren't available in your browser (or are disabled)"; | 56 | let reason = "aren't available in your browser (or are disabled)"; |
| 55 | 57 | ||
| 56 | if (app.config.env !== 'production') { | 58 | if (app.config.env !== "production") { |
| 57 | reason = "are disabled in your development instance of DevDocs (enable them by setting the <code>ENABLE_SERVICE_WORKER</code> environment variable to <code>true</code>)"; | 59 | reason = |
| 60 | "are disabled in your development instance of DevDocs (enable them by setting the <code>ENABLE_SERVICE_WORKER</code> environment variable to <code>true</code>)"; | ||
| 58 | } | 61 | } |
| 59 | 62 | ||
| 60 | return ` No. Service Workers ${reason}, so loading <a href="//devdocs.io">devdocs.io</a> offline won't work.<br> | 63 | return ` No. Service Workers ${reason}, so loading <a href="//devdocs.io">devdocs.io</a> offline won't work.<br> |
| @@ -62,30 +65,31 @@ The current tab will continue to function even when you go offline (provided you · 1 unmodified line | |||
| 62 | } | 65 | } |
| 63 | }; | 66 | }; |
| 64 | 67 | ||
| 65 | app.templates.offlineDoc = function(doc, status) { | 68 | app.templates.offlineDoc = function (doc, status) { |
| 66 | const outdated = doc.isOutdated(status); | 69 | const outdated = doc.isOutdated(status); |
| 67 | 70 | ||
| 68 | let html = `\ | 71 | let html = `\ |
| 69 | <tr data-slug="${doc.slug}"${outdated ? ' class="_highlight"' : ''}> | 72 | <tr data-slug="${doc.slug}"${outdated ? ' class="_highlight"' : ""}> |
| 70 | <td class="_docs-name _icon-${doc.icon}">${doc.fullName}</td> | 73 | <td class="_docs-name _icon-${doc.icon}">${doc.fullName}</td> |
| 71 | <td class="_docs-size">${Math.ceil(doc.db_size / 100000) / 10} <small>MB</small></td>\ | 74 | <td class="_docs-size">${ |
| 75 | Math.ceil(doc.db_size / 100000) / 10 | ||
| 76 | } <small>MB</small></td>\ | ||
| 72 | `; | 77 | `; |
| 73 | 78 | ||
| 74 | html += !(status && status.installed) ? | 79 | html += !(status && status.installed) |
| 75 | `\ | 80 | ? `\ |
| 76 | <td>-</td> | 81 | <td>-</td> |
| 77 | <td><button type="button" class="_btn-link" data-action="install">Install</button></td>\ | 82 | <td><button type="button" class="_btn-link" data-action="install">Install</button></td>\ |
| 78 | ` | 83 | ` |
| 79 | : outdated ? | 84 | : outdated |
| 80 | `\ | 85 | ? `\ |
| 81 | <td><strong>Outdated</strong></td> | 86 | <td><strong>Outdated</strong></td> |
| 82 | <td><button type="button" class="_btn-link _bold" data-action="update">Update</button> - <button type="button" class="_btn-link" data-action="uninstall">Uninstall</button></td>\ | 87 | <td><button type="button" class="_btn-link _bold" data-action="update">Update</button> - <button type="button" class="_btn-link" data-action="uninstall">Uninstall</button></td>\ |
| 83 | ` | 88 | ` |
| 84 | : | 89 | : `\ |
| 85 | `\ | ||
| 86 | <td>Up‑to‑date</td> | 90 | <td>Up‑to‑date</td> |
| 87 | <td><button type="button" class="_btn-link" data-action="uninstall">Uninstall</button></td>\ | 91 | <td><button type="button" class="_btn-link" data-action="uninstall">Uninstall</button></td>\ |
| 88 | `; | 92 | `; |
| 89 | 93 | ||
| 90 | return html + '</tr>'; | 94 | return html + "</tr>"; |
| 91 | }; | 95 | }; |
| @@ -7,23 +7,32 @@ · 6 unmodified lines | |||
| 7 | */ | 7 | */ |
| 8 | const themeOption = ({ label, value }, settings) => `\ | 8 | const themeOption = ({ label, value }, settings) => `\ |
| 9 | <label class="_settings-label _theme-label"> | 9 | <label class="_settings-label _theme-label"> |
| 10 | <input type="radio" name="theme" value="${value}"${settings.theme === value ? ' checked' : ''}> | 10 | <input type="radio" name="theme" value="${value}"${ |
| 11 | settings.theme === value ? " checked" : "" | ||
| 12 | }> | ||
| 11 | ${label} | 13 | ${label} |
| 12 | </label>\ | 14 | </label>\ |
| 13 | `; | 15 | `; |
| 14 | 16 | ||
| 15 | app.templates.settingsPage = settings => `\ | 17 | app.templates.settingsPage = (settings) => `\ |
| 16 | <h1 class="_lined-heading">Preferences</h1> | 18 | <h1 class="_lined-heading">Preferences</h1> |
| 17 | 19 | ||
| 18 | <div class="_settings-fieldset"> | 20 | <div class="_settings-fieldset"> |
| 19 | <h2 class="_settings-legend">Theme:</h2> | 21 | <h2 class="_settings-legend">Theme:</h2> |
| 20 | <div class="_settings-inputs"> | 22 | <div class="_settings-inputs"> |
| 21 | ${settings.autoSupported ? | 23 | ${ |
| 22 | themeOption({label: "Automatic <small>Matches system setting</small>", value: "auto"}, settings) | 24 | settings.autoSupported |
| 23 | : | 25 | ? themeOption( |
| 24 | ""} | 26 | { |
| 25 | ${themeOption({label: "Light", value: "default"}, settings)} | 27 | label: "Automatic <small>Matches system setting</small>", |
| 26 | ${themeOption({label: "Dark", value: "dark"}, settings)} | 28 | value: "auto", |
| 29 | }, | ||
| 30 | settings, | ||
| 31 | ) | ||
| 32 | : "" | ||
| 33 | } | ||
| 34 | ${themeOption({ label: "Light", value: "default" }, settings)} | ||
| 35 | ${themeOption({ label: "Dark", value: "dark" }, settings)} | ||
| 27 | </div> | 36 | </div> |
| 28 | </div> | 37 | </div> |
| 29 | 38 | ||
| @@ -32,24 +41,36 @@ app.templates.settingsPage = settings => `\ · 2 unmodified lines | |||
| 32 | 41 | ||
| 33 | <div class="_settings-inputs"> | 42 | <div class="_settings-inputs"> |
| 34 | <label class="_settings-label _setting-max-width"> | 43 | <label class="_settings-label _setting-max-width"> |
| 35 | <input type="checkbox" form="settings" name="layout" value="_max-width"${settings['_max-width'] ? ' checked' : ''}>Enable fixed-width layout | 44 | <input type="checkbox" form="settings" name="layout" value="_max-width"${ |
| 45 | settings["_max-width"] ? " checked" : "" | ||
| 46 | }>Enable fixed-width layout | ||
| 36 | </label> | 47 | </label> |
| 37 | <label class="_settings-label _setting-text-justify-hyphenate"> | 48 | <label class="_settings-label _setting-text-justify-hyphenate"> |
| 38 | <input type="checkbox" form="settings" name="layout" value="_text-justify-hyphenate"${settings['_text-justify-hyphenate'] ? ' checked' : ''}>Enable justified layout and automatic hyphenation | 49 | <input type="checkbox" form="settings" name="layout" value="_text-justify-hyphenate"${ |
| 50 | settings["_text-justify-hyphenate"] ? " checked" : "" | ||
| 51 | }>Enable justified layout and automatic hyphenation | ||
| 39 | </label> | 52 | </label> |
| 40 | <label class="_settings-label _hide-on-mobile"> | 53 | <label class="_settings-label _hide-on-mobile"> |
| 41 | <input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"${settings['_sidebar-hidden'] ? ' checked' : ''}>Automatically hide and show the sidebar | 54 | <input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"${ |
| 55 | settings["_sidebar-hidden"] ? " checked" : "" | ||
| 56 | }>Automatically hide and show the sidebar | ||
| 42 | <small>Tip: drag the edge of the sidebar to resize it.</small> | 57 | <small>Tip: drag the edge of the sidebar to resize it.</small> |
| 43 | </label> | 58 | </label> |
| 44 | <label class="_settings-label _hide-on-mobile"> | 59 | <label class="_settings-label _hide-on-mobile"> |
| 45 | <input type="checkbox" form="settings" name="noAutofocus" value="_no-autofocus"${settings.noAutofocus ? ' checked' : ''}>Disable autofocus of search input | 60 | <input type="checkbox" form="settings" name="noAutofocus" value="_no-autofocus"${ |
| 61 | settings.noAutofocus ? " checked" : "" | ||
| 62 | }>Disable autofocus of search input | ||
| 46 | </label> | 63 | </label> |
| 47 | <label class="_settings-label"> | 64 | <label class="_settings-label"> |
| 48 | <input type="checkbox" form="settings" name="autoInstall" value="_auto-install"${settings.autoInstall ? ' checked' : ''}>Automatically download documentation for offline use | 65 | <input type="checkbox" form="settings" name="autoInstall" value="_auto-install"${ |
| 66 | settings.autoInstall ? " checked" : "" | ||
| 67 | }>Automatically download documentation for offline use | ||
| 49 | <small>Only enable this when bandwidth isn't a concern to you.</small> | 68 | <small>Only enable this when bandwidth isn't a concern to you.</small> |
| 50 | </label> | 69 | </label> |
| 51 | <label class="_settings-label _hide-in-development"> | 70 | <label class="_settings-label _hide-in-development"> |
| 52 | <input type="checkbox" form="settings" name="analyticsConsent"${settings.analyticsConsent ? ' checked' : ''}>Enable tracking cookies | 71 | <input type="checkbox" form="settings" name="analyticsConsent"${ |
| 72 | settings.analyticsConsent ? " checked" : "" | ||
| 73 | }>Enable tracking cookies | ||
| 53 | <small>With this checked, we enable Google Analytics and Gauges to collect anonymous traffic information.</small> | 74 | <small>With this checked, we enable Google Analytics and Gauges to collect anonymous traffic information.</small> |
| 54 | </label> | 75 | </label> |
| 55 | </div> | 76 | </div> |
| @@ -60,20 +81,30 @@ app.templates.settingsPage = settings => `\ · 4 unmodified lines | |||
| 60 | 81 | ||
| 61 | <div class="_settings-inputs"> | 82 | <div class="_settings-inputs"> |
| 62 | <label class="_settings-label"> | 83 | <label class="_settings-label"> |
| 63 | <input type="checkbox" form="settings" name="smoothScroll" value="1"${settings.smoothScroll ? ' checked' : ''}>Use smooth scrolling | 84 | <input type="checkbox" form="settings" name="smoothScroll" value="1"${ |
| 85 | settings.smoothScroll ? " checked" : "" | ||
| 86 | }>Use smooth scrolling | ||
| 64 | </label> | 87 | </label> |
| 65 | <label class="_settings-label _setting-native-scrollbar"> | 88 | <label class="_settings-label _setting-native-scrollbar"> |
| 66 | <input type="checkbox" form="settings" name="layout" value="_native-scrollbars"${settings['_native-scrollbars'] ? ' checked' : ''}>Use native scrollbars | 89 | <input type="checkbox" form="settings" name="layout" value="_native-scrollbars"${ |
| 90 | settings["_native-scrollbars"] ? " checked" : "" | ||
| 91 | }>Use native scrollbars | ||
| 67 | </label> | 92 | </label> |
| 68 | <label class="_settings-label"> | 93 | <label class="_settings-label"> |
| 69 | <input type="checkbox" form="settings" name="arrowScroll" value="1"${settings.arrowScroll ? ' checked' : ''}>Use arrow keys to scroll the main content area | 94 | <input type="checkbox" form="settings" name="arrowScroll" value="1"${ |
| 95 | settings.arrowScroll ? " checked" : "" | ||
| 96 | }>Use arrow keys to scroll the main content area | ||
| 70 | <small>With this checked, use <code class="_label">shift</code> + <code class="_label">↑</code><code class="_label">↓</code><code class="_label">←</code><code class="_label">→</code> to navigate the sidebar.</small> | 97 | <small>With this checked, use <code class="_label">shift</code> + <code class="_label">↑</code><code class="_label">↓</code><code class="_label">←</code><code class="_label">→</code> to navigate the sidebar.</small> |
| 71 | </label> | 98 | </label> |
| 72 | <label class="_settings-label"> | 99 | <label class="_settings-label"> |
| 73 | <input type="checkbox" form="settings" name="spaceScroll" value="1"${settings.spaceScroll ? ' checked' : ''}>Use spacebar to scroll during search | 100 | <input type="checkbox" form="settings" name="spaceScroll" value="1"${ |
| 101 | settings.spaceScroll ? " checked" : "" | ||
| 102 | }>Use spacebar to scroll during search | ||
| 74 | </label> | 103 | </label> |
| 75 | <label class="_settings-label"> | 104 | <label class="_settings-label"> |
| 76 | <input type="number" step="0.1" form="settings" name="spaceTimeout" min="0" max="5" value="${settings.spaceTimeout}"> Delay until you can scroll by pressing space | 105 | <input type="number" step="0.1" form="settings" name="spaceTimeout" min="0" max="5" value="${ |
| 106 | settings.spaceTimeout | ||
| 107 | }"> Delay until you can scroll by pressing space | ||
| 77 | <small>Time in seconds</small> | 108 | <small>Time in seconds</small> |
| 78 | </label> | 109 | </label> |
| 79 | </div> | 110 | </div> |
| @@ -5,7 +5,13 @@ · 4 unmodified lines | |||
| 5 | * DS102: Remove unnecessary code created because of implicit returns | 5 | * DS102: Remove unnecessary code created because of implicit returns |
| 6 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 6 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 7 | */ | 7 | */ |
| 8 | app.templates.typePage = type => ` <h1>${type.doc.fullName} / ${type.name}</h1> | 8 | app.templates.typePage = (type) => ` <h1>${type.doc.fullName} / ${ |
| 9 | <ul class="_entry-list">${app.templates.render('typePageEntry', type.entries())}</ul> `; | 9 | type.name |
| 10 | }</h1> | ||
| 11 | <ul class="_entry-list">${app.templates.render( | ||
| 12 | "typePageEntry", | ||
| 13 | type.entries(), | ||
| 14 | )}</ul> `; | ||
| 10 | 15 | ||
| 11 | app.templates.typePageEntry = entry => `<li><a href="${entry.fullPath()}">${$.escape(entry.name)}</a></li>`; | 16 | app.templates.typePageEntry = (entry) => |
| 17 | `<li><a href="${entry.fullPath()}">${$.escape(entry.name)}</a></li>`; | ||
| @@ -5,11 +5,19 @@ · 4 unmodified lines | |||
| 5 | * DS102: Remove unnecessary code created because of implicit returns | 5 | * DS102: Remove unnecessary code created because of implicit returns |
| 6 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 6 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 7 | */ | 7 | */ |
| 8 | const arrow = "<svg class=\"_path-arrow\"><use xlink:href=\"#icon-dir\"/></svg>"; | 8 | const arrow = '<svg class="_path-arrow"><use xlink:href="#icon-dir"/></svg>'; |
| 9 | 9 | ||
| 10 | app.templates.path = function(doc, type, entry) { | 10 | app.templates.path = function (doc, type, entry) { |
| 11 | let html = `<a href="${doc.fullPath()}" class="_path-item _icon-${doc.icon}">${doc.fullName}</a>`; | 11 | let html = `<a href="${doc.fullPath()}" class="_path-item _icon-${ |
| 12 | if (type) { html += `${arrow}<a href="${type.fullPath()}" class="_path-item">${type.name}</a>`; } | 12 | doc.icon |
| 13 | if (entry) { html += `${arrow}<span class="_path-item">${$.escape(entry.name)}</span>`; } | 13 | }">${doc.fullName}</a>`; |
| 14 | if (type) { | ||
| 15 | html += `${arrow}<a href="${type.fullPath()}" class="_path-item">${ | ||
| 16 | type.name | ||
| 17 | }</a>`; | ||
| 18 | } | ||
| 19 | if (entry) { | ||
| 20 | html += `${arrow}<span class="_path-item">${$.escape(entry.name)}</span>`; | ||
| 21 | } | ||
| 14 | return html; | 22 | return html; |
| 15 | }; | 23 | }; |
| @@ -6,74 +6,112 @@ · 5 unmodified lines | |||
| 6 | * DS207: Consider shorter variations of null checks | 6 | * DS207: Consider shorter variations of null checks |
| 7 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 7 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 8 | */ | 8 | */ |
| 9 | const { | 9 | const { templates } = app; |
| 10 | templates | ||
| 11 | } = app; | ||
| 12 | 10 | ||
| 13 | const arrow = "<svg class=\"_list-arrow\"><use xlink:href=\"#icon-dir\"/></svg>"; | 11 | const arrow = '<svg class="_list-arrow"><use xlink:href="#icon-dir"/></svg>'; |
| 14 | 12 | ||
| 15 | templates.sidebarDoc = function(doc, options) { | 13 | templates.sidebarDoc = function (doc, options) { |
| 16 | if (options == null) { options = {}; } | 14 | if (options == null) { |
| 17 | let link = `<a href="${doc.fullPath()}" class="_list-item _icon-${doc.icon} `; | 15 | options = {}; |
| 18 | link += options.disabled ? '_list-disabled' : '_list-dir'; | 16 | } |
| 17 | let link = `<a href="${doc.fullPath()}" class="_list-item _icon-${doc.icon} `; | ||
| 18 | link += options.disabled ? "_list-disabled" : "_list-dir"; | ||
| 19 | link += `" data-slug="${doc.slug}" title="${doc.fullName}" tabindex="-1">`; | 19 | link += `" data-slug="${doc.slug}" title="${doc.fullName}" tabindex="-1">`; |
| 20 | if (options.disabled) { | 20 | if (options.disabled) { |
| 21 | link += `<span class="_list-enable" data-enable="${doc.slug}">Enable</span>`; | 21 | link += `<span class="_list-enable" data-enable="${doc.slug}">Enable</span>`; |
| 22 | } else { | 22 | } else { |
| 23 | link += arrow; | 23 | link += arrow; |
| 24 | } | 24 | } |
| 25 | if (doc.release) { link += `<span class="_list-count">${doc.release}</span>`; } | 25 | if (doc.release) { |
| 26 | link += `<span class="_list-count">${doc.release}</span>`; | ||
| 27 | } | ||
| 26 | link += `<span class="_list-text">${doc.name}`; | 28 | link += `<span class="_list-text">${doc.name}`; |
| 27 | if (options.fullName || (options.disabled && doc.version)) { link += ` ${doc.version}`; } | 29 | if (options.fullName || (options.disabled && doc.version)) { |
| 30 | link += ` ${doc.version}`; | ||
| 31 | } | ||
| 28 | return link + "</span></a>"; | 32 | return link + "</span></a>"; |
| 29 | }; | 33 | }; |
| 30 | 34 | ||
| 31 | templates.sidebarType = type => `<a href="${type.fullPath()}" class="_list-item _list-dir" data-slug="${type.slug}" tabindex="-1">${arrow}<span class="_list-count">${type.count}</span><span class="_list-text">${$.escape(type.name)}</span></a>`; | 35 | templates.sidebarType = (type) => |
| 36 | `<a href="${type.fullPath()}" class="_list-item _list-dir" data-slug="${ | ||
| 37 | type.slug | ||
| 38 | }" tabindex="-1">${arrow}<span class="_list-count">${ | ||
| 39 | type.count | ||
| 40 | }</span><span class="_list-text">${$.escape(type.name)}</span></a>`; | ||
| 32 | 41 | ||
| 33 | templates.sidebarEntry = entry => `<a href="${entry.fullPath()}" class="_list-item _list-hover" tabindex="-1">${$.escape(entry.name)}</a>`; | 42 | templates.sidebarEntry = (entry) => |
| 43 | `<a href="${entry.fullPath()}" class="_list-item _list-hover" tabindex="-1">${$.escape( | ||
| 44 | entry.name, | ||
| 45 | )}</a>`; | ||
| 34 | 46 | ||
| 35 | templates.sidebarResult = function(entry) { | 47 | templates.sidebarResult = function (entry) { |
| 36 | let addons = entry.isIndex() && app.disabledDocs.contains(entry.doc) ? | 48 | let addons = |
| 37 | `<span class="_list-enable" data-enable="${entry.doc.slug}">Enable</span>` | 49 | entry.isIndex() && app.disabledDocs.contains(entry.doc) |
| 38 | : | 50 | ? `<span class="_list-enable" data-enable="${entry.doc.slug}">Enable</span>` |
| 39 | "<span class=\"_list-reveal\" data-reset-list title=\"Reveal in list\"></span>"; | 51 | : '<span class="_list-reveal" data-reset-list title="Reveal in list"></span>'; |
| 40 | if (entry.doc.version && !entry.isIndex()) { addons += `<span class="_list-count">${entry.doc.short_version}</span>`; } | 52 | if (entry.doc.version && !entry.isIndex()) { |
| 41 | return `<a href="${entry.fullPath()}" class="_list-item _list-hover _list-result _icon-${entry.doc.icon}" tabindex="-1">${addons}<span class="_list-text">${$.escape(entry.name)}</span></a>`; | 53 | addons += `<span class="_list-count">${entry.doc.short_version}</span>`; |
| 54 | } | ||
| 55 | return `<a href="${entry.fullPath()}" class="_list-item _list-hover _list-result _icon-${ | ||
| 56 | entry.doc.icon | ||
| 57 | }" tabindex="-1">${addons}<span class="_list-text">${$.escape( | ||
| 58 | entry.name, | ||
| 59 | )}</span></a>`; | ||
| 42 | }; | 60 | }; |
| 43 | 61 | ||
| 44 | templates.sidebarNoResults = function() { | 62 | templates.sidebarNoResults = function () { |
| 45 | let html = " <div class=\"_list-note\">No results.</div> "; | 63 | let html = ' <div class="_list-note">No results.</div> '; |
| 46 | if (!app.isSingleDoc() && !app.disabledDocs.isEmpty()) { html += `\ | 64 | if (!app.isSingleDoc() && !app.disabledDocs.isEmpty()) { |
| 65 | html += `\ | ||
| 47 | <div class="_list-note">Note: documentations must be <a href="/settings" class="_list-note-link">enabled</a> to appear in the search.</div>\ | 66 | <div class="_list-note">Note: documentations must be <a href="/settings" class="_list-note-link">enabled</a> to appear in the search.</div>\ |
| 48 | `; } | 67 | `; |
| 68 | } | ||
| 49 | return html; | 69 | return html; |
| 50 | }; | 70 | }; |
| 51 | 71 | ||
| 52 | templates.sidebarPageLink = count => `<span role="link" class="_list-item _list-pagelink">Show more\u2026 (${count})</span>`; | 72 | templates.sidebarPageLink = (count) => |
| 73 | `<span role="link" class="_list-item _list-pagelink">Show more\u2026 (${count})</span>`; | ||
| 53 | 74 | ||
| 54 | templates.sidebarLabel = function(doc, options) { | 75 | templates.sidebarLabel = function (doc, options) { |
| 55 | if (options == null) { options = {}; } | 76 | if (options == null) { |
| 56 | let label = "<label class=\"_list-item"; | 77 | options = {}; |
| 57 | if (!doc.version) { label += ` _icon-${doc.icon}`; } | 78 | } |
| 79 | let label = '<label class="_list-item'; | ||
| 80 | if (!doc.version) { | ||
| 81 | label += ` _icon-${doc.icon}`; | ||
| 82 | } | ||
| 58 | label += `"><input type="checkbox" name="${doc.slug}" class="_list-checkbox" `; | 83 | label += `"><input type="checkbox" name="${doc.slug}" class="_list-checkbox" `; |
| 59 | if (options.checked) { label += "checked"; } | 84 | if (options.checked) { |
| 85 | label += "checked"; | ||
| 86 | } | ||
| 60 | return label + `><span class="_list-text">${doc.fullName}</span></label>`; | 87 | return label + `><span class="_list-text">${doc.fullName}</span></label>`; |
| 61 | }; | 88 | }; |
| 62 | 89 | ||
| 63 | templates.sidebarVersionedDoc = function(doc, versions, options) { | 90 | templates.sidebarVersionedDoc = function (doc, versions, options) { |
| 64 | if (options == null) { options = {}; } | 91 | if (options == null) { |
| 92 | options = {}; | ||
| 93 | } | ||
| 65 | let html = `<div class="_list-item _list-dir _list-rdir _icon-${doc.icon}`; | 94 | let html = `<div class="_list-item _list-dir _list-rdir _icon-${doc.icon}`; |
| 66 | if (options.open) { html += " open"; } | 95 | if (options.open) { |
| 67 | return html + `" tabindex="0">${arrow}${doc.name}</div><div class="_list _list-sub">${versions}</div>`; | 96 | html += " open"; |
| 97 | } | ||
| 98 | return ( | ||
| 99 | html + | ||
| 100 | `" tabindex="0">${arrow}${doc.name}</div><div class="_list _list-sub">${versions}</div>` | ||
| 101 | ); | ||
| 68 | }; | 102 | }; |
| 69 | 103 | ||
| 70 | templates.sidebarDisabled = options => `<h6 class="_list-title">${arrow}Disabled (${options.count}) <a href="/settings" class="_list-title-link" tabindex="-1">Customize</a></h6>`; | 104 | templates.sidebarDisabled = (options) => |
| 105 | `<h6 class="_list-title">${arrow}Disabled (${options.count}) <a href="/settings" class="_list-title-link" tabindex="-1">Customize</a></h6>`; | ||
| 71 | 106 | ||
| 72 | templates.sidebarDisabledList = html => `<div class="_disabled-list">${html}</div>`; | 107 | templates.sidebarDisabledList = (html) => |
| 108 | `<div class="_disabled-list">${html}</div>`; | ||
| 73 | 109 | ||
| 74 | templates.sidebarDisabledVersionedDoc = (doc, versions) => `<a class="_list-item _list-dir _icon-${doc.icon} _list-disabled" data-slug="${doc.slug_without_version}" tabindex="-1">${arrow}${doc.name}</a><div class="_list _list-sub">${versions}</div>`; | 110 | templates.sidebarDisabledVersionedDoc = (doc, versions) => |
| 111 | `<a class="_list-item _list-dir _icon-${doc.icon} _list-disabled" data-slug="${doc.slug_without_version}" tabindex="-1">${arrow}${doc.name}</a><div class="_list _list-sub">${versions}</div>`; | ||
| 75 | 112 | ||
| 76 | templates.docPickerHeader = "<div class=\"_list-picker-head\"><span>Documentation</span> <span>Enable</span></div>"; | 113 | templates.docPickerHeader = |
| 114 | '<div class="_list-picker-head"><span>Documentation</span> <span>Enable</span></div>'; | ||
| 77 | 115 | ||
| 78 | templates.docPickerNote = `\ | 116 | templates.docPickerNote = `\ |
| 79 | <div class="_list-note">Tip: for faster and better search results, select only the docs you need.</div> | 117 | <div class="_list-note">Tip: for faster and better search results, select only the docs you need.</div> |
| @@ -10,8 +10,14 @@ app.templates.tipKeyNav = () => `\ · 9 unmodified lines | |||
| 10 | <strong>ProTip</strong> | 10 | <strong>ProTip</strong> |
| 11 | <span class="_notif-info">(click to dismiss)</span> | 11 | <span class="_notif-info">(click to dismiss)</span> |
| 12 | <p class="_notif-text"> | 12 | <p class="_notif-text"> |
| 13 | Hit ${app.settings.get('arrowScroll') ? '<code class="_label">shift</code> +' : ''} <code class="_label">↓</code> <code class="_label">↑</code> <code class="_label">←</code> <code class="_label">→</code> to navigate the sidebar.<br> | 13 | Hit ${ |
| 14 | Hit <code class="_label">space / shift space</code>${app.settings.get('arrowScroll') ? ' or <code class="_label">↓/↑</code>' : ', <code class="_label">alt ↓/↑</code> or <code class="_label">shift ↓/↑</code>'} to scroll the page. | 14 | app.settings.get("arrowScroll") ? '<code class="_label">shift</code> +' : "" |
| 15 | } <code class="_label">↓</code> <code class="_label">↑</code> <code class="_label">←</code> <code class="_label">→</code> to navigate the sidebar.<br> | ||
| 16 | Hit <code class="_label">space / shift space</code>${ | ||
| 17 | app.settings.get("arrowScroll") | ||
| 18 | ? ' or <code class="_label">↓/↑</code>' | ||
| 19 | : ', <code class="_label">alt ↓/↑</code> or <code class="_label">shift ↓/↑</code>' | ||
| 20 | } to scroll the page. | ||
| 15 | <p class="_notif-text"> | 21 | <p class="_notif-text"> |
| 16 | <a href="/help#shortcuts" class="_notif-link">See all keyboard shortcuts</a>\ | 22 | <a href="/help#shortcuts" class="_notif-link">See all keyboard shortcuts</a>\ |
| 17 | `; | 23 | `; |
| @@ -1,32 +1,55 @@ | |||
| 1 | try { | 1 | try { |
| 2 | if (app.config.env === 'production') { | 2 | if (app.config.env === "production") { |
| 3 | if (Cookies.get('analyticsConsent') === '1') { | 3 | if (Cookies.get("analyticsConsent") === "1") { |
| 4 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | 4 | (function (i, s, o, g, r, a, m) { |
| 5 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | 5 | i["GoogleAnalyticsObject"] = r; |
| 6 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | 6 | (i[r] = |
| 7 | })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); | 7 | i[r] || |
| 8 | ga('create', 'UA-5544833-12', 'devdocs.io'); | 8 | function () { |
| 9 | page.track(function() { | 9 | (i[r].q = i[r].q || []).push(arguments); |
| 10 | ga('send', 'pageview', { | 10 | }), |
| 11 | (i[r].l = 1 * new Date()); | ||
| 12 | (a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]); | ||
| 13 | a.async = 1; | ||
| 14 | a.src = g; | ||
| 15 | m.parentNode.insertBefore(a, m); | ||
| 16 | })( | ||
| 17 | window, | ||
| 18 | document, | ||
| 19 | "script", | ||
| 20 | "https://www.google-analytics.com/analytics.js", | ||
| 21 | "ga", | ||
| 22 | ); | ||
| 23 | ga("create", "UA-5544833-12", "devdocs.io"); | ||
| 24 | page.track(function () { | ||
| 25 | ga("send", "pageview", { | ||
| 11 | page: location.pathname + location.search + location.hash, | 26 | page: location.pathname + location.search + location.hash, |
| 12 | dimension1: app.router.context && app.router.context.doc && app.router.context.doc.slug_without_version | 27 | dimension1: |
| 28 | app.router.context && | ||
| 29 | app.router.context.doc && | ||
| 30 | app.router.context.doc.slug_without_version, | ||
| 13 | }); | 31 | }); |
| 14 | }); | 32 | }); |
| 15 | 33 | ||
| 16 | page.track(function() { | 34 | page.track(function () { |
| 17 | if (window._gauges) | 35 | if (window._gauges) _gauges.push(["track"]); |
| 18 | _gauges.push(['track']); | ||
| 19 | else | 36 | else |
| 20 | (function() { | 37 | (function () { |
| 21 | var _gauges=_gauges||[];!function(){var a=document.createElement("script"); | 38 | var _gauges = _gauges || []; |
| 22 | a.type="text/javascript",a.async=!0,a.id="gauges-tracker", | 39 | !(function () { |
| 23 | a.setAttribute("data-site-id","51c15f82613f5d7819000067"), | 40 | var a = document.createElement("script"); |
| 24 | a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0]; | 41 | (a.type = "text/javascript"), |
| 25 | b.parentNode.insertBefore(a,b)}(); | 42 | (a.async = !0), |
| 43 | (a.id = "gauges-tracker"), | ||
| 44 | a.setAttribute("data-site-id", "51c15f82613f5d7819000067"), | ||
| 45 | (a.src = "https://secure.gaug.es/track.js"); | ||
| 46 | var b = document.getElementsByTagName("script")[0]; | ||
| 47 | b.parentNode.insertBefore(a, b); | ||
| 48 | })(); | ||
| 26 | })(); | 49 | })(); |
| 27 | }); | 50 | }); |
| 28 | } else { | 51 | } else { |
| 29 | resetAnalytics(); | 52 | resetAnalytics(); |
| 30 | } | 53 | } |
| 31 | } | 54 | } |
| 32 | } catch(e) { } | 55 | } catch (e) {} |
| @@ -5,171 +5,204 @@ · 4 unmodified lines | |||
| 5 | * This is free and unencumbered software released into the public domain. | 5 | * This is free and unencumbered software released into the public domain. |
| 6 | */ | 6 | */ |
| 7 | (function (global, undefined) { | 7 | (function (global, undefined) { |
| 8 | 'use strict'; | 8 | "use strict"; |
| 9 | 9 | ||
| 10 | var factory = function (window) { | 10 | var factory = function (window) { |
| 11 | if (typeof window.document !== 'object') { | 11 | if (typeof window.document !== "object") { |
| 12 | throw new Error('Cookies.js requires a `window` with a `document` object'); | 12 | throw new Error( |
| 13 | "Cookies.js requires a `window` with a `document` object", | ||
| 14 | ); | ||
| 15 | } | ||
| 16 | |||
| 17 | var Cookies = function (key, value, options) { | ||
| 18 | return arguments.length === 1 | ||
| 19 | ? Cookies.get(key) | ||
| 20 | : Cookies.set(key, value, options); | ||
| 21 | }; | ||
| 22 | |||
| 23 | // Allows for setter injection in unit tests | ||
| 24 | Cookies._document = window.document; | ||
| 25 | |||
| 26 | // Used to ensure cookie keys do not collide with | ||
| 27 | // built-in `Object` properties | ||
| 28 | Cookies._cacheKeyPrefix = "cookey."; // Hurr hurr, :) | ||
| 29 | |||
| 30 | Cookies._maxExpireDate = new Date("Fri, 31 Dec 9999 23:59:59 UTC"); | ||
| 31 | |||
| 32 | Cookies.defaults = { | ||
| 33 | path: "/", | ||
| 34 | SameSite: "Strict", | ||
| 35 | secure: true, | ||
| 36 | }; | ||
| 37 | |||
| 38 | Cookies.get = function (key) { | ||
| 39 | if (Cookies._cachedDocumentCookie !== Cookies._document.cookie) { | ||
| 40 | Cookies._renewCache(); | ||
| 41 | } | ||
| 42 | |||
| 43 | var value = Cookies._cache[Cookies._cacheKeyPrefix + key]; | ||
| 44 | |||
| 45 | return value === undefined ? undefined : decodeURIComponent(value); | ||
| 46 | }; | ||
| 47 | |||
| 48 | Cookies.set = function (key, value, options) { | ||
| 49 | options = Cookies._getExtendedOptions(options); | ||
| 50 | options.expires = Cookies._getExpiresDate( | ||
| 51 | value === undefined ? -1 : options.expires, | ||
| 52 | ); | ||
| 53 | |||
| 54 | Cookies._document.cookie = Cookies._generateCookieString( | ||
| 55 | key, | ||
| 56 | value, | ||
| 57 | options, | ||
| 58 | ); | ||
| 59 | |||
| 60 | return Cookies; | ||
| 61 | }; | ||
| 62 | |||
| 63 | Cookies.expire = function (key, options) { | ||
| 64 | return Cookies.set(key, undefined, options); | ||
| 65 | }; | ||
| 66 | |||
| 67 | Cookies._getExtendedOptions = function (options) { | ||
| 68 | return { | ||
| 69 | path: (options && options.path) || Cookies.defaults.path, | ||
| 70 | domain: (options && options.domain) || Cookies.defaults.domain, | ||
| 71 | SameSite: (options && options.SameSite) || Cookies.defaults.SameSite, | ||
| 72 | expires: (options && options.expires) || Cookies.defaults.expires, | ||
| 73 | secure: | ||
| 74 | options && options.secure !== undefined | ||
| 75 | ? options.secure | ||
| 76 | : Cookies.defaults.secure, | ||
| 77 | }; | ||
| 78 | }; | ||
| 79 | |||
| 80 | Cookies._isValidDate = function (date) { | ||
| 81 | return ( | ||
| 82 | Object.prototype.toString.call(date) === "[object Date]" && | ||
| 83 | !isNaN(date.getTime()) | ||
| 84 | ); | ||
| 85 | }; | ||
| 86 | |||
| 87 | Cookies._getExpiresDate = function (expires, now) { | ||
| 88 | now = now || new Date(); | ||
| 89 | |||
| 90 | if (typeof expires === "number") { | ||
| 91 | expires = | ||
| 92 | expires === Infinity | ||
| 93 | ? Cookies._maxExpireDate | ||
| 94 | : new Date(now.getTime() + expires * 1000); | ||
| 95 | } else if (typeof expires === "string") { | ||
| 96 | expires = new Date(expires); | ||
| 97 | } | ||
| 98 | |||
| 99 | if (expires && !Cookies._isValidDate(expires)) { | ||
| 100 | throw new Error( | ||
| 101 | "`expires` parameter cannot be converted to a valid Date instance", | ||
| 102 | ); | ||
| 103 | } | ||
| 104 | |||
| 105 | return expires; | ||
| 106 | }; | ||
| 107 | |||
| 108 | Cookies._generateCookieString = function (key, value, options) { | ||
| 109 | key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent); | ||
| 110 | key = key.replace(/\(/g, "%28").replace(/\)/g, "%29"); | ||
| 111 | value = (value + "").replace( | ||
| 112 | /[^!#$&-+\--:<-\[\]-~]/g, | ||
| 113 | encodeURIComponent, | ||
| 114 | ); | ||
| 115 | options = options || {}; | ||
| 116 | |||
| 117 | var cookieString = key + "=" + value; | ||
| 118 | cookieString += options.path ? ";path=" + options.path : ""; | ||
| 119 | cookieString += options.domain ? ";domain=" + options.domain : ""; | ||
| 120 | cookieString += options.SameSite ? ";SameSite=" + options.SameSite : ""; | ||
| 121 | cookieString += options.expires | ||
| 122 | ? ";expires=" + options.expires.toUTCString() | ||
| 123 | : ""; | ||
| 124 | cookieString += options.secure ? ";secure" : ""; | ||
| 125 | |||
| 126 | return cookieString; | ||
| 127 | }; | ||
| 128 | |||
| 129 | Cookies._getCacheFromString = function (documentCookie) { | ||
| 130 | var cookieCache = {}; | ||
| 131 | var cookiesArray = documentCookie ? documentCookie.split("; ") : []; | ||
| 132 | |||
| 133 | for (var i = 0; i < cookiesArray.length; i++) { | ||
| 134 | var cookieKvp = Cookies._getKeyValuePairFromCookieString( | ||
| 135 | cookiesArray[i], | ||
| 136 | ); | ||
| 137 | |||
| 138 | if ( | ||
| 139 | cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] === undefined | ||
| 140 | ) { | ||
| 141 | cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] = | ||
| 142 | cookieKvp.value; | ||
| 13 | } | 143 | } |
| 144 | } | ||
| 14 | 145 | ||
| 15 | var Cookies = function (key, value, options) { | 146 | return cookieCache; |
| 16 | return arguments.length === 1 ? | 147 | }; |
| 17 | Cookies.get(key) : Cookies.set(key, value, options); | ||
| 18 | }; | ||
| 19 | 148 | ||
| 20 | // Allows for setter injection in unit tests | 149 | Cookies._getKeyValuePairFromCookieString = function (cookieString) { |
| 21 | Cookies._document = window.document; | 150 | // "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')` |
| 22 | 151 | var separatorIndex = cookieString.indexOf("="); | |
| 23 | // Used to ensure cookie keys do not collide with | 152 | |
| 24 | // built-in `Object` properties | 153 | // IE omits the "=" when the cookie value is an empty string |
| 25 | Cookies._cacheKeyPrefix = 'cookey.'; // Hurr hurr, :) | 154 | separatorIndex = |
| 155 | separatorIndex < 0 ? cookieString.length : separatorIndex; | ||
| 156 | |||
| 157 | var key = cookieString.substr(0, separatorIndex); | ||
| 158 | var decodedKey; | ||
| 159 | try { | ||
| 160 | decodedKey = decodeURIComponent(key); | ||
| 161 | } catch (e) { | ||
| 162 | if (console && typeof console.error === "function") { | ||
| 163 | console.error('Could not decode cookie with key "' + key + '"', e); | ||
| 164 | } | ||
| 165 | } | ||
| 26 | 166 | ||
| 27 | Cookies._maxExpireDate = new Date('Fri, 31 Dec 9999 23:59:59 UTC'); | 167 | return { |
| 168 | key: decodedKey, | ||
| 169 | value: cookieString.substr(separatorIndex + 1), // Defer decoding value until accessed | ||
| 170 | }; | ||
| 171 | }; | ||
| 28 | 172 | ||
| 29 | Cookies.defaults = { | 173 | Cookies._renewCache = function () { |
| 30 | path: '/', | 174 | Cookies._cache = Cookies._getCacheFromString(Cookies._document.cookie); |
| 31 | SameSite: 'Strict', | 175 | Cookies._cachedDocumentCookie = Cookies._document.cookie; |
| 32 | secure: true | ||
| 33 | }; | ||
| 34 | |||
| 35 | Cookies.get = function (key) { | ||
| 36 | if (Cookies._cachedDocumentCookie !== Cookies._document.cookie) { | ||
| 37 | Cookies._renewCache(); | ||
| 38 | } | ||
| 39 | |||
| 40 | var value = Cookies._cache[Cookies._cacheKeyPrefix + key]; | ||
| 41 | |||
| 42 | return value === undefined ? undefined : decodeURIComponent(value); | ||
| 43 | }; | ||
| 44 | |||
| 45 | Cookies.set = function (key, value, options) { | ||
| 46 | options = Cookies._getExtendedOptions(options); | ||
| 47 | options.expires = Cookies._getExpiresDate(value === undefined ? -1 : options.expires); | ||
| 48 | |||
| 49 | Cookies._document.cookie = Cookies._generateCookieString(key, value, options); | ||
| 50 | |||
| 51 | return Cookies; | ||
| 52 | }; | ||
| 53 | |||
| 54 | Cookies.expire = function (key, options) { | ||
| 55 | return Cookies.set(key, undefined, options); | ||
| 56 | }; | ||
| 57 | |||
| 58 | Cookies._getExtendedOptions = function (options) { | ||
| 59 | return { | ||
| 60 | path: options && options.path || Cookies.defaults.path, | ||
| 61 | domain: options && options.domain || Cookies.defaults.domain, | ||
| 62 | SameSite: options && options.SameSite || Cookies.defaults.SameSite, | ||
| 63 | expires: options && options.expires || Cookies.defaults.expires, | ||
| 64 | secure: options && options.secure !== undefined ? options.secure : Cookies.defaults.secure | ||
| 65 | }; | ||
| 66 | }; | ||
| 67 | |||
| 68 | Cookies._isValidDate = function (date) { | ||
| 69 | return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime()); | ||
| 70 | }; | ||
| 71 | |||
| 72 | Cookies._getExpiresDate = function (expires, now) { | ||
| 73 | now = now || new Date(); | ||
| 74 | |||
| 75 | if (typeof expires === 'number') { | ||
| 76 | expires = expires === Infinity ? | ||
| 77 | Cookies._maxExpireDate : new Date(now.getTime() + expires * 1000); | ||
| 78 | } else if (typeof expires === 'string') { | ||
| 79 | expires = new Date(expires); | ||
| 80 | } | ||
| 81 | |||
| 82 | if (expires && !Cookies._isValidDate(expires)) { | ||
| 83 | throw new Error('`expires` parameter cannot be converted to a valid Date instance'); | ||
| 84 | } | ||
| 85 | |||
| 86 | return expires; | ||
| 87 | }; | ||
| 88 | |||
| 89 | Cookies._generateCookieString = function (key, value, options) { | ||
| 90 | key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent); | ||
| 91 | key = key.replace(/\(/g, '%28').replace(/\)/g, '%29'); | ||
| 92 | value = (value + '').replace(/[^!#$&-+\--:<-\[\]-~]/g, encodeURIComponent); | ||
| 93 | options = options || {}; | ||
| 94 | |||
| 95 | var cookieString = key + '=' + value; | ||
| 96 | cookieString += options.path ? ';path=' + options.path : ''; | ||
| 97 | cookieString += options.domain ? ';domain=' + options.domain : ''; | ||
| 98 | cookieString += options.SameSite ? ';SameSite=' + options.SameSite : ''; | ||
| 99 | cookieString += options.expires ? ';expires=' + options.expires.toUTCString() : ''; | ||
| 100 | cookieString += options.secure ? ';secure' : ''; | ||
| 101 | |||
| 102 | return cookieString; | ||
| 103 | }; | ||
| 104 | |||
| 105 | Cookies._getCacheFromString = function (documentCookie) { | ||
| 106 | var cookieCache = {}; | ||
| 107 | var cookiesArray = documentCookie ? documentCookie.split('; ') : []; | ||
| 108 | |||
| 109 | for (var i = 0; i < cookiesArray.length; i++) { | ||
| 110 | var cookieKvp = Cookies._getKeyValuePairFromCookieString(cookiesArray[i]); | ||
| 111 | |||
| 112 | if (cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] === undefined) { | ||
| 113 | cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] = cookieKvp.value; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | return cookieCache; | ||
| 118 | }; | ||
| 119 | |||
| 120 | Cookies._getKeyValuePairFromCookieString = function (cookieString) { | ||
| 121 | // "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')` | ||
| 122 | var separatorIndex = cookieString.indexOf('='); | ||
| 123 | |||
| 124 | // IE omits the "=" when the cookie value is an empty string | ||
| 125 | separatorIndex = separatorIndex < 0 ? cookieString.length : separatorIndex; | ||
| 126 | |||
| 127 | var key = cookieString.substr(0, separatorIndex); | ||
| 128 | var decodedKey; | ||
| 129 | try { | ||
| 130 | decodedKey = decodeURIComponent(key); | ||
| 131 | } catch (e) { | ||
| 132 | if (console && typeof console.error === 'function') { | ||
| 133 | console.error('Could not decode cookie with key "' + key + '"', e); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | return { | ||
| 138 | key: decodedKey, | ||
| 139 | value: cookieString.substr(separatorIndex + 1) // Defer decoding value until accessed | ||
| 140 | }; | ||
| 141 | }; | ||
| 142 | |||
| 143 | Cookies._renewCache = function () { | ||
| 144 | Cookies._cache = Cookies._getCacheFromString(Cookies._document.cookie); | ||
| 145 | Cookies._cachedDocumentCookie = Cookies._document.cookie; | ||
| 146 | }; | ||
| 147 | |||
| 148 | Cookies._areEnabled = function () { | ||
| 149 | var testKey = 'cookies.js'; | ||
| 150 | var areEnabled = Cookies.set(testKey, 1).get(testKey) === '1'; | ||
| 151 | Cookies.expire(testKey); | ||
| 152 | return areEnabled; | ||
| 153 | }; | ||
| 154 | |||
| 155 | Cookies.enabled = Cookies._areEnabled(); | ||
| 156 | |||
| 157 | return Cookies; | ||
| 158 | }; | 176 | }; |
| 159 | var cookiesExport = (global && typeof global.document === 'object') ? factory(global) : factory; | ||
| 160 | 177 | ||
| 161 | // AMD support | 178 | Cookies._areEnabled = function () { |
| 162 | if (typeof define === 'function' && define.amd) { | 179 | var testKey = "cookies.js"; |
| 163 | define(function () { return cookiesExport; }); | 180 | var areEnabled = Cookies.set(testKey, 1).get(testKey) === "1"; |
| 181 | Cookies.expire(testKey); | ||
| 182 | return areEnabled; | ||
| 183 | }; | ||
| 184 | |||
| 185 | Cookies.enabled = Cookies._areEnabled(); | ||
| 186 | |||
| 187 | return Cookies; | ||
| 188 | }; | ||
| 189 | var cookiesExport = | ||
| 190 | global && typeof global.document === "object" ? factory(global) : factory; | ||
| 191 | |||
| 192 | // AMD support | ||
| 193 | if (typeof define === "function" && define.amd) { | ||
| 194 | define(function () { | ||
| 195 | return cookiesExport; | ||
| 196 | }); | ||
| 164 | // CommonJS/Node.js support | 197 | // CommonJS/Node.js support |
| 165 | } else if (typeof exports === 'object') { | 198 | } else if (typeof exports === "object") { |
| 166 | // Support Node.js specific `module.exports` (which can be a function) | 199 | // Support Node.js specific `module.exports` (which can be a function) |
| 167 | if (typeof module === 'object' && typeof module.exports === 'object') { | 200 | if (typeof module === "object" && typeof module.exports === "object") { |
| 168 | exports = module.exports = cookiesExport; | 201 | exports = module.exports = cookiesExport; |
| 169 | } | ||
| 170 | // But always support CommonJS module 1.1.1 spec (`exports` cannot be a function) | ||
| 171 | exports.Cookies = cookiesExport; | ||
| 172 | } else { | ||
| 173 | global.Cookies = cookiesExport; | ||
| 174 | } | 202 | } |
| 175 | })(typeof window === 'undefined' ? this : window); | 203 | // But always support CommonJS module 1.1.1 spec (`exports` cannot be a function) |
| 204 | exports.Cookies = cookiesExport; | ||
| 205 | } else { | ||
| 206 | global.Cookies = cookiesExport; | ||
| 207 | } | ||
| 208 | })(typeof window === "undefined" ? this : window); | ||
| @@ -4,17 +4,22 @@ · 3 unmodified lines | |||
| 4 | * Adapted from: https://github.com/fred-wang/mathml.css */ | 4 | * Adapted from: https://github.com/fred-wang/mathml.css */ |
| 5 | 5 | ||
| 6 | (function () { | 6 | (function () { |
| 7 | window.addEventListener("load", function() { | 7 | window.addEventListener("load", function () { |
| 8 | var box, div, link, namespaceURI; | 8 | var box, div, link, namespaceURI; |
| 9 | // First check whether the page contains any <math> element. | 9 | // First check whether the page contains any <math> element. |
| 10 | namespaceURI = "http://www.w3.org/1998/Math/MathML"; | 10 | namespaceURI = "http://www.w3.org/1998/Math/MathML"; |
| 11 | // Create a div to test mspace, using Kuma's "offscreen" CSS | 11 | // Create a div to test mspace, using Kuma's "offscreen" CSS |
| 12 | document.body.insertAdjacentHTML("afterbegin", "<div style='border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px;'><math xmlns='" + namespaceURI + "'><mspace height='23px' width='77px'></mspace></math></div>"); | 12 | document.body.insertAdjacentHTML( |
| 13 | "afterbegin", | ||
| 14 | "<div style='border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px;'><math xmlns='" + | ||
| 15 | namespaceURI + | ||
| 16 | "'><mspace height='23px' width='77px'></mspace></math></div>", | ||
| 17 | ); | ||
| 13 | div = document.body.firstChild; | 18 | div = document.body.firstChild; |
| 14 | box = div.firstChild.firstChild.getBoundingClientRect(); | 19 | box = div.firstChild.firstChild.getBoundingClientRect(); |
| 15 | document.body.removeChild(div); | 20 | document.body.removeChild(div); |
| 16 | if (Math.abs(box.height - 23) > 1 || Math.abs(box.width - 77) > 1) { | 21 | if (Math.abs(box.height - 23) > 1 || Math.abs(box.width - 77) > 1) { |
| 17 | window.supportsMathML = false; | 22 | window.supportsMathML = false; |
| 18 | } | 23 | } |
| 19 | }); | 24 | }); |
| 20 | }()); | 25 | })(); |
| @@ -2,13 +2,13 @@ · 1 unmodified line | |||
| 2 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+bash+c+cpp+cmake+coffeescript+crystal+d+dart+diff+django+elixir+erlang+go+groovy+java+json+julia+kotlin+latex+lua+markdown+markup-templating+matlab+nginx+nim+nix+ocaml+perl+php+python+qml+r+jsx+ruby+rust+scss+scala+shell-session+sql+typescript+yaml+zig */ | 2 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+bash+c+cpp+cmake+coffeescript+crystal+d+dart+diff+django+elixir+erlang+go+groovy+java+json+julia+kotlin+latex+lua+markdown+markup-templating+matlab+nginx+nim+nix+ocaml+perl+php+python+qml+r+jsx+ruby+rust+scss+scala+shell-session+sql+typescript+yaml+zig */ |
| 3 | /// <reference lib="WebWorker"/> | 3 | /// <reference lib="WebWorker"/> |
| 4 | 4 | ||
| 5 | var _self = (typeof window !== 'undefined') | 5 | var _self = |
| 6 | ? window // if in browser | 6 | typeof window !== "undefined" |
| 7 | : ( | 7 | ? window // if in browser |
| 8 | (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) | 8 | : typeof WorkerGlobalScope !== "undefined" && |
| 9 | ? self // if in worker | 9 | self instanceof WorkerGlobalScope |
| 10 | : {} // if in node js | 10 | ? self // if in worker |
| 11 | ); | 11 | : {}; // if in node js |
| 12 | 12 | ||
| 13 | /** | 13 | /** |
| 14 | * Prism: Lightweight, robust, elegant syntax highlighting | 14 | * Prism: Lightweight, robust, elegant syntax highlighting |
| @@ -19,1200 +19,1260 @@ var _self = (typeof window !== 'undefined') · 4 unmodified lines | |||
| 19 | * @public | 19 | * @public |
| 20 | */ | 20 | */ |
| 21 | var Prism = (function (_self) { | 21 | var Prism = (function (_self) { |
| 22 | 22 | // Private helper vars | |
| 23 | // Private helper vars | 23 | var lang = /(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i; |
| 24 | var lang = /(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i; | 24 | var uniqueId = 0; |
| 25 | var uniqueId = 0; | 25 | |
| 26 | 26 | // The grammar object for plaintext | |
| 27 | // The grammar object for plaintext | 27 | var plainTextGrammar = {}; |
| 28 | var plainTextGrammar = {}; | 28 | |
| 29 | 29 | var _ = { | |
| 30 | 30 | /** | |
| 31 | var _ = { | 31 | * By default, Prism will attempt to highlight all code elements (by calling {@link Prism.highlightAll}) on the |
| 32 | /** | 32 | * current page after the page finished loading. This might be a problem if e.g. you wanted to asynchronously load |
| 33 | * By default, Prism will attempt to highlight all code elements (by calling {@link Prism.highlightAll}) on the | 33 | * additional languages or plugins yourself. |
| 34 | * current page after the page finished loading. This might be a problem if e.g. you wanted to asynchronously load | 34 | * |
| 35 | * additional languages or plugins yourself. | 35 | * By setting this value to `true`, Prism will not automatically highlight all code elements on the page. |
| 36 | * | 36 | * |
| 37 | * By setting this value to `true`, Prism will not automatically highlight all code elements on the page. | 37 | * You obviously have to change this value before the automatic highlighting started. To do this, you can add an |
| 38 | * | 38 | * empty Prism object into the global scope before loading the Prism script like this: |
| 39 | * You obviously have to change this value before the automatic highlighting started. To do this, you can add an | 39 | * |
| 40 | * empty Prism object into the global scope before loading the Prism script like this: | 40 | * ```js |
| 41 | * | 41 | * window.Prism = window.Prism || {}; |
| 42 | * ```js | 42 | * Prism.manual = true; |
| 43 | * window.Prism = window.Prism || {}; | 43 | * // add a new <script> to load Prism's script |
| 44 | * Prism.manual = true; | 44 | * ``` |
| 45 | * // add a new <script> to load Prism's script | 45 | * |
| 46 | * ``` | 46 | * @default false |
| 47 | * | 47 | * @type {boolean} |
| 48 | * @default false | 48 | * @memberof Prism |
| 49 | * @type {boolean} | 49 | * @public |
| 50 | * @memberof Prism | 50 | */ |
| 51 | * @public | 51 | manual: _self.Prism && _self.Prism.manual, |
| 52 | */ | 52 | /** |
| 53 | manual: _self.Prism && _self.Prism.manual, | 53 | * By default, if Prism is in a web worker, it assumes that it is in a worker it created itself, so it uses |
| 54 | /** | 54 | * `addEventListener` to communicate with its parent instance. However, if you're using Prism manually in your |
| 55 | * By default, if Prism is in a web worker, it assumes that it is in a worker it created itself, so it uses | 55 | * own worker, you don't want it to do this. |
| 56 | * `addEventListener` to communicate with its parent instance. However, if you're using Prism manually in your | 56 | * |
| 57 | * own worker, you don't want it to do this. | 57 | * By setting this value to `true`, Prism will not add its own listeners to the worker. |
| 58 | * | 58 | * |
| 59 | * By setting this value to `true`, Prism will not add its own listeners to the worker. | 59 | * You obviously have to change this value before Prism executes. To do this, you can add an |
| 60 | * | 60 | * empty Prism object into the global scope before loading the Prism script like this: |
| 61 | * You obviously have to change this value before Prism executes. To do this, you can add an | 61 | * |
| 62 | * empty Prism object into the global scope before loading the Prism script like this: | 62 | * ```js |
| 63 | * | 63 | * window.Prism = window.Prism || {}; |
| 64 | * ```js | 64 | * Prism.disableWorkerMessageHandler = true; |
| 65 | * window.Prism = window.Prism || {}; | 65 | * // Load Prism's script |
| 66 | * Prism.disableWorkerMessageHandler = true; | 66 | * ``` |
| 67 | * // Load Prism's script | 67 | * |
| 68 | * ``` | 68 | * @default false |
| 69 | * | 69 | * @type {boolean} |
| 70 | * @default false | 70 | * @memberof Prism |
| 71 | * @type {boolean} | 71 | * @public |
| 72 | * @memberof Prism | 72 | */ |
| 73 | * @public | 73 | disableWorkerMessageHandler: |
| 74 | */ | 74 | _self.Prism && _self.Prism.disableWorkerMessageHandler, |
| 75 | disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler, | 75 | |
| 76 | 76 | /** | |
| 77 | /** | 77 | * A namespace for utility methods. |
| 78 | * A namespace for utility methods. | 78 | * |
| 79 | * | 79 | * All function in this namespace that are not explicitly marked as _public_ are for __internal use only__ and may |
| 80 | * All function in this namespace that are not explicitly marked as _public_ are for __internal use only__ and may | 80 | * change or disappear at any time. |
| 81 | * change or disappear at any time. | 81 | * |
| 82 | * | 82 | * @namespace |
| 83 | * @namespace | 83 | * @memberof Prism |
| 84 | * @memberof Prism | 84 | */ |
| 85 | */ | 85 | util: { |
| 86 | util: { | 86 | encode: function encode(tokens) { |
| 87 | encode: function encode(tokens) { | 87 | if (tokens instanceof Token) { |
| 88 | if (tokens instanceof Token) { | 88 | return new Token(tokens.type, encode(tokens.content), tokens.alias); |
| 89 | return new Token(tokens.type, encode(tokens.content), tokens.alias); | 89 | } else if (Array.isArray(tokens)) { |
| 90 | } else if (Array.isArray(tokens)) { | 90 | return tokens.map(encode); |
| 91 | return tokens.map(encode); | 91 | } else { |
| 92 | } else { | 92 | return tokens |
| 93 | return tokens.replace(/&/g, '&').replace(/</g, '<').replace(/\u00a0/g, ' '); | 93 | .replace(/&/g, "&") |
| 94 | } | 94 | .replace(/</g, "<") |
| 95 | }, | 95 | .replace(/\u00a0/g, " "); |
| 96 | 96 | } | |
| 97 | /** | 97 | }, |
| 98 | * Returns the name of the type of the given value. | 98 | |
| 99 | * | 99 | /** |
| 100 | * @param {any} o | 100 | * Returns the name of the type of the given value. |
| 101 | * @returns {string} | 101 | * |
| 102 | * @example | 102 | * @param {any} o |
| 103 | * type(null) === 'Null' | 103 | * @returns {string} |
| 104 | * type(undefined) === 'Undefined' | 104 | * @example |
| 105 | * type(123) === 'Number' | 105 | * type(null) === 'Null' |
| 106 | * type('foo') === 'String' | 106 | * type(undefined) === 'Undefined' |
| 107 | * type(true) === 'Boolean' | 107 | * type(123) === 'Number' |
| 108 | * type([1, 2]) === 'Array' | 108 | * type('foo') === 'String' |
| 109 | * type({}) === 'Object' | 109 | * type(true) === 'Boolean' |
| 110 | * type(String) === 'Function' | 110 | * type([1, 2]) === 'Array' |
| 111 | * type(/abc+/) === 'RegExp' | 111 | * type({}) === 'Object' |
| 112 | */ | 112 | * type(String) === 'Function' |
| 113 | type: function (o) { | 113 | * type(/abc+/) === 'RegExp' |
| 114 | return Object.prototype.toString.call(o).slice(8, -1); | 114 | */ |
| 115 | }, | 115 | type: function (o) { |
| 116 | 116 | return Object.prototype.toString.call(o).slice(8, -1); | |
| 117 | /** | 117 | }, |
| 118 | * Returns a unique number for the given object. Later calls will still return the same number. | 118 | |
| 119 | * | 119 | /** |
| 120 | * @param {Object} obj | 120 | * Returns a unique number for the given object. Later calls will still return the same number. |
| 121 | * @returns {number} | 121 | * |
| 122 | */ | 122 | * @param {Object} obj |
| 123 | objId: function (obj) { | 123 | * @returns {number} |
| 124 | if (!obj['__id']) { | 124 | */ |
| 125 | Object.defineProperty(obj, '__id', { value: ++uniqueId }); | 125 | objId: function (obj) { |
| 126 | } | 126 | if (!obj["__id"]) { |
| 127 | return obj['__id']; | 127 | Object.defineProperty(obj, "__id", { value: ++uniqueId }); |
| 128 | }, | 128 | } |
| 129 | 129 | return obj["__id"]; | |
| 130 | /** | 130 | }, |
| 131 | * Creates a deep clone of the given object. | 131 | |
| 132 | * | 132 | /** |
| 133 | * The main intended use of this function is to clone language definitions. | 133 | * Creates a deep clone of the given object. |
| 134 | * | 134 | * |
| 135 | * @param {T} o | 135 | * The main intended use of this function is to clone language definitions. |
| 136 | * @param {Record<number, any>} [visited] | 136 | * |
| 137 | * @returns {T} | 137 | * @param {T} o |
| 138 | * @template T | 138 | * @param {Record<number, any>} [visited] |
| 139 | */ | 139 | * @returns {T} |
| 140 | clone: function deepClone(o, visited) { | 140 | * @template T |
| 141 | visited = visited || {}; | 141 | */ |
| 142 | 142 | clone: function deepClone(o, visited) { | |
| 143 | var clone; var id; | 143 | visited = visited || {}; |
| 144 | switch (_.util.type(o)) { | 144 | |
| 145 | case 'Object': | 145 | var clone; |
| 146 | id = _.util.objId(o); | 146 | var id; |
| 147 | if (visited[id]) { | 147 | switch (_.util.type(o)) { |
| 148 | return visited[id]; | 148 | case "Object": |
| 149 | } | 149 | id = _.util.objId(o); |
| 150 | clone = /** @type {Record<string, any>} */ ({}); | 150 | if (visited[id]) { |
| 151 | visited[id] = clone; | 151 | return visited[id]; |
| 152 | 152 | } | |
| 153 | for (var key in o) { | 153 | clone = /** @type {Record<string, any>} */ ({}); |
| 154 | if (o.hasOwnProperty(key)) { | 154 | visited[id] = clone; |
| 155 | clone[key] = deepClone(o[key], visited); | 155 | |
| 156 | } | 156 | for (var key in o) { |
| 157 | } | 157 | if (o.hasOwnProperty(key)) { |
| 158 | 158 | clone[key] = deepClone(o[key], visited); | |
| 159 | return /** @type {any} */ (clone); | 159 | } |
| 160 | 160 | } | |
| 161 | case 'Array': | 161 | |
| 162 | id = _.util.objId(o); | 162 | return /** @type {any} */ (clone); |
| 163 | if (visited[id]) { | 163 | |
| 164 | return visited[id]; | 164 | case "Array": |
| 165 | } | 165 | id = _.util.objId(o); |
| 166 | clone = []; | 166 | if (visited[id]) { |
| 167 | visited[id] = clone; | 167 | return visited[id]; |
| 168 | 168 | } | |
| 169 | (/** @type {Array} */(/** @type {any} */(o))).forEach(function (v, i) { | 169 | clone = []; |
| 170 | clone[i] = deepClone(v, visited); | 170 | visited[id] = clone; |
| 171 | }); | 171 | |
| 172 | 172 | /** @type {Array} */ (/** @type {any} */ (o)).forEach( | |
| 173 | return /** @type {any} */ (clone); | 173 | function (v, i) { |
| 174 | 174 | clone[i] = deepClone(v, visited); | |
| 175 | default: | 175 | }, |
| 176 | return o; | 176 | ); |
| 177 | } | 177 | |
| 178 | }, | 178 | return /** @type {any} */ (clone); |
| 179 | 179 | ||
| 180 | /** | 180 | default: |
| 181 | * Returns the Prism language of the given element set by a `language-xxxx` or `lang-xxxx` class. | 181 | return o; |
| 182 | * | 182 | } |
| 183 | * If no language is set for the element or the element is `null` or `undefined`, `none` will be returned. | 183 | }, |
| 184 | * | 184 | |
| 185 | * @param {Element} element | 185 | /** |
| 186 | * @returns {string} | 186 | * Returns the Prism language of the given element set by a `language-xxxx` or `lang-xxxx` class. |
| 187 | */ | 187 | * |
| 188 | getLanguage: function (element) { | 188 | * If no language is set for the element or the element is `null` or `undefined`, `none` will be returned. |
| 189 | while (element) { | 189 | * |
| 190 | var m = lang.exec(element.className); | 190 | * @param {Element} element |
| 191 | if (m) { | 191 | * @returns {string} |
| 192 | return m[1].toLowerCase(); | 192 | */ |
| 193 | } | 193 | getLanguage: function (element) { |
| 194 | element = element.parentElement; | 194 | while (element) { |
| 195 | } | 195 | var m = lang.exec(element.className); |
| 196 | return 'none'; | 196 | if (m) { |
| 197 | }, | 197 | return m[1].toLowerCase(); |
| 198 | 198 | } | |
| 199 | /** | 199 | element = element.parentElement; |
| 200 | * Sets the Prism `language-xxxx` class of the given element. | 200 | } |
| 201 | * | 201 | return "none"; |
| 202 | * @param {Element} element | 202 | }, |
| 203 | * @param {string} language | 203 | |
| 204 | * @returns {void} | 204 | /** |
| 205 | */ | 205 | * Sets the Prism `language-xxxx` class of the given element. |
| 206 | setLanguage: function (element, language) { | 206 | * |
| 207 | // remove all `language-xxxx` classes | 207 | * @param {Element} element |
| 208 | // (this might leave behind a leading space) | 208 | * @param {string} language |
| 209 | element.className = element.className.replace(RegExp(lang, 'gi'), ''); | 209 | * @returns {void} |
| 210 | 210 | */ | |
| 211 | // add the new `language-xxxx` class | 211 | setLanguage: function (element, language) { |
| 212 | // (using `classList` will automatically clean up spaces for us) | 212 | // remove all `language-xxxx` classes |
| 213 | element.classList.add('language-' + language); | 213 | // (this might leave behind a leading space) |
| 214 | }, | 214 | element.className = element.className.replace(RegExp(lang, "gi"), ""); |
| 215 | 215 | ||
| 216 | /** | 216 | // add the new `language-xxxx` class |
| 217 | * Returns the script element that is currently executing. | 217 | // (using `classList` will automatically clean up spaces for us) |
| 218 | * | 218 | element.classList.add("language-" + language); |
| 219 | * This does __not__ work for line script element. | 219 | }, |
| 220 | * | 220 | |
| 221 | * @returns {HTMLScriptElement | null} | 221 | /** |
| 222 | */ | 222 | * Returns the script element that is currently executing. |
| 223 | currentScript: function () { | 223 | * |
| 224 | if (typeof document === 'undefined') { | 224 | * This does __not__ work for line script element. |
| 225 | return null; | 225 | * |
| 226 | } | 226 | * @returns {HTMLScriptElement | null} |
| 227 | if ('currentScript' in document && 1 < 2 /* hack to trip TS' flow analysis */) { | 227 | */ |
| 228 | return /** @type {any} */ (document.currentScript); | 228 | currentScript: function () { |
| 229 | } | 229 | if (typeof document === "undefined") { |
| 230 | 230 | return null; | |
| 231 | // IE11 workaround | 231 | } |
| 232 | // we'll get the src of the current script by parsing IE11's error stack trace | 232 | if ( |
| 233 | // this will not work for inline scripts | 233 | "currentScript" in document && |
| 234 | 234 | 1 < 2 /* hack to trip TS' flow analysis */ | |
| 235 | try { | 235 | ) { |
| 236 | throw new Error(); | 236 | return /** @type {any} */ (document.currentScript); |
| 237 | } catch (err) { | 237 | } |
| 238 | // Get file src url from stack. Specifically works with the format of stack traces in IE. | 238 | |
| 239 | // A stack will look like this: | 239 | // IE11 workaround |
| 240 | // | 240 | // we'll get the src of the current script by parsing IE11's error stack trace |
| 241 | // Error | 241 | // this will not work for inline scripts |
| 242 | // at _.util.currentScript (http://localhost/components/prism-core.js:119:5) | 242 | |
| 243 | // at Global code (http://localhost/components/prism-core.js:606:1) | 243 | try { |
| 244 | 244 | throw new Error(); | |
| 245 | var src = (/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(err.stack) || [])[1]; | 245 | } catch (err) { |
| 246 | if (src) { | 246 | // Get file src url from stack. Specifically works with the format of stack traces in IE. |
| 247 | var scripts = document.getElementsByTagName('script'); | 247 | // A stack will look like this: |
| 248 | for (var i in scripts) { | 248 | // |
| 249 | if (scripts[i].src == src) { | 249 | // Error |
| 250 | return scripts[i]; | 250 | // at _.util.currentScript (http://localhost/components/prism-core.js:119:5) |
| 251 | } | 251 | // at Global code (http://localhost/components/prism-core.js:606:1) |
| 252 | } | 252 | |
| 253 | } | 253 | var src = (/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(err.stack) || |
| 254 | return null; | 254 | [])[1]; |
| 255 | } | 255 | if (src) { |
| 256 | }, | 256 | var scripts = document.getElementsByTagName("script"); |
| 257 | 257 | for (var i in scripts) { | |
| 258 | /** | 258 | if (scripts[i].src == src) { |
| 259 | * Returns whether a given class is active for `element`. | 259 | return scripts[i]; |
| 260 | * | 260 | } |
| 261 | * The class can be activated if `element` or one of its ancestors has the given class and it can be deactivated | 261 | } |
| 262 | * if `element` or one of its ancestors has the negated version of the given class. The _negated version_ of the | 262 | } |
| 263 | * given class is just the given class with a `no-` prefix. | 263 | return null; |
| 264 | * | 264 | } |
| 265 | * Whether the class is active is determined by the closest ancestor of `element` (where `element` itself is | 265 | }, |
| 266 | * closest ancestor) that has the given class or the negated version of it. If neither `element` nor any of its | 266 | |
| 267 | * ancestors have the given class or the negated version of it, then the default activation will be returned. | 267 | /** |
| 268 | * | 268 | * Returns whether a given class is active for `element`. |
| 269 | * In the paradoxical situation where the closest ancestor contains __both__ the given class and the negated | 269 | * |
| 270 | * version of it, the class is considered active. | 270 | * The class can be activated if `element` or one of its ancestors has the given class and it can be deactivated |
| 271 | * | 271 | * if `element` or one of its ancestors has the negated version of the given class. The _negated version_ of the |
| 272 | * @param {Element} element | 272 | * given class is just the given class with a `no-` prefix. |
| 273 | * @param {string} className | 273 | * |
| 274 | * @param {boolean} [defaultActivation=false] | 274 | * Whether the class is active is determined by the closest ancestor of `element` (where `element` itself is |
| 275 | * @returns {boolean} | 275 | * closest ancestor) that has the given class or the negated version of it. If neither `element` nor any of its |
| 276 | */ | 276 | * ancestors have the given class or the negated version of it, then the default activation will be returned. |
| 277 | isActive: function (element, className, defaultActivation) { | 277 | * |
| 278 | var no = 'no-' + className; | 278 | * In the paradoxical situation where the closest ancestor contains __both__ the given class and the negated |
| 279 | 279 | * version of it, the class is considered active. | |
| 280 | while (element) { | 280 | * |
| 281 | var classList = element.classList; | 281 | * @param {Element} element |
| 282 | if (classList.contains(className)) { | 282 | * @param {string} className |
| 283 | return true; | 283 | * @param {boolean} [defaultActivation=false] |
| 284 | } | 284 | * @returns {boolean} |
| 285 | if (classList.contains(no)) { | 285 | */ |
| 286 | return false; | 286 | isActive: function (element, className, defaultActivation) { |
| 287 | } | 287 | var no = "no-" + className; |
| 288 | element = element.parentElement; | 288 | |
| 289 | } | 289 | while (element) { |
| 290 | return !!defaultActivation; | 290 | var classList = element.classList; |
| 291 | } | 291 | if (classList.contains(className)) { |
| 292 | }, | 292 | return true; |
| 293 | 293 | } | |
| 294 | /** | 294 | if (classList.contains(no)) { |
| 295 | * This namespace contains all currently loaded languages and the some helper functions to create and modify languages. | 295 | return false; |
| 296 | * | 296 | } |
| 297 | * @namespace | 297 | element = element.parentElement; |
| 298 | * @memberof Prism | 298 | } |
| 299 | * @public | 299 | return !!defaultActivation; |
| 300 | */ | 300 | }, |
| 301 | languages: { | 301 | }, |
| 302 | /** | 302 | |
| 303 | * The grammar for plain, unformatted text. | 303 | /** |
| 304 | */ | 304 | * This namespace contains all currently loaded languages and the some helper functions to create and modify languages. |
| 305 | plain: plainTextGrammar, | 305 | * |
| 306 | plaintext: plainTextGrammar, | 306 | * @namespace |
| 307 | text: plainTextGrammar, | 307 | * @memberof Prism |
| 308 | txt: plainTextGrammar, | 308 | * @public |
| 309 | 309 | */ | |
| 310 | /** | 310 | languages: { |
| 311 | * Creates a deep copy of the language with the given id and appends the given tokens. | 311 | /** |
| 312 | * | 312 | * The grammar for plain, unformatted text. |
| 313 | * If a token in `redef` also appears in the copied language, then the existing token in the copied language | 313 | */ |
| 314 | * will be overwritten at its original position. | 314 | plain: plainTextGrammar, |
| 315 | * | 315 | plaintext: plainTextGrammar, |
| 316 | * ## Best practices | 316 | text: plainTextGrammar, |
| 317 | * | 317 | txt: plainTextGrammar, |
| 318 | * Since the position of overwriting tokens (token in `redef` that overwrite tokens in the copied language) | 318 | |
| 319 | * doesn't matter, they can technically be in any order. However, this can be confusing to others that trying to | 319 | /** |
| 320 | * understand the language definition because, normally, the order of tokens matters in Prism grammars. | 320 | * Creates a deep copy of the language with the given id and appends the given tokens. |
| 321 | * | 321 | * |
| 322 | * Therefore, it is encouraged to order overwriting tokens according to the positions of the overwritten tokens. | 322 | * If a token in `redef` also appears in the copied language, then the existing token in the copied language |
| 323 | * Furthermore, all non-overwriting tokens should be placed after the overwriting ones. | 323 | * will be overwritten at its original position. |
| 324 | * | 324 | * |
| 325 | * @param {string} id The id of the language to extend. This has to be a key in `Prism.languages`. | 325 | * ## Best practices |
| 326 | * @param {Grammar} redef The new tokens to append. | 326 | * |
| 327 | * @returns {Grammar} The new language created. | 327 | * Since the position of overwriting tokens (token in `redef` that overwrite tokens in the copied language) |
| 328 | * @public | 328 | * doesn't matter, they can technically be in any order. However, this can be confusing to others that trying to |
| 329 | * @example | 329 | * understand the language definition because, normally, the order of tokens matters in Prism grammars. |
| 330 | * Prism.languages['css-with-colors'] = Prism.languages.extend('css', { | 330 | * |
| 331 | * // Prism.languages.css already has a 'comment' token, so this token will overwrite CSS' 'comment' token | 331 | * Therefore, it is encouraged to order overwriting tokens according to the positions of the overwritten tokens. |
| 332 | * // at its original position | 332 | * Furthermore, all non-overwriting tokens should be placed after the overwriting ones. |
| 333 | * 'comment': { ... }, | 333 | * |
| 334 | * // CSS doesn't have a 'color' token, so this token will be appended | 334 | * @param {string} id The id of the language to extend. This has to be a key in `Prism.languages`. |
| 335 | * 'color': /\b(?:red|green|blue)\b/ | 335 | * @param {Grammar} redef The new tokens to append. |
| 336 | * }); | 336 | * @returns {Grammar} The new language created. |
| 337 | */ | 337 | * @public |
| 338 | extend: function (id, redef) { | 338 | * @example |
| 339 | var lang = _.util.clone(_.languages[id]); | 339 | * Prism.languages['css-with-colors'] = Prism.languages.extend('css', { |
| 340 | 340 | * // Prism.languages.css already has a 'comment' token, so this token will overwrite CSS' 'comment' token | |
| 341 | for (var key in redef) { | 341 | * // at its original position |
| 342 | lang[key] = redef[key]; | 342 | * 'comment': { ... }, |
| 343 | } | 343 | * // CSS doesn't have a 'color' token, so this token will be appended |
| 344 | 344 | * 'color': /\b(?:red|green|blue)\b/ | |
| 345 | return lang; | 345 | * }); |
| 346 | }, | 346 | */ |
| 347 | 347 | extend: function (id, redef) { | |
| 348 | /** | 348 | var lang = _.util.clone(_.languages[id]); |
| 349 | * Inserts tokens _before_ another token in a language definition or any other grammar. | 349 | |
| 350 | * | 350 | for (var key in redef) { |
| 351 | * ## Usage | 351 | lang[key] = redef[key]; |
| 352 | * | 352 | } |
| 353 | * This helper method makes it easy to modify existing languages. For example, the CSS language definition | 353 | |
| 354 | * not only defines CSS highlighting for CSS documents, but also needs to define highlighting for CSS embedded | 354 | return lang; |
| 355 | * in HTML through `<style>` elements. To do this, it needs to modify `Prism.languages.markup` and add the | 355 | }, |
| 356 | * appropriate tokens. However, `Prism.languages.markup` is a regular JavaScript object literal, so if you do | 356 | |
| 357 | * this: | 357 | /** |
| 358 | * | 358 | * Inserts tokens _before_ another token in a language definition or any other grammar. |
| 359 | * ```js | 359 | * |
| 360 | * Prism.languages.markup.style = { | 360 | * ## Usage |
| 361 | * // token | 361 | * |
| 362 | * }; | 362 | * This helper method makes it easy to modify existing languages. For example, the CSS language definition |
| 363 | * ``` | 363 | * not only defines CSS highlighting for CSS documents, but also needs to define highlighting for CSS embedded |
| 364 | * | 364 | * in HTML through `<style>` elements. To do this, it needs to modify `Prism.languages.markup` and add the |
| 365 | * then the `style` token will be added (and processed) at the end. `insertBefore` allows you to insert tokens | 365 | * appropriate tokens. However, `Prism.languages.markup` is a regular JavaScript object literal, so if you do |
| 366 | * before existing tokens. For the CSS example above, you would use it like this: | 366 | * this: |
| 367 | * | 367 | * |
| 368 | * ```js | 368 | * ```js |
| 369 | * Prism.languages.insertBefore('markup', 'cdata', { | 369 | * Prism.languages.markup.style = { |
| 370 | * 'style': { | 370 | * // token |
| 371 | * // token | 371 | * }; |
| 372 | * } | 372 | * ``` |
| 373 | * }); | 373 | * |
| 374 | * ``` | 374 | * then the `style` token will be added (and processed) at the end. `insertBefore` allows you to insert tokens |
| 375 | * | 375 | * before existing tokens. For the CSS example above, you would use it like this: |
| 376 | * ## Special cases | 376 | * |
| 377 | * | 377 | * ```js |
| 378 | * If the grammars of `inside` and `insert` have tokens with the same name, the tokens in `inside`'s grammar | 378 | * Prism.languages.insertBefore('markup', 'cdata', { |
| 379 | * will be ignored. | 379 | * 'style': { |
| 380 | * | 380 | * // token |
| 381 | * This behavior can be used to insert tokens after `before`: | 381 | * } |
| 382 | * | 382 | * }); |
| 383 | * ```js | 383 | * ``` |
| 384 | * Prism.languages.insertBefore('markup', 'comment', { | 384 | * |
| 385 | * 'comment': Prism.languages.markup.comment, | 385 | * ## Special cases |
| 386 | * // tokens after 'comment' | 386 | * |
| 387 | * }); | 387 | * If the grammars of `inside` and `insert` have tokens with the same name, the tokens in `inside`'s grammar |
| 388 | * ``` | 388 | * will be ignored. |
| 389 | * | 389 | * |
| 390 | * ## Limitations | 390 | * This behavior can be used to insert tokens after `before`: |
| 391 | * | 391 | * |
| 392 | * The main problem `insertBefore` has to solve is iteration order. Since ES2015, the iteration order for object | 392 | * ```js |
| 393 | * properties is guaranteed to be the insertion order (except for integer keys) but some browsers behave | 393 | * Prism.languages.insertBefore('markup', 'comment', { |
| 394 | * differently when keys are deleted and re-inserted. So `insertBefore` can't be implemented by temporarily | 394 | * 'comment': Prism.languages.markup.comment, |
| 395 | * deleting properties which is necessary to insert at arbitrary positions. | 395 | * // tokens after 'comment' |
| 396 | * | 396 | * }); |
| 397 | * To solve this problem, `insertBefore` doesn't actually insert the given tokens into the target object. | 397 | * ``` |
| 398 | * Instead, it will create a new object and replace all references to the target object with the new one. This | 398 | * |
| 399 | * can be done without temporarily deleting properties, so the iteration order is well-defined. | 399 | * ## Limitations |
| 400 | * | 400 | * |
| 401 | * However, only references that can be reached from `Prism.languages` or `insert` will be replaced. I.e. if | 401 | * The main problem `insertBefore` has to solve is iteration order. Since ES2015, the iteration order for object |
| 402 | * you hold the target object in a variable, then the value of the variable will not change. | 402 | * properties is guaranteed to be the insertion order (except for integer keys) but some browsers behave |
| 403 | * | 403 | * differently when keys are deleted and re-inserted. So `insertBefore` can't be implemented by temporarily |
| 404 | * ```js | 404 | * deleting properties which is necessary to insert at arbitrary positions. |
| 405 | * var oldMarkup = Prism.languages.markup; | 405 | * |
| 406 | * var newMarkup = Prism.languages.insertBefore('markup', 'comment', { ... }); | 406 | * To solve this problem, `insertBefore` doesn't actually insert the given tokens into the target object. |
| 407 | * | 407 | * Instead, it will create a new object and replace all references to the target object with the new one. This |
| 408 | * assert(oldMarkup !== Prism.languages.markup); | 408 | * can be done without temporarily deleting properties, so the iteration order is well-defined. |
| 409 | * assert(newMarkup === Prism.languages.markup); | 409 | * |
| 410 | * ``` | 410 | * However, only references that can be reached from `Prism.languages` or `insert` will be replaced. I.e. if |
| 411 | * | 411 | * you hold the target object in a variable, then the value of the variable will not change. |
| 412 | * @param {string} inside The property of `root` (e.g. a language id in `Prism.languages`) that contains the | 412 | * |
| 413 | * object to be modified. | 413 | * ```js |
| 414 | * @param {string} before The key to insert before. | 414 | * var oldMarkup = Prism.languages.markup; |
| 415 | * @param {Grammar} insert An object containing the key-value pairs to be inserted. | 415 | * var newMarkup = Prism.languages.insertBefore('markup', 'comment', { ... }); |
| 416 | * @param {Object<string, any>} [root] The object containing `inside`, i.e. the object that contains the | 416 | * |
| 417 | * object to be modified. | 417 | * assert(oldMarkup !== Prism.languages.markup); |
| 418 | * | 418 | * assert(newMarkup === Prism.languages.markup); |
| 419 | * Defaults to `Prism.languages`. | 419 | * ``` |
| 420 | * @returns {Grammar} The new grammar object. | 420 | * |
| 421 | * @public | 421 | * @param {string} inside The property of `root` (e.g. a language id in `Prism.languages`) that contains the |
| 422 | */ | 422 | * object to be modified. |
| 423 | insertBefore: function (inside, before, insert, root) { | 423 | * @param {string} before The key to insert before. |
| 424 | root = root || /** @type {any} */ (_.languages); | 424 | * @param {Grammar} insert An object containing the key-value pairs to be inserted. |
| 425 | var grammar = root[inside]; | 425 | * @param {Object<string, any>} [root] The object containing `inside`, i.e. the object that contains the |
| 426 | /** @type {Grammar} */ | 426 | * object to be modified. |
| 427 | var ret = {}; | 427 | * |
| 428 | 428 | * Defaults to `Prism.languages`. | |
| 429 | for (var token in grammar) { | 429 | * @returns {Grammar} The new grammar object. |
| 430 | if (grammar.hasOwnProperty(token)) { | 430 | * @public |
| 431 | 431 | */ | |
| 432 | if (token == before) { | 432 | insertBefore: function (inside, before, insert, root) { |
| 433 | for (var newToken in insert) { | 433 | root = root || /** @type {any} */ (_.languages); |
| 434 | if (insert.hasOwnProperty(newToken)) { | 434 | var grammar = root[inside]; |
| 435 | ret[newToken] = insert[newToken]; | 435 | /** @type {Grammar} */ |
| 436 | } | 436 | var ret = {}; |
| 437 | } | 437 | |
| 438 | } | 438 | for (var token in grammar) { |
| 439 | 439 | if (grammar.hasOwnProperty(token)) { | |
| 440 | // Do not insert token which also occur in insert. See #1525 | 440 | if (token == before) { |
| 441 | if (!insert.hasOwnProperty(token)) { | 441 | for (var newToken in insert) { |
| 442 | ret[token] = grammar[token]; | 442 | if (insert.hasOwnProperty(newToken)) { |
| 443 | } | 443 | ret[newToken] = insert[newToken]; |
| 444 | } | 444 | } |
| 445 | } | 445 | } |
| 446 | 446 | } | |
| 447 | var old = root[inside]; | 447 | |
| 448 | root[inside] = ret; | 448 | // Do not insert token which also occur in insert. See #1525 |
| 449 | 449 | if (!insert.hasOwnProperty(token)) { | |
| 450 | // Update references in other language definitions | 450 | ret[token] = grammar[token]; |
| 451 | _.languages.DFS(_.languages, function (key, value) { | 451 | } |
| 452 | if (value === old && key != inside) { | 452 | } |
| 453 | this[key] = ret; | 453 | } |
| 454 | } | 454 | |
| 455 | }); | 455 | var old = root[inside]; |
| 456 | 456 | root[inside] = ret; | |
| 457 | return ret; | 457 | |
| 458 | }, | 458 | // Update references in other language definitions |
| 459 | 459 | _.languages.DFS(_.languages, function (key, value) { | |
| 460 | // Traverse a language definition with Depth First Search | 460 | if (value === old && key != inside) { |
| 461 | DFS: function DFS(o, callback, type, visited) { | 461 | this[key] = ret; |
| 462 | visited = visited || {}; | 462 | } |
| 463 | 463 | }); | |
| 464 | var objId = _.util.objId; | 464 | |
| 465 | 465 | return ret; | |
| 466 | for (var i in o) { | 466 | }, |
| 467 | if (o.hasOwnProperty(i)) { | 467 | |
| 468 | callback.call(o, i, o[i], type || i); | 468 | // Traverse a language definition with Depth First Search |
| 469 | 469 | DFS: function DFS(o, callback, type, visited) { | |
| 470 | var property = o[i]; | 470 | visited = visited || {}; |
| 471 | var propertyType = _.util.type(property); | 471 | |
| 472 | 472 | var objId = _.util.objId; | |
| 473 | if (propertyType === 'Object' && !visited[objId(property)]) { | 473 | |
| 474 | visited[objId(property)] = true; | 474 | for (var i in o) { |
| 475 | DFS(property, callback, null, visited); | 475 | if (o.hasOwnProperty(i)) { |
| 476 | } else if (propertyType === 'Array' && !visited[objId(property)]) { | 476 | callback.call(o, i, o[i], type || i); |
| 477 | visited[objId(property)] = true; | 477 | |
| 478 | DFS(property, callback, i, visited); | 478 | var property = o[i]; |
| 479 | } | 479 | var propertyType = _.util.type(property); |
| 480 | } | 480 | |
| 481 | } | 481 | if (propertyType === "Object" && !visited[objId(property)]) { |
| 482 | } | 482 | visited[objId(property)] = true; |
| 483 | }, | 483 | DFS(property, callback, null, visited); |
| 484 | 484 | } else if (propertyType === "Array" && !visited[objId(property)]) { | |
| 485 | plugins: {}, | 485 | visited[objId(property)] = true; |
| 486 | 486 | DFS(property, callback, i, visited); | |
| 487 | /** | 487 | } |
| 488 | * This is the most high-level function in Prism’s API. | 488 | } |
| 489 | * It fetches all the elements that have a `.language-xxxx` class and then calls {@link Prism.highlightElement} on | 489 | } |
| 490 | * each one of them. | 490 | }, |
| 491 | * | 491 | }, |
| 492 | * This is equivalent to `Prism.highlightAllUnder(document, async, callback)`. | 492 | |
| 493 | * | 493 | plugins: {}, |
| 494 | * @param {boolean} [async=false] Same as in {@link Prism.highlightAllUnder}. | 494 | |
| 495 | * @param {HighlightCallback} [callback] Same as in {@link Prism.highlightAllUnder}. | 495 | /** |
| 496 | * @memberof Prism | 496 | * This is the most high-level function in Prism’s API. |
| 497 | * @public | 497 | * It fetches all the elements that have a `.language-xxxx` class and then calls {@link Prism.highlightElement} on |
| 498 | */ | 498 | * each one of them. |
| 499 | highlightAll: function (async, callback) { | 499 | * |
| 500 | _.highlightAllUnder(document, async, callback); | 500 | * This is equivalent to `Prism.highlightAllUnder(document, async, callback)`. |
| 501 | }, | 501 | * |
| 502 | 502 | * @param {boolean} [async=false] Same as in {@link Prism.highlightAllUnder}. | |
| 503 | /** | 503 | * @param {HighlightCallback} [callback] Same as in {@link Prism.highlightAllUnder}. |
| 504 | * Fetches all the descendants of `container` that have a `.language-xxxx` class and then calls | 504 | * @memberof Prism |
| 505 | * {@link Prism.highlightElement} on each one of them. | 505 | * @public |
| 506 | * | 506 | */ |
| 507 | * The following hooks will be run: | 507 | highlightAll: function (async, callback) { |
| 508 | * 1. `before-highlightall` | 508 | _.highlightAllUnder(document, async, callback); |
| 509 | * 2. `before-all-elements-highlight` | 509 | }, |
| 510 | * 3. All hooks of {@link Prism.highlightElement} for each element. | 510 | |
| 511 | * | 511 | /** |
| 512 | * @param {ParentNode} container The root element, whose descendants that have a `.language-xxxx` class will be highlighted. | 512 | * Fetches all the descendants of `container` that have a `.language-xxxx` class and then calls |
| 513 | * @param {boolean} [async=false] Whether each element is to be highlighted asynchronously using Web Workers. | 513 | * {@link Prism.highlightElement} on each one of them. |
| 514 | * @param {HighlightCallback} [callback] An optional callback to be invoked on each element after its highlighting is done. | 514 | * |
| 515 | * @memberof Prism | 515 | * The following hooks will be run: |
| 516 | * @public | 516 | * 1. `before-highlightall` |
| 517 | */ | 517 | * 2. `before-all-elements-highlight` |
| 518 | highlightAllUnder: function (container, async, callback) { | 518 | * 3. All hooks of {@link Prism.highlightElement} for each element. |
| 519 | var env = { | 519 | * |
| 520 | callback: callback, | 520 | * @param {ParentNode} container The root element, whose descendants that have a `.language-xxxx` class will be highlighted. |
| 521 | container: container, | 521 | * @param {boolean} [async=false] Whether each element is to be highlighted asynchronously using Web Workers. |
| 522 | selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code' | 522 | * @param {HighlightCallback} [callback] An optional callback to be invoked on each element after its highlighting is done. |
| 523 | }; | 523 | * @memberof Prism |
| 524 | 524 | * @public | |
| 525 | _.hooks.run('before-highlightall', env); | 525 | */ |
| 526 | 526 | highlightAllUnder: function (container, async, callback) { | |
| 527 | env.elements = Array.prototype.slice.apply(env.container.querySelectorAll(env.selector)); | 527 | var env = { |
| 528 | 528 | callback: callback, | |
| 529 | _.hooks.run('before-all-elements-highlight', env); | 529 | container: container, |
| 530 | 530 | selector: | |
| 531 | for (var i = 0, element; (element = env.elements[i++]);) { | 531 | 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code', |
| 532 | _.highlightElement(element, async === true, env.callback); | 532 | }; |
| 533 | } | 533 | |
| 534 | }, | 534 | _.hooks.run("before-highlightall", env); |
| 535 | 535 | ||
| 536 | /** | 536 | env.elements = Array.prototype.slice.apply( |
| 537 | * Highlights the code inside a single element. | 537 | env.container.querySelectorAll(env.selector), |
| 538 | * | 538 | ); |
| 539 | * The following hooks will be run: | 539 | |
| 540 | * 1. `before-sanity-check` | 540 | _.hooks.run("before-all-elements-highlight", env); |
| 541 | * 2. `before-highlight` | 541 | |
| 542 | * 3. All hooks of {@link Prism.highlight}. These hooks will be run by an asynchronous worker if `async` is `true`. | 542 | for (var i = 0, element; (element = env.elements[i++]); ) { |
| 543 | * 4. `before-insert` | 543 | _.highlightElement(element, async === true, env.callback); |
| 544 | * 5. `after-highlight` | 544 | } |
| 545 | * 6. `complete` | 545 | }, |
| 546 | * | 546 | |
| 547 | * Some the above hooks will be skipped if the element doesn't contain any text or there is no grammar loaded for | 547 | /** |
| 548 | * the element's language. | 548 | * Highlights the code inside a single element. |
| 549 | * | 549 | * |
| 550 | * @param {Element} element The element containing the code. | 550 | * The following hooks will be run: |
| 551 | * It must have a class of `language-xxxx` to be processed, where `xxxx` is a valid language identifier. | 551 | * 1. `before-sanity-check` |
| 552 | * @param {boolean} [async=false] Whether the element is to be highlighted asynchronously using Web Workers | 552 | * 2. `before-highlight` |
| 553 | * to improve performance and avoid blocking the UI when highlighting very large chunks of code. This option is | 553 | * 3. All hooks of {@link Prism.highlight}. These hooks will be run by an asynchronous worker if `async` is `true`. |
| 554 | * [disabled by default](https://prismjs.com/faq.html#why-is-asynchronous-highlighting-disabled-by-default). | 554 | * 4. `before-insert` |
| 555 | * | 555 | * 5. `after-highlight` |
| 556 | * Note: All language definitions required to highlight the code must be included in the main `prism.js` file for | 556 | * 6. `complete` |
| 557 | * asynchronous highlighting to work. You can build your own bundle on the | 557 | * |
| 558 | * [Download page](https://prismjs.com/download.html). | 558 | * Some the above hooks will be skipped if the element doesn't contain any text or there is no grammar loaded for |
| 559 | * @param {HighlightCallback} [callback] An optional callback to be invoked after the highlighting is done. | 559 | * the element's language. |
| 560 | * Mostly useful when `async` is `true`, since in that case, the highlighting is done asynchronously. | 560 | * |
| 561 | * @memberof Prism | 561 | * @param {Element} element The element containing the code. |
| 562 | * @public | 562 | * It must have a class of `language-xxxx` to be processed, where `xxxx` is a valid language identifier. |
| 563 | */ | 563 | * @param {boolean} [async=false] Whether the element is to be highlighted asynchronously using Web Workers |
| 564 | highlightElement: function (element, async, callback) { | 564 | * to improve performance and avoid blocking the UI when highlighting very large chunks of code. This option is |
| 565 | // Find language | 565 | * [disabled by default](https://prismjs.com/faq.html#why-is-asynchronous-highlighting-disabled-by-default). |
| 566 | var language = _.util.getLanguage(element); | 566 | * |
| 567 | var grammar = _.languages[language]; | 567 | * Note: All language definitions required to highlight the code must be included in the main `prism.js` file for |
| 568 | 568 | * asynchronous highlighting to work. You can build your own bundle on the | |
| 569 | // Set language on the element, if not present | 569 | * [Download page](https://prismjs.com/download.html). |
| 570 | _.util.setLanguage(element, language); | 570 | * @param {HighlightCallback} [callback] An optional callback to be invoked after the highlighting is done. |
| 571 | 571 | * Mostly useful when `async` is `true`, since in that case, the highlighting is done asynchronously. | |
| 572 | // Set language on the parent, for styling | 572 | * @memberof Prism |
| 573 | var parent = element.parentElement; | 573 | * @public |
| 574 | if (parent && parent.nodeName.toLowerCase() === 'pre') { | 574 | */ |
| 575 | _.util.setLanguage(parent, language); | 575 | highlightElement: function (element, async, callback) { |
| 576 | } | 576 | // Find language |
| 577 | 577 | var language = _.util.getLanguage(element); | |
| 578 | var code = element.textContent; | 578 | var grammar = _.languages[language]; |
| 579 | 579 | ||
| 580 | var env = { | 580 | // Set language on the element, if not present |
| 581 | element: element, | 581 | _.util.setLanguage(element, language); |
| 582 | language: language, | 582 | |
| 583 | grammar: grammar, | 583 | // Set language on the parent, for styling |
| 584 | code: code | 584 | var parent = element.parentElement; |
| 585 | }; | 585 | if (parent && parent.nodeName.toLowerCase() === "pre") { |
| 586 | 586 | _.util.setLanguage(parent, language); | |
| 587 | function insertHighlightedCode(highlightedCode) { | 587 | } |
| 588 | env.highlightedCode = highlightedCode; | 588 | |
| 589 | 589 | var code = element.textContent; | |
| 590 | _.hooks.run('before-insert', env); | 590 | |
| 591 | 591 | var env = { | |
| 592 | env.element.innerHTML = env.highlightedCode; | 592 | element: element, |
| 593 | 593 | language: language, | |
| 594 | _.hooks.run('after-highlight', env); | 594 | grammar: grammar, |
| 595 | _.hooks.run('complete', env); | 595 | code: code, |
| 596 | callback && callback.call(env.element); | 596 | }; |
| 597 | } | 597 | |
| 598 | 598 | function insertHighlightedCode(highlightedCode) { | |
| 599 | _.hooks.run('before-sanity-check', env); | 599 | env.highlightedCode = highlightedCode; |
| 600 | 600 | ||
| 601 | // plugins may change/add the parent/element | 601 | _.hooks.run("before-insert", env); |
| 602 | parent = env.element.parentElement; | 602 | |
| 603 | if (parent && parent.nodeName.toLowerCase() === 'pre' && !parent.hasAttribute('tabindex')) { | 603 | env.element.innerHTML = env.highlightedCode; |
| 604 | parent.setAttribute('tabindex', '0'); | 604 | |
| 605 | } | 605 | _.hooks.run("after-highlight", env); |
| 606 | 606 | _.hooks.run("complete", env); | |
| 607 | if (!env.code) { | 607 | callback && callback.call(env.element); |
| 608 | _.hooks.run('complete', env); | 608 | } |
| 609 | callback && callback.call(env.element); | 609 | |
| 610 | return; | 610 | _.hooks.run("before-sanity-check", env); |
| 611 | } | 611 | |
| 612 | 612 | // plugins may change/add the parent/element | |
| 613 | _.hooks.run('before-highlight', env); | 613 | parent = env.element.parentElement; |
| 614 | 614 | if ( | |
| 615 | if (!env.grammar) { | 615 | parent && |
| 616 | insertHighlightedCode(_.util.encode(env.code)); | 616 | parent.nodeName.toLowerCase() === "pre" && |
| 617 | return; | 617 | !parent.hasAttribute("tabindex") |
| 618 | } | 618 | ) { |
| 619 | 619 | parent.setAttribute("tabindex", "0"); | |
| 620 | if (async && _self.Worker) { | 620 | } |
| 621 | var worker = new Worker(_.filename); | 621 | |
| 622 | 622 | if (!env.code) { | |
| 623 | worker.onmessage = function (evt) { | 623 | _.hooks.run("complete", env); |
| 624 | insertHighlightedCode(evt.data); | 624 | callback && callback.call(env.element); |
| 625 | }; | 625 | return; |
| 626 | 626 | } | |
| 627 | worker.postMessage(JSON.stringify({ | 627 | |
| 628 | language: env.language, | 628 | _.hooks.run("before-highlight", env); |
| 629 | code: env.code, | 629 | |
| 630 | immediateClose: true | 630 | if (!env.grammar) { |
| 631 | })); | 631 | insertHighlightedCode(_.util.encode(env.code)); |
| 632 | } else { | 632 | return; |
| 633 | insertHighlightedCode(_.highlight(env.code, env.grammar, env.language)); | 633 | } |
| 634 | } | 634 | |
| 635 | }, | 635 | if (async && _self.Worker) { |
| 636 | 636 | var worker = new Worker(_.filename); | |
| 637 | /** | 637 | |
| 638 | * Low-level function, only use if you know what you’re doing. It accepts a string of text as input | 638 | worker.onmessage = function (evt) { |
| 639 | * and the language definitions to use, and returns a string with the HTML produced. | 639 | insertHighlightedCode(evt.data); |
| 640 | * | 640 | }; |
| 641 | * The following hooks will be run: | 641 | |
| 642 | * 1. `before-tokenize` | 642 | worker.postMessage( |
| 643 | * 2. `after-tokenize` | 643 | JSON.stringify({ |
| 644 | * 3. `wrap`: On each {@link Token}. | 644 | language: env.language, |
| 645 | * | 645 | code: env.code, |
| 646 | * @param {string} text A string with the code to be highlighted. | 646 | immediateClose: true, |
| 647 | * @param {Grammar} grammar An object containing the tokens to use. | 647 | }), |
| 648 | * | 648 | ); |
| 649 | * Usually a language definition like `Prism.languages.markup`. | 649 | } else { |
| 650 | * @param {string} language The name of the language definition passed to `grammar`. | 650 | insertHighlightedCode(_.highlight(env.code, env.grammar, env.language)); |
| 651 | * @returns {string} The highlighted HTML. | 651 | } |
| 652 | * @memberof Prism | 652 | }, |
| 653 | * @public | 653 | |
| 654 | * @example | 654 | /** |
| 655 | * Prism.highlight('var foo = true;', Prism.languages.javascript, 'javascript'); | 655 | * Low-level function, only use if you know what you’re doing. It accepts a string of text as input |
| 656 | */ | 656 | * and the language definitions to use, and returns a string with the HTML produced. |
| 657 | highlight: function (text, grammar, language) { | 657 | * |
| 658 | var env = { | 658 | * The following hooks will be run: |
| 659 | code: text, | 659 | * 1. `before-tokenize` |
| 660 | grammar: grammar, | 660 | * 2. `after-tokenize` |
| 661 | language: language | 661 | * 3. `wrap`: On each {@link Token}. |
| 662 | }; | 662 | * |
| 663 | _.hooks.run('before-tokenize', env); | 663 | * @param {string} text A string with the code to be highlighted. |
| 664 | if (!env.grammar) { | 664 | * @param {Grammar} grammar An object containing the tokens to use. |
| 665 | throw new Error('The language "' + env.language + '" has no grammar.'); | 665 | * |
| 666 | } | 666 | * Usually a language definition like `Prism.languages.markup`. |
| 667 | env.tokens = _.tokenize(env.code, env.grammar); | 667 | * @param {string} language The name of the language definition passed to `grammar`. |
| 668 | _.hooks.run('after-tokenize', env); | 668 | * @returns {string} The highlighted HTML. |
| 669 | return Token.stringify(_.util.encode(env.tokens), env.language); | 669 | * @memberof Prism |
| 670 | }, | 670 | * @public |
| 671 | 671 | * @example | |
| 672 | /** | 672 | * Prism.highlight('var foo = true;', Prism.languages.javascript, 'javascript'); |
| 673 | * This is the heart of Prism, and the most low-level function you can use. It accepts a string of text as input | 673 | */ |
| 674 | * and the language definitions to use, and returns an array with the tokenized code. | 674 | highlight: function (text, grammar, language) { |
| 675 | * | 675 | var env = { |
| 676 | * When the language definition includes nested tokens, the function is called recursively on each of these tokens. | 676 | code: text, |
| 677 | * | 677 | grammar: grammar, |
| 678 | * This method could be useful in other contexts as well, as a very crude parser. | 678 | language: language, |
| 679 | * | 679 | }; |
| 680 | * @param {string} text A string with the code to be highlighted. | 680 | _.hooks.run("before-tokenize", env); |
| 681 | * @param {Grammar} grammar An object containing the tokens to use. | 681 | if (!env.grammar) { |
| 682 | * | 682 | throw new Error('The language "' + env.language + '" has no grammar.'); |
| 683 | * Usually a language definition like `Prism.languages.markup`. | 683 | } |
| 684 | * @returns {TokenStream} An array of strings and tokens, a token stream. | 684 | env.tokens = _.tokenize(env.code, env.grammar); |
| 685 | * @memberof Prism | 685 | _.hooks.run("after-tokenize", env); |
| 686 | * @public | 686 | return Token.stringify(_.util.encode(env.tokens), env.language); |
| 687 | * @example | 687 | }, |
| 688 | * let code = `var foo = 0;`; | 688 | |
| 689 | * let tokens = Prism.tokenize(code, Prism.languages.javascript); | 689 | /** |
| 690 | * tokens.forEach(token => { | 690 | * This is the heart of Prism, and the most low-level function you can use. It accepts a string of text as input |
| 691 | * if (token instanceof Prism.Token && token.type === 'number') { | 691 | * and the language definitions to use, and returns an array with the tokenized code. |
| 692 | * console.log(`Found numeric literal: ${token.content}`); | 692 | * |
| 693 | * } | 693 | * When the language definition includes nested tokens, the function is called recursively on each of these tokens. |
| 694 | * }); | 694 | * |
| 695 | */ | 695 | * This method could be useful in other contexts as well, as a very crude parser. |
| 696 | tokenize: function (text, grammar) { | 696 | * |
| 697 | var rest = grammar.rest; | 697 | * @param {string} text A string with the code to be highlighted. |
| 698 | if (rest) { | 698 | * @param {Grammar} grammar An object containing the tokens to use. |
| 699 | for (var token in rest) { | 699 | * |
| 700 | grammar[token] = rest[token]; | 700 | * Usually a language definition like `Prism.languages.markup`. |
| 701 | } | 701 | * @returns {TokenStream} An array of strings and tokens, a token stream. |
| 702 | 702 | * @memberof Prism | |
| 703 | delete grammar.rest; | 703 | * @public |
| 704 | } | 704 | * @example |
| 705 | 705 | * let code = `var foo = 0;`; | |
| 706 | var tokenList = new LinkedList(); | 706 | * let tokens = Prism.tokenize(code, Prism.languages.javascript); |
| 707 | addAfter(tokenList, tokenList.head, text); | 707 | * tokens.forEach(token => { |
| 708 | 708 | * if (token instanceof Prism.Token && token.type === 'number') { | |
| 709 | matchGrammar(text, tokenList, grammar, tokenList.head, 0); | 709 | * console.log(`Found numeric literal: ${token.content}`); |
| 710 | 710 | * } | |
| 711 | return toArray(tokenList); | 711 | * }); |
| 712 | }, | 712 | */ |
| 713 | 713 | tokenize: function (text, grammar) { | |
| 714 | /** | 714 | var rest = grammar.rest; |
| 715 | * @namespace | 715 | if (rest) { |
| 716 | * @memberof Prism | 716 | for (var token in rest) { |
| 717 | * @public | 717 | grammar[token] = rest[token]; |
| 718 | */ | 718 | } |
| 719 | hooks: { | 719 | |
| 720 | all: {}, | 720 | delete grammar.rest; |
| 721 | 721 | } | |
| 722 | /** | 722 | |
| 723 | * Adds the given callback to the list of callbacks for the given hook. | 723 | var tokenList = new LinkedList(); |
| 724 | * | 724 | addAfter(tokenList, tokenList.head, text); |
| 725 | * The callback will be invoked when the hook it is registered for is run. | 725 | |
| 726 | * Hooks are usually directly run by a highlight function but you can also run hooks yourself. | 726 | matchGrammar(text, tokenList, grammar, tokenList.head, 0); |
| 727 | * | 727 | |
| 728 | * One callback function can be registered to multiple hooks and the same hook multiple times. | 728 | return toArray(tokenList); |
| 729 | * | 729 | }, |
| 730 | * @param {string} name The name of the hook. | 730 | |
| 731 | * @param {HookCallback} callback The callback function which is given environment variables. | 731 | /** |
| 732 | * @public | 732 | * @namespace |
| 733 | */ | 733 | * @memberof Prism |
| 734 | add: function (name, callback) { | 734 | * @public |
| 735 | var hooks = _.hooks.all; | 735 | */ |
| 736 | 736 | hooks: { | |
| 737 | hooks[name] = hooks[name] || []; | 737 | all: {}, |
| 738 | 738 | ||
| 739 | hooks[name].push(callback); | 739 | /** |
| 740 | }, | 740 | * Adds the given callback to the list of callbacks for the given hook. |
| 741 | 741 | * | |
| 742 | /** | 742 | * The callback will be invoked when the hook it is registered for is run. |
| 743 | * Runs a hook invoking all registered callbacks with the given environment variables. | 743 | * Hooks are usually directly run by a highlight function but you can also run hooks yourself. |
| 744 | * | 744 | * |
| 745 | * Callbacks will be invoked synchronously and in the order in which they were registered. | 745 | * One callback function can be registered to multiple hooks and the same hook multiple times. |
| 746 | * | 746 | * |
| 747 | * @param {string} name The name of the hook. | 747 | * @param {string} name The name of the hook. |
| 748 | * @param {Object<string, any>} env The environment variables of the hook passed to all callbacks registered. | 748 | * @param {HookCallback} callback The callback function which is given environment variables. |
| 749 | * @public | 749 | * @public |
| 750 | */ | 750 | */ |
| 751 | run: function (name, env) { | 751 | add: function (name, callback) { |
| 752 | var callbacks = _.hooks.all[name]; | 752 | var hooks = _.hooks.all; |
| 753 | 753 | ||
| 754 | if (!callbacks || !callbacks.length) { | 754 | hooks[name] = hooks[name] || []; |
| 755 | return; | 755 | |
| 756 | } | 756 | hooks[name].push(callback); |
| 757 | 757 | }, | |
| 758 | for (var i = 0, callback; (callback = callbacks[i++]);) { | 758 | |
| 759 | callback(env); | 759 | /** |
| 760 | } | 760 | * Runs a hook invoking all registered callbacks with the given environment variables. |
| 761 | } | 761 | * |
| 762 | }, | 762 | * Callbacks will be invoked synchronously and in the order in which they were registered. |
| 763 | 763 | * | |
| 764 | Token: Token | 764 | * @param {string} name The name of the hook. |
| 765 | }; | 765 | * @param {Object<string, any>} env The environment variables of the hook passed to all callbacks registered. |
| 766 | _self.Prism = _; | 766 | * @public |
| 767 | 767 | */ | |
| 768 | 768 | run: function (name, env) { | |
| 769 | // Typescript note: | 769 | var callbacks = _.hooks.all[name]; |
| 770 | // The following can be used to import the Token type in JSDoc: | 770 | |
| 771 | // | 771 | if (!callbacks || !callbacks.length) { |
| 772 | // @typedef {InstanceType<import("./prism-core")["Token"]>} Token | 772 | return; |
| 773 | 773 | } | |
| 774 | /** | 774 | |
| 775 | * Creates a new token. | 775 | for (var i = 0, callback; (callback = callbacks[i++]); ) { |
| 776 | * | 776 | callback(env); |
| 777 | * @param {string} type See {@link Token#type type} | 777 | } |
| 778 | * @param {string | TokenStream} content See {@link Token#content content} | 778 | }, |
| 779 | * @param {string|string[]} [alias] The alias(es) of the token. | 779 | }, |
| 780 | * @param {string} [matchedStr=""] A copy of the full string this token was created from. | 780 | |
| 781 | * @class | 781 | Token: Token, |
| 782 | * @global | 782 | }; |
| 783 | * @public | 783 | _self.Prism = _; |
| 784 | */ | 784 | |
| 785 | function Token(type, content, alias, matchedStr) { | 785 | // Typescript note: |
| 786 | /** | 786 | // The following can be used to import the Token type in JSDoc: |
| 787 | * The type of the token. | 787 | // |
| 788 | * | 788 | // @typedef {InstanceType<import("./prism-core")["Token"]>} Token |
| 789 | * This is usually the key of a pattern in a {@link Grammar}. | 789 | |
| 790 | * | 790 | /** |
| 791 | * @type {string} | 791 | * Creates a new token. |
| 792 | * @see GrammarToken | 792 | * |
| 793 | * @public | 793 | * @param {string} type See {@link Token#type type} |
| 794 | */ | 794 | * @param {string | TokenStream} content See {@link Token#content content} |
| 795 | this.type = type; | 795 | * @param {string|string[]} [alias] The alias(es) of the token. |
| 796 | /** | 796 | * @param {string} [matchedStr=""] A copy of the full string this token was created from. |
| 797 | * The strings or tokens contained by this token. | 797 | * @class |
| 798 | * | 798 | * @global |
| 799 | * This will be a token stream if the pattern matched also defined an `inside` grammar. | 799 | * @public |
| 800 | * | 800 | */ |
| 801 | * @type {string | TokenStream} | 801 | function Token(type, content, alias, matchedStr) { |
| 802 | * @public | 802 | /** |
| 803 | */ | 803 | * The type of the token. |
| 804 | this.content = content; | 804 | * |
| 805 | /** | 805 | * This is usually the key of a pattern in a {@link Grammar}. |
| 806 | * The alias(es) of the token. | 806 | * |
| 807 | * | 807 | * @type {string} |
| 808 | * @type {string|string[]} | 808 | * @see GrammarToken |
| 809 | * @see GrammarToken | 809 | * @public |
| 810 | * @public | 810 | */ |
| 811 | */ | 811 | this.type = type; |
| 812 | this.alias = alias; | 812 | /** |
| 813 | // Copy of the full string this token was created from | 813 | * The strings or tokens contained by this token. |
| 814 | this.length = (matchedStr || '').length | 0; | 814 | * |
| 815 | } | 815 | * This will be a token stream if the pattern matched also defined an `inside` grammar. |
| 816 | 816 | * | |
| 817 | /** | 817 | * @type {string | TokenStream} |
| 818 | * A token stream is an array of strings and {@link Token Token} objects. | 818 | * @public |
| 819 | * | 819 | */ |
| 820 | * Token streams have to fulfill a few properties that are assumed by most functions (mostly internal ones) that process | 820 | this.content = content; |
| 821 | * them. | 821 | /** |
| 822 | * | 822 | * The alias(es) of the token. |
| 823 | * 1. No adjacent strings. | 823 | * |
| 824 | * 2. No empty strings. | 824 | * @type {string|string[]} |
| 825 | * | 825 | * @see GrammarToken |
| 826 | * The only exception here is the token stream that only contains the empty string and nothing else. | 826 | * @public |
| 827 | * | 827 | */ |
| 828 | * @typedef {Array<string | Token>} TokenStream | 828 | this.alias = alias; |
| 829 | * @global | 829 | // Copy of the full string this token was created from |
| 830 | * @public | 830 | this.length = (matchedStr || "").length | 0; |
| 831 | */ | 831 | } |
| 832 | 832 | ||
| 833 | /** | 833 | /** |
| 834 | * Converts the given token or token stream to an HTML representation. | 834 | * A token stream is an array of strings and {@link Token Token} objects. |
| 835 | * | 835 | * |
| 836 | * The following hooks will be run: | 836 | * Token streams have to fulfill a few properties that are assumed by most functions (mostly internal ones) that process |
| 837 | * 1. `wrap`: On each {@link Token}. | 837 | * them. |
| 838 | * | 838 | * |
| 839 | * @param {string | Token | TokenStream} o The token or token stream to be converted. | 839 | * 1. No adjacent strings. |
| 840 | * @param {string} language The name of current language. | 840 | * 2. No empty strings. |
| 841 | * @returns {string} The HTML representation of the token or token stream. | 841 | * |
| 842 | * @memberof Token | 842 | * The only exception here is the token stream that only contains the empty string and nothing else. |
| 843 | * @static | 843 | * |
| 844 | */ | 844 | * @typedef {Array<string | Token>} TokenStream |
| 845 | Token.stringify = function stringify(o, language) { | 845 | * @global |
| 846 | if (typeof o == 'string') { | 846 | * @public |
| 847 | return o; | 847 | */ |
| 848 | } | 848 | |
| 849 | if (Array.isArray(o)) { | 849 | /** |
| 850 | var s = ''; | 850 | * Converts the given token or token stream to an HTML representation. |
| 851 | o.forEach(function (e) { | 851 | * |
| 852 | s += stringify(e, language); | 852 | * The following hooks will be run: |
| 853 | }); | 853 | * 1. `wrap`: On each {@link Token}. |
| 854 | return s; | 854 | * |
| 855 | } | 855 | * @param {string | Token | TokenStream} o The token or token stream to be converted. |
| 856 | 856 | * @param {string} language The name of current language. | |
| 857 | var env = { | 857 | * @returns {string} The HTML representation of the token or token stream. |
| 858 | type: o.type, | 858 | * @memberof Token |
| 859 | content: stringify(o.content, language), | 859 | * @static |
| 860 | tag: 'span', | 860 | */ |
| 861 | classes: ['token', o.type], | 861 | Token.stringify = function stringify(o, language) { |
| 862 | attributes: {}, | 862 | if (typeof o == "string") { |
| 863 | language: language | 863 | return o; |
| 864 | }; | 864 | } |
| 865 | 865 | if (Array.isArray(o)) { | |
| 866 | var aliases = o.alias; | 866 | var s = ""; |
| 867 | if (aliases) { | 867 | o.forEach(function (e) { |
| 868 | if (Array.isArray(aliases)) { | 868 | s += stringify(e, language); |
| 869 | Array.prototype.push.apply(env.classes, aliases); | 869 | }); |
| 870 | } else { | 870 | return s; |
| 871 | env.classes.push(aliases); | 871 | } |
| 872 | } | 872 | |
| 873 | } | 873 | var env = { |
| 874 | 874 | type: o.type, | |
| 875 | _.hooks.run('wrap', env); | 875 | content: stringify(o.content, language), |
| 876 | 876 | tag: "span", | |
| 877 | var attributes = ''; | 877 | classes: ["token", o.type], |
| 878 | for (var name in env.attributes) { | 878 | attributes: {}, |
| 879 | attributes += ' ' + name + '="' + (env.attributes[name] || '').replace(/"/g, '"') + '"'; | 879 | language: language, |
| 880 | } | 880 | }; |
| 881 | 881 | ||
| 882 | return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + attributes + '>' + env.content + '</' + env.tag + '>'; | 882 | var aliases = o.alias; |
| 883 | }; | 883 | if (aliases) { |
| 884 | 884 | if (Array.isArray(aliases)) { | |
| 885 | /** | 885 | Array.prototype.push.apply(env.classes, aliases); |
| 886 | * @param {RegExp} pattern | 886 | } else { |
| 887 | * @param {number} pos | 887 | env.classes.push(aliases); |
| 888 | * @param {string} text | 888 | } |
| 889 | * @param {boolean} lookbehind | 889 | } |
| 890 | * @returns {RegExpExecArray | null} | 890 | |
| 891 | */ | 891 | _.hooks.run("wrap", env); |
| 892 | function matchPattern(pattern, pos, text, lookbehind) { | 892 | |
| 893 | pattern.lastIndex = pos; | 893 | var attributes = ""; |
| 894 | var match = pattern.exec(text); | 894 | for (var name in env.attributes) { |
| 895 | if (match && lookbehind && match[1]) { | 895 | attributes += |
| 896 | // change the match to remove the text matched by the Prism lookbehind group | 896 | " " + |
| 897 | var lookbehindLength = match[1].length; | 897 | name + |
| 898 | match.index += lookbehindLength; | 898 | '="' + |
| 899 | match[0] = match[0].slice(lookbehindLength); | 899 | (env.attributes[name] || "").replace(/"/g, """) + |
| 900 | } | 900 | '"'; |
| 901 | return match; | 901 | } |
| 902 | } | 902 | |
| 903 | 903 | return ( | |
| 904 | /** | 904 | "<" + |
| 905 | * @param {string} text | 905 | env.tag + |
| 906 | * @param {LinkedList<string | Token>} tokenList | 906 | ' class="' + |
| 907 | * @param {any} grammar | 907 | env.classes.join(" ") + |
| 908 | * @param {LinkedListNode<string | Token>} startNode | 908 | '"' + |
| 909 | * @param {number} startPos | 909 | attributes + |
| 910 | * @param {RematchOptions} [rematch] | 910 | ">" + |
| 911 | * @returns {void} | 911 | env.content + |
| 912 | * @private | 912 | "</" + |
| 913 | * | 913 | env.tag + |
| 914 | * @typedef RematchOptions | 914 | ">" |
| 915 | * @property {string} cause | 915 | ); |
| 916 | * @property {number} reach | 916 | }; |
| 917 | */ | 917 | |
| 918 | function matchGrammar(text, tokenList, grammar, startNode, startPos, rematch) { | 918 | /** |
| 919 | for (var token in grammar) { | 919 | * @param {RegExp} pattern |
| 920 | if (!grammar.hasOwnProperty(token) || !grammar[token]) { | 920 | * @param {number} pos |
| 921 | continue; | 921 | * @param {string} text |
| 922 | } | 922 | * @param {boolean} lookbehind |
| 923 | 923 | * @returns {RegExpExecArray | null} | |
| 924 | var patterns = grammar[token]; | 924 | */ |
| 925 | patterns = Array.isArray(patterns) ? patterns : [patterns]; | 925 | function matchPattern(pattern, pos, text, lookbehind) { |
| 926 | 926 | pattern.lastIndex = pos; | |
| 927 | for (var j = 0; j < patterns.length; ++j) { | 927 | var match = pattern.exec(text); |
| 928 | if (rematch && rematch.cause == token + ',' + j) { | 928 | if (match && lookbehind && match[1]) { |
| 929 | return; | 929 | // change the match to remove the text matched by the Prism lookbehind group |
| 930 | } | 930 | var lookbehindLength = match[1].length; |
| 931 | 931 | match.index += lookbehindLength; | |
| 932 | var patternObj = patterns[j]; | 932 | match[0] = match[0].slice(lookbehindLength); |
| 933 | var inside = patternObj.inside; | 933 | } |
| 934 | var lookbehind = !!patternObj.lookbehind; | 934 | return match; |
| 935 | var greedy = !!patternObj.greedy; | 935 | } |
| 936 | var alias = patternObj.alias; | 936 | |
| 937 | 937 | /** | |
| 938 | if (greedy && !patternObj.pattern.global) { | 938 | * @param {string} text |
| 939 | // Without the global flag, lastIndex won't work | 939 | * @param {LinkedList<string | Token>} tokenList |
| 940 | var flags = patternObj.pattern.toString().match(/[imsuy]*$/)[0]; | 940 | * @param {any} grammar |
| 941 | patternObj.pattern = RegExp(patternObj.pattern.source, flags + 'g'); | 941 | * @param {LinkedListNode<string | Token>} startNode |
| 942 | } | 942 | * @param {number} startPos |
| 943 | 943 | * @param {RematchOptions} [rematch] | |
| 944 | /** @type {RegExp} */ | 944 | * @returns {void} |
| 945 | var pattern = patternObj.pattern || patternObj; | 945 | * @private |
| 946 | 946 | * | |
| 947 | for ( // iterate the token list and keep track of the current token/string position | 947 | * @typedef RematchOptions |
| 948 | var currentNode = startNode.next, pos = startPos; | 948 | * @property {string} cause |
| 949 | currentNode !== tokenList.tail; | 949 | * @property {number} reach |
| 950 | pos += currentNode.value.length, currentNode = currentNode.next | 950 | */ |
| 951 | ) { | 951 | function matchGrammar( |
| 952 | 952 | text, | |
| 953 | if (rematch && pos >= rematch.reach) { | 953 | tokenList, |
| 954 | break; | 954 | grammar, |
| 955 | } | 955 | startNode, |
| 956 | 956 | startPos, | |
| 957 | var str = currentNode.value; | 957 | rematch, |
| 958 | 958 | ) { | |
| 959 | if (tokenList.length > text.length) { | 959 | for (var token in grammar) { |
| 960 | // Something went terribly wrong, ABORT, ABORT! | 960 | if (!grammar.hasOwnProperty(token) || !grammar[token]) { |
| 961 | return; | 961 | continue; |
| 962 | } | 962 | } |
| 963 | 963 | ||
| 964 | if (str instanceof Token) { | 964 | var patterns = grammar[token]; |
| 965 | continue; | 965 | patterns = Array.isArray(patterns) ? patterns : [patterns]; |
| 966 | } | 966 | |
| 967 | 967 | for (var j = 0; j < patterns.length; ++j) { | |
| 968 | var removeCount = 1; // this is the to parameter of removeBetween | 968 | if (rematch && rematch.cause == token + "," + j) { |
| 969 | var match; | 969 | return; |
| 970 | 970 | } | |
| 971 | if (greedy) { | 971 | |
| 972 | match = matchPattern(pattern, pos, text, lookbehind); | 972 | var patternObj = patterns[j]; |
| 973 | if (!match || match.index >= text.length) { | 973 | var inside = patternObj.inside; |
| 974 | break; | 974 | var lookbehind = !!patternObj.lookbehind; |
| 975 | } | 975 | var greedy = !!patternObj.greedy; |
| 976 | 976 | var alias = patternObj.alias; | |
| 977 | var from = match.index; | 977 | |
| 978 | var to = match.index + match[0].length; | 978 | if (greedy && !patternObj.pattern.global) { |
| 979 | var p = pos; | 979 | // Without the global flag, lastIndex won't work |
| 980 | 980 | var flags = patternObj.pattern.toString().match(/[imsuy]*$/)[0]; | |
| 981 | // find the node that contains the match | 981 | patternObj.pattern = RegExp(patternObj.pattern.source, flags + "g"); |
| 982 | p += currentNode.value.length; | 982 | } |
| 983 | while (from >= p) { | 983 | |
| 984 | currentNode = currentNode.next; | 984 | /** @type {RegExp} */ |
| 985 | p += currentNode.value.length; | 985 | var pattern = patternObj.pattern || patternObj; |
| 986 | } | 986 | |
| 987 | // adjust pos (and p) | 987 | for ( |
| 988 | p -= currentNode.value.length; | 988 | // iterate the token list and keep track of the current token/string position |
| 989 | pos = p; | 989 | var currentNode = startNode.next, pos = startPos; |
| 990 | 990 | currentNode !== tokenList.tail; | |
| 991 | // the current node is a Token, then the match starts inside another Token, which is invalid | 991 | pos += currentNode.value.length, currentNode = currentNode.next |
| 992 | if (currentNode.value instanceof Token) { | 992 | ) { |
| 993 | continue; | 993 | if (rematch && pos >= rematch.reach) { |
| 994 | } | 994 | break; |
| 995 | 995 | } | |
| 996 | // find the last node which is affected by this match | 996 | |
| 997 | for ( | 997 | var str = currentNode.value; |
| 998 | var k = currentNode; | 998 | |
| 999 | k !== tokenList.tail && (p < to || typeof k.value === 'string'); | 999 | if (tokenList.length > text.length) { |
| 1000 | k = k.next | 1000 | // Something went terribly wrong, ABORT, ABORT! |
| 1001 | ) { | 1001 | return; |
| 1002 | removeCount++; | 1002 | } |
| 1003 | p += k.value.length; | 1003 | |
| 1004 | } | 1004 | if (str instanceof Token) { |
| 1005 | removeCount--; | 1005 | continue; |
| 1006 | 1006 | } | |
| 1007 | // replace with the new match | 1007 | |
| 1008 | str = text.slice(pos, p); | 1008 | var removeCount = 1; // this is the to parameter of removeBetween |
| 1009 | match.index -= pos; | 1009 | var match; |
| 1010 | } else { | 1010 | |
| 1011 | match = matchPattern(pattern, 0, str, lookbehind); | 1011 | if (greedy) { |
| 1012 | if (!match) { | 1012 | match = matchPattern(pattern, pos, text, lookbehind); |
| 1013 | continue; | 1013 | if (!match || match.index >= text.length) { |
| 1014 | } | 1014 | break; |
| 1015 | } | 1015 | } |
| 1016 | 1016 | ||
| 1017 | // eslint-disable-next-line no-redeclare | 1017 | var from = match.index; |
| 1018 | var from = match.index; | 1018 | var to = match.index + match[0].length; |
| 1019 | var matchStr = match[0]; | 1019 | var p = pos; |
| 1020 | var before = str.slice(0, from); | 1020 | |
| 1021 | var after = str.slice(from + matchStr.length); | 1021 | // find the node that contains the match |
| 1022 | 1022 | p += currentNode.value.length; | |
| 1023 | var reach = pos + str.length; | 1023 | while (from >= p) { |
| 1024 | if (rematch && reach > rematch.reach) { | 1024 | currentNode = currentNode.next; |
| 1025 | rematch.reach = reach; | 1025 | p += currentNode.value.length; |
| 1026 | } | 1026 | } |
| 1027 | 1027 | // adjust pos (and p) | |
| 1028 | var removeFrom = currentNode.prev; | 1028 | p -= currentNode.value.length; |
| 1029 | 1029 | pos = p; | |
| 1030 | if (before) { | 1030 | |
| 1031 | removeFrom = addAfter(tokenList, removeFrom, before); | 1031 | // the current node is a Token, then the match starts inside another Token, which is invalid |
| 1032 | pos += before.length; | 1032 | if (currentNode.value instanceof Token) { |
| 1033 | } | 1033 | continue; |
| 1034 | 1034 | } | |
| 1035 | removeRange(tokenList, removeFrom, removeCount); | 1035 | |
| 1036 | 1036 | // find the last node which is affected by this match | |
| 1037 | var wrapped = new Token(token, inside ? _.tokenize(matchStr, inside) : matchStr, alias, matchStr); | 1037 | for ( |
| 1038 | currentNode = addAfter(tokenList, removeFrom, wrapped); | 1038 | var k = currentNode; |
| 1039 | 1039 | k !== tokenList.tail && (p < to || typeof k.value === "string"); | |
| 1040 | if (after) { | 1040 | k = k.next |
| 1041 | addAfter(tokenList, currentNode, after); | 1041 | ) { |
| 1042 | } | 1042 | removeCount++; |
| 1043 | 1043 | p += k.value.length; | |
| 1044 | if (removeCount > 1) { | 1044 | } |
| 1045 | // at least one Token object was removed, so we have to do some rematching | 1045 | removeCount--; |
| 1046 | // this can only happen if the current pattern is greedy | 1046 | |
| 1047 | 1047 | // replace with the new match | |
| 1048 | /** @type {RematchOptions} */ | 1048 | str = text.slice(pos, p); |
| 1049 | var nestedRematch = { | 1049 | match.index -= pos; |
| 1050 | cause: token + ',' + j, | 1050 | } else { |
| 1051 | reach: reach | 1051 | match = matchPattern(pattern, 0, str, lookbehind); |
| 1052 | }; | 1052 | if (!match) { |
| 1053 | matchGrammar(text, tokenList, grammar, currentNode.prev, pos, nestedRematch); | 1053 | continue; |
| 1054 | 1054 | } | |
| 1055 | // the reach might have been extended because of the rematching | 1055 | } |
| 1056 | if (rematch && nestedRematch.reach > rematch.reach) { | 1056 | |
| 1057 | rematch.reach = nestedRematch.reach; | 1057 | // eslint-disable-next-line no-redeclare |
| 1058 | } | 1058 | var from = match.index; |
| 1059 | } | 1059 | var matchStr = match[0]; |
| 1060 | } | 1060 | var before = str.slice(0, from); |
| 1061 | } | 1061 | var after = str.slice(from + matchStr.length); |
| 1062 | } | 1062 | |
| 1063 | } | 1063 | var reach = pos + str.length; |
| 1064 | 1064 | if (rematch && reach > rematch.reach) { | |
| 1065 | /** | 1065 | rematch.reach = reach; |
| 1066 | * @typedef LinkedListNode | 1066 | } |
| 1067 | * @property {T} value | 1067 | |
| 1068 | * @property {LinkedListNode<T> | null} prev The previous node. | 1068 | var removeFrom = currentNode.prev; |
| 1069 | * @property {LinkedListNode<T> | null} next The next node. | 1069 | |
| 1070 | * @template T | 1070 | if (before) { |
| 1071 | * @private | 1071 | removeFrom = addAfter(tokenList, removeFrom, before); |
| 1072 | */ | 1072 | pos += before.length; |
| 1073 | 1073 | } | |
| 1074 | /** | 1074 | |
| 1075 | * @template T | 1075 | removeRange(tokenList, removeFrom, removeCount); |
| 1076 | * @private | 1076 | |
| 1077 | */ | 1077 | var wrapped = new Token( |
| 1078 | function LinkedList() { | 1078 | token, |
| 1079 | /** @type {LinkedListNode<T>} */ | 1079 | inside ? _.tokenize(matchStr, inside) : matchStr, |
| 1080 | var head = { value: null, prev: null, next: null }; | 1080 | alias, |
| 1081 | /** @type {LinkedListNode<T>} */ | 1081 | matchStr, |
| 1082 | var tail = { value: null, prev: head, next: null }; | 1082 | ); |
| 1083 | head.next = tail; | 1083 | currentNode = addAfter(tokenList, removeFrom, wrapped); |
| 1084 | 1084 | ||
| 1085 | /** @type {LinkedListNode<T>} */ | 1085 | if (after) { |
| 1086 | this.head = head; | 1086 | addAfter(tokenList, currentNode, after); |
| 1087 | /** @type {LinkedListNode<T>} */ | 1087 | } |
| 1088 | this.tail = tail; | 1088 | |
| 1089 | this.length = 0; | 1089 | if (removeCount > 1) { |
| 1090 | } | 1090 | // at least one Token object was removed, so we have to do some rematching |
| 1091 | 1091 | // this can only happen if the current pattern is greedy | |
| 1092 | /** | 1092 | |
| 1093 | * Adds a new node with the given value to the list. | 1093 | /** @type {RematchOptions} */ |
| 1094 | * | 1094 | var nestedRematch = { |
| 1095 | * @param {LinkedList<T>} list | 1095 | cause: token + "," + j, |
| 1096 | * @param {LinkedListNode<T>} node | 1096 | reach: reach, |
| 1097 | * @param {T} value | 1097 | }; |
| 1098 | * @returns {LinkedListNode<T>} The added node. | 1098 | matchGrammar( |
| 1099 | * @template T | 1099 | text, |
| 1100 | */ | 1100 | tokenList, |
| 1101 | function addAfter(list, node, value) { | 1101 | grammar, |
| 1102 | // assumes that node != list.tail && values.length >= 0 | 1102 | currentNode.prev, |
| 1103 | var next = node.next; | 1103 | pos, |
| 1104 | 1104 | nestedRematch, | |
| 1105 | var newNode = { value: value, prev: node, next: next }; | 1105 | ); |
| 1106 | node.next = newNode; | 1106 | |
| 1107 | next.prev = newNode; | 1107 | // the reach might have been extended because of the rematching |
| 1108 | list.length++; | 1108 | if (rematch && nestedRematch.reach > rematch.reach) { |
| 1109 | 1109 | rematch.reach = nestedRematch.reach; | |
| 1110 | return newNode; | 1110 | } |
| 1111 | } | 1111 | } |
| 1112 | /** | 1112 | } |
| 1113 | * Removes `count` nodes after the given node. The given node will not be removed. | 1113 | } |
| 1114 | * | 1114 | } |
| 1115 | * @param {LinkedList<T>} list | 1115 | } |
| 1116 | * @param {LinkedListNode<T>} node | 1116 | |
| 1117 | * @param {number} count | 1117 | /** |
| 1118 | * @template T | 1118 | * @typedef LinkedListNode |
| 1119 | */ | 1119 | * @property {T} value |
| 1120 | function removeRange(list, node, count) { | 1120 | * @property {LinkedListNode<T> | null} prev The previous node. |
| 1121 | var next = node.next; | 1121 | * @property {LinkedListNode<T> | null} next The next node. |
| 1122 | for (var i = 0; i < count && next !== list.tail; i++) { | 1122 | * @template T |
| 1123 | next = next.next; | 1123 | * @private |
| 1124 | } | 1124 | */ |
| 1125 | node.next = next; | 1125 | |
| 1126 | next.prev = node; | 1126 | /** |
| 1127 | list.length -= i; | 1127 | * @template T |
| 1128 | } | 1128 | * @private |
| 1129 | /** | 1129 | */ |
| 1130 | * @param {LinkedList<T>} list | 1130 | function LinkedList() { |
| 1131 | * @returns {T[]} | 1131 | /** @type {LinkedListNode<T>} */ |
| 1132 | * @template T | 1132 | var head = { value: null, prev: null, next: null }; |
| 1133 | */ | 1133 | /** @type {LinkedListNode<T>} */ |
| 1134 | function toArray(list) { | 1134 | var tail = { value: null, prev: head, next: null }; |
| 1135 | var array = []; | 1135 | head.next = tail; |
| 1136 | var node = list.head.next; | 1136 | |
| 1137 | while (node !== list.tail) { | 1137 | /** @type {LinkedListNode<T>} */ |
| 1138 | array.push(node.value); | 1138 | this.head = head; |
| 1139 | node = node.next; | 1139 | /** @type {LinkedListNode<T>} */ |
| 1140 | } | 1140 | this.tail = tail; |
| 1141 | return array; | 1141 | this.length = 0; |
| 1142 | } | 1142 | } |
| 1143 | 1143 | ||
| 1144 | 1144 | /** | |
| 1145 | if (!_self.document) { | 1145 | * Adds a new node with the given value to the list. |
| 1146 | if (!_self.addEventListener) { | 1146 | * |
| 1147 | // in Node.js | 1147 | * @param {LinkedList<T>} list |
| 1148 | return _; | 1148 | * @param {LinkedListNode<T>} node |
| 1149 | } | 1149 | * @param {T} value |
| 1150 | 1150 | * @returns {LinkedListNode<T>} The added node. | |
| 1151 | if (!_.disableWorkerMessageHandler) { | 1151 | * @template T |
| 1152 | // In worker | 1152 | */ |
| 1153 | _self.addEventListener('message', function (evt) { | 1153 | function addAfter(list, node, value) { |
| 1154 | var message = JSON.parse(evt.data); | 1154 | // assumes that node != list.tail && values.length >= 0 |
| 1155 | var lang = message.language; | 1155 | var next = node.next; |
| 1156 | var code = message.code; | 1156 | |
| 1157 | var immediateClose = message.immediateClose; | 1157 | var newNode = { value: value, prev: node, next: next }; |
| 1158 | 1158 | node.next = newNode; | |
| 1159 | _self.postMessage(_.highlight(code, _.languages[lang], lang)); | 1159 | next.prev = newNode; |
| 1160 | if (immediateClose) { | 1160 | list.length++; |
| 1161 | _self.close(); | 1161 | |
| 1162 | } | 1162 | return newNode; |
| 1163 | }, false); | 1163 | } |
| 1164 | } | 1164 | /** |
| 1165 | 1165 | * Removes `count` nodes after the given node. The given node will not be removed. | |
| 1166 | return _; | 1166 | * |
| 1167 | } | 1167 | * @param {LinkedList<T>} list |
| 1168 | 1168 | * @param {LinkedListNode<T>} node | |
| 1169 | // Get current script and highlight | 1169 | * @param {number} count |
| 1170 | var script = _.util.currentScript(); | 1170 | * @template T |
| 1171 | 1171 | */ | |
| 1172 | if (script) { | 1172 | function removeRange(list, node, count) { |
| 1173 | _.filename = script.src; | 1173 | var next = node.next; |
| 1174 | 1174 | for (var i = 0; i < count && next !== list.tail; i++) { | |
| 1175 | if (script.hasAttribute('data-manual')) { | 1175 | next = next.next; |
| 1176 | _.manual = true; | 1176 | } |
| 1177 | } | 1177 | node.next = next; |
| 1178 | } | 1178 | next.prev = node; |
| 1179 | 1179 | list.length -= i; | |
| 1180 | function highlightAutomaticallyCallback() { | 1180 | } |
| 1181 | if (!_.manual) { | 1181 | /** |
| 1182 | _.highlightAll(); | 1182 | * @param {LinkedList<T>} list |
| 1183 | } | 1183 | * @returns {T[]} |
| 1184 | } | 1184 | * @template T |
| 1185 | 1185 | */ | |
| 1186 | if (!_.manual) { | 1186 | function toArray(list) { |
| 1187 | // If the document state is "loading", then we'll use DOMContentLoaded. | 1187 | var array = []; |
| 1188 | // If the document state is "interactive" and the prism.js script is deferred, then we'll also use the | 1188 | var node = list.head.next; |
| 1189 | // DOMContentLoaded event because there might be some plugins or languages which have also been deferred and they | 1189 | while (node !== list.tail) { |
| 1190 | // might take longer one animation frame to execute which can create a race condition where only some plugins have | 1190 | array.push(node.value); |
| 1191 | // been loaded when Prism.highlightAll() is executed, depending on how fast resources are loaded. | 1191 | node = node.next; |
| 1192 | // See https://github.com/PrismJS/prism/issues/2102 | 1192 | } |
| 1193 | var readyState = document.readyState; | 1193 | return array; |
| 1194 | if (readyState === 'loading' || readyState === 'interactive' && script && script.defer) { | 1194 | } |
| 1195 | document.addEventListener('DOMContentLoaded', highlightAutomaticallyCallback); | 1195 | |
| 1196 | } else { | 1196 | if (!_self.document) { |
| 1197 | if (window.requestAnimationFrame) { | 1197 | if (!_self.addEventListener) { |
| 1198 | window.requestAnimationFrame(highlightAutomaticallyCallback); | 1198 | // in Node.js |
| 1199 | } else { | 1199 | return _; |
| 1200 | window.setTimeout(highlightAutomaticallyCallback, 16); | 1200 | } |
| 1201 | } | 1201 | |
| 1202 | } | 1202 | if (!_.disableWorkerMessageHandler) { |
| 1203 | } | 1203 | // In worker |
| 1204 | 1204 | _self.addEventListener( | |
| 1205 | return _; | 1205 | "message", |
| 1206 | 1206 | function (evt) { | |
| 1207 | }(_self)); | 1207 | var message = JSON.parse(evt.data); |
| 1208 | 1208 | var lang = message.language; | |
| 1209 | if (typeof module !== 'undefined' && module.exports) { | 1209 | var code = message.code; |
| 1210 | module.exports = Prism; | 1210 | var immediateClose = message.immediateClose; |
| 1211 | |||
| 1212 | _self.postMessage(_.highlight(code, _.languages[lang], lang)); | ||
| 1213 | if (immediateClose) { | ||
| 1214 | _self.close(); | ||
| 1215 | } | ||
| 1216 | }, | ||
| 1217 | false, | ||
| 1218 | ); | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | return _; | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | // Get current script and highlight | ||
| 1225 | var script = _.util.currentScript(); | ||
| 1226 | |||
| 1227 | if (script) { | ||
| 1228 | _.filename = script.src; | ||
| 1229 | |||
| 1230 | if (script.hasAttribute("data-manual")) { | ||
| 1231 | _.manual = true; | ||
| 1232 | } | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | function highlightAutomaticallyCallback() { | ||
| 1236 | if (!_.manual) { | ||
| 1237 | _.highlightAll(); | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | if (!_.manual) { | ||
| 1242 | // If the document state is "loading", then we'll use DOMContentLoaded. | ||
| 1243 | // If the document state is "interactive" and the prism.js script is deferred, then we'll also use the | ||
| 1244 | // DOMContentLoaded event because there might be some plugins or languages which have also been deferred and they | ||
| 1245 | // might take longer one animation frame to execute which can create a race condition where only some plugins have | ||
| 1246 | // been loaded when Prism.highlightAll() is executed, depending on how fast resources are loaded. | ||
| 1247 | // See https://github.com/PrismJS/prism/issues/2102 | ||
| 1248 | var readyState = document.readyState; | ||
| 1249 | if ( | ||
| 1250 | readyState === "loading" || | ||
| 1251 | (readyState === "interactive" && script && script.defer) | ||
| 1252 | ) { | ||
| 1253 | document.addEventListener( | ||
| 1254 | "DOMContentLoaded", | ||
| 1255 | highlightAutomaticallyCallback, | ||
| 1256 | ); | ||
| 1257 | } else { | ||
| 1258 | if (window.requestAnimationFrame) { | ||
| 1259 | window.requestAnimationFrame(highlightAutomaticallyCallback); | ||
| 1260 | } else { | ||
| 1261 | window.setTimeout(highlightAutomaticallyCallback, 16); | ||
| 1262 | } | ||
| 1263 | } | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | return _; | ||
| 1267 | })(_self); | ||
| 1268 | |||
| 1269 | if (typeof module !== "undefined" && module.exports) { | ||
| 1270 | module.exports = Prism; | ||
| 1211 | } | 1271 | } |
| 1212 | 1272 | ||
| 1213 | // hack for components to work correctly in node.js | 1273 | // hack for components to work correctly in node.js |
| 1214 | if (typeof global !== 'undefined') { | 1274 | if (typeof global !== "undefined") { |
| 1215 | global.Prism = Prism; | 1275 | global.Prism = Prism; |
| 1216 | } | 1276 | } |
| 1217 | 1277 | ||
| 1218 | // some additional documentation/types | 1278 | // some additional documentation/types |
| @@ -1263,1007 +1323,1096 @@ if (typeof global !== 'undefined') { · 44 unmodified lines | |||
| 1263 | * @global | 1323 | * @global |
| 1264 | * @public | 1324 | * @public |
| 1265 | */ | 1325 | */ |
| 1266 | ; | ||
| 1267 | Prism.languages.markup = { | 1326 | Prism.languages.markup = { |
| 1268 | 'comment': { | 1327 | comment: { |
| 1269 | pattern: /<!--(?:(?!<!--)[\s\S])*?-->/, | 1328 | pattern: /<!--(?:(?!<!--)[\s\S])*?-->/, |
| 1270 | greedy: true | 1329 | greedy: true, |
| 1271 | }, | 1330 | }, |
| 1272 | 'prolog': { | 1331 | prolog: { |
| 1273 | pattern: /<\?[\s\S]+?\?>/, | 1332 | pattern: /<\?[\s\S]+?\?>/, |
| 1274 | greedy: true | 1333 | greedy: true, |
| 1275 | }, | 1334 | }, |
| 1276 | 'doctype': { | 1335 | doctype: { |
| 1277 | // https://www.w3.org/TR/xml/#NT-doctypedecl | 1336 | // https://www.w3.org/TR/xml/#NT-doctypedecl |
| 1278 | pattern: /<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i, | 1337 | pattern: |
| 1279 | greedy: true, | 1338 | /<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i, |
| 1280 | inside: { | 1339 | greedy: true, |
| 1281 | 'internal-subset': { | 1340 | inside: { |
| 1282 | pattern: /(^[^\[]*\[)[\s\S]+(?=\]>$)/, | 1341 | "internal-subset": { |
| 1283 | lookbehind: true, | 1342 | pattern: /(^[^\[]*\[)[\s\S]+(?=\]>$)/, |
| 1284 | greedy: true, | 1343 | lookbehind: true, |
| 1285 | inside: null // see below | 1344 | greedy: true, |
| 1286 | }, | 1345 | inside: null, // see below |
| 1287 | 'string': { | 1346 | }, |
| 1288 | pattern: /"[^"]*"|'[^']*'/, | 1347 | string: { |
| 1289 | greedy: true | 1348 | pattern: /"[^"]*"|'[^']*'/, |
| 1290 | }, | 1349 | greedy: true, |
| 1291 | 'punctuation': /^<!|>$|[[\]]/, | 1350 | }, |
| 1292 | 'doctype-tag': /^DOCTYPE/i, | 1351 | punctuation: /^<!|>$|[[\]]/, |
| 1293 | 'name': /[^\s<>'"]+/ | 1352 | "doctype-tag": /^DOCTYPE/i, |
| 1294 | } | 1353 | name: /[^\s<>'"]+/, |
| 1295 | }, | 1354 | }, |
| 1296 | 'cdata': { | 1355 | }, |
| 1297 | pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i, | 1356 | cdata: { |
| 1298 | greedy: true | 1357 | pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i, |
| 1299 | }, | 1358 | greedy: true, |
| 1300 | 'tag': { | 1359 | }, |
| 1301 | pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/, | 1360 | tag: { |
| 1302 | greedy: true, | 1361 | pattern: |
| 1303 | inside: { | 1362 | /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/, |
| 1304 | 'tag': { | 1363 | greedy: true, |
| 1305 | pattern: /^<\/?[^\s>\/]+/, | 1364 | inside: { |
| 1306 | inside: { | 1365 | tag: { |
| 1307 | 'punctuation': /^<\/?/, | 1366 | pattern: /^<\/?[^\s>\/]+/, |
| 1308 | 'namespace': /^[^\s>\/:]+:/ | 1367 | inside: { |
| 1309 | } | 1368 | punctuation: /^<\/?/, |
| 1310 | }, | 1369 | namespace: /^[^\s>\/:]+:/, |
| 1311 | 'special-attr': [], | 1370 | }, |
| 1312 | 'attr-value': { | 1371 | }, |
| 1313 | pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/, | 1372 | "special-attr": [], |
| 1314 | inside: { | 1373 | "attr-value": { |
| 1315 | 'punctuation': [ | 1374 | pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/, |
| 1316 | { | 1375 | inside: { |
| 1317 | pattern: /^=/, | 1376 | punctuation: [ |
| 1318 | alias: 'attr-equals' | 1377 | { |
| 1319 | }, | 1378 | pattern: /^=/, |
| 1320 | { | 1379 | alias: "attr-equals", |
| 1321 | pattern: /^(\s*)["']|["']$/, | 1380 | }, |
| 1322 | lookbehind: true | 1381 | { |
| 1323 | } | 1382 | pattern: /^(\s*)["']|["']$/, |
| 1324 | ] | 1383 | lookbehind: true, |
| 1325 | } | 1384 | }, |
| 1326 | }, | 1385 | ], |
| 1327 | 'punctuation': /\/?>/, | 1386 | }, |
| 1328 | 'attr-name': { | 1387 | }, |
| 1329 | pattern: /[^\s>\/]+/, | 1388 | punctuation: /\/?>/, |
| 1330 | inside: { | 1389 | "attr-name": { |
| 1331 | 'namespace': /^[^\s>\/:]+:/ | 1390 | pattern: /[^\s>\/]+/, |
| 1332 | } | 1391 | inside: { |
| 1333 | } | 1392 | namespace: /^[^\s>\/:]+:/, |
| 1334 | 1393 | }, | |
| 1335 | } | 1394 | }, |
| 1336 | }, | 1395 | }, |
| 1337 | 'entity': [ | 1396 | }, |
| 1338 | { | 1397 | entity: [ |
| 1339 | pattern: /&[\da-z]{1,8};/i, | 1398 | { |
| 1340 | alias: 'named-entity' | 1399 | pattern: /&[\da-z]{1,8};/i, |
| 1341 | }, | 1400 | alias: "named-entity", |
| 1342 | /&#x?[\da-f]{1,8};/i | 1401 | }, |
| 1343 | ] | 1402 | /&#x?[\da-f]{1,8};/i, |
| 1403 | ], | ||
| 1344 | }; | 1404 | }; |
| 1345 | 1405 | ||
| 1346 | Prism.languages.markup['tag'].inside['attr-value'].inside['entity'] = | 1406 | Prism.languages.markup["tag"].inside["attr-value"].inside["entity"] = |
| 1347 | Prism.languages.markup['entity']; | 1407 | Prism.languages.markup["entity"]; |
| 1348 | Prism.languages.markup['doctype'].inside['internal-subset'].inside = Prism.languages.markup; | 1408 | Prism.languages.markup["doctype"].inside["internal-subset"].inside = |
| 1409 | Prism.languages.markup; | ||
| 1349 | 1410 | ||
| 1350 | // Plugin to make entity title show the real entity, idea by Roman Komarov | 1411 | // Plugin to make entity title show the real entity, idea by Roman Komarov |
| 1351 | Prism.hooks.add('wrap', function (env) { | 1412 | Prism.hooks.add("wrap", function (env) { |
| 1352 | 1413 | if (env.type === "entity") { | |
| 1353 | if (env.type === 'entity') { | 1414 | env.attributes["title"] = env.content.replace(/&/, "&"); |
| 1354 | env.attributes['title'] = env.content.replace(/&/, '&'); | 1415 | } |
| 1355 | } | ||
| 1356 | }); | 1416 | }); |
| 1357 | 1417 | ||
| 1358 | Object.defineProperty(Prism.languages.markup.tag, 'addInlined', { | 1418 | Object.defineProperty(Prism.languages.markup.tag, "addInlined", { |
| 1359 | /** | 1419 | /** |
| 1360 | * Adds an inlined language to markup. | 1420 | * Adds an inlined language to markup. |
| 1361 | * | 1421 | * |
| 1362 | * An example of an inlined language is CSS with `<style>` tags. | 1422 | * An example of an inlined language is CSS with `<style>` tags. |
| 1363 | * | 1423 | * |
| 1364 | * @param {string} tagName The name of the tag that contains the inlined language. This name will be treated as | 1424 | * @param {string} tagName The name of the tag that contains the inlined language. This name will be treated as |
| 1365 | * case insensitive. | 1425 | * case insensitive. |
| 1366 | * @param {string} lang The language key. | 1426 | * @param {string} lang The language key. |
| 1367 | * @example | 1427 | * @example |
| 1368 | * addInlined('style', 'css'); | 1428 | * addInlined('style', 'css'); |
| 1369 | */ | 1429 | */ |
| 1370 | value: function addInlined(tagName, lang) { | 1430 | value: function addInlined(tagName, lang) { |
| 1371 | var includedCdataInside = {}; | 1431 | var includedCdataInside = {}; |
| 1372 | includedCdataInside['language-' + lang] = { | 1432 | includedCdataInside["language-" + lang] = { |
| 1373 | pattern: /(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i, | 1433 | pattern: /(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i, |
| 1374 | lookbehind: true, | 1434 | lookbehind: true, |
| 1375 | inside: Prism.languages[lang] | 1435 | inside: Prism.languages[lang], |
| 1376 | }; | 1436 | }; |
| 1377 | includedCdataInside['cdata'] = /^<!\[CDATA\[|\]\]>$/i; | 1437 | includedCdataInside["cdata"] = /^<!\[CDATA\[|\]\]>$/i; |
| 1378 | 1438 | ||
| 1379 | var inside = { | 1439 | var inside = { |
| 1380 | 'included-cdata': { | 1440 | "included-cdata": { |
| 1381 | pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i, | 1441 | pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i, |
| 1382 | inside: includedCdataInside | 1442 | inside: includedCdataInside, |
| 1383 | } | 1443 | }, |
| 1384 | }; | 1444 | }; |
| 1385 | inside['language-' + lang] = { | 1445 | inside["language-" + lang] = { |
| 1386 | pattern: /[\s\S]+/, | 1446 | pattern: /[\s\S]+/, |
| 1387 | inside: Prism.languages[lang] | 1447 | inside: Prism.languages[lang], |
| 1388 | }; | 1448 | }; |
| 1389 | 1449 | ||
| 1390 | var def = {}; | 1450 | var def = {}; |
| 1391 | def[tagName] = { | 1451 | def[tagName] = { |
| 1392 | pattern: RegExp(/(<__[^>]*>)(?:<!\[CDATA\[(?:[^\]]|\](?!\]>))*\]\]>|(?!<!\[CDATA\[)[\s\S])*?(?=<\/__>)/.source.replace(/__/g, function () { return tagName; }), 'i'), | 1452 | pattern: RegExp( |
| 1393 | lookbehind: true, | 1453 | /(<__[^>]*>)(?:<!\[CDATA\[(?:[^\]]|\](?!\]>))*\]\]>|(?!<!\[CDATA\[)[\s\S])*?(?=<\/__>)/.source.replace( |
| 1394 | greedy: true, | 1454 | /__/g, |
| 1395 | inside: inside | 1455 | function () { |
| 1396 | }; | 1456 | return tagName; |
| 1397 | 1457 | }, | |
| 1398 | Prism.languages.insertBefore('markup', 'cdata', def); | 1458 | ), |
| 1399 | } | 1459 | "i", |
| 1460 | ), | ||
| 1461 | lookbehind: true, | ||
| 1462 | greedy: true, | ||
| 1463 | inside: inside, | ||
| 1464 | }; | ||
| 1465 | |||
| 1466 | Prism.languages.insertBefore("markup", "cdata", def); | ||
| 1467 | }, | ||
| 1400 | }); | 1468 | }); |
| 1401 | Object.defineProperty(Prism.languages.markup.tag, 'addAttribute', { | 1469 | Object.defineProperty(Prism.languages.markup.tag, "addAttribute", { |
| 1402 | /** | 1470 | /** |
| 1403 | * Adds an pattern to highlight languages embedded in HTML attributes. | 1471 | * Adds an pattern to highlight languages embedded in HTML attributes. |
| 1404 | * | 1472 | * |
| 1405 | * An example of an inlined language is CSS with `style` attributes. | 1473 | * An example of an inlined language is CSS with `style` attributes. |
| 1406 | * | 1474 | * |
| 1407 | * @param {string} attrName The name of the tag that contains the inlined language. This name will be treated as | 1475 | * @param {string} attrName The name of the tag that contains the inlined language. This name will be treated as |
| 1408 | * case insensitive. | 1476 | * case insensitive. |
| 1409 | * @param {string} lang The language key. | 1477 | * @param {string} lang The language key. |
| 1410 | * @example | 1478 | * @example |
| 1411 | * addAttribute('style', 'css'); | 1479 | * addAttribute('style', 'css'); |
| 1412 | */ | 1480 | */ |
| 1413 | value: function (attrName, lang) { | 1481 | value: function (attrName, lang) { |
| 1414 | Prism.languages.markup.tag.inside['special-attr'].push({ | 1482 | Prism.languages.markup.tag.inside["special-attr"].push({ |
| 1415 | pattern: RegExp( | 1483 | pattern: RegExp( |
| 1416 | /(^|["'\s])/.source + '(?:' + attrName + ')' + /\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source, | 1484 | /(^|["'\s])/.source + |
| 1417 | 'i' | 1485 | "(?:" + |
| 1418 | ), | 1486 | attrName + |
| 1419 | lookbehind: true, | 1487 | ")" + |
| 1420 | inside: { | 1488 | /\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source, |
| 1421 | 'attr-name': /^[^\s=]+/, | 1489 | "i", |
| 1422 | 'attr-value': { | 1490 | ), |
| 1423 | pattern: /=[\s\S]+/, | 1491 | lookbehind: true, |
| 1424 | inside: { | 1492 | inside: { |
| 1425 | 'value': { | 1493 | "attr-name": /^[^\s=]+/, |
| 1426 | pattern: /(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/, | 1494 | "attr-value": { |
| 1427 | lookbehind: true, | 1495 | pattern: /=[\s\S]+/, |
| 1428 | alias: [lang, 'language-' + lang], | 1496 | inside: { |
| 1429 | inside: Prism.languages[lang] | 1497 | value: { |
| 1430 | }, | 1498 | pattern: /(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/, |
| 1431 | 'punctuation': [ | 1499 | lookbehind: true, |
| 1432 | { | 1500 | alias: [lang, "language-" + lang], |
| 1433 | pattern: /^=/, | 1501 | inside: Prism.languages[lang], |
| 1434 | alias: 'attr-equals' | 1502 | }, |
| 1435 | }, | 1503 | punctuation: [ |
| 1436 | /"|'/ | 1504 | { |
| 1437 | ] | 1505 | pattern: /^=/, |
| 1438 | } | 1506 | alias: "attr-equals", |
| 1439 | } | 1507 | }, |
| 1440 | } | 1508 | /"|'/, |
| 1441 | }); | 1509 | ], |
| 1442 | } | 1510 | }, |
| 1511 | }, | ||
| 1512 | }, | ||
| 1513 | }); | ||
| 1514 | }, | ||
| 1443 | }); | 1515 | }); |
| 1444 | 1516 | ||
| 1445 | Prism.languages.html = Prism.languages.markup; | 1517 | Prism.languages.html = Prism.languages.markup; |
| 1446 | Prism.languages.mathml = Prism.languages.markup; | 1518 | Prism.languages.mathml = Prism.languages.markup; |
| 1447 | Prism.languages.svg = Prism.languages.markup; | 1519 | Prism.languages.svg = Prism.languages.markup; |
| 1448 | 1520 | ||
| 1449 | Prism.languages.xml = Prism.languages.extend('markup', {}); | 1521 | Prism.languages.xml = Prism.languages.extend("markup", {}); |
| 1450 | Prism.languages.ssml = Prism.languages.xml; | 1522 | Prism.languages.ssml = Prism.languages.xml; |
| 1451 | Prism.languages.atom = Prism.languages.xml; | 1523 | Prism.languages.atom = Prism.languages.xml; |
| 1452 | Prism.languages.rss = Prism.languages.xml; | 1524 | Prism.languages.rss = Prism.languages.xml; |
| 1453 | 1525 | ||
| 1454 | (function (Prism) { | 1526 | (function (Prism) { |
| 1455 | 1527 | var string = | |
| 1456 | var string = /(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/; | 1528 | /(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/; |
| 1457 | 1529 | ||
| 1458 | Prism.languages.css = { | 1530 | Prism.languages.css = { |
| 1459 | 'comment': /\/\*[\s\S]*?\*\//, | 1531 | comment: /\/\*[\s\S]*?\*\//, |
| 1460 | 'atrule': { | 1532 | atrule: { |
| 1461 | pattern: RegExp('@[\\w-](?:' + /[^;{\s"']|\s+(?!\s)/.source + '|' + string.source + ')*?' + /(?:;|(?=\s*\{))/.source), | 1533 | pattern: RegExp( |
| 1462 | inside: { | 1534 | "@[\\w-](?:" + |
| 1463 | 'rule': /^@[\w-]+/, | 1535 | /[^;{\s"']|\s+(?!\s)/.source + |
| 1464 | 'selector-function-argument': { | 1536 | "|" + |
| 1465 | pattern: /(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/, | 1537 | string.source + |
| 1466 | lookbehind: true, | 1538 | ")*?" + |
| 1467 | alias: 'selector' | 1539 | /(?:;|(?=\s*\{))/.source, |
| 1468 | }, | 1540 | ), |
| 1469 | 'keyword': { | 1541 | inside: { |
| 1470 | pattern: /(^|[^\w-])(?:and|not|only|or)(?![\w-])/, | 1542 | rule: /^@[\w-]+/, |
| 1471 | lookbehind: true | 1543 | "selector-function-argument": { |
| 1472 | } | 1544 | pattern: |
| 1473 | // See rest below | 1545 | /(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/, |
| 1474 | } | 1546 | lookbehind: true, |
| 1475 | }, | 1547 | alias: "selector", |
| 1476 | 'url': { | 1548 | }, |
| 1477 | // https://drafts.csswg.org/css-values-3/#urls | 1549 | keyword: { |
| 1478 | pattern: RegExp('\\burl\\((?:' + string.source + '|' + /(?:[^\\\r\n()"']|\\[\s\S])*/.source + ')\\)', 'i'), | 1550 | pattern: /(^|[^\w-])(?:and|not|only|or)(?![\w-])/, |
| 1479 | greedy: true, | 1551 | lookbehind: true, |
| 1480 | inside: { | 1552 | }, |
| 1481 | 'function': /^url/i, | 1553 | // See rest below |
| 1482 | 'punctuation': /^\(|\)$/, | 1554 | }, |
| 1483 | 'string': { | 1555 | }, |
| 1484 | pattern: RegExp('^' + string.source + '$'), | 1556 | url: { |
| 1485 | alias: 'url' | 1557 | // https://drafts.csswg.org/css-values-3/#urls |
| 1486 | } | 1558 | pattern: RegExp( |
| 1487 | } | 1559 | "\\burl\\((?:" + |
| 1488 | }, | 1560 | string.source + |
| 1489 | 'selector': { | 1561 | "|" + |
| 1490 | pattern: RegExp('(^|[{}\\s])[^{}\\s](?:[^{};"\'\\s]|\\s+(?![\\s{])|' + string.source + ')*(?=\\s*\\{)'), | 1562 | /(?:[^\\\r\n()"']|\\[\s\S])*/.source + |
| 1491 | lookbehind: true | 1563 | ")\\)", |
| 1492 | }, | 1564 | "i", |
| 1493 | 'string': { | 1565 | ), |
| 1494 | pattern: string, | 1566 | greedy: true, |
| 1495 | greedy: true | 1567 | inside: { |
| 1496 | }, | 1568 | function: /^url/i, |
| 1497 | 'property': { | 1569 | punctuation: /^\(|\)$/, |
| 1498 | pattern: /(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i, | 1570 | string: { |
| 1499 | lookbehind: true | 1571 | pattern: RegExp("^" + string.source + "$"), |
| 1500 | }, | 1572 | alias: "url", |
| 1501 | 'important': /!important\b/i, | 1573 | }, |
| 1502 | 'function': { | 1574 | }, |
| 1503 | pattern: /(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i, | 1575 | }, |
| 1504 | lookbehind: true | 1576 | selector: { |
| 1505 | }, | 1577 | pattern: RegExp( |
| 1506 | 'punctuation': /[(){};:,]/ | 1578 | "(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|" + |
| 1507 | }; | 1579 | string.source + |
| 1508 | 1580 | ")*(?=\\s*\\{)", | |
| 1509 | Prism.languages.css['atrule'].inside.rest = Prism.languages.css; | 1581 | ), |
| 1510 | 1582 | lookbehind: true, | |
| 1511 | var markup = Prism.languages.markup; | 1583 | }, |
| 1512 | if (markup) { | 1584 | string: { |
| 1513 | markup.tag.addInlined('style', 'css'); | 1585 | pattern: string, |
| 1514 | markup.tag.addAttribute('style', 'css'); | 1586 | greedy: true, |
| 1515 | } | 1587 | }, |
| 1516 | 1588 | property: { | |
| 1517 | }(Prism)); | 1589 | pattern: |
| 1590 | /(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i, | ||
| 1591 | lookbehind: true, | ||
| 1592 | }, | ||
| 1593 | important: /!important\b/i, | ||
| 1594 | function: { | ||
| 1595 | pattern: /(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i, | ||
| 1596 | lookbehind: true, | ||
| 1597 | }, | ||
| 1598 | punctuation: /[(){};:,]/, | ||
| 1599 | }; | ||
| 1600 | |||
| 1601 | Prism.languages.css["atrule"].inside.rest = Prism.languages.css; | ||
| 1602 | |||
| 1603 | var markup = Prism.languages.markup; | ||
| 1604 | if (markup) { | ||
| 1605 | markup.tag.addInlined("style", "css"); | ||
| 1606 | markup.tag.addAttribute("style", "css"); | ||
| 1607 | } | ||
| 1608 | })(Prism); | ||
| 1518 | 1609 | ||
| 1519 | Prism.languages.clike = { | 1610 | Prism.languages.clike = { |
| 1520 | 'comment': [ | 1611 | comment: [ |
| 1521 | { | 1612 | { |
| 1522 | pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/, | 1613 | pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/, |
| 1523 | lookbehind: true, | 1614 | lookbehind: true, |
| 1524 | greedy: true | 1615 | greedy: true, |
| 1525 | }, | 1616 | }, |
| 1526 | { | 1617 | { |
| 1527 | pattern: /(^|[^\\:])\/\/.*/, | 1618 | pattern: /(^|[^\\:])\/\/.*/, |
| 1528 | lookbehind: true, | 1619 | lookbehind: true, |
| 1529 | greedy: true | 1620 | greedy: true, |
| 1530 | } | 1621 | }, |
| 1531 | ], | 1622 | ], |
| 1532 | 'string': { | 1623 | string: { |
| 1533 | pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, | 1624 | pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, |
| 1534 | greedy: true | 1625 | greedy: true, |
| 1535 | }, | 1626 | }, |
| 1536 | 'class-name': { | 1627 | "class-name": { |
| 1537 | pattern: /(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i, | 1628 | pattern: |
| 1538 | lookbehind: true, | 1629 | /(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i, |
| 1539 | inside: { | 1630 | lookbehind: true, |
| 1540 | 'punctuation': /[.\\]/ | 1631 | inside: { |
| 1541 | } | 1632 | punctuation: /[.\\]/, |
| 1542 | }, | 1633 | }, |
| 1543 | 'keyword': /\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/, | 1634 | }, |
| 1544 | 'boolean': /\b(?:false|true)\b/, | 1635 | keyword: |
| 1545 | 'function': /\b\w+(?=\()/, | 1636 | /\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/, |
| 1546 | 'number': /\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i, | 1637 | boolean: /\b(?:false|true)\b/, |
| 1547 | 'operator': /[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/, | 1638 | function: /\b\w+(?=\()/, |
| 1548 | 'punctuation': /[{}[\];(),.:]/ | 1639 | number: /\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i, |
| 1640 | operator: /[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/, | ||
| 1641 | punctuation: /[{}[\];(),.:]/, | ||
| 1549 | }; | 1642 | }; |
| 1550 | 1643 | ||
| 1551 | Prism.languages.javascript = Prism.languages.extend('clike', { | 1644 | Prism.languages.javascript = Prism.languages.extend("clike", { |
| 1552 | 'class-name': [ | 1645 | "class-name": [ |
| 1553 | Prism.languages.clike['class-name'], | 1646 | Prism.languages.clike["class-name"], |
| 1554 | { | 1647 | { |
| 1555 | pattern: /(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/, | 1648 | pattern: |
| 1556 | lookbehind: true | 1649 | /(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/, |
| 1557 | } | 1650 | lookbehind: true, |
| 1558 | ], | 1651 | }, |
| 1559 | 'keyword': [ | 1652 | ], |
| 1560 | { | 1653 | keyword: [ |
| 1561 | pattern: /((?:^|\})\s*)catch\b/, | 1654 | { |
| 1562 | lookbehind: true | 1655 | pattern: /((?:^|\})\s*)catch\b/, |
| 1563 | }, | 1656 | lookbehind: true, |
| 1564 | { | 1657 | }, |
| 1565 | pattern: /(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/, | 1658 | { |
| 1566 | lookbehind: true | 1659 | pattern: |
| 1567 | }, | 1660 | /(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/, |
| 1568 | ], | 1661 | lookbehind: true, |
| 1569 | // Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444) | 1662 | }, |
| 1570 | 'function': /#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/, | 1663 | ], |
| 1571 | 'number': { | 1664 | // Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444) |
| 1572 | pattern: RegExp( | 1665 | function: |
| 1573 | /(^|[^\w$])/.source + | 1666 | /#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/, |
| 1574 | '(?:' + | 1667 | number: { |
| 1575 | ( | 1668 | pattern: RegExp( |
| 1576 | // constant | 1669 | /(^|[^\w$])/.source + |
| 1577 | /NaN|Infinity/.source + | 1670 | "(?:" + |
| 1578 | '|' + | 1671 | // constant |
| 1579 | // binary integer | 1672 | (/NaN|Infinity/.source + |
| 1580 | /0[bB][01]+(?:_[01]+)*n?/.source + | 1673 | "|" + |
| 1581 | '|' + | 1674 | // binary integer |
| 1582 | // octal integer | 1675 | /0[bB][01]+(?:_[01]+)*n?/.source + |
| 1583 | /0[oO][0-7]+(?:_[0-7]+)*n?/.source + | 1676 | "|" + |
| 1584 | '|' + | 1677 | // octal integer |
| 1585 | // hexadecimal integer | 1678 | /0[oO][0-7]+(?:_[0-7]+)*n?/.source + |
| 1586 | /0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source + | 1679 | "|" + |
| 1587 | '|' + | 1680 | // hexadecimal integer |
| 1588 | // decimal bigint | 1681 | /0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source + |
| 1589 | /\d+(?:_\d+)*n/.source + | 1682 | "|" + |
| 1590 | '|' + | 1683 | // decimal bigint |
| 1591 | // decimal number (integer or float) but no bigint | 1684 | /\d+(?:_\d+)*n/.source + |
| 1592 | /(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source | 1685 | "|" + |
| 1593 | ) + | 1686 | // decimal number (integer or float) but no bigint |
| 1594 | ')' + | 1687 | /(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/ |
| 1595 | /(?![\w$])/.source | 1688 | .source) + |
| 1596 | ), | 1689 | ")" + |
| 1597 | lookbehind: true | 1690 | /(?![\w$])/.source, |
| 1598 | }, | 1691 | ), |
| 1599 | 'operator': /--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/ | 1692 | lookbehind: true, |
| 1693 | }, | ||
| 1694 | operator: | ||
| 1695 | /--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/, | ||
| 1600 | }); | 1696 | }); |
| 1601 | 1697 | ||
| 1602 | Prism.languages.javascript['class-name'][0].pattern = /(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/; | 1698 | Prism.languages.javascript["class-name"][0].pattern = |
| 1603 | 1699 | /(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/; | |
| 1604 | Prism.languages.insertBefore('javascript', 'keyword', { | 1700 | |
| 1605 | 'regex': { | 1701 | Prism.languages.insertBefore("javascript", "keyword", { |
| 1606 | pattern: RegExp( | 1702 | regex: { |
| 1607 | // lookbehind | 1703 | pattern: RegExp( |
| 1608 | // eslint-disable-next-line regexp/no-dupe-characters-character-class | 1704 | // lookbehind |
| 1609 | /((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source + | 1705 | // eslint-disable-next-line regexp/no-dupe-characters-character-class |
| 1610 | // Regex pattern: | 1706 | /((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source + |
| 1611 | // There are 2 regex patterns here. The RegExp set notation proposal added support for nested character | 1707 | // Regex pattern: |
| 1612 | // classes if the `v` flag is present. Unfortunately, nested CCs are both context-free and incompatible | 1708 | // There are 2 regex patterns here. The RegExp set notation proposal added support for nested character |
| 1613 | // with the only syntax, so we have to define 2 different regex patterns. | 1709 | // classes if the `v` flag is present. Unfortunately, nested CCs are both context-free and incompatible |
| 1614 | /\//.source + | 1710 | // with the only syntax, so we have to define 2 different regex patterns. |
| 1615 | '(?:' + | 1711 | /\//.source + |
| 1616 | /(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source + | 1712 | "(?:" + |
| 1617 | '|' + | 1713 | /(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/ |
| 1618 | // `v` flag syntax. This supports 3 levels of nested character classes. | 1714 | .source + |
| 1619 | /(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source + | 1715 | "|" + |
| 1620 | ')' + | 1716 | // `v` flag syntax. This supports 3 levels of nested character classes. |
| 1621 | // lookahead | 1717 | /(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/ |
| 1622 | /(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source | 1718 | .source + |
| 1623 | ), | 1719 | ")" + |
| 1624 | lookbehind: true, | 1720 | // lookahead |
| 1625 | greedy: true, | 1721 | /(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/ |
| 1626 | inside: { | 1722 | .source, |
| 1627 | 'regex-source': { | 1723 | ), |
| 1628 | pattern: /^(\/)[\s\S]+(?=\/[a-z]*$)/, | 1724 | lookbehind: true, |
| 1629 | lookbehind: true, | 1725 | greedy: true, |
| 1630 | alias: 'language-regex', | 1726 | inside: { |
| 1631 | inside: Prism.languages.regex | 1727 | "regex-source": { |
| 1632 | }, | 1728 | pattern: /^(\/)[\s\S]+(?=\/[a-z]*$)/, |
| 1633 | 'regex-delimiter': /^\/|\/$/, | 1729 | lookbehind: true, |
| 1634 | 'regex-flags': /^[a-z]+$/, | 1730 | alias: "language-regex", |
| 1635 | } | 1731 | inside: Prism.languages.regex, |
| 1636 | }, | 1732 | }, |
| 1637 | // This must be declared before keyword because we use "function" inside the look-forward | 1733 | "regex-delimiter": /^\/|\/$/, |
| 1638 | 'function-variable': { | 1734 | "regex-flags": /^[a-z]+$/, |
| 1639 | pattern: /#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/, | 1735 | }, |
| 1640 | alias: 'function' | 1736 | }, |
| 1641 | }, | 1737 | // This must be declared before keyword because we use "function" inside the look-forward |
| 1642 | 'parameter': [ | 1738 | "function-variable": { |
| 1643 | { | 1739 | pattern: |
| 1644 | pattern: /(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/, | 1740 | /#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/, |
| 1645 | lookbehind: true, | 1741 | alias: "function", |
| 1646 | inside: Prism.languages.javascript | 1742 | }, |
| 1647 | }, | 1743 | parameter: [ |
| 1648 | { | 1744 | { |
| 1649 | pattern: /(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i, | 1745 | pattern: |
| 1650 | lookbehind: true, | 1746 | /(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/, |
| 1651 | inside: Prism.languages.javascript | 1747 | lookbehind: true, |
| 1652 | }, | 1748 | inside: Prism.languages.javascript, |
| 1653 | { | 1749 | }, |
| 1654 | pattern: /(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/, | 1750 | { |
| 1655 | lookbehind: true, | 1751 | pattern: |
| 1656 | inside: Prism.languages.javascript | 1752 | /(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i, |
| 1657 | }, | 1753 | lookbehind: true, |
| 1658 | { | 1754 | inside: Prism.languages.javascript, |
| 1659 | pattern: /((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/, | 1755 | }, |
| 1660 | lookbehind: true, | 1756 | { |
| 1661 | inside: Prism.languages.javascript | 1757 | pattern: |
| 1662 | } | 1758 | /(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/, |
| 1663 | ], | 1759 | lookbehind: true, |
| 1664 | 'constant': /\b[A-Z](?:[A-Z_]|\dx?)*\b/ | 1760 | inside: Prism.languages.javascript, |
| 1761 | }, | ||
| 1762 | { | ||
| 1763 | pattern: | ||
| 1764 | /((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/, | ||
| 1765 | lookbehind: true, | ||
| 1766 | inside: Prism.languages.javascript, | ||
| 1767 | }, | ||
| 1768 | ], | ||
| 1769 | constant: /\b[A-Z](?:[A-Z_]|\dx?)*\b/, | ||
| 1665 | }); | 1770 | }); |
| 1666 | 1771 | ||
| 1667 | Prism.languages.insertBefore('javascript', 'string', { | 1772 | Prism.languages.insertBefore("javascript", "string", { |
| 1668 | 'hashbang': { | 1773 | hashbang: { |
| 1669 | pattern: /^#!.*/, | 1774 | pattern: /^#!.*/, |
| 1670 | greedy: true, | 1775 | greedy: true, |
| 1671 | alias: 'comment' | 1776 | alias: "comment", |
| 1672 | }, | 1777 | }, |
| 1673 | 'template-string': { | 1778 | "template-string": { |
| 1674 | pattern: /`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/, | 1779 | pattern: |
| 1675 | greedy: true, | 1780 | /`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/, |
| 1676 | inside: { | 1781 | greedy: true, |
| 1677 | 'template-punctuation': { | 1782 | inside: { |
| 1678 | pattern: /^`|`$/, | 1783 | "template-punctuation": { |
| 1679 | alias: 'string' | 1784 | pattern: /^`|`$/, |
| 1680 | }, | 1785 | alias: "string", |
| 1681 | 'interpolation': { | 1786 | }, |
| 1682 | pattern: /((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/, | 1787 | interpolation: { |
| 1683 | lookbehind: true, | 1788 | pattern: |
| 1684 | inside: { | 1789 | /((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/, |
| 1685 | 'interpolation-punctuation': { | 1790 | lookbehind: true, |
| 1686 | pattern: /^\$\{|\}$/, | 1791 | inside: { |
| 1687 | alias: 'punctuation' | 1792 | "interpolation-punctuation": { |
| 1688 | }, | 1793 | pattern: /^\$\{|\}$/, |
| 1689 | rest: Prism.languages.javascript | 1794 | alias: "punctuation", |
| 1690 | } | 1795 | }, |
| 1691 | }, | 1796 | rest: Prism.languages.javascript, |
| 1692 | 'string': /[\s\S]+/ | 1797 | }, |
| 1693 | } | 1798 | }, |
| 1694 | }, | 1799 | string: /[\s\S]+/, |
| 1695 | 'string-property': { | 1800 | }, |
| 1696 | pattern: /((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m, | 1801 | }, |
| 1697 | lookbehind: true, | 1802 | "string-property": { |
| 1698 | greedy: true, | 1803 | pattern: |
| 1699 | alias: 'property' | 1804 | /((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m, |
| 1700 | } | 1805 | lookbehind: true, |
| 1806 | greedy: true, | ||
| 1807 | alias: "property", | ||
| 1808 | }, | ||
| 1701 | }); | 1809 | }); |
| 1702 | 1810 | ||
| 1703 | Prism.languages.insertBefore('javascript', 'operator', { | 1811 | Prism.languages.insertBefore("javascript", "operator", { |
| 1704 | 'literal-property': { | 1812 | "literal-property": { |
| 1705 | pattern: /((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m, | 1813 | pattern: |
| 1706 | lookbehind: true, | 1814 | /((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m, |
| 1707 | alias: 'property' | 1815 | lookbehind: true, |
| 1708 | }, | 1816 | alias: "property", |
| 1817 | }, | ||
| 1709 | }); | 1818 | }); |
| 1710 | 1819 | ||
| 1711 | if (Prism.languages.markup) { | 1820 | if (Prism.languages.markup) { |
| 1712 | Prism.languages.markup.tag.addInlined('script', 'javascript'); | 1821 | Prism.languages.markup.tag.addInlined("script", "javascript"); |
| 1713 | 1822 | ||
| 1714 | // add attribute support for all DOM events. | 1823 | // add attribute support for all DOM events. |
| 1715 | // https://developer.mozilla.org/en-US/docs/Web/Events#Standard_events | 1824 | // https://developer.mozilla.org/en-US/docs/Web/Events#Standard_events |
| 1716 | Prism.languages.markup.tag.addAttribute( | 1825 | Prism.languages.markup.tag.addAttribute( |
| 1717 | /on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source, | 1826 | /on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/ |
| 1718 | 'javascript' | 1827 | .source, |
| 1719 | ); | 1828 | "javascript", |
| 1829 | ); | ||
| 1720 | } | 1830 | } |
| 1721 | 1831 | ||
| 1722 | Prism.languages.js = Prism.languages.javascript; | 1832 | Prism.languages.js = Prism.languages.javascript; |
| 1723 | 1833 | ||
| 1724 | (function (Prism) { | 1834 | (function (Prism) { |
| 1725 | // $ set | grep '^[A-Z][^[:space:]]*=' | cut -d= -f1 | tr '\n' '|' | 1835 | // $ set | grep '^[A-Z][^[:space:]]*=' | cut -d= -f1 | tr '\n' '|' |
| 1726 | // + LC_ALL, RANDOM, REPLY, SECONDS. | 1836 | // + LC_ALL, RANDOM, REPLY, SECONDS. |
| 1727 | // + make sure PS1..4 are here as they are not always set, | 1837 | // + make sure PS1..4 are here as they are not always set, |
| 1728 | // - some useless things. | 1838 | // - some useless things. |
| 1729 | var envVars = '\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b'; | 1839 | var envVars = |
| 1730 | 1840 | "\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b"; | |
| 1731 | var commandAfterHeredoc = { | 1841 | |
| 1732 | pattern: /(^(["']?)\w+\2)[ \t]+\S.*/, | 1842 | var commandAfterHeredoc = { |
| 1733 | lookbehind: true, | 1843 | pattern: /(^(["']?)\w+\2)[ \t]+\S.*/, |
| 1734 | alias: 'punctuation', // this looks reasonably well in all themes | 1844 | lookbehind: true, |
| 1735 | inside: null // see below | 1845 | alias: "punctuation", // this looks reasonably well in all themes |
| 1736 | }; | 1846 | inside: null, // see below |
| 1737 | 1847 | }; | |
| 1738 | var insideString = { | 1848 | |
| 1739 | 'bash': commandAfterHeredoc, | 1849 | var insideString = { |
| 1740 | 'environment': { | 1850 | bash: commandAfterHeredoc, |
| 1741 | pattern: RegExp('\\$' + envVars), | 1851 | environment: { |
| 1742 | alias: 'constant' | 1852 | pattern: RegExp("\\$" + envVars), |
| 1743 | }, | 1853 | alias: "constant", |
| 1744 | 'variable': [ | 1854 | }, |
| 1745 | // [0]: Arithmetic Environment | 1855 | variable: [ |
| 1746 | { | 1856 | // [0]: Arithmetic Environment |
| 1747 | pattern: /\$?\(\([\s\S]+?\)\)/, | 1857 | { |
| 1748 | greedy: true, | 1858 | pattern: /\$?\(\([\s\S]+?\)\)/, |
| 1749 | inside: { | 1859 | greedy: true, |
| 1750 | // If there is a $ sign at the beginning highlight $(( and )) as variable | 1860 | inside: { |
| 1751 | 'variable': [ | 1861 | // If there is a $ sign at the beginning highlight $(( and )) as variable |
| 1752 | { | 1862 | variable: [ |
| 1753 | pattern: /(^\$\(\([\s\S]+)\)\)/, | 1863 | { |
| 1754 | lookbehind: true | 1864 | pattern: /(^\$\(\([\s\S]+)\)\)/, |
| 1755 | }, | 1865 | lookbehind: true, |
| 1756 | /^\$\(\(/ | 1866 | }, |
| 1757 | ], | 1867 | /^\$\(\(/, |
| 1758 | 'number': /\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/, | 1868 | ], |
| 1759 | // Operators according to https://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic | 1869 | number: /\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/, |
| 1760 | 'operator': /--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/, | 1870 | // Operators according to https://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic |
| 1761 | // If there is no $ sign at the beginning highlight (( and )) as punctuation | 1871 | operator: /--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/, |
| 1762 | 'punctuation': /\(\(?|\)\)?|,|;/ | 1872 | // If there is no $ sign at the beginning highlight (( and )) as punctuation |
| 1763 | } | 1873 | punctuation: /\(\(?|\)\)?|,|;/, |
| 1764 | }, | 1874 | }, |
| 1765 | // [1]: Command Substitution | 1875 | }, |
| 1766 | { | 1876 | // [1]: Command Substitution |
| 1767 | pattern: /\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/, | 1877 | { |
| 1768 | greedy: true, | 1878 | pattern: /\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/, |
| 1769 | inside: { | 1879 | greedy: true, |
| 1770 | 'variable': /^\$\(|^`|\)$|`$/ | 1880 | inside: { |
| 1771 | } | 1881 | variable: /^\$\(|^`|\)$|`$/, |
| 1772 | }, | 1882 | }, |
| 1773 | // [2]: Brace expansion | 1883 | }, |
| 1774 | { | 1884 | // [2]: Brace expansion |
| 1775 | pattern: /\$\{[^}]+\}/, | 1885 | { |
| 1776 | greedy: true, | 1886 | pattern: /\$\{[^}]+\}/, |
| 1777 | inside: { | 1887 | greedy: true, |
| 1778 | 'operator': /:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/, | 1888 | inside: { |
| 1779 | 'punctuation': /[\[\]]/, | 1889 | operator: /:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/, |
| 1780 | 'environment': { | 1890 | punctuation: /[\[\]]/, |
| 1781 | pattern: RegExp('(\\{)' + envVars), | 1891 | environment: { |
| 1782 | lookbehind: true, | 1892 | pattern: RegExp("(\\{)" + envVars), |
| 1783 | alias: 'constant' | 1893 | lookbehind: true, |
| 1784 | } | 1894 | alias: "constant", |
| 1785 | } | 1895 | }, |
| 1786 | }, | 1896 | }, |
| 1787 | /\$(?:\w+|[#?*!@$])/ | 1897 | }, |
| 1788 | ], | 1898 | /\$(?:\w+|[#?*!@$])/, |
| 1789 | // Escape sequences from echo and printf's manuals, and escaped quotes. | 1899 | ], |
| 1790 | 'entity': /\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/ | 1900 | // Escape sequences from echo and printf's manuals, and escaped quotes. |
| 1791 | }; | 1901 | entity: |
| 1792 | 1902 | /\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/, | |
| 1793 | Prism.languages.bash = { | 1903 | }; |
| 1794 | 'shebang': { | 1904 | |
| 1795 | pattern: /^#!\s*\/.*/, | 1905 | Prism.languages.bash = { |
| 1796 | alias: 'important' | 1906 | shebang: { |
| 1797 | }, | 1907 | pattern: /^#!\s*\/.*/, |
| 1798 | 'comment': { | 1908 | alias: "important", |
| 1799 | pattern: /(^|[^"{\\$])#.*/, | 1909 | }, |
| 1800 | lookbehind: true | 1910 | comment: { |
| 1801 | }, | 1911 | pattern: /(^|[^"{\\$])#.*/, |
| 1802 | 'function-name': [ | 1912 | lookbehind: true, |
| 1803 | // a) function foo { | 1913 | }, |
| 1804 | // b) foo() { | 1914 | "function-name": [ |
| 1805 | // c) function foo() { | 1915 | // a) function foo { |
| 1806 | // but not “foo {” | 1916 | // b) foo() { |
| 1807 | { | 1917 | // c) function foo() { |
| 1808 | // a) and c) | 1918 | // but not “foo {” |
| 1809 | pattern: /(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/, | 1919 | { |
| 1810 | lookbehind: true, | 1920 | // a) and c) |
| 1811 | alias: 'function' | 1921 | pattern: /(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/, |
| 1812 | }, | 1922 | lookbehind: true, |
| 1813 | { | 1923 | alias: "function", |
| 1814 | // b) | 1924 | }, |
| 1815 | pattern: /\b[\w-]+(?=\s*\(\s*\)\s*\{)/, | 1925 | { |
| 1816 | alias: 'function' | 1926 | // b) |
| 1817 | } | 1927 | pattern: /\b[\w-]+(?=\s*\(\s*\)\s*\{)/, |
| 1818 | ], | 1928 | alias: "function", |
| 1819 | // Highlight variable names as variables in for and select beginnings. | 1929 | }, |
| 1820 | 'for-or-select': { | 1930 | ], |
| 1821 | pattern: /(\b(?:for|select)\s+)\w+(?=\s+in\s)/, | 1931 | // Highlight variable names as variables in for and select beginnings. |
| 1822 | alias: 'variable', | 1932 | "for-or-select": { |
| 1823 | lookbehind: true | 1933 | pattern: /(\b(?:for|select)\s+)\w+(?=\s+in\s)/, |
| 1824 | }, | 1934 | alias: "variable", |
| 1825 | // Highlight variable names as variables in the left-hand part | 1935 | lookbehind: true, |
| 1826 | // of assignments (“=” and “+=”). | 1936 | }, |
| 1827 | 'assign-left': { | 1937 | // Highlight variable names as variables in the left-hand part |
| 1828 | pattern: /(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/, | 1938 | // of assignments (“=” and “+=”). |
| 1829 | inside: { | 1939 | "assign-left": { |
| 1830 | 'environment': { | 1940 | pattern: /(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/, |
| 1831 | pattern: RegExp('(^|[\\s;|&]|[<>]\\()' + envVars), | 1941 | inside: { |
| 1832 | lookbehind: true, | 1942 | environment: { |
| 1833 | alias: 'constant' | 1943 | pattern: RegExp("(^|[\\s;|&]|[<>]\\()" + envVars), |
| 1834 | } | 1944 | lookbehind: true, |
| 1835 | }, | 1945 | alias: "constant", |
| 1836 | alias: 'variable', | 1946 | }, |
| 1837 | lookbehind: true | 1947 | }, |
| 1838 | }, | 1948 | alias: "variable", |
| 1839 | // Highlight parameter names as variables | 1949 | lookbehind: true, |
| 1840 | 'parameter': { | 1950 | }, |
| 1841 | pattern: /(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/, | 1951 | // Highlight parameter names as variables |
| 1842 | alias: 'variable', | 1952 | parameter: { |
| 1843 | lookbehind: true | 1953 | pattern: /(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/, |
| 1844 | }, | 1954 | alias: "variable", |
| 1845 | 'string': [ | 1955 | lookbehind: true, |
| 1846 | // Support for Here-documents https://en.wikipedia.org/wiki/Here_document | 1956 | }, |
| 1847 | { | 1957 | string: [ |
| 1848 | pattern: /((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/, | 1958 | // Support for Here-documents https://en.wikipedia.org/wiki/Here_document |
| 1849 | lookbehind: true, | 1959 | { |
| 1850 | greedy: true, | 1960 | pattern: /((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/, |
| 1851 | inside: insideString | 1961 | lookbehind: true, |
| 1852 | }, | 1962 | greedy: true, |
| 1853 | // Here-document with quotes around the tag | 1963 | inside: insideString, |
| 1854 | // → No expansion (so no “inside”). | 1964 | }, |
| 1855 | { | 1965 | // Here-document with quotes around the tag |
| 1856 | pattern: /((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/, | 1966 | // → No expansion (so no “inside”). |
| 1857 | lookbehind: true, | 1967 | { |
| 1858 | greedy: true, | 1968 | pattern: /((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/, |
| 1859 | inside: { | 1969 | lookbehind: true, |
| 1860 | 'bash': commandAfterHeredoc | 1970 | greedy: true, |
| 1861 | } | 1971 | inside: { |
| 1862 | }, | 1972 | bash: commandAfterHeredoc, |
| 1863 | // “Normal” string | 1973 | }, |
| 1864 | { | 1974 | }, |
| 1865 | // https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html | 1975 | // “Normal” string |
| 1866 | pattern: /(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/, | 1976 | { |
| 1867 | lookbehind: true, | 1977 | // https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html |
| 1868 | greedy: true, | 1978 | pattern: |
| 1869 | inside: insideString | 1979 | /(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/, |
| 1870 | }, | 1980 | lookbehind: true, |
| 1871 | { | 1981 | greedy: true, |
| 1872 | // https://www.gnu.org/software/bash/manual/html_node/Single-Quotes.html | 1982 | inside: insideString, |
| 1873 | pattern: /(^|[^$\\])'[^']*'/, | 1983 | }, |
| 1874 | lookbehind: true, | 1984 | { |
| 1875 | greedy: true | 1985 | // https://www.gnu.org/software/bash/manual/html_node/Single-Quotes.html |
| 1876 | }, | 1986 | pattern: /(^|[^$\\])'[^']*'/, |
| 1877 | { | 1987 | lookbehind: true, |
| 1878 | // https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html | 1988 | greedy: true, |
| 1879 | pattern: /\$'(?:[^'\\]|\\[\s\S])*'/, | 1989 | }, |
| 1880 | greedy: true, | 1990 | { |
| 1881 | inside: { | 1991 | // https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html |
| 1882 | 'entity': insideString.entity | 1992 | pattern: /\$'(?:[^'\\]|\\[\s\S])*'/, |
| 1883 | } | 1993 | greedy: true, |
| 1884 | } | 1994 | inside: { |
| 1885 | ], | 1995 | entity: insideString.entity, |
| 1886 | 'environment': { | 1996 | }, |
| 1887 | pattern: RegExp('\\$?' + envVars), | 1997 | }, |
| 1888 | alias: 'constant' | 1998 | ], |
| 1889 | }, | 1999 | environment: { |
| 1890 | 'variable': insideString.variable, | 2000 | pattern: RegExp("\\$?" + envVars), |
| 1891 | 'function': { | 2001 | alias: "constant", |
| 1892 | pattern: /(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/, | 2002 | }, |
| 1893 | lookbehind: true | 2003 | variable: insideString.variable, |
| 1894 | }, | 2004 | function: { |
| 1895 | 'keyword': { | 2005 | pattern: |
| 1896 | pattern: /(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/, | 2006 | /(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/, |
| 1897 | lookbehind: true | 2007 | lookbehind: true, |
| 1898 | }, | 2008 | }, |
| 1899 | // https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html | 2009 | keyword: { |
| 1900 | 'builtin': { | 2010 | pattern: |
| 1901 | pattern: /(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/, | 2011 | /(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/, |
| 1902 | lookbehind: true, | 2012 | lookbehind: true, |
| 1903 | // Alias added to make those easier to distinguish from strings. | 2013 | }, |
| 1904 | alias: 'class-name' | 2014 | // https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html |
| 1905 | }, | 2015 | builtin: { |
| 1906 | 'boolean': { | 2016 | pattern: |
| 1907 | pattern: /(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/, | 2017 | /(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/, |
| 1908 | lookbehind: true | 2018 | lookbehind: true, |
| 1909 | }, | 2019 | // Alias added to make those easier to distinguish from strings. |
| 1910 | 'file-descriptor': { | 2020 | alias: "class-name", |
| 1911 | pattern: /\B&\d\b/, | 2021 | }, |
| 1912 | alias: 'important' | 2022 | boolean: { |
| 1913 | }, | 2023 | pattern: /(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/, |
| 1914 | 'operator': { | 2024 | lookbehind: true, |
| 1915 | // Lots of redirections here, but not just that. | 2025 | }, |
| 1916 | pattern: /\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/, | 2026 | "file-descriptor": { |
| 1917 | inside: { | 2027 | pattern: /\B&\d\b/, |
| 1918 | 'file-descriptor': { | 2028 | alias: "important", |
| 1919 | pattern: /^\d/, | 2029 | }, |
| 1920 | alias: 'important' | 2030 | operator: { |
| 1921 | } | 2031 | // Lots of redirections here, but not just that. |
| 1922 | } | 2032 | pattern: |
| 1923 | }, | 2033 | /\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/, |
| 1924 | 'punctuation': /\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/, | 2034 | inside: { |
| 1925 | 'number': { | 2035 | "file-descriptor": { |
| 1926 | pattern: /(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/, | 2036 | pattern: /^\d/, |
| 1927 | lookbehind: true | 2037 | alias: "important", |
| 1928 | } | 2038 | }, |
| 1929 | }; | 2039 | }, |
| 1930 | 2040 | }, | |
| 1931 | commandAfterHeredoc.inside = Prism.languages.bash; | 2041 | punctuation: /\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/, |
| 1932 | 2042 | number: { | |
| 1933 | /* Patterns in command substitution. */ | 2043 | pattern: /(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/, |
| 1934 | var toBeCopied = [ | 2044 | lookbehind: true, |
| 1935 | 'comment', | 2045 | }, |
| 1936 | 'function-name', | 2046 | }; |
| 1937 | 'for-or-select', | 2047 | |
| 1938 | 'assign-left', | 2048 | commandAfterHeredoc.inside = Prism.languages.bash; |
| 1939 | 'parameter', | 2049 | |
| 1940 | 'string', | 2050 | /* Patterns in command substitution. */ |
| 1941 | 'environment', | 2051 | var toBeCopied = [ |
| 1942 | 'function', | 2052 | "comment", |
| 1943 | 'keyword', | 2053 | "function-name", |
| 1944 | 'builtin', | 2054 | "for-or-select", |
| 1945 | 'boolean', | 2055 | "assign-left", |
| 1946 | 'file-descriptor', | 2056 | "parameter", |
| 1947 | 'operator', | 2057 | "string", |
| 1948 | 'punctuation', | 2058 | "environment", |
| 1949 | 'number' | 2059 | "function", |
| 1950 | ]; | 2060 | "keyword", |
| 1951 | var inside = insideString.variable[1].inside; | 2061 | "builtin", |
| 1952 | for (var i = 0; i < toBeCopied.length; i++) { | 2062 | "boolean", |
| 1953 | inside[toBeCopied[i]] = Prism.languages.bash[toBeCopied[i]]; | 2063 | "file-descriptor", |
| 1954 | } | 2064 | "operator", |
| 1955 | 2065 | "punctuation", | |
| 1956 | Prism.languages.sh = Prism.languages.bash; | 2066 | "number", |
| 1957 | Prism.languages.shell = Prism.languages.bash; | 2067 | ]; |
| 1958 | }(Prism)); | 2068 | var inside = insideString.variable[1].inside; |
| 1959 | 2069 | for (var i = 0; i < toBeCopied.length; i++) { | |
| 1960 | Prism.languages.c = Prism.languages.extend('clike', { | 2070 | inside[toBeCopied[i]] = Prism.languages.bash[toBeCopied[i]]; |
| 1961 | 'comment': { | 2071 | } |
| 1962 | pattern: /\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/, | 2072 | |
| 1963 | greedy: true | 2073 | Prism.languages.sh = Prism.languages.bash; |
| 1964 | }, | 2074 | Prism.languages.shell = Prism.languages.bash; |
| 1965 | 'string': { | 2075 | })(Prism); |
| 1966 | // https://en.cppreference.com/w/c/language/string_literal | 2076 | |
| 1967 | pattern: /"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/, | 2077 | Prism.languages.c = Prism.languages.extend("clike", { |
| 1968 | greedy: true | 2078 | comment: { |
| 1969 | }, | 2079 | pattern: |
| 1970 | 'class-name': { | 2080 | /\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/, |
| 1971 | pattern: /(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/, | 2081 | greedy: true, |
| 1972 | lookbehind: true | 2082 | }, |
| 1973 | }, | 2083 | string: { |
| 1974 | 'keyword': /\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/, | 2084 | // https://en.cppreference.com/w/c/language/string_literal |
| 1975 | 'function': /\b[a-z_]\w*(?=\s*\()/i, | 2085 | pattern: /"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/, |
| 1976 | 'number': /(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i, | 2086 | greedy: true, |
| 1977 | 'operator': />>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/ | 2087 | }, |
| 2088 | "class-name": { | ||
| 2089 | pattern: | ||
| 2090 | /(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/, | ||
| 2091 | lookbehind: true, | ||
| 2092 | }, | ||
| 2093 | keyword: | ||
| 2094 | /\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/, | ||
| 2095 | function: /\b[a-z_]\w*(?=\s*\()/i, | ||
| 2096 | number: | ||
| 2097 | /(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i, | ||
| 2098 | operator: />>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/, | ||
| 1978 | }); | 2099 | }); |
| 1979 | 2100 | ||
| 1980 | Prism.languages.insertBefore('c', 'string', { | 2101 | Prism.languages.insertBefore("c", "string", { |
| 1981 | 'char': { | 2102 | char: { |
| 1982 | // https://en.cppreference.com/w/c/language/character_constant | 2103 | // https://en.cppreference.com/w/c/language/character_constant |
| 1983 | pattern: /'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/, | 2104 | pattern: /'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/, |
| 1984 | greedy: true | 2105 | greedy: true, |
| 1985 | } | 2106 | }, |
| 1986 | }); | 2107 | }); |
| 1987 | 2108 | ||
| 1988 | Prism.languages.insertBefore('c', 'string', { | 2109 | Prism.languages.insertBefore("c", "string", { |
| 1989 | 'macro': { | 2110 | macro: { |
| 1990 | // allow for multiline macro definitions | 2111 | // allow for multiline macro definitions |
| 1991 | // spaces after the # character compile fine with gcc | 2112 | // spaces after the # character compile fine with gcc |
| 1992 | pattern: /(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im, | 2113 | pattern: |
| 1993 | lookbehind: true, | 2114 | /(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im, |
| 1994 | greedy: true, | 2115 | lookbehind: true, |
| 1995 | alias: 'property', | 2116 | greedy: true, |
| 1996 | inside: { | 2117 | alias: "property", |
| 1997 | 'string': [ | 2118 | inside: { |
| 1998 | { | 2119 | string: [ |
| 1999 | // highlight the path of the include statement as a string | 2120 | { |
| 2000 | pattern: /^(#\s*include\s*)<[^>]+>/, | 2121 | // highlight the path of the include statement as a string |
| 2001 | lookbehind: true | 2122 | pattern: /^(#\s*include\s*)<[^>]+>/, |
| 2002 | }, | 2123 | lookbehind: true, |
| 2003 | Prism.languages.c['string'] | 2124 | }, |
| 2004 | ], | 2125 | Prism.languages.c["string"], |
| 2005 | 'char': Prism.languages.c['char'], | 2126 | ], |
| 2006 | 'comment': Prism.languages.c['comment'], | 2127 | char: Prism.languages.c["char"], |
| 2007 | 'macro-name': [ | 2128 | comment: Prism.languages.c["comment"], |
| 2008 | { | 2129 | "macro-name": [ |
| 2009 | pattern: /(^#\s*define\s+)\w+\b(?!\()/i, | 2130 | { |
| 2010 | lookbehind: true | 2131 | pattern: /(^#\s*define\s+)\w+\b(?!\()/i, |
| 2011 | }, | 2132 | lookbehind: true, |
| 2012 | { | 2133 | }, |
| 2013 | pattern: /(^#\s*define\s+)\w+\b(?=\()/i, | 2134 | { |
| 2014 | lookbehind: true, | 2135 | pattern: /(^#\s*define\s+)\w+\b(?=\()/i, |
| 2015 | alias: 'function' | 2136 | lookbehind: true, |
| 2016 | } | 2137 | alias: "function", |
| 2017 | ], | 2138 | }, |
| 2018 | // highlight macro directives as keywords | 2139 | ], |
| 2019 | 'directive': { | 2140 | // highlight macro directives as keywords |
| 2020 | pattern: /^(#\s*)[a-z]+/, | 2141 | directive: { |
| 2021 | lookbehind: true, | 2142 | pattern: /^(#\s*)[a-z]+/, |
| 2022 | alias: 'keyword' | 2143 | lookbehind: true, |
| 2023 | }, | 2144 | alias: "keyword", |
| 2024 | 'directive-hash': /^#/, | 2145 | }, |
| 2025 | 'punctuation': /##|\\(?=[\r\n])/, | 2146 | "directive-hash": /^#/, |
| 2026 | 'expression': { | 2147 | punctuation: /##|\\(?=[\r\n])/, |
| 2027 | pattern: /\S[\s\S]*/, | 2148 | expression: { |
| 2028 | inside: Prism.languages.c | 2149 | pattern: /\S[\s\S]*/, |
| 2029 | } | 2150 | inside: Prism.languages.c, |
| 2030 | } | 2151 | }, |
| 2031 | } | 2152 | }, |
| 2153 | }, | ||
| 2032 | }); | 2154 | }); |
| 2033 | 2155 | ||
| 2034 | Prism.languages.insertBefore('c', 'function', { | 2156 | Prism.languages.insertBefore("c", "function", { |
| 2035 | // highlight predefined macros as constants | 2157 | // highlight predefined macros as constants |
| 2036 | 'constant': /\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/ | 2158 | constant: |
| 2159 | /\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/, | ||
| 2037 | }); | 2160 | }); |
| 2038 | 2161 | ||
| 2039 | delete Prism.languages.c['boolean']; | 2162 | delete Prism.languages.c["boolean"]; |
| 2040 | 2163 | ||
| 2041 | (function (Prism) { | 2164 | (function (Prism) { |
| 2042 | 2165 | var keyword = | |
| 2043 | var keyword = /\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/; | 2166 | /\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/; |
| 2044 | var modName = /\b(?!<keyword>)\w+(?:\s*\.\s*\w+)*\b/.source.replace(/<keyword>/g, function () { return keyword.source; }); | 2167 | var modName = /\b(?!<keyword>)\w+(?:\s*\.\s*\w+)*\b/.source.replace( |
| 2045 | 2168 | /<keyword>/g, | |
| 2046 | Prism.languages.cpp = Prism.languages.extend('c', { | 2169 | function () { |
| 2047 | 'class-name': [ | 2170 | return keyword.source; |
| 2048 | { | 2171 | }, |
| 2049 | pattern: RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!<keyword>)\w+/.source | 2172 | ); |
| 2050 | .replace(/<keyword>/g, function () { return keyword.source; })), | 2173 | |
| 2051 | lookbehind: true | 2174 | Prism.languages.cpp = Prism.languages.extend("c", { |
| 2052 | }, | 2175 | "class-name": [ |
| 2053 | // This is intended to capture the class name of method implementations like: | 2176 | { |
| 2054 | // void foo::bar() const {} | 2177 | pattern: RegExp( |
| 2055 | // However! The `foo` in the above example could also be a namespace, so we only capture the class name if | 2178 | /(\b(?:class|concept|enum|struct|typename)\s+)(?!<keyword>)\w+/.source.replace( |
| 2056 | // it starts with an uppercase letter. This approximation should give decent results. | 2179 | /<keyword>/g, |
| 2057 | /\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/, | 2180 | function () { |
| 2058 | // This will capture the class name before destructors like: | 2181 | return keyword.source; |
| 2059 | // Foo::~Foo() {} | 2182 | }, |
| 2060 | /\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i, | 2183 | ), |
| 2061 | // This also intends to capture the class name of method implementations but here the class has template | 2184 | ), |
| 2062 | // parameters, so it can't be a namespace (until C++ adds generic namespaces). | 2185 | lookbehind: true, |
| 2063 | /\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/ | 2186 | }, |
| 2064 | ], | 2187 | // This is intended to capture the class name of method implementations like: |
| 2065 | 'keyword': keyword, | 2188 | // void foo::bar() const {} |
| 2066 | 'number': { | 2189 | // However! The `foo` in the above example could also be a namespace, so we only capture the class name if |
| 2067 | pattern: /(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i, | 2190 | // it starts with an uppercase letter. This approximation should give decent results. |
| 2068 | greedy: true | 2191 | /\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/, |
| 2069 | }, | 2192 | // This will capture the class name before destructors like: |
| 2070 | 'operator': />>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/, | 2193 | // Foo::~Foo() {} |
| 2071 | 'boolean': /\b(?:false|true)\b/ | 2194 | /\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i, |
| 2072 | }); | 2195 | // This also intends to capture the class name of method implementations but here the class has template |
| 2073 | 2196 | // parameters, so it can't be a namespace (until C++ adds generic namespaces). | |
| 2074 | Prism.languages.insertBefore('cpp', 'string', { | 2197 | /\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/, |
| 2075 | 'module': { | 2198 | ], |
| 2076 | // https://en.cppreference.com/w/cpp/language/modules | 2199 | keyword: keyword, |
| 2077 | pattern: RegExp( | 2200 | number: { |
| 2078 | /(\b(?:import|module)\s+)/.source + | 2201 | pattern: |
| 2079 | '(?:' + | 2202 | /(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i, |
| 2080 | // header-name | 2203 | greedy: true, |
| 2081 | /"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source + | 2204 | }, |
| 2082 | '|' + | 2205 | operator: |
| 2083 | // module name or partition or both | 2206 | />>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/, |
| 2084 | /<mod-name>(?:\s*:\s*<mod-name>)?|:\s*<mod-name>/.source.replace(/<mod-name>/g, function () { return modName; }) + | 2207 | boolean: /\b(?:false|true)\b/, |
| 2085 | ')' | 2208 | }); |
| 2086 | ), | 2209 | |
| 2087 | lookbehind: true, | 2210 | Prism.languages.insertBefore("cpp", "string", { |
| 2088 | greedy: true, | 2211 | module: { |
| 2089 | inside: { | 2212 | // https://en.cppreference.com/w/cpp/language/modules |
| 2090 | 'string': /^[<"][\s\S]+/, | 2213 | pattern: RegExp( |
| 2091 | 'operator': /:/, | 2214 | /(\b(?:import|module)\s+)/.source + |
| 2092 | 'punctuation': /\./ | 2215 | "(?:" + |
| 2093 | } | 2216 | // header-name |
| 2094 | }, | 2217 | /"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source + |
| 2095 | 'raw-string': { | 2218 | "|" + |
| 2096 | pattern: /R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/, | 2219 | // module name or partition or both |
| 2097 | alias: 'string', | 2220 | /<mod-name>(?:\s*:\s*<mod-name>)?|:\s*<mod-name>/.source.replace( |
| 2098 | greedy: true | 2221 | /<mod-name>/g, |
| 2099 | } | 2222 | function () { |
| 2100 | }); | 2223 | return modName; |
| 2101 | 2224 | }, | |
| 2102 | Prism.languages.insertBefore('cpp', 'keyword', { | 2225 | ) + |
| 2103 | 'generic-function': { | 2226 | ")", |
| 2104 | pattern: /\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i, | 2227 | ), |
| 2105 | inside: { | 2228 | lookbehind: true, |
| 2106 | 'function': /^\w+/, | 2229 | greedy: true, |
| 2107 | 'generic': { | 2230 | inside: { |
| 2108 | pattern: /<[\s\S]+/, | 2231 | string: /^[<"][\s\S]+/, |
| 2109 | alias: 'class-name', | 2232 | operator: /:/, |
| 2110 | inside: Prism.languages.cpp | 2233 | punctuation: /\./, |
| 2111 | } | 2234 | }, |
| 2112 | } | 2235 | }, |
| 2113 | } | 2236 | "raw-string": { |
| 2114 | }); | 2237 | pattern: /R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/, |
| 2115 | 2238 | alias: "string", | |
| 2116 | Prism.languages.insertBefore('cpp', 'operator', { | 2239 | greedy: true, |
| 2117 | 'double-colon': { | 2240 | }, |
| 2118 | pattern: /::/, | 2241 | }); |
| 2119 | alias: 'punctuation' | 2242 | |
| 2120 | } | 2243 | Prism.languages.insertBefore("cpp", "keyword", { |
| 2121 | }); | 2244 | "generic-function": { |
| 2122 | 2245 | pattern: /\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i, | |
| 2123 | Prism.languages.insertBefore('cpp', 'class-name', { | 2246 | inside: { |
| 2124 | // the base clause is an optional list of parent classes | 2247 | function: /^\w+/, |
| 2125 | // https://en.cppreference.com/w/cpp/language/class | 2248 | generic: { |
| 2126 | 'base-clause': { | 2249 | pattern: /<[\s\S]+/, |
| 2127 | pattern: /(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/, | 2250 | alias: "class-name", |
| 2128 | lookbehind: true, | 2251 | inside: Prism.languages.cpp, |
| 2129 | greedy: true, | 2252 | }, |
| 2130 | inside: Prism.languages.extend('cpp', {}) | 2253 | }, |
| 2131 | } | 2254 | }, |
| 2132 | }); | 2255 | }); |
| 2133 | 2256 | ||
| 2134 | Prism.languages.insertBefore('inside', 'double-colon', { | 2257 | Prism.languages.insertBefore("cpp", "operator", { |
| 2135 | // All untokenized words that are not namespaces should be class names | 2258 | "double-colon": { |
| 2136 | 'class-name': /\b[a-z_]\w*\b(?!\s*::)/i | 2259 | pattern: /::/, |
| 2137 | }, Prism.languages.cpp['base-clause']); | 2260 | alias: "punctuation", |
| 2138 | 2261 | }, | |
| 2139 | }(Prism)); | 2262 | }); |
| 2263 | |||
| 2264 | Prism.languages.insertBefore("cpp", "class-name", { | ||
| 2265 | // the base clause is an optional list of parent classes | ||
| 2266 | // https://en.cppreference.com/w/cpp/language/class | ||
| 2267 | "base-clause": { | ||
| 2268 | pattern: | ||
| 2269 | /(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/, | ||
| 2270 | lookbehind: true, | ||
| 2271 | greedy: true, | ||
| 2272 | inside: Prism.languages.extend("cpp", {}), | ||
| 2273 | }, | ||
| 2274 | }); | ||
| 2275 | |||
| 2276 | Prism.languages.insertBefore( | ||
| 2277 | "inside", | ||
| 2278 | "double-colon", | ||
| 2279 | { | ||
| 2280 | // All untokenized words that are not namespaces should be class names | ||
| 2281 | "class-name": /\b[a-z_]\w*\b(?!\s*::)/i, | ||
| 2282 | }, | ||
| 2283 | Prism.languages.cpp["base-clause"], | ||
| 2284 | ); | ||
| 2285 | })(Prism); | ||
| 2140 | 2286 | ||
| 2141 | Prism.languages.cmake = { | 2287 | Prism.languages.cmake = { |
| 2142 | 'comment': /#.*/, | 2288 | comment: /#.*/, |
| 2143 | 'string': { | 2289 | string: { |
| 2144 | pattern: /"(?:[^\\"]|\\.)*"/, | 2290 | pattern: /"(?:[^\\"]|\\.)*"/, |
| 2145 | greedy: true, | 2291 | greedy: true, |
| 2146 | inside: { | 2292 | inside: { |
| 2147 | 'interpolation': { | 2293 | interpolation: { |
| 2148 | pattern: /\$\{(?:[^{}$]|\$\{[^{}$]*\})*\}/, | 2294 | pattern: /\$\{(?:[^{}$]|\$\{[^{}$]*\})*\}/, |
| 2149 | inside: { | 2295 | inside: { |
| 2150 | 'punctuation': /\$\{|\}/, | 2296 | punctuation: /\$\{|\}/, |
| 2151 | 'variable': /\w+/ | 2297 | variable: /\w+/, |
| 2152 | } | 2298 | }, |
| 2153 | } | 2299 | }, |
| 2154 | } | 2300 | }, |
| 2155 | }, | 2301 | }, |
| 2156 | 'variable': /\b(?:CMAKE_\w+|\w+_(?:(?:BINARY|SOURCE)_DIR|DESCRIPTION|HOMEPAGE_URL|ROOT|VERSION(?:_MAJOR|_MINOR|_PATCH|_TWEAK)?)|(?:ANDROID|APPLE|BORLAND|BUILD_SHARED_LIBS|CACHE|CPACK_(?:ABSOLUTE_DESTINATION_FILES|COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY|ERROR_ON_ABSOLUTE_INSTALL_DESTINATION|INCLUDE_TOPLEVEL_DIRECTORY|INSTALL_DEFAULT_DIRECTORY_PERMISSIONS|INSTALL_SCRIPT|PACKAGING_INSTALL_PREFIX|SET_DESTDIR|WARN_ON_ABSOLUTE_INSTALL_DESTINATION)|CTEST_(?:BINARY_DIRECTORY|BUILD_COMMAND|BUILD_NAME|BZR_COMMAND|BZR_UPDATE_OPTIONS|CHANGE_ID|CHECKOUT_COMMAND|CONFIGURATION_TYPE|CONFIGURE_COMMAND|COVERAGE_COMMAND|COVERAGE_EXTRA_FLAGS|CURL_OPTIONS|CUSTOM_(?:COVERAGE_EXCLUDE|ERROR_EXCEPTION|ERROR_MATCH|ERROR_POST_CONTEXT|ERROR_PRE_CONTEXT|MAXIMUM_FAILED_TEST_OUTPUT_SIZE|MAXIMUM_NUMBER_OF_(?:ERRORS|WARNINGS)|MAXIMUM_PASSED_TEST_OUTPUT_SIZE|MEMCHECK_IGNORE|POST_MEMCHECK|POST_TEST|PRE_MEMCHECK|PRE_TEST|TESTS_IGNORE|WARNING_EXCEPTION|WARNING_MATCH)|CVS_CHECKOUT|CVS_COMMAND|CVS_UPDATE_OPTIONS|DROP_LOCATION|DROP_METHOD|DROP_SITE|DROP_SITE_CDASH|DROP_SITE_PASSWORD|DROP_SITE_USER|EXTRA_COVERAGE_GLOB|GIT_COMMAND|GIT_INIT_SUBMODULES|GIT_UPDATE_CUSTOM|GIT_UPDATE_OPTIONS|HG_COMMAND|HG_UPDATE_OPTIONS|LABELS_FOR_SUBPROJECTS|MEMORYCHECK_(?:COMMAND|COMMAND_OPTIONS|SANITIZER_OPTIONS|SUPPRESSIONS_FILE|TYPE)|NIGHTLY_START_TIME|P4_CLIENT|P4_COMMAND|P4_OPTIONS|P4_UPDATE_OPTIONS|RUN_CURRENT_SCRIPT|SCP_COMMAND|SITE|SOURCE_DIRECTORY|SUBMIT_URL|SVN_COMMAND|SVN_OPTIONS|SVN_UPDATE_OPTIONS|TEST_LOAD|TEST_TIMEOUT|TRIGGER_SITE|UPDATE_COMMAND|UPDATE_OPTIONS|UPDATE_VERSION_ONLY|USE_LAUNCHERS)|CYGWIN|ENV|EXECUTABLE_OUTPUT_PATH|GHS-MULTI|IOS|LIBRARY_OUTPUT_PATH|MINGW|MSVC(?:10|11|12|14|60|70|71|80|90|_IDE|_TOOLSET_VERSION|_VERSION)?|MSYS|PROJECT_NAME|UNIX|WIN32|WINCE|WINDOWS_PHONE|WINDOWS_STORE|XCODE))\b/, | 2302 | variable: |
| 2157 | 'property': /\b(?:cxx_\w+|(?:ARCHIVE_OUTPUT_(?:DIRECTORY|NAME)|COMPILE_DEFINITIONS|COMPILE_PDB_NAME|COMPILE_PDB_OUTPUT_DIRECTORY|EXCLUDE_FROM_DEFAULT_BUILD|IMPORTED_(?:IMPLIB|LIBNAME|LINK_DEPENDENT_LIBRARIES|LINK_INTERFACE_LANGUAGES|LINK_INTERFACE_LIBRARIES|LINK_INTERFACE_MULTIPLICITY|LOCATION|NO_SONAME|OBJECTS|SONAME)|INTERPROCEDURAL_OPTIMIZATION|LIBRARY_OUTPUT_DIRECTORY|LIBRARY_OUTPUT_NAME|LINK_FLAGS|LINK_INTERFACE_LIBRARIES|LINK_INTERFACE_MULTIPLICITY|LOCATION|MAP_IMPORTED_CONFIG|OSX_ARCHITECTURES|OUTPUT_NAME|PDB_NAME|PDB_OUTPUT_DIRECTORY|RUNTIME_OUTPUT_DIRECTORY|RUNTIME_OUTPUT_NAME|STATIC_LIBRARY_FLAGS|VS_CSHARP|VS_DOTNET_REFERENCEPROP|VS_DOTNET_REFERENCE|VS_GLOBAL_SECTION_POST|VS_GLOBAL_SECTION_PRE|VS_GLOBAL|XCODE_ATTRIBUTE)_\w+|\w+_(?:CLANG_TIDY|COMPILER_LAUNCHER|CPPCHECK|CPPLINT|INCLUDE_WHAT_YOU_USE|OUTPUT_NAME|POSTFIX|VISIBILITY_PRESET)|ABSTRACT|ADDITIONAL_MAKE_CLEAN_FILES|ADVANCED|ALIASED_TARGET|ALLOW_DUPLICATE_CUSTOM_TARGETS|ANDROID_(?:ANT_ADDITIONAL_OPTIONS|API|API_MIN|ARCH|ASSETS_DIRECTORIES|GUI|JAR_DEPENDENCIES|NATIVE_LIB_DEPENDENCIES|NATIVE_LIB_DIRECTORIES|PROCESS_MAX|PROGUARD|PROGUARD_CONFIG_PATH|SECURE_PROPS_PATH|SKIP_ANT_STEP|STL_TYPE)|ARCHIVE_OUTPUT_DIRECTORY|ATTACHED_FILES|ATTACHED_FILES_ON_FAIL|AUTOGEN_(?:BUILD_DIR|ORIGIN_DEPENDS|PARALLEL|SOURCE_GROUP|TARGETS_FOLDER|TARGET_DEPENDS)|AUTOMOC|AUTOMOC_(?:COMPILER_PREDEFINES|DEPEND_FILTERS|EXECUTABLE|MACRO_NAMES|MOC_OPTIONS|SOURCE_GROUP|TARGETS_FOLDER)|AUTORCC|AUTORCC_EXECUTABLE|AUTORCC_OPTIONS|AUTORCC_SOURCE_GROUP|AUTOUIC|AUTOUIC_EXECUTABLE|AUTOUIC_OPTIONS|AUTOUIC_SEARCH_PATHS|BINARY_DIR|BUILDSYSTEM_TARGETS|BUILD_RPATH|BUILD_RPATH_USE_ORIGIN|BUILD_WITH_INSTALL_NAME_DIR|BUILD_WITH_INSTALL_RPATH|BUNDLE|BUNDLE_EXTENSION|CACHE_VARIABLES|CLEAN_NO_CUSTOM|COMMON_LANGUAGE_RUNTIME|COMPATIBLE_INTERFACE_(?:BOOL|NUMBER_MAX|NUMBER_MIN|STRING)|COMPILE_(?:DEFINITIONS|FEATURES|FLAGS|OPTIONS|PDB_NAME|PDB_OUTPUT_DIRECTORY)|COST|CPACK_DESKTOP_SHORTCUTS|CPACK_NEVER_OVERWRITE|CPACK_PERMANENT|CPACK_STARTUP_SHORTCUTS|CPACK_START_MENU_SHORTCUTS|CPACK_WIX_ACL|CROSSCOMPILING_EMULATOR|CUDA_EXTENSIONS|CUDA_PTX_COMPILATION|CUDA_RESOLVE_DEVICE_SYMBOLS|CUDA_SEPARABLE_COMPILATION|CUDA_STANDARD|CUDA_STANDARD_REQUIRED|CXX_EXTENSIONS|CXX_STANDARD|CXX_STANDARD_REQUIRED|C_EXTENSIONS|C_STANDARD|C_STANDARD_REQUIRED|DEBUG_CONFIGURATIONS|DEFINE_SYMBOL|DEFINITIONS|DEPENDS|DEPLOYMENT_ADDITIONAL_FILES|DEPLOYMENT_REMOTE_DIRECTORY|DISABLED|DISABLED_FEATURES|ECLIPSE_EXTRA_CPROJECT_CONTENTS|ECLIPSE_EXTRA_NATURES|ENABLED_FEATURES|ENABLED_LANGUAGES|ENABLE_EXPORTS|ENVIRONMENT|EXCLUDE_FROM_ALL|EXCLUDE_FROM_DEFAULT_BUILD|EXPORT_NAME|EXPORT_PROPERTIES|EXTERNAL_OBJECT|EchoString|FAIL_REGULAR_EXPRESSION|FIND_LIBRARY_USE_LIB32_PATHS|FIND_LIBRARY_USE_LIB64_PATHS|FIND_LIBRARY_USE_LIBX32_PATHS|FIND_LIBRARY_USE_OPENBSD_VERSIONING|FIXTURES_CLEANUP|FIXTURES_REQUIRED|FIXTURES_SETUP|FOLDER|FRAMEWORK|Fortran_FORMAT|Fortran_MODULE_DIRECTORY|GENERATED|GENERATOR_FILE_NAME|GENERATOR_IS_MULTI_CONFIG|GHS_INTEGRITY_APP|GHS_NO_SOURCE_GROUP_FILE|GLOBAL_DEPENDS_DEBUG_MODE|GLOBAL_DEPENDS_NO_CYCLES|GNUtoMS|HAS_CXX|HEADER_FILE_ONLY|HELPSTRING|IMPLICIT_DEPENDS_INCLUDE_TRANSFORM|IMPORTED|IMPORTED_(?:COMMON_LANGUAGE_RUNTIME|CONFIGURATIONS|GLOBAL|IMPLIB|LIBNAME|LINK_DEPENDENT_LIBRARIES|LINK_INTERFACE_(?:LANGUAGES|LIBRARIES|MULTIPLICITY)|LOCATION|NO_SONAME|OBJECTS|SONAME)|IMPORT_PREFIX|IMPORT_SUFFIX|INCLUDE_DIRECTORIES|INCLUDE_REGULAR_EXPRESSION|INSTALL_NAME_DIR|INSTALL_RPATH|INSTALL_RPATH_USE_LINK_PATH|INTERFACE_(?:AUTOUIC_OPTIONS|COMPILE_DEFINITIONS|COMPILE_FEATURES|COMPILE_OPTIONS|INCLUDE_DIRECTORIES|LINK_DEPENDS|LINK_DIRECTORIES|LINK_LIBRARIES|LINK_OPTIONS|POSITION_INDEPENDENT_CODE|SOURCES|SYSTEM_INCLUDE_DIRECTORIES)|INTERPROCEDURAL_OPTIMIZATION|IN_TRY_COMPILE|IOS_INSTALL_COMBINED|JOB_POOLS|JOB_POOL_COMPILE|JOB_POOL_LINK|KEEP_EXTENSION|LABELS|LANGUAGE|LIBRARY_OUTPUT_DIRECTORY|LINKER_LANGUAGE|LINK_(?:DEPENDS|DEPENDS_NO_SHARED|DIRECTORIES|FLAGS|INTERFACE_LIBRARIES|INTERFACE_MULTIPLICITY|LIBRARIES|OPTIONS|SEARCH_END_STATIC|SEARCH_START_STATIC|WHAT_YOU_USE)|LISTFILE_STACK|LOCATION|MACOSX_BUNDLE|MACOSX_BUNDLE_INFO_PLIST|MACOSX_FRAMEWORK_INFO_PLIST|MACOSX_PACKAGE_LOCATION|MACOSX_RPATH|MACROS|MANUALLY_ADDED_DEPENDENCIES|MEASUREMENT|MODIFIED|NAME|NO_SONAME|NO_SYSTEM_FROM_IMPORTED|OBJECT_DEPENDS|OBJECT_OUTPUTS|OSX_ARCHITECTURES|OUTPUT_NAME|PACKAGES_FOUND|PACKAGES_NOT_FOUND|PARENT_DIRECTORY|PASS_REGULAR_EXPRESSION|PDB_NAME|PDB_OUTPUT_DIRECTORY|POSITION_INDEPENDENT_CODE|POST_INSTALL_SCRIPT|PREDEFINED_TARGETS_FOLDER|PREFIX|PRE_INSTALL_SCRIPT|PRIVATE_HEADER|PROCESSORS|PROCESSOR_AFFINITY|PROJECT_LABEL|PUBLIC_HEADER|REPORT_UNDEFINED_PROPERTIES|REQUIRED_FILES|RESOURCE|RESOURCE_LOCK|RULE_LAUNCH_COMPILE|RULE_LAUNCH_CUSTOM|RULE_LAUNCH_LINK|RULE_MESSAGES|RUNTIME_OUTPUT_DIRECTORY|RUN_SERIAL|SKIP_AUTOGEN|SKIP_AUTOMOC|SKIP_AUTORCC|SKIP_AUTOUIC|SKIP_BUILD_RPATH|SKIP_RETURN_CODE|SOURCES|SOURCE_DIR|SOVERSION|STATIC_LIBRARY_FLAGS|STATIC_LIBRARY_OPTIONS|STRINGS|SUBDIRECTORIES|SUFFIX|SYMBOLIC|TARGET_ARCHIVES_MAY_BE_SHARED_LIBS|TARGET_MESSAGES|TARGET_SUPPORTS_SHARED_LIBS|TESTS|TEST_INCLUDE_FILE|TEST_INCLUDE_FILES|TIMEOUT|TIMEOUT_AFTER_MATCH|TYPE|USE_FOLDERS|VALUE|VARIABLES|VERSION|VISIBILITY_INLINES_HIDDEN|VS_(?:CONFIGURATION_TYPE|COPY_TO_OUT_DIR|DEBUGGER_(?:COMMAND|COMMAND_ARGUMENTS|ENVIRONMENT|WORKING_DIRECTORY)|DEPLOYMENT_CONTENT|DEPLOYMENT_LOCATION|DOTNET_REFERENCES|DOTNET_REFERENCES_COPY_LOCAL|INCLUDE_IN_VSIX|IOT_STARTUP_TASK|KEYWORD|RESOURCE_GENERATOR|SCC_AUXPATH|SCC_LOCALPATH|SCC_PROJECTNAME|SCC_PROVIDER|SDK_REFERENCES|SHADER_(?:DISABLE_OPTIMIZATIONS|ENABLE_DEBUG|ENTRYPOINT|FLAGS|MODEL|OBJECT_FILE_NAME|OUTPUT_HEADER_FILE|TYPE|VARIABLE_NAME)|STARTUP_PROJECT|TOOL_OVERRIDE|USER_PROPS|WINRT_COMPONENT|WINRT_EXTENSIONS|WINRT_REFERENCES|XAML_TYPE)|WILL_FAIL|WIN32_EXECUTABLE|WINDOWS_EXPORT_ALL_SYMBOLS|WORKING_DIRECTORY|WRAP_EXCLUDE|XCODE_(?:EMIT_EFFECTIVE_PLATFORM_NAME|EXPLICIT_FILE_TYPE|FILE_ATTRIBUTES|LAST_KNOWN_FILE_TYPE|PRODUCT_TYPE|SCHEME_(?:ADDRESS_SANITIZER|ADDRESS_SANITIZER_USE_AFTER_RETURN|ARGUMENTS|DISABLE_MAIN_THREAD_CHECKER|DYNAMIC_LIBRARY_LOADS|DYNAMIC_LINKER_API_USAGE|ENVIRONMENT|EXECUTABLE|GUARD_MALLOC|MAIN_THREAD_CHECKER_STOP|MALLOC_GUARD_EDGES|MALLOC_SCRIBBLE|MALLOC_STACK|THREAD_SANITIZER(?:_STOP)?|UNDEFINED_BEHAVIOUR_SANITIZER(?:_STOP)?|ZOMBIE_OBJECTS))|XCTEST)\b/, | 2303 | /\b(?:CMAKE_\w+|\w+_(?:(?:BINARY|SOURCE)_DIR|DESCRIPTION|HOMEPAGE_URL|ROOT|VERSION(?:_MAJOR|_MINOR|_PATCH|_TWEAK)?)|(?:ANDROID|APPLE|BORLAND|BUILD_SHARED_LIBS|CACHE|CPACK_(?:ABSOLUTE_DESTINATION_FILES|COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY|ERROR_ON_ABSOLUTE_INSTALL_DESTINATION|INCLUDE_TOPLEVEL_DIRECTORY|INSTALL_DEFAULT_DIRECTORY_PERMISSIONS|INSTALL_SCRIPT|PACKAGING_INSTALL_PREFIX|SET_DESTDIR|WARN_ON_ABSOLUTE_INSTALL_DESTINATION)|CTEST_(?:BINARY_DIRECTORY|BUILD_COMMAND|BUILD_NAME|BZR_COMMAND|BZR_UPDATE_OPTIONS|CHANGE_ID|CHECKOUT_COMMAND|CONFIGURATION_TYPE|CONFIGURE_COMMAND|COVERAGE_COMMAND|COVERAGE_EXTRA_FLAGS|CURL_OPTIONS|CUSTOM_(?:COVERAGE_EXCLUDE|ERROR_EXCEPTION|ERROR_MATCH|ERROR_POST_CONTEXT|ERROR_PRE_CONTEXT|MAXIMUM_FAILED_TEST_OUTPUT_SIZE|MAXIMUM_NUMBER_OF_(?:ERRORS|WARNINGS)|MAXIMUM_PASSED_TEST_OUTPUT_SIZE|MEMCHECK_IGNORE|POST_MEMCHECK|POST_TEST|PRE_MEMCHECK|PRE_TEST|TESTS_IGNORE|WARNING_EXCEPTION|WARNING_MATCH)|CVS_CHECKOUT|CVS_COMMAND|CVS_UPDATE_OPTIONS|DROP_LOCATION|DROP_METHOD|DROP_SITE|DROP_SITE_CDASH|DROP_SITE_PASSWORD|DROP_SITE_USER|EXTRA_COVERAGE_GLOB|GIT_COMMAND|GIT_INIT_SUBMODULES|GIT_UPDATE_CUSTOM|GIT_UPDATE_OPTIONS|HG_COMMAND|HG_UPDATE_OPTIONS|LABELS_FOR_SUBPROJECTS|MEMORYCHECK_(?:COMMAND|COMMAND_OPTIONS|SANITIZER_OPTIONS|SUPPRESSIONS_FILE|TYPE)|NIGHTLY_START_TIME|P4_CLIENT|P4_COMMAND|P4_OPTIONS|P4_UPDATE_OPTIONS|RUN_CURRENT_SCRIPT|SCP_COMMAND|SITE|SOURCE_DIRECTORY|SUBMIT_URL|SVN_COMMAND|SVN_OPTIONS|SVN_UPDATE_OPTIONS|TEST_LOAD|TEST_TIMEOUT|TRIGGER_SITE|UPDATE_COMMAND|UPDATE_OPTIONS|UPDATE_VERSION_ONLY|USE_LAUNCHERS)|CYGWIN|ENV|EXECUTABLE_OUTPUT_PATH|GHS-MULTI|IOS|LIBRARY_OUTPUT_PATH|MINGW|MSVC(?:10|11|12|14|60|70|71|80|90|_IDE|_TOOLSET_VERSION|_VERSION)?|MSYS|PROJECT_NAME|UNIX|WIN32|WINCE|WINDOWS_PHONE|WINDOWS_STORE|XCODE))\b/, |
| 2158 | 'keyword': /\b(?:add_compile_definitions|add_compile_options|add_custom_command|add_custom_target|add_definitions|add_dependencies|add_executable|add_library|add_link_options|add_subdirectory|add_test|aux_source_directory|break|build_command|build_name|cmake_host_system_information|cmake_minimum_required|cmake_parse_arguments|cmake_policy|configure_file|continue|create_test_sourcelist|ctest_build|ctest_configure|ctest_coverage|ctest_empty_binary_directory|ctest_memcheck|ctest_read_custom_files|ctest_run_script|ctest_sleep|ctest_start|ctest_submit|ctest_test|ctest_update|ctest_upload|define_property|else|elseif|enable_language|enable_testing|endforeach|endfunction|endif|endmacro|endwhile|exec_program|execute_process|export|export_library_dependencies|file|find_file|find_library|find_package|find_path|find_program|fltk_wrap_ui|foreach|function|get_cmake_property|get_directory_property|get_filename_component|get_property|get_source_file_property|get_target_property|get_test_property|if|include|include_directories|include_external_msproject|include_guard|include_regular_expression|install|install_files|install_programs|install_targets|link_directories|link_libraries|list|load_cache|load_command|macro|make_directory|mark_as_advanced|math|message|option|output_required_files|project|qt_wrap_cpp|qt_wrap_ui|remove|remove_definitions|return|separate_arguments|set|set_directory_properties|set_property|set_source_files_properties|set_target_properties|set_tests_properties|site_name|source_group|string|subdir_depends|subdirs|target_compile_definitions|target_compile_features|target_compile_options|target_include_directories|target_link_directories|target_link_libraries|target_link_options|target_sources|try_compile|try_run|unset|use_mangled_mesa|utility_source|variable_requires|variable_watch|while|write_file)(?=\s*\()\b/, | 2304 | property: |
| 2159 | 'boolean': /\b(?:FALSE|OFF|ON|TRUE)\b/, | 2305 | /\b(?:cxx_\w+|(?:ARCHIVE_OUTPUT_(?:DIRECTORY|NAME)|COMPILE_DEFINITIONS|COMPILE_PDB_NAME|COMPILE_PDB_OUTPUT_DIRECTORY|EXCLUDE_FROM_DEFAULT_BUILD|IMPORTED_(?:IMPLIB|LIBNAME|LINK_DEPENDENT_LIBRARIES|LINK_INTERFACE_LANGUAGES|LINK_INTERFACE_LIBRARIES|LINK_INTERFACE_MULTIPLICITY|LOCATION|NO_SONAME|OBJECTS|SONAME)|INTERPROCEDURAL_OPTIMIZATION|LIBRARY_OUTPUT_DIRECTORY|LIBRARY_OUTPUT_NAME|LINK_FLAGS|LINK_INTERFACE_LIBRARIES|LINK_INTERFACE_MULTIPLICITY|LOCATION|MAP_IMPORTED_CONFIG|OSX_ARCHITECTURES|OUTPUT_NAME|PDB_NAME|PDB_OUTPUT_DIRECTORY|RUNTIME_OUTPUT_DIRECTORY|RUNTIME_OUTPUT_NAME|STATIC_LIBRARY_FLAGS|VS_CSHARP|VS_DOTNET_REFERENCEPROP|VS_DOTNET_REFERENCE|VS_GLOBAL_SECTION_POST|VS_GLOBAL_SECTION_PRE|VS_GLOBAL|XCODE_ATTRIBUTE)_\w+|\w+_(?:CLANG_TIDY|COMPILER_LAUNCHER|CPPCHECK|CPPLINT|INCLUDE_WHAT_YOU_USE|OUTPUT_NAME|POSTFIX|VISIBILITY_PRESET)|ABSTRACT|ADDITIONAL_MAKE_CLEAN_FILES|ADVANCED|ALIASED_TARGET|ALLOW_DUPLICATE_CUSTOM_TARGETS|ANDROID_(?:ANT_ADDITIONAL_OPTIONS|API|API_MIN|ARCH|ASSETS_DIRECTORIES|GUI|JAR_DEPENDENCIES|NATIVE_LIB_DEPENDENCIES|NATIVE_LIB_DIRECTORIES|PROCESS_MAX|PROGUARD|PROGUARD_CONFIG_PATH|SECURE_PROPS_PATH|SKIP_ANT_STEP|STL_TYPE)|ARCHIVE_OUTPUT_DIRECTORY|ATTACHED_FILES|ATTACHED_FILES_ON_FAIL|AUTOGEN_(?:BUILD_DIR|ORIGIN_DEPENDS|PARALLEL|SOURCE_GROUP|TARGETS_FOLDER|TARGET_DEPENDS)|AUTOMOC|AUTOMOC_(?:COMPILER_PREDEFINES|DEPEND_FILTERS|EXECUTABLE|MACRO_NAMES|MOC_OPTIONS|SOURCE_GROUP|TARGETS_FOLDER)|AUTORCC|AUTORCC_EXECUTABLE|AUTORCC_OPTIONS|AUTORCC_SOURCE_GROUP|AUTOUIC|AUTOUIC_EXECUTABLE|AUTOUIC_OPTIONS|AUTOUIC_SEARCH_PATHS|BINARY_DIR|BUILDSYSTEM_TARGETS|BUILD_RPATH|BUILD_RPATH_USE_ORIGIN|BUILD_WITH_INSTALL_NAME_DIR|BUILD_WITH_INSTALL_RPATH|BUNDLE|BUNDLE_EXTENSION|CACHE_VARIABLES|CLEAN_NO_CUSTOM|COMMON_LANGUAGE_RUNTIME|COMPATIBLE_INTERFACE_(?:BOOL|NUMBER_MAX|NUMBER_MIN|STRING)|COMPILE_(?:DEFINITIONS|FEATURES|FLAGS|OPTIONS|PDB_NAME|PDB_OUTPUT_DIRECTORY)|COST|CPACK_DESKTOP_SHORTCUTS|CPACK_NEVER_OVERWRITE|CPACK_PERMANENT|CPACK_STARTUP_SHORTCUTS|CPACK_START_MENU_SHORTCUTS|CPACK_WIX_ACL|CROSSCOMPILING_EMULATOR|CUDA_EXTENSIONS|CUDA_PTX_COMPILATION|CUDA_RESOLVE_DEVICE_SYMBOLS|CUDA_SEPARABLE_COMPILATION|CUDA_STANDARD|CUDA_STANDARD_REQUIRED|CXX_EXTENSIONS|CXX_STANDARD|CXX_STANDARD_REQUIRED|C_EXTENSIONS|C_STANDARD|C_STANDARD_REQUIRED|DEBUG_CONFIGURATIONS|DEFINE_SYMBOL|DEFINITIONS|DEPENDS|DEPLOYMENT_ADDITIONAL_FILES|DEPLOYMENT_REMOTE_DIRECTORY|DISABLED|DISABLED_FEATURES|ECLIPSE_EXTRA_CPROJECT_CONTENTS|ECLIPSE_EXTRA_NATURES|ENABLED_FEATURES|ENABLED_LANGUAGES|ENABLE_EXPORTS|ENVIRONMENT|EXCLUDE_FROM_ALL|EXCLUDE_FROM_DEFAULT_BUILD|EXPORT_NAME|EXPORT_PROPERTIES|EXTERNAL_OBJECT|EchoString|FAIL_REGULAR_EXPRESSION|FIND_LIBRARY_USE_LIB32_PATHS|FIND_LIBRARY_USE_LIB64_PATHS|FIND_LIBRARY_USE_LIBX32_PATHS|FIND_LIBRARY_USE_OPENBSD_VERSIONING|FIXTURES_CLEANUP|FIXTURES_REQUIRED|FIXTURES_SETUP|FOLDER|FRAMEWORK|Fortran_FORMAT|Fortran_MODULE_DIRECTORY|GENERATED|GENERATOR_FILE_NAME|GENERATOR_IS_MULTI_CONFIG|GHS_INTEGRITY_APP|GHS_NO_SOURCE_GROUP_FILE|GLOBAL_DEPENDS_DEBUG_MODE|GLOBAL_DEPENDS_NO_CYCLES|GNUtoMS|HAS_CXX|HEADER_FILE_ONLY|HELPSTRING|IMPLICIT_DEPENDS_INCLUDE_TRANSFORM|IMPORTED|IMPORTED_(?:COMMON_LANGUAGE_RUNTIME|CONFIGURATIONS|GLOBAL|IMPLIB|LIBNAME|LINK_DEPENDENT_LIBRARIES|LINK_INTERFACE_(?:LANGUAGES|LIBRARIES|MULTIPLICITY)|LOCATION|NO_SONAME|OBJECTS|SONAME)|IMPORT_PREFIX|IMPORT_SUFFIX|INCLUDE_DIRECTORIES|INCLUDE_REGULAR_EXPRESSION|INSTALL_NAME_DIR|INSTALL_RPATH|INSTALL_RPATH_USE_LINK_PATH|INTERFACE_(?:AUTOUIC_OPTIONS|COMPILE_DEFINITIONS|COMPILE_FEATURES|COMPILE_OPTIONS|INCLUDE_DIRECTORIES|LINK_DEPENDS|LINK_DIRECTORIES|LINK_LIBRARIES|LINK_OPTIONS|POSITION_INDEPENDENT_CODE|SOURCES|SYSTEM_INCLUDE_DIRECTORIES)|INTERPROCEDURAL_OPTIMIZATION|IN_TRY_COMPILE|IOS_INSTALL_COMBINED|JOB_POOLS|JOB_POOL_COMPILE|JOB_POOL_LINK|KEEP_EXTENSION|LABELS|LANGUAGE|LIBRARY_OUTPUT_DIRECTORY|LINKER_LANGUAGE|LINK_(?:DEPENDS|DEPENDS_NO_SHARED|DIRECTORIES|FLAGS|INTERFACE_LIBRARIES|INTERFACE_MULTIPLICITY|LIBRARIES|OPTIONS|SEARCH_END_STATIC|SEARCH_START_STATIC|WHAT_YOU_USE)|LISTFILE_STACK|LOCATION|MACOSX_BUNDLE|MACOSX_BUNDLE_INFO_PLIST|MACOSX_FRAMEWORK_INFO_PLIST|MACOSX_PACKAGE_LOCATION|MACOSX_RPATH|MACROS|MANUALLY_ADDED_DEPENDENCIES|MEASUREMENT|MODIFIED|NAME|NO_SONAME|NO_SYSTEM_FROM_IMPORTED|OBJECT_DEPENDS|OBJECT_OUTPUTS|OSX_ARCHITECTURES|OUTPUT_NAME|PACKAGES_FOUND|PACKAGES_NOT_FOUND|PARENT_DIRECTORY|PASS_REGULAR_EXPRESSION|PDB_NAME|PDB_OUTPUT_DIRECTORY|POSITION_INDEPENDENT_CODE|POST_INSTALL_SCRIPT|PREDEFINED_TARGETS_FOLDER|PREFIX|PRE_INSTALL_SCRIPT|PRIVATE_HEADER|PROCESSORS|PROCESSOR_AFFINITY|PROJECT_LABEL|PUBLIC_HEADER|REPORT_UNDEFINED_PROPERTIES|REQUIRED_FILES|RESOURCE|RESOURCE_LOCK|RULE_LAUNCH_COMPILE|RULE_LAUNCH_CUSTOM|RULE_LAUNCH_LINK|RULE_MESSAGES|RUNTIME_OUTPUT_DIRECTORY|RUN_SERIAL|SKIP_AUTOGEN|SKIP_AUTOMOC|SKIP_AUTORCC|SKIP_AUTOUIC|SKIP_BUILD_RPATH|SKIP_RETURN_CODE|SOURCES|SOURCE_DIR|SOVERSION|STATIC_LIBRARY_FLAGS|STATIC_LIBRARY_OPTIONS|STRINGS|SUBDIRECTORIES|SUFFIX|SYMBOLIC|TARGET_ARCHIVES_MAY_BE_SHARED_LIBS|TARGET_MESSAGES|TARGET_SUPPORTS_SHARED_LIBS|TESTS|TEST_INCLUDE_FILE|TEST_INCLUDE_FILES|TIMEOUT|TIMEOUT_AFTER_MATCH|TYPE|USE_FOLDERS|VALUE|VARIABLES|VERSION|VISIBILITY_INLINES_HIDDEN|VS_(?:CONFIGURATION_TYPE|COPY_TO_OUT_DIR|DEBUGGER_(?:COMMAND|COMMAND_ARGUMENTS|ENVIRONMENT|WORKING_DIRECTORY)|DEPLOYMENT_CONTENT|DEPLOYMENT_LOCATION|DOTNET_REFERENCES|DOTNET_REFERENCES_COPY_LOCAL|INCLUDE_IN_VSIX|IOT_STARTUP_TASK|KEYWORD|RESOURCE_GENERATOR|SCC_AUXPATH|SCC_LOCALPATH|SCC_PROJECTNAME|SCC_PROVIDER|SDK_REFERENCES|SHADER_(?:DISABLE_OPTIMIZATIONS|ENABLE_DEBUG|ENTRYPOINT|FLAGS|MODEL|OBJECT_FILE_NAME|OUTPUT_HEADER_FILE|TYPE|VARIABLE_NAME)|STARTUP_PROJECT|TOOL_OVERRIDE|USER_PROPS|WINRT_COMPONENT|WINRT_EXTENSIONS|WINRT_REFERENCES|XAML_TYPE)|WILL_FAIL|WIN32_EXECUTABLE|WINDOWS_EXPORT_ALL_SYMBOLS|WORKING_DIRECTORY|WRAP_EXCLUDE|XCODE_(?:EMIT_EFFECTIVE_PLATFORM_NAME|EXPLICIT_FILE_TYPE|FILE_ATTRIBUTES|LAST_KNOWN_FILE_TYPE|PRODUCT_TYPE|SCHEME_(?:ADDRESS_SANITIZER|ADDRESS_SANITIZER_USE_AFTER_RETURN|ARGUMENTS|DISABLE_MAIN_THREAD_CHECKER|DYNAMIC_LIBRARY_LOADS|DYNAMIC_LINKER_API_USAGE|ENVIRONMENT|EXECUTABLE|GUARD_MALLOC|MAIN_THREAD_CHECKER_STOP|MALLOC_GUARD_EDGES|MALLOC_SCRIBBLE|MALLOC_STACK|THREAD_SANITIZER(?:_STOP)?|UNDEFINED_BEHAVIOUR_SANITIZER(?:_STOP)?|ZOMBIE_OBJECTS))|XCTEST)\b/, |
| 2160 | 'namespace': /\b(?:INTERFACE|PRIVATE|PROPERTIES|PUBLIC|SHARED|STATIC|TARGET_OBJECTS)\b/, | 2306 | keyword: |
| 2161 | 'operator': /\b(?:AND|DEFINED|EQUAL|GREATER|LESS|MATCHES|NOT|OR|STREQUAL|STRGREATER|STRLESS|VERSION_EQUAL|VERSION_GREATER|VERSION_LESS)\b/, | 2307 | /\b(?:add_compile_definitions|add_compile_options|add_custom_command|add_custom_target|add_definitions|add_dependencies|add_executable|add_library|add_link_options|add_subdirectory|add_test|aux_source_directory|break|build_command|build_name|cmake_host_system_information|cmake_minimum_required|cmake_parse_arguments|cmake_policy|configure_file|continue|create_test_sourcelist|ctest_build|ctest_configure|ctest_coverage|ctest_empty_binary_directory|ctest_memcheck|ctest_read_custom_files|ctest_run_script|ctest_sleep|ctest_start|ctest_submit|ctest_test|ctest_update|ctest_upload|define_property|else|elseif|enable_language|enable_testing|endforeach|endfunction|endif|endmacro|endwhile|exec_program|execute_process|export|export_library_dependencies|file|find_file|find_library|find_package|find_path|find_program|fltk_wrap_ui|foreach|function|get_cmake_property|get_directory_property|get_filename_component|get_property|get_source_file_property|get_target_property|get_test_property|if|include|include_directories|include_external_msproject|include_guard|include_regular_expression|install|install_files|install_programs|install_targets|link_directories|link_libraries|list|load_cache|load_command|macro|make_directory|mark_as_advanced|math|message|option|output_required_files|project|qt_wrap_cpp|qt_wrap_ui|remove|remove_definitions|return|separate_arguments|set|set_directory_properties|set_property|set_source_files_properties|set_target_properties|set_tests_properties|site_name|source_group|string|subdir_depends|subdirs|target_compile_definitions|target_compile_features|target_compile_options|target_include_directories|target_link_directories|target_link_libraries|target_link_options|target_sources|try_compile|try_run|unset|use_mangled_mesa|utility_source|variable_requires|variable_watch|while|write_file)(?=\s*\()\b/, |
| 2162 | 'inserted': { | 2308 | boolean: /\b(?:FALSE|OFF|ON|TRUE)\b/, |
| 2163 | pattern: /\b\w+::\w+\b/, | 2309 | namespace: |
| 2164 | alias: 'class-name' | 2310 | /\b(?:INTERFACE|PRIVATE|PROPERTIES|PUBLIC|SHARED|STATIC|TARGET_OBJECTS)\b/, |
| 2165 | }, | 2311 | operator: |
| 2166 | 'number': /\b\d+(?:\.\d+)*\b/, | 2312 | /\b(?:AND|DEFINED|EQUAL|GREATER|LESS|MATCHES|NOT|OR|STREQUAL|STRGREATER|STRLESS|VERSION_EQUAL|VERSION_GREATER|VERSION_LESS)\b/, |
| 2167 | 'function': /\b[a-z_]\w*(?=\s*\()\b/i, | 2313 | inserted: { |
| 2168 | 'punctuation': /[()>}]|\$[<{]/ | 2314 | pattern: /\b\w+::\w+\b/, |
| 2315 | alias: "class-name", | ||
| 2316 | }, | ||
| 2317 | number: /\b\d+(?:\.\d+)*\b/, | ||
| 2318 | function: /\b[a-z_]\w*(?=\s*\()\b/i, | ||
| 2319 | punctuation: /[()>}]|\$[<{]/, | ||
| 2169 | }; | 2320 | }; |
| 2170 | 2321 | ||
| 2171 | (function (Prism) { | 2322 | (function (Prism) { |
| 2172 | 2323 | // Ignore comments starting with { to privilege string interpolation highlighting | |
| 2173 | // Ignore comments starting with { to privilege string interpolation highlighting | 2324 | var comment = /#(?!\{).+/; |
| 2174 | var comment = /#(?!\{).+/; | 2325 | var interpolation = { |
| 2175 | var interpolation = { | 2326 | pattern: /#\{[^}]+\}/, |
| 2176 | pattern: /#\{[^}]+\}/, | 2327 | alias: "variable", |
| 2177 | alias: 'variable' | 2328 | }; |
| 2178 | }; | 2329 | |
| 2179 | 2330 | Prism.languages.coffeescript = Prism.languages.extend("javascript", { | |
| 2180 | Prism.languages.coffeescript = Prism.languages.extend('javascript', { | 2331 | comment: comment, |
| 2181 | 'comment': comment, | 2332 | string: [ |
| 2182 | 'string': [ | 2333 | // Strings are multiline |
| 2183 | 2334 | { | |
| 2184 | // Strings are multiline | 2335 | pattern: /'(?:\\[\s\S]|[^\\'])*'/, |
| 2185 | { | 2336 | greedy: true, |
| 2186 | pattern: /'(?:\\[\s\S]|[^\\'])*'/, | 2337 | }, |
| 2187 | greedy: true | 2338 | |
| 2188 | }, | 2339 | { |
| 2189 | 2340 | // Strings are multiline | |
| 2190 | { | 2341 | pattern: /"(?:\\[\s\S]|[^\\"])*"/, |
| 2191 | // Strings are multiline | 2342 | greedy: true, |
| 2192 | pattern: /"(?:\\[\s\S]|[^\\"])*"/, | 2343 | inside: { |
| 2193 | greedy: true, | 2344 | interpolation: interpolation, |
| 2194 | inside: { | 2345 | }, |
| 2195 | 'interpolation': interpolation | 2346 | }, |
| 2196 | } | 2347 | ], |
| 2197 | } | 2348 | keyword: |
| 2198 | ], | 2349 | /\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/, |
| 2199 | 'keyword': /\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/, | 2350 | "class-member": { |
| 2200 | 'class-member': { | 2351 | pattern: /@(?!\d)\w+/, |
| 2201 | pattern: /@(?!\d)\w+/, | 2352 | alias: "variable", |
| 2202 | alias: 'variable' | 2353 | }, |
| 2203 | } | 2354 | }); |
| 2204 | }); | 2355 | |
| 2205 | 2356 | Prism.languages.insertBefore("coffeescript", "comment", { | |
| 2206 | Prism.languages.insertBefore('coffeescript', 'comment', { | 2357 | "multiline-comment": { |
| 2207 | 'multiline-comment': { | 2358 | pattern: /###[\s\S]+?###/, |
| 2208 | pattern: /###[\s\S]+?###/, | 2359 | alias: "comment", |
| 2209 | alias: 'comment' | 2360 | }, |
| 2210 | }, | 2361 | |
| 2211 | 2362 | // Block regexp can contain comments and interpolation | |
| 2212 | // Block regexp can contain comments and interpolation | 2363 | "block-regex": { |
| 2213 | 'block-regex': { | 2364 | pattern: /\/{3}[\s\S]*?\/{3}/, |
| 2214 | pattern: /\/{3}[\s\S]*?\/{3}/, | 2365 | alias: "regex", |
| 2215 | alias: 'regex', | 2366 | inside: { |
| 2216 | inside: { | 2367 | comment: comment, |
| 2217 | 'comment': comment, | 2368 | interpolation: interpolation, |
| 2218 | 'interpolation': interpolation | 2369 | }, |
| 2219 | } | 2370 | }, |
| 2220 | } | 2371 | }); |
| 2221 | }); | 2372 | |
| 2222 | 2373 | Prism.languages.insertBefore("coffeescript", "string", { | |
| 2223 | Prism.languages.insertBefore('coffeescript', 'string', { | 2374 | "inline-javascript": { |
| 2224 | 'inline-javascript': { | 2375 | pattern: /`(?:\\[\s\S]|[^\\`])*`/, |
| 2225 | pattern: /`(?:\\[\s\S]|[^\\`])*`/, | 2376 | inside: { |
| 2226 | inside: { | 2377 | delimiter: { |
| 2227 | 'delimiter': { | 2378 | pattern: /^`|`$/, |
| 2228 | pattern: /^`|`$/, | 2379 | alias: "punctuation", |
| 2229 | alias: 'punctuation' | 2380 | }, |
| 2230 | }, | 2381 | script: { |
| 2231 | 'script': { | 2382 | pattern: /[\s\S]+/, |
| 2232 | pattern: /[\s\S]+/, | 2383 | alias: "language-javascript", |
| 2233 | alias: 'language-javascript', | 2384 | inside: Prism.languages.javascript, |
| 2234 | inside: Prism.languages.javascript | 2385 | }, |
| 2235 | } | 2386 | }, |
| 2236 | } | 2387 | }, |
| 2237 | }, | 2388 | |
| 2238 | 2389 | // Block strings | |
| 2239 | // Block strings | 2390 | "multiline-string": [ |
| 2240 | 'multiline-string': [ | 2391 | { |
| 2241 | { | 2392 | pattern: /'''[\s\S]*?'''/, |
| 2242 | pattern: /'''[\s\S]*?'''/, | 2393 | greedy: true, |
| 2243 | greedy: true, | 2394 | alias: "string", |
| 2244 | alias: 'string' | 2395 | }, |
| 2245 | }, | 2396 | { |
| 2246 | { | 2397 | pattern: /"""[\s\S]*?"""/, |
| 2247 | pattern: /"""[\s\S]*?"""/, | 2398 | greedy: true, |
| 2248 | greedy: true, | 2399 | alias: "string", |
| 2249 | alias: 'string', | 2400 | inside: { |
| 2250 | inside: { | 2401 | interpolation: interpolation, |
| 2251 | interpolation: interpolation | 2402 | }, |
| 2252 | } | 2403 | }, |
| 2253 | } | 2404 | ], |
| 2254 | ] | 2405 | }); |
| 2255 | 2406 | ||
| 2256 | }); | 2407 | Prism.languages.insertBefore("coffeescript", "keyword", { |
| 2257 | 2408 | // Object property | |
| 2258 | Prism.languages.insertBefore('coffeescript', 'keyword', { | 2409 | property: /(?!\d)\w+(?=\s*:(?!:))/, |
| 2259 | // Object property | 2410 | }); |
| 2260 | 'property': /(?!\d)\w+(?=\s*:(?!:))/ | 2411 | |
| 2261 | }); | 2412 | delete Prism.languages.coffeescript["template-string"]; |
| 2262 | 2413 | ||
| 2263 | delete Prism.languages.coffeescript['template-string']; | 2414 | Prism.languages.coffee = Prism.languages.coffeescript; |
| 2264 | 2415 | })(Prism); | |
| 2265 | Prism.languages.coffee = Prism.languages.coffeescript; | ||
| 2266 | }(Prism)); | ||
| 2267 | 2416 | ||
| 2268 | /** | 2417 | /** |
| 2269 | * Original by Samuel Flores | 2418 | * Original by Samuel Flores |
| @@ -2272,1833 +2421,1968 @@ Prism.languages.cmake = { · 2 unmodified lines | |||
| 2272 | * constant, builtin, variable, symbol, regex | 2421 | * constant, builtin, variable, symbol, regex |
| 2273 | */ | 2422 | */ |
| 2274 | (function (Prism) { | 2423 | (function (Prism) { |
| 2275 | Prism.languages.ruby = Prism.languages.extend('clike', { | 2424 | Prism.languages.ruby = Prism.languages.extend("clike", { |
| 2276 | 'comment': { | 2425 | comment: { |
| 2277 | pattern: /#.*|^=begin\s[\s\S]*?^=end/m, | 2426 | pattern: /#.*|^=begin\s[\s\S]*?^=end/m, |
| 2278 | greedy: true | 2427 | greedy: true, |
| 2279 | }, | 2428 | }, |
| 2280 | 'class-name': { | 2429 | "class-name": { |
| 2281 | pattern: /(\b(?:class|module)\s+|\bcatch\s+\()[\w.\\]+|\b[A-Z_]\w*(?=\s*\.\s*new\b)/, | 2430 | pattern: |
| 2282 | lookbehind: true, | 2431 | /(\b(?:class|module)\s+|\bcatch\s+\()[\w.\\]+|\b[A-Z_]\w*(?=\s*\.\s*new\b)/, |
| 2283 | inside: { | 2432 | lookbehind: true, |
| 2284 | 'punctuation': /[.\\]/ | 2433 | inside: { |
| 2285 | } | 2434 | punctuation: /[.\\]/, |
| 2286 | }, | 2435 | }, |
| 2287 | 'keyword': /\b(?:BEGIN|END|alias|and|begin|break|case|class|def|define_method|defined|do|each|else|elsif|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|private|protected|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/, | 2436 | }, |
| 2288 | 'operator': /\.{2,3}|&\.|===|<?=>|[!=]?~|(?:&&|\|\||<<|>>|\*\*|[+\-*/%<>!^&|=])=?|[?:]/, | 2437 | keyword: |
| 2289 | 'punctuation': /[(){}[\].,;]/, | 2438 | /\b(?:BEGIN|END|alias|and|begin|break|case|class|def|define_method|defined|do|each|else|elsif|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|private|protected|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/, |
| 2290 | }); | 2439 | operator: |
| 2291 | 2440 | /\.{2,3}|&\.|===|<?=>|[!=]?~|(?:&&|\|\||<<|>>|\*\*|[+\-*/%<>!^&|=])=?|[?:]/, | |
| 2292 | Prism.languages.insertBefore('ruby', 'operator', { | 2441 | punctuation: /[(){}[\].,;]/, |
| 2293 | 'double-colon': { | 2442 | }); |
| 2294 | pattern: /::/, | 2443 | |
| 2295 | alias: 'punctuation' | 2444 | Prism.languages.insertBefore("ruby", "operator", { |
| 2296 | }, | 2445 | "double-colon": { |
| 2297 | }); | 2446 | pattern: /::/, |
| 2298 | 2447 | alias: "punctuation", | |
| 2299 | var interpolation = { | 2448 | }, |
| 2300 | pattern: /((?:^|[^\\])(?:\\{2})*)#\{(?:[^{}]|\{[^{}]*\})*\}/, | 2449 | }); |
| 2301 | lookbehind: true, | 2450 | |
| 2302 | inside: { | 2451 | var interpolation = { |
| 2303 | 'content': { | 2452 | pattern: /((?:^|[^\\])(?:\\{2})*)#\{(?:[^{}]|\{[^{}]*\})*\}/, |
| 2304 | pattern: /^(#\{)[\s\S]+(?=\}$)/, | 2453 | lookbehind: true, |
| 2305 | lookbehind: true, | 2454 | inside: { |
| 2306 | inside: Prism.languages.ruby | 2455 | content: { |
| 2307 | }, | 2456 | pattern: /^(#\{)[\s\S]+(?=\}$)/, |
| 2308 | 'delimiter': { | 2457 | lookbehind: true, |
| 2309 | pattern: /^#\{|\}$/, | 2458 | inside: Prism.languages.ruby, |
| 2310 | alias: 'punctuation' | 2459 | }, |
| 2311 | } | 2460 | delimiter: { |
| 2312 | } | 2461 | pattern: /^#\{|\}$/, |
| 2313 | }; | 2462 | alias: "punctuation", |
| 2314 | 2463 | }, | |
| 2315 | delete Prism.languages.ruby.function; | 2464 | }, |
| 2316 | 2465 | }; | |
| 2317 | var percentExpression = '(?:' + [ | 2466 | |
| 2318 | /([^a-zA-Z0-9\s{(\[<=])(?:(?!\1)[^\\]|\\[\s\S])*\1/.source, | 2467 | delete Prism.languages.ruby.function; |
| 2319 | /\((?:[^()\\]|\\[\s\S]|\((?:[^()\\]|\\[\s\S])*\))*\)/.source, | 2468 | |
| 2320 | /\{(?:[^{}\\]|\\[\s\S]|\{(?:[^{}\\]|\\[\s\S])*\})*\}/.source, | 2469 | var percentExpression = |
| 2321 | /\[(?:[^\[\]\\]|\\[\s\S]|\[(?:[^\[\]\\]|\\[\s\S])*\])*\]/.source, | 2470 | "(?:" + |
| 2322 | /<(?:[^<>\\]|\\[\s\S]|<(?:[^<>\\]|\\[\s\S])*>)*>/.source | 2471 | [ |
| 2323 | ].join('|') + ')'; | 2472 | /([^a-zA-Z0-9\s{(\[<=])(?:(?!\1)[^\\]|\\[\s\S])*\1/.source, |
| 2324 | 2473 | /\((?:[^()\\]|\\[\s\S]|\((?:[^()\\]|\\[\s\S])*\))*\)/.source, | |
| 2325 | var symbolName = /(?:"(?:\\.|[^"\\\r\n])*"|(?:\b[a-zA-Z_]\w*|[^\s\0-\x7F]+)[?!]?|\$.)/.source; | 2474 | /\{(?:[^{}\\]|\\[\s\S]|\{(?:[^{}\\]|\\[\s\S])*\})*\}/.source, |
| 2326 | 2475 | /\[(?:[^\[\]\\]|\\[\s\S]|\[(?:[^\[\]\\]|\\[\s\S])*\])*\]/.source, | |
| 2327 | Prism.languages.insertBefore('ruby', 'keyword', { | 2476 | /<(?:[^<>\\]|\\[\s\S]|<(?:[^<>\\]|\\[\s\S])*>)*>/.source, |
| 2328 | 'regex-literal': [ | 2477 | ].join("|") + |
| 2329 | { | 2478 | ")"; |
| 2330 | pattern: RegExp(/%r/.source + percentExpression + /[egimnosux]{0,6}/.source), | 2479 | |
| 2331 | greedy: true, | 2480 | var symbolName = |
| 2332 | inside: { | 2481 | /(?:"(?:\\.|[^"\\\r\n])*"|(?:\b[a-zA-Z_]\w*|[^\s\0-\x7F]+)[?!]?|\$.)/ |
| 2333 | 'interpolation': interpolation, | 2482 | .source; |
| 2334 | 'regex': /[\s\S]+/ | 2483 | |
| 2335 | } | 2484 | Prism.languages.insertBefore("ruby", "keyword", { |
| 2336 | }, | 2485 | "regex-literal": [ |
| 2337 | { | 2486 | { |
| 2338 | pattern: /(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/, | 2487 | pattern: RegExp( |
| 2339 | lookbehind: true, | 2488 | /%r/.source + percentExpression + /[egimnosux]{0,6}/.source, |
| 2340 | greedy: true, | 2489 | ), |
| 2341 | inside: { | 2490 | greedy: true, |
| 2342 | 'interpolation': interpolation, | 2491 | inside: { |
| 2343 | 'regex': /[\s\S]+/ | 2492 | interpolation: interpolation, |
| 2344 | } | 2493 | regex: /[\s\S]+/, |
| 2345 | } | 2494 | }, |
| 2346 | ], | 2495 | }, |
| 2347 | 'variable': /[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/, | 2496 | { |
| 2348 | 'symbol': [ | 2497 | pattern: |
| 2349 | { | 2498 | /(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/, |
| 2350 | pattern: RegExp(/(^|[^:]):/.source + symbolName), | 2499 | lookbehind: true, |
| 2351 | lookbehind: true, | 2500 | greedy: true, |
| 2352 | greedy: true | 2501 | inside: { |
| 2353 | }, | 2502 | interpolation: interpolation, |
| 2354 | { | 2503 | regex: /[\s\S]+/, |
| 2355 | pattern: RegExp(/([\r\n{(,][ \t]*)/.source + symbolName + /(?=:(?!:))/.source), | 2504 | }, |
| 2356 | lookbehind: true, | 2505 | }, |
| 2357 | greedy: true | 2506 | ], |
| 2358 | }, | 2507 | variable: /[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/, |
| 2359 | ], | 2508 | symbol: [ |
| 2360 | 'method-definition': { | 2509 | { |
| 2361 | pattern: /(\bdef\s+)\w+(?:\s*\.\s*\w+)?/, | 2510 | pattern: RegExp(/(^|[^:]):/.source + symbolName), |
| 2362 | lookbehind: true, | 2511 | lookbehind: true, |
| 2363 | inside: { | 2512 | greedy: true, |
| 2364 | 'function': /\b\w+$/, | 2513 | }, |
| 2365 | 'keyword': /^self\b/, | 2514 | { |
| 2366 | 'class-name': /^\w+/, | 2515 | pattern: RegExp( |
| 2367 | 'punctuation': /\./ | 2516 | /([\r\n{(,][ \t]*)/.source + symbolName + /(?=:(?!:))/.source, |
| 2368 | } | 2517 | ), |
| 2369 | } | 2518 | lookbehind: true, |
| 2370 | }); | 2519 | greedy: true, |
| 2371 | 2520 | }, | |
| 2372 | Prism.languages.insertBefore('ruby', 'string', { | 2521 | ], |
| 2373 | 'string-literal': [ | 2522 | "method-definition": { |
| 2374 | { | 2523 | pattern: /(\bdef\s+)\w+(?:\s*\.\s*\w+)?/, |
| 2375 | pattern: RegExp(/%[qQiIwWs]?/.source + percentExpression), | 2524 | lookbehind: true, |
| 2376 | greedy: true, | 2525 | inside: { |
| 2377 | inside: { | 2526 | function: /\b\w+$/, |
| 2378 | 'interpolation': interpolation, | 2527 | keyword: /^self\b/, |
| 2379 | 'string': /[\s\S]+/ | 2528 | "class-name": /^\w+/, |
| 2380 | } | 2529 | punctuation: /\./, |
| 2381 | }, | 2530 | }, |
| 2382 | { | 2531 | }, |
| 2383 | pattern: /("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/, | 2532 | }); |
| 2384 | greedy: true, | 2533 | |
| 2385 | inside: { | 2534 | Prism.languages.insertBefore("ruby", "string", { |
| 2386 | 'interpolation': interpolation, | 2535 | "string-literal": [ |
| 2387 | 'string': /[\s\S]+/ | 2536 | { |
| 2388 | } | 2537 | pattern: RegExp(/%[qQiIwWs]?/.source + percentExpression), |
| 2389 | }, | 2538 | greedy: true, |
| 2390 | { | 2539 | inside: { |
| 2391 | pattern: /<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i, | 2540 | interpolation: interpolation, |
| 2392 | alias: 'heredoc-string', | 2541 | string: /[\s\S]+/, |
| 2393 | greedy: true, | 2542 | }, |
| 2394 | inside: { | 2543 | }, |
| 2395 | 'delimiter': { | 2544 | { |
| 2396 | pattern: /^<<[-~]?[a-z_]\w*|\b[a-z_]\w*$/i, | 2545 | pattern: |
| 2397 | inside: { | 2546 | /("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/, |
| 2398 | 'symbol': /\b\w+/, | 2547 | greedy: true, |
| 2399 | 'punctuation': /^<<[-~]?/ | 2548 | inside: { |
| 2400 | } | 2549 | interpolation: interpolation, |
| 2401 | }, | 2550 | string: /[\s\S]+/, |
| 2402 | 'interpolation': interpolation, | 2551 | }, |
| 2403 | 'string': /[\s\S]+/ | 2552 | }, |
| 2404 | } | 2553 | { |
| 2405 | }, | 2554 | pattern: /<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i, |
| 2406 | { | 2555 | alias: "heredoc-string", |
| 2407 | pattern: /<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i, | 2556 | greedy: true, |
| 2408 | alias: 'heredoc-string', | 2557 | inside: { |
| 2409 | greedy: true, | 2558 | delimiter: { |
| 2410 | inside: { | 2559 | pattern: /^<<[-~]?[a-z_]\w*|\b[a-z_]\w*$/i, |
| 2411 | 'delimiter': { | 2560 | inside: { |
| 2412 | pattern: /^<<[-~]?'[a-z_]\w*'|\b[a-z_]\w*$/i, | 2561 | symbol: /\b\w+/, |
| 2413 | inside: { | 2562 | punctuation: /^<<[-~]?/, |
| 2414 | 'symbol': /\b\w+/, | 2563 | }, |
| 2415 | 'punctuation': /^<<[-~]?'|'$/, | 2564 | }, |
| 2416 | } | 2565 | interpolation: interpolation, |
| 2417 | }, | 2566 | string: /[\s\S]+/, |
| 2418 | 'string': /[\s\S]+/ | 2567 | }, |
| 2419 | } | 2568 | }, |
| 2420 | } | 2569 | { |
| 2421 | ], | 2570 | pattern: /<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i, |
| 2422 | 'command-literal': [ | 2571 | alias: "heredoc-string", |
| 2423 | { | 2572 | greedy: true, |
| 2424 | pattern: RegExp(/%x/.source + percentExpression), | 2573 | inside: { |
| 2425 | greedy: true, | 2574 | delimiter: { |
| 2426 | inside: { | 2575 | pattern: /^<<[-~]?'[a-z_]\w*'|\b[a-z_]\w*$/i, |
| 2427 | 'interpolation': interpolation, | 2576 | inside: { |
| 2428 | 'command': { | 2577 | symbol: /\b\w+/, |
| 2429 | pattern: /[\s\S]+/, | 2578 | punctuation: /^<<[-~]?'|'$/, |
| 2430 | alias: 'string' | 2579 | }, |
| 2431 | } | 2580 | }, |
| 2432 | } | 2581 | string: /[\s\S]+/, |
| 2433 | }, | 2582 | }, |
| 2434 | { | 2583 | }, |
| 2435 | pattern: /`(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|[^\\`#\r\n])*`/, | 2584 | ], |
| 2436 | greedy: true, | 2585 | "command-literal": [ |
| 2437 | inside: { | 2586 | { |
| 2438 | 'interpolation': interpolation, | 2587 | pattern: RegExp(/%x/.source + percentExpression), |
| 2439 | 'command': { | 2588 | greedy: true, |
| 2440 | pattern: /[\s\S]+/, | 2589 | inside: { |
| 2441 | alias: 'string' | 2590 | interpolation: interpolation, |
| 2442 | } | 2591 | command: { |
| 2443 | } | 2592 | pattern: /[\s\S]+/, |
| 2444 | } | 2593 | alias: "string", |
| 2445 | ] | 2594 | }, |
| 2446 | }); | 2595 | }, |
| 2447 | 2596 | }, | |
| 2448 | delete Prism.languages.ruby.string; | 2597 | { |
| 2449 | 2598 | pattern: /`(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|[^\\`#\r\n])*`/, | |
| 2450 | Prism.languages.insertBefore('ruby', 'number', { | 2599 | greedy: true, |
| 2451 | 'builtin': /\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Fixnum|Float|Hash|IO|Integer|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|Stat|String|Struct|Symbol|TMS|Thread|ThreadGroup|Time|TrueClass)\b/, | 2600 | inside: { |
| 2452 | 'constant': /\b[A-Z][A-Z0-9_]*(?:[?!]|\b)/ | 2601 | interpolation: interpolation, |
| 2453 | }); | 2602 | command: { |
| 2454 | 2603 | pattern: /[\s\S]+/, | |
| 2455 | Prism.languages.rb = Prism.languages.ruby; | 2604 | alias: "string", |
| 2456 | }(Prism)); | 2605 | }, |
| 2606 | }, | ||
| 2607 | }, | ||
| 2608 | ], | ||
| 2609 | }); | ||
| 2610 | |||
| 2611 | delete Prism.languages.ruby.string; | ||
| 2612 | |||
| 2613 | Prism.languages.insertBefore("ruby", "number", { | ||
| 2614 | builtin: | ||
| 2615 | /\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Fixnum|Float|Hash|IO|Integer|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|Stat|String|Struct|Symbol|TMS|Thread|ThreadGroup|Time|TrueClass)\b/, | ||
| 2616 | constant: /\b[A-Z][A-Z0-9_]*(?:[?!]|\b)/, | ||
| 2617 | }); | ||
| 2618 | |||
| 2619 | Prism.languages.rb = Prism.languages.ruby; | ||
| 2620 | })(Prism); | ||
| 2457 | 2621 | ||
| 2458 | (function (Prism) { | 2622 | (function (Prism) { |
| 2459 | Prism.languages.crystal = Prism.languages.extend('ruby', { | ||
| 2460 | 'keyword': [ | ||
| 2461 | /\b(?:__DIR__|__END_LINE__|__FILE__|__LINE__|abstract|alias|annotation|as|asm|begin|break|case|class|def|do|else|elsif|end|ensure|enum|extend|for|fun|if|ifdef|include|instance_sizeof|lib|macro|module|next|of|out|pointerof|private|protected|ptr|require|rescue|return|select|self|sizeof|struct|super|then|type|typeof|undef|uninitialized|union|unless|until|when|while|with|yield)\b/, | ||
| 2462 | { | ||
| 2463 | pattern: /(\.\s*)(?:is_a|responds_to)\?/, | ||
| 2464 | lookbehind: true | ||
| 2465 | } | ||
| 2466 | ], | ||
| 2467 | 'number': /\b(?:0b[01_]*[01]|0o[0-7_]*[0-7]|0x[\da-fA-F_]*[\da-fA-F]|(?:\d(?:[\d_]*\d)?)(?:\.[\d_]*\d)?(?:[eE][+-]?[\d_]*\d)?)(?:_(?:[uif](?:8|16|32|64))?)?\b/, | ||
| 2468 | 'operator': [ | ||
| 2469 | /->/, | ||
| 2470 | Prism.languages.ruby.operator, | ||
| 2471 | ], | ||
| 2472 | 'punctuation': /[(){}[\].,;\\]/, | ||
| 2473 | }); | ||
| 2474 | |||
| 2475 | Prism.languages.insertBefore('crystal', 'string-literal', { | ||
| 2476 | 'attribute': { | ||
| 2477 | pattern: /@\[.*?\]/, | ||
| 2478 | inside: { | ||
| 2479 | 'delimiter': { | ||
| 2480 | pattern: /^@\[|\]$/, | ||
| 2481 | alias: 'punctuation' | ||
| 2482 | }, | ||
| 2483 | 'attribute': { | ||
| 2484 | pattern: /^(\s*)\w+/, | ||
| 2485 | lookbehind: true, | ||
| 2486 | alias: 'class-name' | ||
| 2487 | }, | ||
| 2488 | 'args': { | ||
| 2489 | pattern: /\S(?:[\s\S]*\S)?/, | ||
| 2490 | inside: Prism.languages.crystal | ||
| 2491 | }, | ||
| 2492 | } | ||
| 2493 | }, | ||
| 2494 | 'expansion': { | ||
| 2495 | pattern: /\{(?:\{.*?\}|%.*?%)\}/, | ||
| 2496 | inside: { | ||
| 2497 | 'content': { | ||
| 2498 | pattern: /^(\{.)[\s\S]+(?=.\}$)/, | ||
| 2499 | lookbehind: true, | ||
| 2500 | inside: Prism.languages.crystal | ||
| 2501 | }, | ||
| 2502 | 'delimiter': { | ||
| 2503 | pattern: /^\{[\{%]|[\}%]\}$/, | ||
| 2504 | alias: 'operator' | ||
| 2505 | } | ||
| 2506 | } | ||
| 2507 | }, | ||
| 2508 | 'char': { | ||
| 2509 | pattern: /'(?:[^\\\r\n]{1,2}|\\(?:.|u(?:[A-Fa-f0-9]{1,4}|\{[A-Fa-f0-9]{1,6}\})))'/, | ||
| 2510 | greedy: true | ||
| 2511 | } | ||
| 2512 | }); | ||
| 2513 | |||
| 2514 | }(Prism)); | ||
| 2515 | |||
| 2516 | Prism.languages.d = Prism.languages.extend('clike', { | ||
| 2517 | 'comment': [ | ||
| 2518 | { | ||
| 2519 | // Shebang | ||
| 2520 | pattern: /^\s*#!.+/, | ||
| 2521 | greedy: true | ||
| 2522 | }, | ||
| 2523 | { | ||
| 2524 | pattern: RegExp(/(^|[^\\])/.source + '(?:' + [ | ||
| 2525 | // /+ comment +/ | ||
| 2526 | // Allow one level of nesting | ||
| 2527 | /\/\+(?:\/\+(?:[^+]|\+(?!\/))*\+\/|(?!\/\+)[\s\S])*?\+\//.source, | ||
| 2528 | // // comment | ||
| 2529 | /\/\/.*/.source, | ||
| 2530 | // /* comment */ | ||
| 2531 | /\/\*[\s\S]*?\*\//.source | ||
| 2532 | ].join('|') + ')'), | ||
| 2533 | lookbehind: true, | ||
| 2534 | greedy: true | ||
| 2535 | } | ||
| 2536 | ], | ||
| 2537 | 'string': [ | ||
| 2538 | { | ||
| 2539 | pattern: RegExp([ | ||
| 2540 | // r"", x"" | ||
| 2541 | /\b[rx]"(?:\\[\s\S]|[^\\"])*"[cwd]?/.source, | ||
| 2542 | |||
| 2543 | // q"[]", q"()", q"<>", q"{}" | ||
| 2544 | /\bq"(?:\[[\s\S]*?\]|\([\s\S]*?\)|<[\s\S]*?>|\{[\s\S]*?\})"/.source, | ||
| 2545 | |||
| 2546 | // q"IDENT | ||
| 2547 | // ... | ||
| 2548 | // IDENT" | ||
| 2549 | /\bq"((?!\d)\w+)$[\s\S]*?^\1"/.source, | ||
| 2550 | |||
| 2551 | // q"//", q"||", etc. | ||
| 2552 | // eslint-disable-next-line regexp/strict | ||
| 2553 | /\bq"(.)[\s\S]*?\2"/.source, | ||
| 2554 | |||
6302 more lines aren’t shown here.
| @@ -10,2457 +10,2776 @@ · 9 unmodified lines | |||
| 10 | * | 10 | * |
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Raven = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ | 13 | (function (f) { |
| 14 | function RavenConfigError(message) { | 14 | if (typeof exports === "object" && typeof module !== "undefined") { |
| 15 | this.name = 'RavenConfigError'; | 15 | module.exports = f(); |
| 16 | this.message = message; | 16 | } else if (typeof define === "function" && define.amd) { |
| 17 | } | 17 | define([], f); |
| 18 | RavenConfigError.prototype = new Error(); | 18 | } else { |
| 19 | RavenConfigError.prototype.constructor = RavenConfigError; | 19 | var g; |
| 20 | 20 | if (typeof window !== "undefined") { | |
| 21 | module.exports = RavenConfigError; | 21 | g = window; |
| 22 | 22 | } else if (typeof global !== "undefined") { | |
| 23 | },{}],2:[function(_dereq_,module,exports){ | 23 | g = global; |
| 24 | var wrapMethod = function(console, level, callback) { | 24 | } else if (typeof self !== "undefined") { |
| 25 | var originalConsoleLevel = console[level]; | 25 | g = self; |
| 26 | var originalConsole = console; | 26 | } else { |
| 27 | 27 | g = this; | |
| 28 | if (!(level in console)) { | 28 | } |
| 29 | return; | 29 | g.Raven = f(); |
| 30 | } | 30 | } |
| 31 | })(function () { | ||
| 32 | var define, module, exports; | ||
| 33 | return (function e(t, n, r) { | ||
| 34 | function s(o, u) { | ||
| 35 | if (!n[o]) { | ||
| 36 | if (!t[o]) { | ||
| 37 | var a = typeof require == "function" && require; | ||
| 38 | if (!u && a) return a(o, !0); | ||
| 39 | if (i) return i(o, !0); | ||
| 40 | var f = new Error("Cannot find module '" + o + "'"); | ||
| 41 | throw ((f.code = "MODULE_NOT_FOUND"), f); | ||
| 42 | } | ||
| 43 | var l = (n[o] = { exports: {} }); | ||
| 44 | t[o][0].call( | ||
| 45 | l.exports, | ||
| 46 | function (e) { | ||
| 47 | var n = t[o][1][e]; | ||
| 48 | return s(n ? n : e); | ||
| 49 | }, | ||
| 50 | l, | ||
| 51 | l.exports, | ||
| 52 | e, | ||
| 53 | t, | ||
| 54 | n, | ||
| 55 | r, | ||
| 56 | ); | ||
| 57 | } | ||
| 58 | return n[o].exports; | ||
| 59 | } | ||
| 60 | var i = typeof require == "function" && require; | ||
| 61 | for (var o = 0; o < r.length; o++) s(r[o]); | ||
| 62 | return s; | ||
| 63 | })( | ||
| 64 | { | ||
| 65 | 1: [ | ||
| 66 | function (_dereq_, module, exports) { | ||
| 67 | function RavenConfigError(message) { | ||
| 68 | this.name = "RavenConfigError"; | ||
| 69 | this.message = message; | ||
| 70 | } | ||
| 71 | RavenConfigError.prototype = new Error(); | ||
| 72 | RavenConfigError.prototype.constructor = RavenConfigError; | ||
| 31 | 73 | ||
| 32 | var sentryLevel = level === 'warn' ? 'warning' : level; | 74 | module.exports = RavenConfigError; |
| 75 | }, | ||
| 76 | {}, | ||
| 77 | ], | ||
| 78 | 2: [ | ||
| 79 | function (_dereq_, module, exports) { | ||
| 80 | var wrapMethod = function (console, level, callback) { | ||
| 81 | var originalConsoleLevel = console[level]; | ||
| 82 | var originalConsole = console; | ||
| 83 | |||
| 84 | if (!(level in console)) { | ||
| 85 | return; | ||
| 86 | } | ||
| 33 | 87 | ||
| 34 | console[level] = function() { | 88 | var sentryLevel = level === "warn" ? "warning" : level; |
| 35 | var args = [].slice.call(arguments); | ||
| 36 | 89 | ||
| 37 | var msg = '' + args.join(' '); | 90 | console[level] = function () { |
| 38 | var data = {level: sentryLevel, logger: 'console', extra: {arguments: args}}; | 91 | var args = [].slice.call(arguments); |
| 39 | 92 | ||
| 40 | if (level === 'assert') { | 93 | var msg = "" + args.join(" "); |
| 41 | if (args[0] === false) { | 94 | var data = { |
| 42 | // Default browsers message | 95 | level: sentryLevel, |
| 43 | msg = 'Assertion failed: ' + (args.slice(1).join(' ') || 'console.assert'); | 96 | logger: "console", |
| 44 | data.extra.arguments = args.slice(1); | 97 | extra: { arguments: args }, |
| 45 | callback && callback(msg, data); | 98 | }; |
| 46 | } | ||
| 47 | } else { | ||
| 48 | callback && callback(msg, data); | ||
| 49 | } | ||
| 50 | 99 | ||
| 51 | // this fails for some browsers. :( | 100 | if (level === "assert") { |
| 52 | if (originalConsoleLevel) { | 101 | if (args[0] === false) { |
| 53 | // IE9 doesn't allow calling apply on console functions directly | 102 | // Default browsers message |
| 54 | // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193 | 103 | msg = |
| 55 | Function.prototype.apply.call(originalConsoleLevel, originalConsole, args); | 104 | "Assertion failed: " + |
| 56 | } | 105 | (args.slice(1).join(" ") || "console.assert"); |
| 57 | }; | 106 | data.extra.arguments = args.slice(1); |
| 58 | }; | 107 | callback && callback(msg, data); |
| 59 | 108 | } | |
| 60 | module.exports = { | 109 | } else { |
| 61 | wrapMethod: wrapMethod | 110 | callback && callback(msg, data); |
| 62 | }; | 111 | } |
| 63 | |||
| 64 | },{}],3:[function(_dereq_,module,exports){ | ||
| 65 | (function (global){ | ||
| 66 | /*global XDomainRequest:false */ | ||
| 67 | |||
| 68 | var TraceKit = _dereq_(6); | ||
| 69 | var stringify = _dereq_(7); | ||
| 70 | var RavenConfigError = _dereq_(1); | ||
| 71 | |||
| 72 | var utils = _dereq_(5); | ||
| 73 | var isError = utils.isError; | ||
| 74 | var isObject = utils.isObject; | ||
| 75 | var isErrorEvent = utils.isErrorEvent; | ||
| 76 | var isUndefined = utils.isUndefined; | ||
| 77 | var isFunction = utils.isFunction; | ||
| 78 | var isString = utils.isString; | ||
| 79 | var isArray = utils.isArray; | ||
| 80 | var isEmptyObject = utils.isEmptyObject; | ||
| 81 | var each = utils.each; | ||
| 82 | var objectMerge = utils.objectMerge; | ||
| 83 | var truncate = utils.truncate; | ||
| 84 | var objectFrozen = utils.objectFrozen; | ||
| 85 | var hasKey = utils.hasKey; | ||
| 86 | var joinRegExp = utils.joinRegExp; | ||
| 87 | var urlencode = utils.urlencode; | ||
| 88 | var uuid4 = utils.uuid4; | ||
| 89 | var htmlTreeAsString = utils.htmlTreeAsString; | ||
| 90 | var isSameException = utils.isSameException; | ||
| 91 | var isSameStacktrace = utils.isSameStacktrace; | ||
| 92 | var parseUrl = utils.parseUrl; | ||
| 93 | var fill = utils.fill; | ||
| 94 | |||
| 95 | var wrapConsoleMethod = _dereq_(2).wrapMethod; | ||
| 96 | |||
| 97 | var dsnKeys = 'source protocol user pass host port path'.split(' '), | ||
| 98 | dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/; | ||
| 99 | |||
| 100 | function now() { | ||
| 101 | return +new Date(); | ||
| 102 | } | ||
| 103 | |||
| 104 | // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) | ||
| 105 | var _window = | ||
| 106 | typeof window !== 'undefined' | ||
| 107 | ? window | ||
| 108 | : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | ||
| 109 | var _document = _window.document; | ||
| 110 | var _navigator = _window.navigator; | ||
| 111 | |||
| 112 | function keepOriginalCallback(original, callback) { | ||
| 113 | return isFunction(callback) | ||
| 114 | ? function(data) { | ||
| 115 | return callback(data, original); | ||
| 116 | } | ||
| 117 | : callback; | ||
| 118 | } | ||
| 119 | |||
| 120 | // First, check for JSON support | ||
| 121 | // If there is no JSON, we no-op the core features of Raven | ||
| 122 | // since JSON is required to encode the payload | ||
| 123 | function Raven() { | ||
| 124 | this._hasJSON = !!(typeof JSON === 'object' && JSON.stringify); | ||
| 125 | // Raven can run in contexts where there's no document (react-native) | ||
| 126 | this._hasDocument = !isUndefined(_document); | ||
| 127 | this._hasNavigator = !isUndefined(_navigator); | ||
| 128 | this._lastCapturedException = null; | ||
| 129 | this._lastData = null; | ||
| 130 | this._lastEventId = null; | ||
| 131 | this._globalServer = null; | ||
| 132 | this._globalKey = null; | ||
| 133 | this._globalProject = null; | ||
| 134 | this._globalContext = {}; | ||
| 135 | this._globalOptions = { | ||
| 136 | logger: 'javascript', | ||
| 137 | ignoreErrors: [], | ||
| 138 | ignoreUrls: [], | ||
| 139 | whitelistUrls: [], | ||
| 140 | includePaths: [], | ||
| 141 | collectWindowErrors: true, | ||
| 142 | maxMessageLength: 0, | ||
| 143 | |||
| 144 | // By default, truncates URL values to 250 chars | ||
| 145 | maxUrlLength: 250, | ||
| 146 | stackTraceLimit: 50, | ||
| 147 | autoBreadcrumbs: true, | ||
| 148 | instrument: true, | ||
| 149 | sampleRate: 1 | ||
| 150 | }; | ||
| 151 | this._ignoreOnError = 0; | ||
| 152 | this._isRavenInstalled = false; | ||
| 153 | this._originalErrorStackTraceLimit = Error.stackTraceLimit; | ||
| 154 | // capture references to window.console *and* all its methods first | ||
| 155 | // before the console plugin has a chance to monkey patch | ||
| 156 | this._originalConsole = _window.console || {}; | ||
| 157 | this._originalConsoleMethods = {}; | ||
| 158 | this._plugins = []; | ||
| 159 | this._startTime = now(); | ||
| 160 | this._wrappedBuiltIns = []; | ||
| 161 | this._breadcrumbs = []; | ||
| 162 | this._lastCapturedEvent = null; | ||
| 163 | this._keypressTimeout; | ||
| 164 | this._location = _window.location; | ||
| 165 | this._lastHref = this._location && this._location.href; | ||
| 166 | this._resetBackoff(); | ||
| 167 | |||
| 168 | // eslint-disable-next-line guard-for-in | ||
| 169 | for (var method in this._originalConsole) { | ||
| 170 | this._originalConsoleMethods[method] = this._originalConsole[method]; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | 112 | ||
| 174 | /* | 113 | // this fails for some browsers. :( |
| 175 | * The core Raven singleton | 114 | if (originalConsoleLevel) { |
| 176 | * | 115 | // IE9 doesn't allow calling apply on console functions directly |
| 177 | * @this {Raven} | 116 | // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193 |
| 178 | */ | 117 | Function.prototype.apply.call( |
| 118 | originalConsoleLevel, | ||
| 119 | originalConsole, | ||
| 120 | args, | ||
| 121 | ); | ||
| 122 | } | ||
| 123 | }; | ||
| 124 | }; | ||
| 179 | 125 | ||
| 180 | Raven.prototype = { | 126 | module.exports = { |
| 181 | // Hardcode version string so that raven source can be loaded directly via | 127 | wrapMethod: wrapMethod, |
| 182 | // webpack (using a build step causes webpack #1617). Grunt verifies that | 128 | }; |
| 183 | // this value matches package.json during build. | 129 | }, |
| 184 | // See: https://github.com/getsentry/raven-js/issues/465 | 130 | {}, |
| 185 | VERSION: '3.20.1', | 131 | ], |
| 186 | 132 | 3: [ | |
| 187 | debug: false, | 133 | function (_dereq_, module, exports) { |
| 188 | 134 | (function (global) { | |
| 189 | TraceKit: TraceKit, // alias to TraceKit | 135 | /*global XDomainRequest:false */ |
| 190 | 136 | ||
| 191 | /* | 137 | var TraceKit = _dereq_(6); |
| 192 | * Configure Raven with a DSN and extra options | 138 | var stringify = _dereq_(7); |
| 193 | * | 139 | var RavenConfigError = _dereq_(1); |
| 194 | * @param {string} dsn The public Sentry DSN | 140 | |
| 195 | * @param {object} options Set of global options [optional] | 141 | var utils = _dereq_(5); |
| 196 | * @return {Raven} | 142 | var isError = utils.isError; |
| 197 | */ | 143 | var isObject = utils.isObject; |
| 198 | config: function(dsn, options) { | 144 | var isErrorEvent = utils.isErrorEvent; |
| 199 | var self = this; | 145 | var isUndefined = utils.isUndefined; |
| 200 | 146 | var isFunction = utils.isFunction; | |
| 201 | if (self._globalServer) { | 147 | var isString = utils.isString; |
| 202 | this._logDebug('error', 'Error: Raven has already been configured'); | 148 | var isArray = utils.isArray; |
| 203 | return self; | 149 | var isEmptyObject = utils.isEmptyObject; |
| 204 | } | 150 | var each = utils.each; |
| 205 | if (!dsn) return self; | 151 | var objectMerge = utils.objectMerge; |
| 206 | 152 | var truncate = utils.truncate; | |
| 207 | var globalOptions = self._globalOptions; | 153 | var objectFrozen = utils.objectFrozen; |
| 208 | 154 | var hasKey = utils.hasKey; | |
| 209 | // merge in options | 155 | var joinRegExp = utils.joinRegExp; |
| 210 | if (options) { | 156 | var urlencode = utils.urlencode; |
| 211 | each(options, function(key, value) { | 157 | var uuid4 = utils.uuid4; |
| 212 | // tags and extra are special and need to be put into context | 158 | var htmlTreeAsString = utils.htmlTreeAsString; |
| 213 | if (key === 'tags' || key === 'extra' || key === 'user') { | 159 | var isSameException = utils.isSameException; |
| 214 | self._globalContext[key] = value; | 160 | var isSameStacktrace = utils.isSameStacktrace; |
| 215 | } else { | 161 | var parseUrl = utils.parseUrl; |
| 216 | globalOptions[key] = value; | 162 | var fill = utils.fill; |
| 217 | } | 163 | |
| 218 | }); | 164 | var wrapConsoleMethod = _dereq_(2).wrapMethod; |
| 219 | } | 165 | |
| 166 | var dsnKeys = "source protocol user pass host port path".split(" "), | ||
| 167 | dsnPattern = | ||
| 168 | /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/; | ||
| 169 | |||
| 170 | function now() { | ||
| 171 | return +new Date(); | ||
| 172 | } | ||
| 220 | 173 | ||
| 221 | self.setDSN(dsn); | 174 | // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) |
| 222 | 175 | var _window = | |
| 223 | // "Script error." is hard coded into browsers for errors that it can't read. | 176 | typeof window !== "undefined" |
| 224 | // this is the result of a script being pulled in from an external domain and CORS. | 177 | ? window |
| 225 | globalOptions.ignoreErrors.push(/^Script error\.?$/); | 178 | : typeof global !== "undefined" |
| 226 | globalOptions.ignoreErrors.push(/^Javascript error: Script error\.? on line 0$/); | 179 | ? global |
| 227 | 180 | : typeof self !== "undefined" | |
| 228 | // join regexp rules into one big rule | 181 | ? self |
| 229 | globalOptions.ignoreErrors = joinRegExp(globalOptions.ignoreErrors); | 182 | : {}; |
| 230 | globalOptions.ignoreUrls = globalOptions.ignoreUrls.length | 183 | var _document = _window.document; |
| 231 | ? joinRegExp(globalOptions.ignoreUrls) | 184 | var _navigator = _window.navigator; |
| 232 | : false; | 185 | |
| 233 | globalOptions.whitelistUrls = globalOptions.whitelistUrls.length | 186 | function keepOriginalCallback(original, callback) { |
| 234 | ? joinRegExp(globalOptions.whitelistUrls) | 187 | return isFunction(callback) |
| 235 | : false; | 188 | ? function (data) { |
| 236 | globalOptions.includePaths = joinRegExp(globalOptions.includePaths); | 189 | return callback(data, original); |
| 237 | globalOptions.maxBreadcrumbs = Math.max( | 190 | } |
| 238 | 0, | 191 | : callback; |
| 239 | Math.min(globalOptions.maxBreadcrumbs || 100, 100) | 192 | } |
| 240 | ); // default and hard limit is 100 | ||
| 241 | |||
| 242 | var autoBreadcrumbDefaults = { | ||
| 243 | xhr: true, | ||
| 244 | console: true, | ||
| 245 | dom: true, | ||
| 246 | location: true, | ||
| 247 | sentry: true | ||
| 248 | }; | ||
| 249 | |||
| 250 | var autoBreadcrumbs = globalOptions.autoBreadcrumbs; | ||
| 251 | if ({}.toString.call(autoBreadcrumbs) === '[object Object]') { | ||
| 252 | autoBreadcrumbs = objectMerge(autoBreadcrumbDefaults, autoBreadcrumbs); | ||
| 253 | } else if (autoBreadcrumbs !== false) { | ||
| 254 | autoBreadcrumbs = autoBreadcrumbDefaults; | ||
| 255 | } | ||
| 256 | globalOptions.autoBreadcrumbs = autoBreadcrumbs; | ||
| 257 | 193 | ||
| 258 | var instrumentDefaults = { | 194 | // First, check for JSON support |
| 259 | tryCatch: true | 195 | // If there is no JSON, we no-op the core features of Raven |
| 260 | }; | 196 | // since JSON is required to encode the payload |
| 197 | function Raven() { | ||
| 198 | this._hasJSON = !!(typeof JSON === "object" && JSON.stringify); | ||
| 199 | // Raven can run in contexts where there's no document (react-native) | ||
| 200 | this._hasDocument = !isUndefined(_document); | ||
| 201 | this._hasNavigator = !isUndefined(_navigator); | ||
| 202 | this._lastCapturedException = null; | ||
| 203 | this._lastData = null; | ||
| 204 | this._lastEventId = null; | ||
| 205 | this._globalServer = null; | ||
| 206 | this._globalKey = null; | ||
| 207 | this._globalProject = null; | ||
| 208 | this._globalContext = {}; | ||
| 209 | this._globalOptions = { | ||
| 210 | logger: "javascript", | ||
| 211 | ignoreErrors: [], | ||
| 212 | ignoreUrls: [], | ||
| 213 | whitelistUrls: [], | ||
| 214 | includePaths: [], | ||
| 215 | collectWindowErrors: true, | ||
| 216 | maxMessageLength: 0, | ||
| 217 | |||
| 218 | // By default, truncates URL values to 250 chars | ||
| 219 | maxUrlLength: 250, | ||
| 220 | stackTraceLimit: 50, | ||
| 221 | autoBreadcrumbs: true, | ||
| 222 | instrument: true, | ||
| 223 | sampleRate: 1, | ||
| 224 | }; | ||
| 225 | this._ignoreOnError = 0; | ||
| 226 | this._isRavenInstalled = false; | ||
| 227 | this._originalErrorStackTraceLimit = Error.stackTraceLimit; | ||
| 228 | // capture references to window.console *and* all its methods first | ||
| 229 | // before the console plugin has a chance to monkey patch | ||
| 230 | this._originalConsole = _window.console || {}; | ||
| 231 | this._originalConsoleMethods = {}; | ||
| 232 | this._plugins = []; | ||
| 233 | this._startTime = now(); | ||
| 234 | this._wrappedBuiltIns = []; | ||
| 235 | this._breadcrumbs = []; | ||
| 236 | this._lastCapturedEvent = null; | ||
| 237 | this._keypressTimeout; | ||
| 238 | this._location = _window.location; | ||
| 239 | this._lastHref = this._location && this._location.href; | ||
| 240 | this._resetBackoff(); | ||
| 241 | |||
| 242 | // eslint-disable-next-line guard-for-in | ||
| 243 | for (var method in this._originalConsole) { | ||
| 244 | this._originalConsoleMethods[method] = | ||
| 245 | this._originalConsole[method]; | ||
| 246 | } | ||
| 247 | } | ||
| 261 | 248 | ||
| 262 | var instrument = globalOptions.instrument; | 249 | /* |
| 263 | if ({}.toString.call(instrument) === '[object Object]') { | 250 | * The core Raven singleton |
| 264 | instrument = objectMerge(instrumentDefaults, instrument); | 251 | * |
| 265 | } else if (instrument !== false) { | 252 | * @this {Raven} |
| 266 | instrument = instrumentDefaults; | 253 | */ |
| 267 | } | 254 | |
| 268 | globalOptions.instrument = instrument; | 255 | Raven.prototype = { |
| 269 | 256 | // Hardcode version string so that raven source can be loaded directly via | |
| 270 | TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors; | 257 | // webpack (using a build step causes webpack #1617). Grunt verifies that |
| 271 | 258 | // this value matches package.json during build. | |
| 272 | // return for chaining | 259 | // See: https://github.com/getsentry/raven-js/issues/465 |
| 273 | return self; | 260 | VERSION: "3.20.1", |
| 274 | }, | 261 | |
| 275 | 262 | debug: false, | |
| 276 | /* | 263 | |
| 277 | * Installs a global window.onerror error handler | 264 | TraceKit: TraceKit, // alias to TraceKit |
| 278 | * to capture and report uncaught exceptions. | 265 | |
| 279 | * At this point, install() is required to be called due | 266 | /* |
| 280 | * to the way TraceKit is set up. | 267 | * Configure Raven with a DSN and extra options |
| 281 | * | 268 | * |
| 282 | * @return {Raven} | 269 | * @param {string} dsn The public Sentry DSN |
| 283 | */ | 270 | * @param {object} options Set of global options [optional] |
| 284 | install: function() { | 271 | * @return {Raven} |
| 285 | var self = this; | 272 | */ |
| 286 | if (self.isSetup() && !self._isRavenInstalled) { | 273 | config: function (dsn, options) { |
| 287 | TraceKit.report.subscribe(function() { | 274 | var self = this; |
| 288 | self._handleOnErrorStackInfo.apply(self, arguments); | 275 | |
| 289 | }); | 276 | if (self._globalServer) { |
| 290 | 277 | this._logDebug( | |
| 291 | self._patchFunctionToString(); | 278 | "error", |
| 292 | 279 | "Error: Raven has already been configured", | |
| 293 | if (self._globalOptions.instrument && self._globalOptions.instrument.tryCatch) { | 280 | ); |
| 294 | self._instrumentTryCatch(); | 281 | return self; |
| 295 | } | 282 | } |
| 283 | if (!dsn) return self; | ||
| 284 | |||
| 285 | var globalOptions = self._globalOptions; | ||
| 286 | |||
| 287 | // merge in options | ||
| 288 | if (options) { | ||
| 289 | each(options, function (key, value) { | ||
| 290 | // tags and extra are special and need to be put into context | ||
| 291 | if (key === "tags" || key === "extra" || key === "user") { | ||
| 292 | self._globalContext[key] = value; | ||
| 293 | } else { | ||
| 294 | globalOptions[key] = value; | ||
| 295 | } | ||
| 296 | }); | ||
| 297 | } | ||
| 296 | 298 | ||
| 297 | if (self._globalOptions.autoBreadcrumbs) self._instrumentBreadcrumbs(); | 299 | self.setDSN(dsn); |
| 300 | |||
| 301 | // "Script error." is hard coded into browsers for errors that it can't read. | ||
| 302 | // this is the result of a script being pulled in from an external domain and CORS. | ||
| 303 | globalOptions.ignoreErrors.push(/^Script error\.?$/); | ||
| 304 | globalOptions.ignoreErrors.push( | ||
| 305 | /^Javascript error: Script error\.? on line 0$/, | ||
| 306 | ); | ||
| 307 | |||
| 308 | // join regexp rules into one big rule | ||
| 309 | globalOptions.ignoreErrors = joinRegExp( | ||
| 310 | globalOptions.ignoreErrors, | ||
| 311 | ); | ||
| 312 | globalOptions.ignoreUrls = globalOptions.ignoreUrls.length | ||
| 313 | ? joinRegExp(globalOptions.ignoreUrls) | ||
| 314 | : false; | ||
| 315 | globalOptions.whitelistUrls = globalOptions.whitelistUrls.length | ||
| 316 | ? joinRegExp(globalOptions.whitelistUrls) | ||
| 317 | : false; | ||
| 318 | globalOptions.includePaths = joinRegExp( | ||
| 319 | globalOptions.includePaths, | ||
| 320 | ); | ||
| 321 | globalOptions.maxBreadcrumbs = Math.max( | ||
| 322 | 0, | ||
| 323 | Math.min(globalOptions.maxBreadcrumbs || 100, 100), | ||
| 324 | ); // default and hard limit is 100 | ||
| 325 | |||
| 326 | var autoBreadcrumbDefaults = { | ||
| 327 | xhr: true, | ||
| 328 | console: true, | ||
| 329 | dom: true, | ||
| 330 | location: true, | ||
| 331 | sentry: true, | ||
| 332 | }; | ||
| 298 | 333 | ||
| 299 | // Install all of the plugins | 334 | var autoBreadcrumbs = globalOptions.autoBreadcrumbs; |
| 300 | self._drainPlugins(); | 335 | if ({}.toString.call(autoBreadcrumbs) === "[object Object]") { |
| 336 | autoBreadcrumbs = objectMerge( | ||
| 337 | autoBreadcrumbDefaults, | ||
| 338 | autoBreadcrumbs, | ||
| 339 | ); | ||
| 340 | } else if (autoBreadcrumbs !== false) { | ||
| 341 | autoBreadcrumbs = autoBreadcrumbDefaults; | ||
| 342 | } | ||
| 343 | globalOptions.autoBreadcrumbs = autoBreadcrumbs; | ||
| 301 | 344 | ||
| 302 | self._isRavenInstalled = true; | 345 | var instrumentDefaults = { |
| 303 | } | 346 | tryCatch: true, |
| 347 | }; | ||
| 304 | 348 | ||
| 305 | Error.stackTraceLimit = self._globalOptions.stackTraceLimit; | 349 | var instrument = globalOptions.instrument; |
| 306 | return this; | 350 | if ({}.toString.call(instrument) === "[object Object]") { |
| 307 | }, | 351 | instrument = objectMerge(instrumentDefaults, instrument); |
| 308 | 352 | } else if (instrument !== false) { | |
| 309 | /* | 353 | instrument = instrumentDefaults; |
| 310 | * Set the DSN (can be called multiple time unlike config) | 354 | } |
| 311 | * | 355 | globalOptions.instrument = instrument; |
| 312 | * @param {string} dsn The public Sentry DSN | 356 | |
| 313 | */ | 357 | TraceKit.collectWindowErrors = |
| 314 | setDSN: function(dsn) { | 358 | !!globalOptions.collectWindowErrors; |
| 315 | var self = this, | 359 | |
| 316 | uri = self._parseDSN(dsn), | 360 | // return for chaining |
| 317 | lastSlash = uri.path.lastIndexOf('/'), | 361 | return self; |
| 318 | path = uri.path.substr(1, lastSlash); | 362 | }, |
| 319 | 363 | ||
| 320 | self._dsn = dsn; | 364 | /* |
| 321 | self._globalKey = uri.user; | 365 | * Installs a global window.onerror error handler |
| 322 | self._globalSecret = uri.pass && uri.pass.substr(1); | 366 | * to capture and report uncaught exceptions. |
| 323 | self._globalProject = uri.path.substr(lastSlash + 1); | 367 | * At this point, install() is required to be called due |
| 324 | 368 | * to the way TraceKit is set up. | |
| 325 | self._globalServer = self._getGlobalServer(uri); | 369 | * |
| 326 | 370 | * @return {Raven} | |
| 327 | self._globalEndpoint = | 371 | */ |
| 328 | self._globalServer + '/' + path + 'api/' + self._globalProject + '/store/'; | 372 | install: function () { |
| 329 | 373 | var self = this; | |
| 330 | // Reset backoff state since we may be pointing at a | 374 | if (self.isSetup() && !self._isRavenInstalled) { |
| 331 | // new project/server | 375 | TraceKit.report.subscribe(function () { |
| 332 | this._resetBackoff(); | 376 | self._handleOnErrorStackInfo.apply(self, arguments); |
| 333 | }, | 377 | }); |
| 334 | 378 | ||
| 335 | /* | 379 | self._patchFunctionToString(); |
| 336 | * Wrap code within a context so Raven can capture errors | 380 | |
| 337 | * reliably across domains that is executed immediately. | 381 | if ( |
| 338 | * | 382 | self._globalOptions.instrument && |
| 339 | * @param {object} options A specific set of options for this context [optional] | 383 | self._globalOptions.instrument.tryCatch |
| 340 | * @param {function} func The callback to be immediately executed within the context | 384 | ) { |
| 341 | * @param {array} args An array of arguments to be called with the callback [optional] | 385 | self._instrumentTryCatch(); |
| 342 | */ | 386 | } |
| 343 | context: function(options, func, args) { | ||
| 344 | if (isFunction(options)) { | ||
| 345 | args = func || []; | ||
| 346 | func = options; | ||
| 347 | options = undefined; | ||
| 348 | } | ||
| 349 | 387 | ||
| 350 | return this.wrap(options, func).apply(this, args); | 388 | if (self._globalOptions.autoBreadcrumbs) |
| 351 | }, | 389 | self._instrumentBreadcrumbs(); |
| 352 | |||
| 353 | /* | ||
| 354 | * Wrap code within a context and returns back a new function to be executed | ||
| 355 | * | ||
| 356 | * @param {object} options A specific set of options for this context [optional] | ||
| 357 | * @param {function} func The function to be wrapped in a new context | ||
| 358 | * @param {function} func A function to call before the try/catch wrapper [optional, private] | ||
| 359 | * @return {function} The newly wrapped functions with a context | ||
| 360 | */ | ||
| 361 | wrap: function(options, func, _before) { | ||
| 362 | var self = this; | ||
| 363 | // 1 argument has been passed, and it's not a function | ||
| 364 | // so just return it | ||
| 365 | if (isUndefined(func) && !isFunction(options)) { | ||
| 366 | return options; | ||
| 367 | } | ||
| 368 | 390 | ||
| 369 | // options is optional | 391 | // Install all of the plugins |
| 370 | if (isFunction(options)) { | 392 | self._drainPlugins(); |
| 371 | func = options; | ||
| 372 | options = undefined; | ||
| 373 | } | ||
| 374 | 393 | ||
| 375 | // At this point, we've passed along 2 arguments, and the second one | 394 | self._isRavenInstalled = true; |
| 376 | // is not a function either, so we'll just return the second argument. | 395 | } |
| 377 | if (!isFunction(func)) { | ||
| 378 | return func; | ||
| 379 | } | ||
| 380 | 396 | ||
| 381 | // We don't wanna wrap it twice! | 397 | Error.stackTraceLimit = self._globalOptions.stackTraceLimit; |
| 382 | try { | 398 | return this; |
| 383 | if (func.__raven__) { | 399 | }, |
| 384 | return func; | 400 | |
| 385 | } | 401 | /* |
| 402 | * Set the DSN (can be called multiple time unlike config) | ||
| 403 | * | ||
| 404 | * @param {string} dsn The public Sentry DSN | ||
| 405 | */ | ||
| 406 | setDSN: function (dsn) { | ||
| 407 | var self = this, | ||
| 408 | uri = self._parseDSN(dsn), | ||
| 409 | lastSlash = uri.path.lastIndexOf("/"), | ||
| 410 | path = uri.path.substr(1, lastSlash); | ||
| 411 | |||
| 412 | self._dsn = dsn; | ||
| 413 | self._globalKey = uri.user; | ||
| 414 | self._globalSecret = uri.pass && uri.pass.substr(1); | ||
| 415 | self._globalProject = uri.path.substr(lastSlash + 1); | ||
| 416 | |||
| 417 | self._globalServer = self._getGlobalServer(uri); | ||
| 418 | |||
| 419 | self._globalEndpoint = | ||
| 420 | self._globalServer + | ||
| 421 | "/" + | ||
| 422 | path + | ||
| 423 | "api/" + | ||
| 424 | self._globalProject + | ||
| 425 | "/store/"; | ||
| 426 | |||
| 427 | // Reset backoff state since we may be pointing at a | ||
| 428 | // new project/server | ||
| 429 | this._resetBackoff(); | ||
| 430 | }, | ||
| 431 | |||
| 432 | /* | ||
| 433 | * Wrap code within a context so Raven can capture errors | ||
| 434 | * reliably across domains that is executed immediately. | ||
| 435 | * | ||
| 436 | * @param {object} options A specific set of options for this context [optional] | ||
| 437 | * @param {function} func The callback to be immediately executed within the context | ||
| 438 | * @param {array} args An array of arguments to be called with the callback [optional] | ||
| 439 | */ | ||
| 440 | context: function (options, func, args) { | ||
| 441 | if (isFunction(options)) { | ||
| 442 | args = func || []; | ||
| 443 | func = options; | ||
| 444 | options = undefined; | ||
| 445 | } | ||
| 386 | 446 | ||
| 387 | // If this has already been wrapped in the past, return that | 447 | return this.wrap(options, func).apply(this, args); |
| 388 | if (func.__raven_wrapper__) { | 448 | }, |
| 389 | return func.__raven_wrapper__; | 449 | |
| 390 | } | 450 | /* |
| 391 | } catch (e) { | 451 | * Wrap code within a context and returns back a new function to be executed |
| 392 | // Just accessing custom props in some Selenium environments | 452 | * |
| 393 | // can cause a "Permission denied" exception (see raven-js#495). | 453 | * @param {object} options A specific set of options for this context [optional] |
| 394 | // Bail on wrapping and return the function as-is (defers to window.onerror). | 454 | * @param {function} func The function to be wrapped in a new context |
| 395 | return func; | 455 | * @param {function} func A function to call before the try/catch wrapper [optional, private] |
| 396 | } | 456 | * @return {function} The newly wrapped functions with a context |
| 457 | */ | ||
| 458 | wrap: function (options, func, _before) { | ||
| 459 | var self = this; | ||
| 460 | // 1 argument has been passed, and it's not a function | ||
| 461 | // so just return it | ||
| 462 | if (isUndefined(func) && !isFunction(options)) { | ||
| 463 | return options; | ||
| 464 | } | ||
| 397 | 465 | ||
| 398 | function wrapped() { | 466 | // options is optional |
| 399 | var args = [], | 467 | if (isFunction(options)) { |
| 400 | i = arguments.length, | 468 | func = options; |
| 401 | deep = !options || (options && options.deep !== false); | 469 | options = undefined; |
| 470 | } | ||
| 402 | 471 | ||
| 403 | if (_before && isFunction(_before)) { | 472 | // At this point, we've passed along 2 arguments, and the second one |
| 404 | _before.apply(this, arguments); | 473 | // is not a function either, so we'll just return the second argument. |
| 405 | } | 474 | if (!isFunction(func)) { |
| 475 | return func; | ||
| 476 | } | ||
| 406 | 477 | ||
| 407 | // Recursively wrap all of a function's arguments that are | 478 | // We don't wanna wrap it twice! |
| 408 | // functions themselves. | 479 | try { |
| 409 | while (i--) args[i] = deep ? self.wrap(options, arguments[i]) : arguments[i]; | 480 | if (func.__raven__) { |
| 410 | 481 | return func; | |
| 411 | try { | 482 | } |
| 412 | // Attempt to invoke user-land function | ||
| 413 | // NOTE: If you are a Sentry user, and you are seeing this stack frame, it | ||
| 414 | // means Raven caught an error invoking your application code. This is | ||
| 415 | // expected behavior and NOT indicative of a bug with Raven.js. | ||
| 416 | return func.apply(this, args); | ||
| 417 | } catch (e) { | ||
| 418 | self._ignoreNextOnError(); | ||
| 419 | self.captureException(e, options); | ||
| 420 | throw e; | ||
| 421 | } | ||
| 422 | } | ||
| 423 | 483 | ||
| 424 | // copy over properties of the old function | 484 | // If this has already been wrapped in the past, return that |
| 425 | for (var property in func) { | 485 | if (func.__raven_wrapper__) { |
| 426 | if (hasKey(func, property)) { | 486 | return func.__raven_wrapper__; |
| 427 | wrapped[property] = func[property]; | 487 | } |
| 428 | } | 488 | } catch (e) { |
| 429 | } | 489 | // Just accessing custom props in some Selenium environments |
| 430 | wrapped.prototype = func.prototype; | 490 | // can cause a "Permission denied" exception (see raven-js#495). |
| 431 | 491 | // Bail on wrapping and return the function as-is (defers to window.onerror). | |
| 432 | func.__raven_wrapper__ = wrapped; | 492 | return func; |
| 433 | // Signal that this function has been wrapped/filled already | 493 | } |
| 434 | // for both debugging and to prevent it to being wrapped/filled twice | ||
| 435 | wrapped.__raven__ = true; | ||
| 436 | wrapped.__orig__ = func; | ||
| 437 | |||
| 438 | return wrapped; | ||
| 439 | }, | ||
| 440 | |||
| 441 | /* | ||
| 442 | * Uninstalls the global error handler. | ||
| 443 | * | ||
| 444 | * @return {Raven} | ||
| 445 | */ | ||
| 446 | uninstall: function() { | ||
| 447 | TraceKit.report.uninstall(); | ||
| 448 | |||
| 449 | this._unpatchFunctionToString(); | ||
| 450 | this._restoreBuiltIns(); | ||
| 451 | |||
| 452 | Error.stackTraceLimit = this._originalErrorStackTraceLimit; | ||
| 453 | this._isRavenInstalled = false; | ||
| 454 | |||
| 455 | return this; | ||
| 456 | }, | ||
| 457 | |||
| 458 | /* | ||
| 459 | * Manually capture an exception and send it over to Sentry | ||
| 460 | * | ||
| 461 | * @param {error} ex An exception to be logged | ||
| 462 | * @param {object} options A specific set of options for this error [optional] | ||
| 463 | * @return {Raven} | ||
| 464 | */ | ||
| 465 | captureException: function(ex, options) { | ||
| 466 | // Cases for sending ex as a message, rather than an exception | ||
| 467 | var isNotError = !isError(ex); | ||
| 468 | var isNotErrorEvent = !isErrorEvent(ex); | ||
| 469 | var isErrorEventWithoutError = isErrorEvent(ex) && !ex.error; | ||
| 470 | |||
| 471 | if ((isNotError && isNotErrorEvent) || isErrorEventWithoutError) { | ||
| 472 | return this.captureMessage( | ||
| 473 | ex, | ||
| 474 | objectMerge( | ||
| 475 | { | ||
| 476 | trimHeadFrames: 1, | ||
| 477 | stacktrace: true // if we fall back to captureMessage, default to attempting a new trace | ||
| 478 | }, | ||
| 479 | options | ||
| 480 | ) | ||
| 481 | ); | ||
| 482 | } | ||
| 483 | 494 | ||
| 484 | // Get actual Error from ErrorEvent | 495 | function wrapped() { |
| 485 | if (isErrorEvent(ex)) ex = ex.error; | 496 | var args = [], |
| 486 | 497 | i = arguments.length, | |
| 487 | // Store the raw exception object for potential debugging and introspection | 498 | deep = !options || (options && options.deep !== false); |
| 488 | this._lastCapturedException = ex; | ||
| 489 | |||
| 490 | // TraceKit.report will re-raise any exception passed to it, | ||
| 491 | // which means you have to wrap it in try/catch. Instead, we | ||
| 492 | // can wrap it here and only re-raise if TraceKit.report | ||
| 493 | // raises an exception different from the one we asked to | ||
| 494 | // report on. | ||
| 495 | try { | ||
| 496 | var stack = TraceKit.computeStackTrace(ex); | ||
| 497 | this._handleStackInfo(stack, options); | ||
| 498 | } catch (ex1) { | ||
| 499 | if (ex !== ex1) { | ||
| 500 | throw ex1; | ||
| 501 | } | ||
| 502 | } | ||
| 503 | 499 | ||
| 504 | return this; | 500 | if (_before && isFunction(_before)) { |
| 505 | }, | 501 | _before.apply(this, arguments); |
| 506 | 502 | } | |
| 507 | /* | ||
| 508 | * Manually send a message to Sentry | ||
| 509 | * | ||
| 510 | * @param {string} msg A plain message to be captured in Sentry | ||
| 511 | * @param {object} options A specific set of options for this message [optional] | ||
| 512 | * @return {Raven} | ||
| 513 | */ | ||
| 514 | captureMessage: function(msg, options) { | ||
| 515 | // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an | ||
| 516 | // early call; we'll error on the side of logging anything called before configuration since it's | ||
| 517 | // probably something you should see: | ||
| 518 | if ( | ||
| 519 | !!this._globalOptions.ignoreErrors.test && | ||
| 520 | this._globalOptions.ignoreErrors.test(msg) | ||
| 521 | ) { | ||
| 522 | return; | ||
| 523 | } | ||
| 524 | 503 | ||
| 525 | options = options || {}; | 504 | // Recursively wrap all of a function's arguments that are |
| 526 | 505 | // functions themselves. | |
| 527 | var data = objectMerge( | 506 | while (i--) |
| 528 | { | 507 | args[i] = deep |
| 529 | message: msg + '' // Make sure it's actually a string | 508 | ? self.wrap(options, arguments[i]) |
| 530 | }, | 509 | : arguments[i]; |
| 531 | options | ||
| 532 | ); | ||
| 533 | |||
| 534 | var ex; | ||
| 535 | // Generate a "synthetic" stack trace from this point. | ||
| 536 | // NOTE: If you are a Sentry user, and you are seeing this stack frame, it is NOT indicative | ||
| 537 | // of a bug with Raven.js. Sentry generates synthetic traces either by configuration, | ||
| 538 | // or if it catches a thrown object without a "stack" property. | ||
| 539 | try { | ||
| 540 | throw new Error(msg); | ||
| 541 | } catch (ex1) { | ||
| 542 | ex = ex1; | ||
| 543 | } | ||
| 544 | 510 | ||
| 545 | // null exception name so `Error` isn't prefixed to msg | 511 | try { |
| 546 | ex.name = null; | 512 | // Attempt to invoke user-land function |
| 547 | var stack = TraceKit.computeStackTrace(ex); | 513 | // NOTE: If you are a Sentry user, and you are seeing this stack frame, it |
| 514 | // means Raven caught an error invoking your application code. This is | ||
| 515 | // expected behavior and NOT indicative of a bug with Raven.js. | ||
| 516 | return func.apply(this, args); | ||
| 517 | } catch (e) { | ||
| 518 | self._ignoreNextOnError(); | ||
| 519 | self.captureException(e, options); | ||
| 520 | throw e; | ||
| 521 | } | ||
| 522 | } | ||
| 548 | 523 | ||
| 549 | // stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1] | 524 | // copy over properties of the old function |
| 550 | var initialCall = isArray(stack.stack) && stack.stack[1]; | 525 | for (var property in func) { |
| 551 | var fileurl = (initialCall && initialCall.url) || ''; | 526 | if (hasKey(func, property)) { |
| 527 | wrapped[property] = func[property]; | ||
| 528 | } | ||
| 529 | } | ||
| 530 | wrapped.prototype = func.prototype; | ||
| 531 | |||
| 532 | func.__raven_wrapper__ = wrapped; | ||
| 533 | // Signal that this function has been wrapped/filled already | ||
| 534 | // for both debugging and to prevent it to being wrapped/filled twice | ||
| 535 | wrapped.__raven__ = true; | ||
| 536 | wrapped.__orig__ = func; | ||
| 537 | |||
| 538 | return wrapped; | ||
| 539 | }, | ||
| 540 | |||
| 541 | /* | ||
| 542 | * Uninstalls the global error handler. | ||
| 543 | * | ||
| 544 | * @return {Raven} | ||
| 545 | */ | ||
| 546 | uninstall: function () { | ||
| 547 | TraceKit.report.uninstall(); | ||
| 548 | |||
| 549 | this._unpatchFunctionToString(); | ||
| 550 | this._restoreBuiltIns(); | ||
| 551 | |||
| 552 | Error.stackTraceLimit = this._originalErrorStackTraceLimit; | ||
| 553 | this._isRavenInstalled = false; | ||
| 554 | |||
| 555 | return this; | ||
| 556 | }, | ||
| 557 | |||
| 558 | /* | ||
| 559 | * Manually capture an exception and send it over to Sentry | ||
| 560 | * | ||
| 561 | * @param {error} ex An exception to be logged | ||
| 562 | * @param {object} options A specific set of options for this error [optional] | ||
| 563 | * @return {Raven} | ||
| 564 | */ | ||
| 565 | captureException: function (ex, options) { | ||
| 566 | // Cases for sending ex as a message, rather than an exception | ||
| 567 | var isNotError = !isError(ex); | ||
| 568 | var isNotErrorEvent = !isErrorEvent(ex); | ||
| 569 | var isErrorEventWithoutError = isErrorEvent(ex) && !ex.error; | ||
| 570 | |||
| 571 | if ( | ||
| 572 | (isNotError && isNotErrorEvent) || | ||
| 573 | isErrorEventWithoutError | ||
| 574 | ) { | ||
| 575 | return this.captureMessage( | ||
| 576 | ex, | ||
| 577 | objectMerge( | ||
| 578 | { | ||
| 579 | trimHeadFrames: 1, | ||
| 580 | stacktrace: true, // if we fall back to captureMessage, default to attempting a new trace | ||
| 581 | }, | ||
| 582 | options, | ||
| 583 | ), | ||
| 584 | ); | ||
| 585 | } | ||
| 552 | 586 | ||
| 553 | if ( | 587 | // Get actual Error from ErrorEvent |
| 554 | !!this._globalOptions.ignoreUrls.test && | 588 | if (isErrorEvent(ex)) ex = ex.error; |
| 555 | this._globalOptions.ignoreUrls.test(fileurl) | ||
| 556 | ) { | ||
| 557 | return; | ||
| 558 | } | ||
| 559 | 589 | ||
| 560 | if ( | 590 | // Store the raw exception object for potential debugging and introspection |
| 561 | !!this._globalOptions.whitelistUrls.test && | 591 | this._lastCapturedException = ex; |
| 562 | !this._globalOptions.whitelistUrls.test(fileurl) | ||
| 563 | ) { | ||
| 564 | return; | ||
| 565 | } | ||
| 566 | 592 | ||
| 567 | if (this._globalOptions.stacktrace || (options && options.stacktrace)) { | 593 | // TraceKit.report will re-raise any exception passed to it, |
| 568 | options = objectMerge( | 594 | // which means you have to wrap it in try/catch. Instead, we |
| 569 | { | 595 | // can wrap it here and only re-raise if TraceKit.report |
| 570 | // fingerprint on msg, not stack trace (legacy behavior, could be | 596 | // raises an exception different from the one we asked to |
| 571 | // revisited) | 597 | // report on. |
| 572 | fingerprint: msg, | 598 | try { |
| 573 | // since we know this is a synthetic trace, the top N-most frames | 599 | var stack = TraceKit.computeStackTrace(ex); |
| 574 | // MUST be from Raven.js, so mark them as in_app later by setting | 600 | this._handleStackInfo(stack, options); |
| 575 | // trimHeadFrames | 601 | } catch (ex1) { |
| 576 | trimHeadFrames: (options.trimHeadFrames || 0) + 1 | 602 | if (ex !== ex1) { |
| 577 | }, | 603 | throw ex1; |
| 578 | options | 604 | } |
| 579 | ); | 605 | } |
| 580 | |||
| 581 | var frames = this._prepareFrames(stack, options); | ||
| 582 | data.stacktrace = { | ||
| 583 | // Sentry expects frames oldest to newest | ||
| 584 | frames: frames.reverse() | ||
| 585 | }; | ||
| 586 | } | ||
| 587 | 606 | ||
| 588 | // Fire away! | 607 | return this; |
| 589 | this._send(data); | 608 | }, |
| 609 | |||
| 610 | /* | ||
| 611 | * Manually send a message to Sentry | ||
| 612 | * | ||
| 613 | * @param {string} msg A plain message to be captured in Sentry | ||
| 614 | * @param {object} options A specific set of options for this message [optional] | ||
| 615 | * @return {Raven} | ||
| 616 | */ | ||
| 617 | captureMessage: function (msg, options) { | ||
| 618 | // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an | ||
| 619 | // early call; we'll error on the side of logging anything called before configuration since it's | ||
| 620 | // probably something you should see: | ||
| 621 | if ( | ||
| 622 | !!this._globalOptions.ignoreErrors.test && | ||
| 623 | this._globalOptions.ignoreErrors.test(msg) | ||
| 624 | ) { | ||
| 625 | return; | ||
| 626 | } | ||
| 590 | 627 | ||
| 591 | return this; | 628 | options = options || {}; |
| 592 | }, | ||
| 593 | 629 | ||
| 594 | captureBreadcrumb: function(obj) { | 630 | var data = objectMerge( |
| 595 | var crumb = objectMerge( | 631 | { |
| 596 | { | 632 | message: msg + "", // Make sure it's actually a string |
| 597 | timestamp: now() / 1000 | 633 | }, |
| 598 | }, | 634 | options, |
| 599 | obj | 635 | ); |
| 600 | ); | ||
| 601 | 636 | ||
| 602 | if (isFunction(this._globalOptions.breadcrumbCallback)) { | 637 | var ex; |
| 603 | var result = this._globalOptions.breadcrumbCallback(crumb); | 638 | // Generate a "synthetic" stack trace from this point. |
| 639 | // NOTE: If you are a Sentry user, and you are seeing this stack frame, it is NOT indicative | ||
| 640 | // of a bug with Raven.js. Sentry generates synthetic traces either by configuration, | ||
| 641 | // or if it catches a thrown object without a "stack" property. | ||
| 642 | try { | ||
| 643 | throw new Error(msg); | ||
| 644 | } catch (ex1) { | ||
| 645 | ex = ex1; | ||
| 646 | } | ||
| 604 | 647 | ||
| 605 | if (isObject(result) && !isEmptyObject(result)) { | 648 | // null exception name so `Error` isn't prefixed to msg |
| 606 | crumb = result; | 649 | ex.name = null; |
| 607 | } else if (result === false) { | 650 | var stack = TraceKit.computeStackTrace(ex); |
| 608 | return this; | ||
| 609 | } | ||
| 610 | } | ||
| 611 | 651 | ||
| 612 | this._breadcrumbs.push(crumb); | 652 | // stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1] |
| 613 | if (this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs) { | 653 | var initialCall = isArray(stack.stack) && stack.stack[1]; |
| 614 | this._breadcrumbs.shift(); | 654 | var fileurl = (initialCall && initialCall.url) || ""; |
| 615 | } | ||
| 616 | return this; | ||
| 617 | }, | ||
| 618 | 655 | ||
| 619 | addPlugin: function(plugin /*arg1, arg2, ... argN*/) { | 656 | if ( |
| 620 | var pluginArgs = [].slice.call(arguments, 1); | 657 | !!this._globalOptions.ignoreUrls.test && |
| 658 | this._globalOptions.ignoreUrls.test(fileurl) | ||
| 659 | ) { | ||
| 660 | return; | ||
| 661 | } | ||
| 621 | 662 | ||
| 622 | this._plugins.push([plugin, pluginArgs]); | 663 | if ( |
| 623 | if (this._isRavenInstalled) { | 664 | !!this._globalOptions.whitelistUrls.test && |
| 624 | this._drainPlugins(); | 665 | !this._globalOptions.whitelistUrls.test(fileurl) |
| 625 | } | 666 | ) { |
| 667 | return; | ||
| 668 | } | ||
| 626 | 669 | ||
| 627 | return this; | 670 | if ( |
| 628 | }, | 671 | this._globalOptions.stacktrace || |
| 629 | 672 | (options && options.stacktrace) | |
| 630 | /* | 673 | ) { |
| 631 | * Set/clear a user to be sent along with the payload. | 674 | options = objectMerge( |
| 632 | * | 675 | { |
| 633 | * @param {object} user An object representing user data [optional] | 676 | // fingerprint on msg, not stack trace (legacy behavior, could be |
| 634 | * @return {Raven} | 677 | // revisited) |
| 635 | */ | 678 | fingerprint: msg, |
| 636 | setUserContext: function(user) { | 679 | // since we know this is a synthetic trace, the top N-most frames |
| 637 | // Intentionally do not merge here since that's an unexpected behavior. | 680 | // MUST be from Raven.js, so mark them as in_app later by setting |
| 638 | this._globalContext.user = user; | 681 | // trimHeadFrames |
| 639 | 682 | trimHeadFrames: (options.trimHeadFrames || 0) + 1, | |
| 640 | return this; | 683 | }, |
| 641 | }, | 684 | options, |
| 642 | 685 | ); | |
| 643 | /* | 686 | |
| 644 | * Merge extra attributes to be sent along with the payload. | 687 | var frames = this._prepareFrames(stack, options); |
| 645 | * | 688 | data.stacktrace = { |
| 646 | * @param {object} extra An object representing extra data [optional] | 689 | // Sentry expects frames oldest to newest |
| 647 | * @return {Raven} | 690 | frames: frames.reverse(), |
| 648 | */ | 691 | }; |
| 649 | setExtraContext: function(extra) { | 692 | } |
| 650 | this._mergeContext('extra', extra); | ||
| 651 | |||
| 652 | return this; | ||
| 653 | }, | ||
| 654 | |||
| 655 | /* | ||
| 656 | * Merge tags to be sent along with the payload. | ||
| 657 | * | ||
| 658 | * @param {object} tags An object representing tags [optional] | ||
| 659 | * @return {Raven} | ||
| 660 | */ | ||
| 661 | setTagsContext: function(tags) { | ||
| 662 | this._mergeContext('tags', tags); | ||
| 663 | |||
| 664 | return this; | ||
| 665 | }, | ||
| 666 | |||
| 667 | /* | ||
| 668 | * Clear all of the context. | ||
| 669 | * | ||
| 670 | * @return {Raven} | ||
| 671 | */ | ||
| 672 | clearContext: function() { | ||
| 673 | this._globalContext = {}; | ||
| 674 | |||
| 675 | return this; | ||
| 676 | }, | ||
| 677 | |||
| 678 | /* | ||
| 679 | * Get a copy of the current context. This cannot be mutated. | ||
| 680 | * | ||
| 681 | * @return {object} copy of context | ||
| 682 | */ | ||
| 683 | getContext: function() { | ||
| 684 | // lol javascript | ||
| 685 | return JSON.parse(stringify(this._globalContext)); | ||
| 686 | }, | ||
| 687 | |||
| 688 | /* | ||
| 689 | * Set environment of application | ||
| 690 | * | ||
| 691 | * @param {string} environment Typically something like 'production'. | ||
| 692 | * @return {Raven} | ||
| 693 | */ | ||
| 694 | setEnvironment: function(environment) { | ||
| 695 | this._globalOptions.environment = environment; | ||
| 696 | |||
| 697 | return this; | ||
| 698 | }, | ||
| 699 | |||
| 700 | /* | ||
| 701 | * Set release version of application | ||
| 702 | * | ||
| 703 | * @param {string} release Typically something like a git SHA to identify version | ||
| 704 | * @return {Raven} | ||
| 705 | */ | ||
| 706 | setRelease: function(release) { | ||
| 707 | this._globalOptions.release = release; | ||
| 708 | |||
| 709 | return this; | ||
| 710 | }, | ||
| 711 | |||
| 712 | /* | ||
| 713 | * Set the dataCallback option | ||
| 714 | * | ||
| 715 | * @param {function} callback The callback to run which allows the | ||
| 716 | * data blob to be mutated before sending | ||
| 717 | * @return {Raven} | ||
| 718 | */ | ||
| 719 | setDataCallback: function(callback) { | ||
| 720 | var original = this._globalOptions.dataCallback; | ||
| 721 | this._globalOptions.dataCallback = keepOriginalCallback(original, callback); | ||
| 722 | return this; | ||
| 723 | }, | ||
| 724 | |||
| 725 | /* | ||
| 726 | * Set the breadcrumbCallback option | ||
| 727 | * | ||
| 728 | * @param {function} callback The callback to run which allows filtering | ||
| 729 | * or mutating breadcrumbs | ||
| 730 | * @return {Raven} | ||
| 731 | */ | ||
| 732 | setBreadcrumbCallback: function(callback) { | ||
| 733 | var original = this._globalOptions.breadcrumbCallback; | ||
| 734 | this._globalOptions.breadcrumbCallback = keepOriginalCallback(original, callback); | ||
| 735 | return this; | ||
| 736 | }, | ||
| 737 | |||
| 738 | /* | ||
| 739 | * Set the shouldSendCallback option | ||
| 740 | * | ||
| 741 | * @param {function} callback The callback to run which allows | ||
| 742 | * introspecting the blob before sending | ||
| 743 | * @return {Raven} | ||
| 744 | */ | ||
| 745 | setShouldSendCallback: function(callback) { | ||
| 746 | var original = this._globalOptions.shouldSendCallback; | ||
| 747 | this._globalOptions.shouldSendCallback = keepOriginalCallback(original, callback); | ||
| 748 | return this; | ||
| 749 | }, | ||
| 750 | |||
| 751 | /** | ||
| 752 | * Override the default HTTP transport mechanism that transmits data | ||
| 753 | * to the Sentry server. | ||
| 754 | * | ||
| 755 | * @param {function} transport Function invoked instead of the default | ||
| 756 | * `makeRequest` handler. | ||
| 757 | * | ||
| 758 | * @return {Raven} | ||
| 759 | */ | ||
| 760 | setTransport: function(transport) { | ||
| 761 | this._globalOptions.transport = transport; | ||
| 762 | |||
| 763 | return this; | ||
| 764 | }, | ||
| 765 | |||
| 766 | /* | ||
| 767 | * Get the latest raw exception that was captured by Raven. | ||
| 768 | * | ||
| 769 | * @return {error} | ||
| 770 | */ | ||
| 771 | lastException: function() { | ||
| 772 | return this._lastCapturedException; | ||
| 773 | }, | ||
| 774 | |||
| 775 | /* | ||
| 776 | * Get the last event id | ||
| 777 | * | ||
| 778 | * @return {string} | ||
| 779 | */ | ||
| 780 | lastEventId: function() { | ||
| 781 | return this._lastEventId; | ||
| 782 | }, | ||
| 783 | |||
| 784 | /* | ||
| 785 | * Determine if Raven is setup and ready to go. | ||
| 786 | * | ||
| 787 | * @return {boolean} | ||
| 788 | */ | ||
| 789 | isSetup: function() { | ||
| 790 | if (!this._hasJSON) return false; // needs JSON support | ||
| 791 | if (!this._globalServer) { | ||
| 792 | if (!this.ravenNotConfiguredError) { | ||
| 793 | this.ravenNotConfiguredError = true; | ||
| 794 | this._logDebug('error', 'Error: Raven has not been configured.'); | ||
| 795 | } | ||
| 796 | return false; | ||
| 797 | } | ||
| 798 | return true; | ||
| 799 | }, | ||
| 800 | 693 | ||
| 801 | afterLoad: function() { | 694 | // Fire away! |
| 802 | // TODO: remove window dependence? | 695 | this._send(data); |
| 803 | 696 | ||
| 804 | // Attempt to initialize Raven on load | 697 | return this; |
| 805 | var RavenConfig = _window.RavenConfig; | 698 | }, |
| 806 | if (RavenConfig) { | ||
| 807 | this.config(RavenConfig.dsn, RavenConfig.config).install(); | ||
| 808 | } | ||
| 809 | }, | ||
| 810 | 699 | ||
| 811 | showReportDialog: function(options) { | 700 | captureBreadcrumb: function (obj) { |
| 812 | if ( | 701 | var crumb = objectMerge( |
| 813 | !_document // doesn't work without a document (React native) | 702 | { |
| 814 | ) | 703 | timestamp: now() / 1000, |
| 815 | return; | 704 | }, |
| 705 | obj, | ||
| 706 | ); | ||
| 816 | 707 | ||
| 817 | options = options || {}; | 708 | if (isFunction(this._globalOptions.breadcrumbCallback)) { |
| 709 | var result = this._globalOptions.breadcrumbCallback(crumb); | ||
| 818 | 710 | ||
| 819 | var lastEventId = options.eventId || this.lastEventId(); | 711 | if (isObject(result) && !isEmptyObject(result)) { |
| 820 | if (!lastEventId) { | 712 | crumb = result; |
| 821 | throw new RavenConfigError('Missing eventId'); | 713 | } else if (result === false) { |
| 822 | } | 714 | return this; |
| 715 | } | ||
| 716 | } | ||
| 823 | 717 | ||
| 824 | var dsn = options.dsn || this._dsn; | 718 | this._breadcrumbs.push(crumb); |
| 825 | if (!dsn) { | 719 | if ( |
| 826 | throw new RavenConfigError('Missing DSN'); | 720 | this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs |
| 827 | } | 721 | ) { |
| 722 | this._breadcrumbs.shift(); | ||
| 723 | } | ||
| 724 | return this; | ||
| 725 | }, | ||
| 828 | 726 | ||
| 829 | var encode = encodeURIComponent; | 727 | addPlugin: function (plugin /*arg1, arg2, ... argN*/) { |
| 830 | var qs = ''; | 728 | var pluginArgs = [].slice.call(arguments, 1); |
| 831 | qs += '?eventId=' + encode(lastEventId); | ||
| 832 | qs += '&dsn=' + encode(dsn); | ||
| 833 | 729 | ||
| 834 | var user = options.user || this._globalContext.user; | 730 | this._plugins.push([plugin, pluginArgs]); |
| 835 | if (user) { | 731 | if (this._isRavenInstalled) { |
| 836 | if (user.name) qs += '&name=' + encode(user.name); | 732 | this._drainPlugins(); |
| 837 | if (user.email) qs += '&email=' + encode(user.email); | 733 | } |
| 838 | } | ||
| 839 | 734 | ||
| 840 | var globalServer = this._getGlobalServer(this._parseDSN(dsn)); | 735 | return this; |
| 736 | }, | ||
| 737 | |||
| 738 | /* | ||
| 739 | * Set/clear a user to be sent along with the payload. | ||
| 740 | * | ||
| 741 | * @param {object} user An object representing user data [optional] | ||
| 742 | * @return {Raven} | ||
| 743 | */ | ||
| 744 | setUserContext: function (user) { | ||
| 745 | // Intentionally do not merge here since that's an unexpected behavior. | ||
| 746 | this._globalContext.user = user; | ||
| 747 | |||
| 748 | return this; | ||
| 749 | }, | ||
| 750 | |||
| 751 | /* | ||
| 752 | * Merge extra attributes to be sent along with the payload. | ||
| 753 | * | ||
| 754 | * @param {object} extra An object representing extra data [optional] | ||
| 755 | * @return {Raven} | ||
| 756 | */ | ||
| 757 | setExtraContext: function (extra) { | ||
| 758 | this._mergeContext("extra", extra); | ||
| 759 | |||
| 760 | return this; | ||
| 761 | }, | ||
| 762 | |||
| 763 | /* | ||
| 764 | * Merge tags to be sent along with the payload. | ||
| 765 | * | ||
| 766 | * @param {object} tags An object representing tags [optional] | ||
| 767 | * @return {Raven} | ||
| 768 | */ | ||
| 769 | setTagsContext: function (tags) { | ||
| 770 | this._mergeContext("tags", tags); | ||
| 771 | |||
| 772 | return this; | ||
| 773 | }, | ||
| 774 | |||
| 775 | /* | ||
| 776 | * Clear all of the context. | ||
| 777 | * | ||
| 778 | * @return {Raven} | ||
| 779 | */ | ||
| 780 | clearContext: function () { | ||
| 781 | this._globalContext = {}; | ||
| 782 | |||
| 783 | return this; | ||
| 784 | }, | ||
| 785 | |||
| 786 | /* | ||
| 787 | * Get a copy of the current context. This cannot be mutated. | ||
| 788 | * | ||
| 789 | * @return {object} copy of context | ||
| 790 | */ | ||
| 791 | getContext: function () { | ||
| 792 | // lol javascript | ||
| 793 | return JSON.parse(stringify(this._globalContext)); | ||
| 794 | }, | ||
| 795 | |||
| 796 | /* | ||
| 797 | * Set environment of application | ||
| 798 | * | ||
| 799 | * @param {string} environment Typically something like 'production'. | ||
| 800 | * @return {Raven} | ||
| 801 | */ | ||
| 802 | setEnvironment: function (environment) { | ||
| 803 | this._globalOptions.environment = environment; | ||
| 804 | |||
| 805 | return this; | ||
| 806 | }, | ||
| 807 | |||
| 808 | /* | ||
| 809 | * Set release version of application | ||
| 810 | * | ||
| 811 | * @param {string} release Typically something like a git SHA to identify version | ||
| 812 | * @return {Raven} | ||
| 813 | */ | ||
| 814 | setRelease: function (release) { | ||
| 815 | this._globalOptions.release = release; | ||
| 816 | |||
| 817 | return this; | ||
| 818 | }, | ||
| 819 | |||
| 820 | /* | ||
| 821 | * Set the dataCallback option | ||
| 822 | * | ||
| 823 | * @param {function} callback The callback to run which allows the | ||
| 824 | * data blob to be mutated before sending | ||
| 825 | * @return {Raven} | ||
| 826 | */ | ||
| 827 | setDataCallback: function (callback) { | ||
| 828 | var original = this._globalOptions.dataCallback; | ||
| 829 | this._globalOptions.dataCallback = keepOriginalCallback( | ||
| 830 | original, | ||
| 831 | callback, | ||
| 832 | ); | ||
| 833 | return this; | ||
| 834 | }, | ||
| 835 | |||
| 836 | /* | ||
| 837 | * Set the breadcrumbCallback option | ||
| 838 | * | ||
| 839 | * @param {function} callback The callback to run which allows filtering | ||
| 840 | * or mutating breadcrumbs | ||
| 841 | * @return {Raven} | ||
| 842 | */ | ||
| 843 | setBreadcrumbCallback: function (callback) { | ||
| 844 | var original = this._globalOptions.breadcrumbCallback; | ||
| 845 | this._globalOptions.breadcrumbCallback = keepOriginalCallback( | ||
| 846 | original, | ||
| 847 | callback, | ||
| 848 | ); | ||
| 849 | return this; | ||
| 850 | }, | ||
| 851 | |||
| 852 | /* | ||
| 853 | * Set the shouldSendCallback option | ||
| 854 | * | ||
| 855 | * @param {function} callback The callback to run which allows | ||
| 856 | * introspecting the blob before sending | ||
| 857 | * @return {Raven} | ||
| 858 | */ | ||
| 859 | setShouldSendCallback: function (callback) { | ||
| 860 | var original = this._globalOptions.shouldSendCallback; | ||
| 861 | this._globalOptions.shouldSendCallback = keepOriginalCallback( | ||
| 862 | original, | ||
| 863 | callback, | ||
| 864 | ); | ||
| 865 | return this; | ||
| 866 | }, | ||
| 867 | |||
| 868 | /** | ||
| 869 | * Override the default HTTP transport mechanism that transmits data | ||
| 870 | * to the Sentry server. | ||
| 871 | * | ||
| 872 | * @param {function} transport Function invoked instead of the default | ||
| 873 | * `makeRequest` handler. | ||
| 874 | * | ||
| 875 | * @return {Raven} | ||
| 876 | */ | ||
| 877 | setTransport: function (transport) { | ||
| 878 | this._globalOptions.transport = transport; | ||
| 879 | |||
| 880 | return this; | ||
| 881 | }, | ||
| 882 | |||
| 883 | /* | ||
| 884 | * Get the latest raw exception that was captured by Raven. | ||
| 885 | * | ||
| 886 | * @return {error} | ||
| 887 | */ | ||
| 888 | lastException: function () { | ||
| 889 | return this._lastCapturedException; | ||
| 890 | }, | ||
| 891 | |||
| 892 | /* | ||
| 893 | * Get the last event id | ||
| 894 | * | ||
| 895 | * @return {string} | ||
| 896 | */ | ||
| 897 | lastEventId: function () { | ||
| 898 | return this._lastEventId; | ||
| 899 | }, | ||
| 900 | |||
| 901 | /* | ||
| 902 | * Determine if Raven is setup and ready to go. | ||
| 903 | * | ||
| 904 | * @return {boolean} | ||
| 905 | */ | ||
| 906 | isSetup: function () { | ||
| 907 | if (!this._hasJSON) return false; // needs JSON support | ||
| 908 | if (!this._globalServer) { | ||
| 909 | if (!this.ravenNotConfiguredError) { | ||
| 910 | this.ravenNotConfiguredError = true; | ||
| 911 | this._logDebug( | ||
| 912 | "error", | ||
| 913 | "Error: Raven has not been configured.", | ||
| 914 | ); | ||
| 915 | } | ||
| 916 | return false; | ||
| 917 | } | ||
| 918 | return true; | ||
| 919 | }, | ||
| 841 | 920 | ||
| 842 | var script = _document.createElement('script'); | 921 | afterLoad: function () { |
| 843 | script.async = true; | 922 | // TODO: remove window dependence? |
| 844 | script.src = globalServer + '/api/embed/error-page/' + qs; | ||
| 845 | (_document.head || _document.body).appendChild(script); | ||
| 846 | }, | ||
| 847 | 923 | ||
| 848 | /**** Private functions ****/ | 924 | // Attempt to initialize Raven on load |
| 849 | _ignoreNextOnError: function() { | 925 | var RavenConfig = _window.RavenConfig; |
| 850 | var self = this; | 926 | if (RavenConfig) { |
| 851 | this._ignoreOnError += 1; | 927 | this.config(RavenConfig.dsn, RavenConfig.config).install(); |
| 852 | setTimeout(function() { | 928 | } |
| 853 | // onerror should trigger before setTimeout | 929 | }, |
| 854 | self._ignoreOnError -= 1; | ||
| 855 | }); | ||
| 856 | }, | ||
| 857 | 930 | ||
| 858 | _triggerEvent: function(eventType, options) { | 931 | showReportDialog: function (options) { |
| 859 | // NOTE: `event` is a native browser thing, so let's avoid conflicting with it | 932 | if ( |
| 860 | var evt, key; | 933 | !_document // doesn't work without a document (React native) |
| 934 | ) | ||
| 935 | return; | ||
| 861 | 936 | ||
| 862 | if (!this._hasDocument) return; | 937 | options = options || {}; |
| 863 | 938 | ||
| 864 | options = options || {}; | 939 | var lastEventId = options.eventId || this.lastEventId(); |
| 940 | if (!lastEventId) { | ||
| 941 | throw new RavenConfigError("Missing eventId"); | ||
| 942 | } | ||
| 865 | 943 | ||
| 866 | eventType = 'raven' + eventType.substr(0, 1).toUpperCase() + eventType.substr(1); | 944 | var dsn = options.dsn || this._dsn; |
| 945 | if (!dsn) { | ||
| 946 | throw new RavenConfigError("Missing DSN"); | ||
| 947 | } | ||
| 867 | 948 | ||
| 868 | if (_document.createEvent) { | 949 | var encode = encodeURIComponent; |
| 869 | evt = _document.createEvent('HTMLEvents'); | 950 | var qs = ""; |
| 870 | evt.initEvent(eventType, true, true); | 951 | qs += "?eventId=" + encode(lastEventId); |
| 871 | } else { | 952 | qs += "&dsn=" + encode(dsn); |
| 872 | evt = _document.createEventObject(); | ||
| 873 | evt.eventType = eventType; | ||
| 874 | } | ||
| 875 | 953 | ||
| 876 | for (key in options) | 954 | var user = options.user || this._globalContext.user; |
| 877 | if (hasKey(options, key)) { | 955 | if (user) { |
| 878 | evt[key] = options[key]; | 956 | if (user.name) qs += "&name=" + encode(user.name); |
| 879 | } | 957 | if (user.email) qs += "&email=" + encode(user.email); |
| 958 | } | ||
| 880 | 959 | ||
| 881 | if (_document.createEvent) { | 960 | var globalServer = this._getGlobalServer(this._parseDSN(dsn)); |
| 882 | // IE9 if standards | 961 | |
| 883 | _document.dispatchEvent(evt); | 962 | var script = _document.createElement("script"); |
| 884 | } else { | 963 | script.async = true; |
| 885 | // IE8 regardless of Quirks or Standards | 964 | script.src = globalServer + "/api/embed/error-page/" + qs; |
| 886 | // IE9 if quirks | 965 | (_document.head || _document.body).appendChild(script); |
| 887 | try { | 966 | }, |
| 888 | _document.fireEvent('on' + evt.eventType.toLowerCase(), evt); | 967 | |
| 889 | } catch (e) { | 968 | /**** Private functions ****/ |
| 890 | // Do nothing | 969 | _ignoreNextOnError: function () { |
| 891 | } | 970 | var self = this; |
| 892 | } | 971 | this._ignoreOnError += 1; |
| 893 | }, | 972 | setTimeout(function () { |
| 894 | 973 | // onerror should trigger before setTimeout | |
| 895 | /** | 974 | self._ignoreOnError -= 1; |
| 896 | * Wraps addEventListener to capture UI breadcrumbs | 975 | }); |
| 897 | * @param evtName the event name (e.g. "click") | 976 | }, |
| 898 | * @returns {Function} | ||
| 899 | * @private | ||
| 900 | */ | ||
| 901 | _breadcrumbEventHandler: function(evtName) { | ||
| 902 | var self = this; | ||
| 903 | return function(evt) { | ||
| 904 | // reset keypress timeout; e.g. triggering a 'click' after | ||
| 905 | // a 'keypress' will reset the keypress debounce so that a new | ||
| 906 | // set of keypresses can be recorded | ||
| 907 | self._keypressTimeout = null; | ||
| 908 | |||
| 909 | // It's possible this handler might trigger multiple times for the same | ||
| 910 | // event (e.g. event propagation through node ancestors). Ignore if we've | ||
| 911 | // already captured the event. | ||
| 912 | if (self._lastCapturedEvent === evt) return; | ||
| 913 | |||
| 914 | self._lastCapturedEvent = evt; | ||
| 915 | |||
| 916 | // try/catch both: | ||
| 917 | // - accessing evt.target (see getsentry/raven-js#838, #768) | ||
| 918 | // - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly | ||
| 919 | // can throw an exception in some circumstances. | ||
| 920 | var target; | ||
| 921 | try { | ||
| 922 | target = htmlTreeAsString(evt.target); | ||
| 923 | } catch (e) { | ||
| 924 | target = '<unknown>'; | ||
| 925 | } | ||
| 926 | 977 | ||
| 927 | self.captureBreadcrumb({ | 978 | _triggerEvent: function (eventType, options) { |
| 928 | category: 'ui.' + evtName, // e.g. ui.click, ui.input | 979 | // NOTE: `event` is a native browser thing, so let's avoid conflicting with it |
| 929 | message: target | 980 | var evt, key; |
| 930 | }); | ||
| 931 | }; | ||
| 932 | }, | ||
| 933 | |||
| 934 | /** | ||
| 935 | * Wraps addEventListener to capture keypress UI events | ||
| 936 | * @returns {Function} | ||
| 937 | * @private | ||
| 938 | */ | ||
| 939 | _keypressEventHandler: function() { | ||
| 940 | var self = this, | ||
| 941 | debounceDuration = 1000; // milliseconds | ||
| 942 | |||
| 943 | // TODO: if somehow user switches keypress target before | ||
| 944 | // debounce timeout is triggered, we will only capture | ||
| 945 | // a single breadcrumb from the FIRST target (acceptable?) | ||
| 946 | return function(evt) { | ||
| 947 | var target; | ||
| 948 | try { | ||
| 949 | target = evt.target; | ||
| 950 | } catch (e) { | ||
| 951 | // just accessing event properties can throw an exception in some rare circumstances | ||
| 952 | // see: https://github.com/getsentry/raven-js/issues/838 | ||
| 953 | return; | ||
| 954 | } | ||
| 955 | var tagName = target && target.tagName; | ||
| 956 | |||
| 957 | // only consider keypress events on actual input elements | ||
| 958 | // this will disregard keypresses targeting body (e.g. tabbing | ||
| 959 | // through elements, hotkeys, etc) | ||
| 960 | if ( | ||
| 961 | !tagName || | ||
| 962 | (tagName !== 'INPUT' && tagName !== 'TEXTAREA' && !target.isContentEditable) | ||
| 963 | ) | ||
| 964 | return; | ||
| 965 | |||
| 966 | // record first keypress in a series, but ignore subsequent | ||
| 967 | // keypresses until debounce clears | ||
| 968 | var timeout = self._keypressTimeout; | ||
| 969 | if (!timeout) { | ||
| 970 | self._breadcrumbEventHandler('input')(evt); | ||
| 971 | } | ||
| 972 | clearTimeout(timeout); | ||
| 973 | self._keypressTimeout = setTimeout(function() { | ||
| 974 | self._keypressTimeout = null; | ||
| 975 | }, debounceDuration); | ||
| 976 | }; | ||
| 977 | }, | ||
| 978 | |||
| 979 | /** | ||
| 980 | * Captures a breadcrumb of type "navigation", normalizing input URLs | ||
| 981 | * @param to the originating URL | ||
| 982 | * @param from the target URL | ||
| 983 | * @private | ||
| 984 | */ | ||
| 985 | _captureUrlChange: function(from, to) { | ||
| 986 | var parsedLoc = parseUrl(this._location.href); | ||
| 987 | var parsedTo = parseUrl(to); | ||
| 988 | var parsedFrom = parseUrl(from); | ||
| 989 | |||
| 990 | // because onpopstate only tells you the "new" (to) value of location.href, and | ||
| 991 | // not the previous (from) value, we need to track the value of the current URL | ||
| 992 | // state ourselves | ||
| 993 | this._lastHref = to; | ||
| 994 | |||
| 995 | // Use only the path component of the URL if the URL matches the current | ||
| 996 | // document (almost all the time when using pushState) | ||
| 997 | if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host) | ||
| 998 | to = parsedTo.relative; | ||
| 999 | if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host) | ||
| 1000 | from = parsedFrom.relative; | ||
| 1001 | |||
| 1002 | this.captureBreadcrumb({ | ||
| 1003 | category: 'navigation', | ||
| 1004 | data: { | ||
| 1005 | to: to, | ||
| 1006 | from: from | ||
| 1007 | } | ||
| 1008 | }); | ||
| 1009 | }, | ||
| 1010 | |||
| 1011 | _patchFunctionToString: function() { | ||
| 1012 | var self = this; | ||
| 1013 | self._originalFunctionToString = Function.prototype.toString; | ||
| 1014 | // eslint-disable-next-line no-extend-native | ||
| 1015 | Function.prototype.toString = function() { | ||
| 1016 | if (typeof this === 'function' && this.__raven__) { | ||
| 1017 | return self._originalFunctionToString.apply(this.__orig__, arguments); | ||
| 1018 | } | ||
| 1019 | return self._originalFunctionToString.apply(this, arguments); | ||
| 1020 | }; | ||
| 1021 | }, | ||
| 1022 | |||
| 1023 | _unpatchFunctionToString: function() { | ||
| 1024 | if (this._originalFunctionToString) { | ||
| 1025 | // eslint-disable-next-line no-extend-native | ||
| 1026 | Function.prototype.toString = this._originalFunctionToString; | ||
| 1027 | } | ||
| 1028 | }, | ||
| 1029 | |||
| 1030 | /** | ||
| 1031 | * Wrap timer functions and event targets to catch errors and provide | ||
| 1032 | * better metadata. | ||
| 1033 | */ | ||
| 1034 | _instrumentTryCatch: function() { | ||
| 1035 | var self = this; | ||
| 1036 | |||
| 1037 | var wrappedBuiltIns = self._wrappedBuiltIns; | ||
| 1038 | |||
| 1039 | function wrapTimeFn(orig) { | ||
| 1040 | return function(fn, t) { | ||
| 1041 | // preserve arity | ||
| 1042 | // Make a copy of the arguments to prevent deoptimization | ||
| 1043 | // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments | ||
| 1044 | var args = new Array(arguments.length); | ||
| 1045 | for (var i = 0; i < args.length; ++i) { | ||
| 1046 | args[i] = arguments[i]; | ||
| 1047 | } | ||
| 1048 | var originalCallback = args[0]; | ||
| 1049 | if (isFunction(originalCallback)) { | ||
| 1050 | args[0] = self.wrap(originalCallback); | ||
| 1051 | } | ||
| 1052 | 981 | ||
| 1053 | // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it | 982 | if (!this._hasDocument) return; |
| 1054 | // also supports only two arguments and doesn't care what this is, so we | ||
| 1055 | // can just call the original function directly. | ||
| 1056 | if (orig.apply) { | ||
| 1057 | return orig.apply(this, args); | ||
| 1058 | } else { | ||
| 1059 | return orig(args[0], args[1]); | ||
| 1060 | } | ||
| 1061 | }; | ||
| 1062 | } | ||
| 1063 | 983 | ||
| 1064 | var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs; | 984 | options = options || {}; |
| 1065 | 985 | ||
| 1066 | function wrapEventTarget(global) { | 986 | eventType = |
| 1067 | var proto = _window[global] && _window[global].prototype; | 987 | "raven" + |
| 1068 | if (proto && proto.hasOwnProperty && proto.hasOwnProperty('addEventListener')) { | 988 | eventType.substr(0, 1).toUpperCase() + |
| 1069 | fill( | 989 | eventType.substr(1); |
| 1070 | proto, | 990 | |
| 1071 | 'addEventListener', | 991 | if (_document.createEvent) { |
| 1072 | function(orig) { | 992 | evt = _document.createEvent("HTMLEvents"); |
| 1073 | return function(evtName, fn, capture, secure) { | 993 | evt.initEvent(eventType, true, true); |
| 1074 | // preserve arity | 994 | } else { |
| 1075 | try { | 995 | evt = _document.createEventObject(); |
| 1076 | if (fn && fn.handleEvent) { | 996 | evt.eventType = eventType; |
| 1077 | fn.handleEvent = self.wrap(fn.handleEvent); | ||
| 1078 | } | 997 | } |
| 1079 | } catch (err) { | ||
| 1080 | // can sometimes get 'Permission denied to access property "handle Event' | ||
| 1081 | } | ||
| 1082 | 998 | ||
| 1083 | // More breadcrumb DOM capture ... done here and not in `_instrumentBreadcrumbs` | 999 | for (key in options) |
| 1084 | // so that we don't have more than one wrapper function | 1000 | if (hasKey(options, key)) { |
| 1085 | var before, clickHandler, keypressHandler; | 1001 | evt[key] = options[key]; |
| 1002 | } | ||
| 1086 | 1003 | ||
| 1087 | if ( | 1004 | if (_document.createEvent) { |
| 1088 | autoBreadcrumbs && | 1005 | // IE9 if standards |
| 1089 | autoBreadcrumbs.dom && | 1006 | _document.dispatchEvent(evt); |
| 1090 | (global === 'EventTarget' || global === 'Node') | 1007 | } else { |
| 1091 | ) { | 1008 | // IE8 regardless of Quirks or Standards |
| 1092 | // NOTE: generating multiple handlers per addEventListener invocation, should | 1009 | // IE9 if quirks |
| 1093 | // revisit and verify we can just use one (almost certainly) | 1010 | try { |
| 1094 | clickHandler = self._breadcrumbEventHandler('click'); | 1011 | _document.fireEvent( |
| 1095 | keypressHandler = self._keypressEventHandler(); | 1012 | "on" + evt.eventType.toLowerCase(), |
| 1096 | before = function(evt) { | 1013 | evt, |
| 1097 | // need to intercept every DOM event in `before` argument, in case that | 1014 | ); |
| 1098 | // same wrapped method is re-used for different events (e.g. mousemove THEN click) | 1015 | } catch (e) { |
| 1099 | // see #724 | 1016 | // Do nothing |
| 1100 | if (!evt) return; | 1017 | } |
| 1101 | 1018 | } | |
| 1102 | var eventType; | 1019 | }, |
| 1020 | |||
| 1021 | /** | ||
| 1022 | * Wraps addEventListener to capture UI breadcrumbs | ||
| 1023 | * @param evtName the event name (e.g. "click") | ||
| 1024 | * @returns {Function} | ||
| 1025 | * @private | ||
| 1026 | */ | ||
| 1027 | _breadcrumbEventHandler: function (evtName) { | ||
| 1028 | var self = this; | ||
| 1029 | return function (evt) { | ||
| 1030 | // reset keypress timeout; e.g. triggering a 'click' after | ||
| 1031 | // a 'keypress' will reset the keypress debounce so that a new | ||
| 1032 | // set of keypresses can be recorded | ||
| 1033 | self._keypressTimeout = null; | ||
| 1034 | |||
| 1035 | // It's possible this handler might trigger multiple times for the same | ||
| 1036 | // event (e.g. event propagation through node ancestors). Ignore if we've | ||
| 1037 | // already captured the event. | ||
| 1038 | if (self._lastCapturedEvent === evt) return; | ||
| 1039 | |||
| 1040 | self._lastCapturedEvent = evt; | ||
| 1041 | |||
| 1042 | // try/catch both: | ||
| 1043 | // - accessing evt.target (see getsentry/raven-js#838, #768) | ||
| 1044 | // - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly | ||
| 1045 | // can throw an exception in some circumstances. | ||
| 1046 | var target; | ||
| 1047 | try { | ||
| 1048 | target = htmlTreeAsString(evt.target); | ||
| 1049 | } catch (e) { | ||
| 1050 | target = "<unknown>"; | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | self.captureBreadcrumb({ | ||
| 1054 | category: "ui." + evtName, // e.g. ui.click, ui.input | ||
| 1055 | message: target, | ||
| 1056 | }); | ||
| 1057 | }; | ||
| 1058 | }, | ||
| 1059 | |||
| 1060 | /** | ||
| 1061 | * Wraps addEventListener to capture keypress UI events | ||
| 1062 | * @returns {Function} | ||
| 1063 | * @private | ||
| 1064 | */ | ||
| 1065 | _keypressEventHandler: function () { | ||
| 1066 | var self = this, | ||
| 1067 | debounceDuration = 1000; // milliseconds | ||
| 1068 | |||
| 1069 | // TODO: if somehow user switches keypress target before | ||
| 1070 | // debounce timeout is triggered, we will only capture | ||
| 1071 | // a single breadcrumb from the FIRST target (acceptable?) | ||
| 1072 | return function (evt) { | ||
| 1073 | var target; | ||
| 1103 | try { | 1074 | try { |
| 1104 | eventType = evt.type; | 1075 | target = evt.target; |
| 1105 | } catch (e) { | 1076 | } catch (e) { |
| 1106 | // just accessing event properties can throw an exception in some rare circumstances | 1077 | // just accessing event properties can throw an exception in some rare circumstances |
| 1107 | // see: https://github.com/getsentry/raven-js/issues/838 | 1078 | // see: https://github.com/getsentry/raven-js/issues/838 |
| 1108 | return; | 1079 | return; |
| 1109 | } | 1080 | } |
| 1110 | if (eventType === 'click') return clickHandler(evt); | 1081 | var tagName = target && target.tagName; |
| 1111 | else if (eventType === 'keypress') return keypressHandler(evt); | 1082 | |
| 1083 | // only consider keypress events on actual input elements | ||
| 1084 | // this will disregard keypresses targeting body (e.g. tabbing | ||
| 1085 | // through elements, hotkeys, etc) | ||
| 1086 | if ( | ||
| 1087 | !tagName || | ||
| 1088 | (tagName !== "INPUT" && | ||
| 1089 | tagName !== "TEXTAREA" && | ||
| 1090 | !target.isContentEditable) | ||
| 1091 | ) | ||
| 1092 | return; | ||
| 1093 | |||
| 1094 | // record first keypress in a series, but ignore subsequent | ||
| 1095 | // keypresses until debounce clears | ||
| 1096 | var timeout = self._keypressTimeout; | ||
| 1097 | if (!timeout) { | ||
| 1098 | self._breadcrumbEventHandler("input")(evt); | ||
| 1099 | } | ||
| 1100 | clearTimeout(timeout); | ||
| 1101 | self._keypressTimeout = setTimeout(function () { | ||
| 1102 | self._keypressTimeout = null; | ||
| 1103 | }, debounceDuration); | ||
| 1112 | }; | 1104 | }; |
| 1113 | } | 1105 | }, |
| 1114 | return orig.call( | 1106 | |
| 1115 | this, | 1107 | /** |
| 1116 | evtName, | 1108 | * Captures a breadcrumb of type "navigation", normalizing input URLs |
| 1117 | self.wrap(fn, undefined, before), | 1109 | * @param to the originating URL |
| 1118 | capture, | 1110 | * @param from the target URL |
| 1119 | secure | 1111 | * @private |
| 1120 | ); | 1112 | */ |
| 1121 | }; | 1113 | _captureUrlChange: function (from, to) { |
| 1122 | }, | 1114 | var parsedLoc = parseUrl(this._location.href); |
| 1123 | wrappedBuiltIns | 1115 | var parsedTo = parseUrl(to); |
| 1124 | ); | 1116 | var parsedFrom = parseUrl(from); |
| 1125 | fill( | 1117 | |
| 1126 | proto, | 1118 | // because onpopstate only tells you the "new" (to) value of location.href, and |
| 1127 | 'removeEventListener', | 1119 | // not the previous (from) value, we need to track the value of the current URL |
| 1128 | function(orig) { | 1120 | // state ourselves |
| 1129 | return function(evt, fn, capture, secure) { | 1121 | this._lastHref = to; |
| 1130 | try { | 1122 | |
| 1131 | fn = fn && (fn.__raven_wrapper__ ? fn.__raven_wrapper__ : fn); | 1123 | // Use only the path component of the URL if the URL matches the current |
| 1132 | } catch (e) { | 1124 | // document (almost all the time when using pushState) |
| 1133 | // ignore, accessing __raven_wrapper__ will throw in some Selenium environments | 1125 | if ( |
| 1134 | } | 1126 | parsedLoc.protocol === parsedTo.protocol && |
| 1135 | return orig.call(this, evt, fn, capture, secure); | 1127 | parsedLoc.host === parsedTo.host |
| 1136 | }; | 1128 | ) |
| 1137 | }, | 1129 | to = parsedTo.relative; |
| 1138 | wrappedBuiltIns | 1130 | if ( |
| 1139 | ); | 1131 | parsedLoc.protocol === parsedFrom.protocol && |
| 1140 | } | 1132 | parsedLoc.host === parsedFrom.host |
| 1141 | } | 1133 | ) |
| 1134 | from = parsedFrom.relative; | ||
| 1135 | |||
| 1136 | this.captureBreadcrumb({ | ||
| 1137 | category: "navigation", | ||
| 1138 | data: { | ||
| 1139 | to: to, | ||
| 1140 | from: from, | ||
| 1141 | }, | ||
| 1142 | }); | ||
| 1143 | }, | ||
| 1144 | |||
| 1145 | _patchFunctionToString: function () { | ||
| 1146 | var self = this; | ||
| 1147 | self._originalFunctionToString = Function.prototype.toString; | ||
| 1148 | // eslint-disable-next-line no-extend-native | ||
| 1149 | Function.prototype.toString = function () { | ||
| 1150 | if (typeof this === "function" && this.__raven__) { | ||
| 1151 | return self._originalFunctionToString.apply( | ||
| 1152 | this.__orig__, | ||
| 1153 | arguments, | ||
| 1154 | ); | ||
| 1155 | } | ||
| 1156 | return self._originalFunctionToString.apply(this, arguments); | ||
| 1157 | }; | ||
| 1158 | }, | ||
| 1142 | 1159 | ||
| 1143 | fill(_window, 'setTimeout', wrapTimeFn, wrappedBuiltIns); | 1160 | _unpatchFunctionToString: function () { |
| 1144 | fill(_window, 'setInterval', wrapTimeFn, wrappedBuiltIns); | 1161 | if (this._originalFunctionToString) { |
| 1145 | if (_window.requestAnimationFrame) { | 1162 | // eslint-disable-next-line no-extend-native |
| 1146 | fill( | 1163 | Function.prototype.toString = this._originalFunctionToString; |
| 1147 | _window, | 1164 | } |
| 1148 | 'requestAnimationFrame', | 1165 | }, |
| 1149 | function(orig) { | 1166 | |
| 1150 | return function(cb) { | 1167 | /** |
| 1151 | return orig(self.wrap(cb)); | 1168 | * Wrap timer functions and event targets to catch errors and provide |
| 1152 | }; | 1169 | * better metadata. |
| 1153 | }, | 1170 | */ |
| 1154 | wrappedBuiltIns | 1171 | _instrumentTryCatch: function () { |
| 1155 | ); | 1172 | var self = this; |
| 1156 | } | 1173 | |
| 1174 | var wrappedBuiltIns = self._wrappedBuiltIns; | ||
| 1175 | |||
| 1176 | function wrapTimeFn(orig) { | ||
| 1177 | return function (fn, t) { | ||
| 1178 | // preserve arity | ||
| 1179 | // Make a copy of the arguments to prevent deoptimization | ||
| 1180 | // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments | ||
| 1181 | var args = new Array(arguments.length); | ||
| 1182 | for (var i = 0; i < args.length; ++i) { | ||
| 1183 | args[i] = arguments[i]; | ||
| 1184 | } | ||
| 1185 | var originalCallback = args[0]; | ||
| 1186 | if (isFunction(originalCallback)) { | ||
| 1187 | args[0] = self.wrap(originalCallback); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it | ||
| 1191 | // also supports only two arguments and doesn't care what this is, so we | ||
| 1192 | // can just call the original function directly. | ||
| 1193 | if (orig.apply) { | ||
| 1194 | return orig.apply(this, args); | ||
| 1195 | } else { | ||
| 1196 | return orig(args[0], args[1]); | ||
| 1197 | } | ||
| 1198 | }; | ||
| 1199 | } | ||
| 1157 | 1200 | ||
| 1158 | // event targets borrowed from bugsnag-js: | 1201 | var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs; |
| 1159 | // https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L666 | 1202 | |
| 1160 | var eventTargets = [ | 1203 | function wrapEventTarget(global) { |
| 1161 | 'EventTarget', | 1204 | var proto = _window[global] && _window[global].prototype; |
| 1162 | 'Window', | 1205 | if ( |
| 1163 | 'Node', | 1206 | proto && |
| 1164 | 'ApplicationCache', | 1207 | proto.hasOwnProperty && |
| 1165 | 'AudioTrackList', | 1208 | proto.hasOwnProperty("addEventListener") |
| 1166 | 'ChannelMergerNode', | 1209 | ) { |
| 1167 | 'CryptoOperation', | 1210 | fill( |
| 1168 | 'EventSource', | 1211 | proto, |
| 1169 | 'FileReader', | 1212 | "addEventListener", |
| 1170 | 'HTMLUnknownElement', | 1213 | function (orig) { |
| 1171 | 'IDBDatabase', | 1214 | return function (evtName, fn, capture, secure) { |
| 1172 | 'IDBRequest', | 1215 | // preserve arity |
| 1173 | 'IDBTransaction', | 1216 | try { |
| 1174 | 'KeyOperation', | 1217 | if (fn && fn.handleEvent) { |
| 1175 | 'MediaController', | 1218 | fn.handleEvent = self.wrap(fn.handleEvent); |
| 1176 | 'MessagePort', | 1219 | } |
| 1177 | 'ModalWindow', | 1220 | } catch (err) { |
| 1178 | 'Notification', | 1221 | // can sometimes get 'Permission denied to access property "handle Event' |
| 1179 | 'SVGElementInstance', | 1222 | } |
| 1180 | 'Screen', | 1223 | |
| 1181 | 'TextTrack', | 1224 | // More breadcrumb DOM capture ... done here and not in `_instrumentBreadcrumbs` |
| 1182 | 'TextTrackCue', | 1225 | // so that we don't have more than one wrapper function |
| 1183 | 'TextTrackList', | 1226 | var before, clickHandler, keypressHandler; |
| 1184 | 'WebSocket', | 1227 | |
| 1185 | 'WebSocketWorker', | 1228 | if ( |
| 1186 | 'Worker', | 1229 | autoBreadcrumbs && |
| 1187 | 'XMLHttpRequest', | 1230 | autoBreadcrumbs.dom && |
| 1188 | 'XMLHttpRequestEventTarget', | 1231 | (global === "EventTarget" || global === "Node") |
| 1189 | 'XMLHttpRequestUpload' | 1232 | ) { |
| 1190 | ]; | 1233 | // NOTE: generating multiple handlers per addEventListener invocation, should |
| 1191 | for (var i = 0; i < eventTargets.length; i++) { | 1234 | // revisit and verify we can just use one (almost certainly) |
| 1192 | wrapEventTarget(eventTargets[i]); | 1235 | clickHandler = |
| 1193 | } | 1236 | self._breadcrumbEventHandler("click"); |
| 1194 | }, | 1237 | keypressHandler = self._keypressEventHandler(); |
| 1195 | 1238 | before = function (evt) { | |
| 1196 | /** | 1239 | // need to intercept every DOM event in `before` argument, in case that |
| 1197 | * Instrument browser built-ins w/ breadcrumb capturing | 1240 | // same wrapped method is re-used for different events (e.g. mousemove THEN click) |
| 1198 | * - XMLHttpRequests | 1241 | // see #724 |
| 1199 | * - DOM interactions (click/typing) | 1242 | if (!evt) return; |
| 1200 | * - window.location changes | 1243 | |
| 1201 | * - console | 1244 | var eventType; |
| 1202 | * | 1245 | try { |
| 1203 | * Can be disabled or individually configured via the `autoBreadcrumbs` config option | 1246 | eventType = evt.type; |
| 1204 | */ | 1247 | } catch (e) { |
| 1205 | _instrumentBreadcrumbs: function() { | 1248 | // just accessing event properties can throw an exception in some rare circumstances |
| 1206 | var self = this; | 1249 | // see: https://github.com/getsentry/raven-js/issues/838 |
| 1207 | var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs; | 1250 | return; |
| 1208 | 1251 | } | |
| 1209 | var wrappedBuiltIns = self._wrappedBuiltIns; | 1252 | if (eventType === "click") |
| 1210 | 1253 | return clickHandler(evt); | |
| 1211 | function wrapProp(prop, xhr) { | 1254 | else if (eventType === "keypress") |
| 1212 | if (prop in xhr && isFunction(xhr[prop])) { | 1255 | return keypressHandler(evt); |
| 1213 | fill(xhr, prop, function(orig) { | 1256 | }; |
| 1214 | return self.wrap(orig); | 1257 | } |
| 1215 | }); // intentionally don't track filled methods on XHR instances | 1258 | return orig.call( |
| 1216 | } | 1259 | this, |
| 1217 | } | 1260 | evtName, |
| 1261 | self.wrap(fn, undefined, before), | ||
| 1262 | capture, | ||
| 1263 | secure, | ||
| 1264 | ); | ||
| 1265 | }; | ||
| 1266 | }, | ||
| 1267 | wrappedBuiltIns, | ||
| 1268 | ); | ||
| 1269 | fill( | ||
| 1270 | proto, | ||
| 1271 | "removeEventListener", | ||
| 1272 | function (orig) { | ||
| 1273 | return function (evt, fn, capture, secure) { | ||
| 1274 | try { | ||
| 1275 | fn = | ||
| 1276 | fn && | ||
| 1277 | (fn.__raven_wrapper__ | ||
| 1278 | ? fn.__raven_wrapper__ | ||
| 1279 | : fn); | ||
| 1280 | } catch (e) { | ||
| 1281 | // ignore, accessing __raven_wrapper__ will throw in some Selenium environments | ||
| 1282 | } | ||
| 1283 | return orig.call(this, evt, fn, capture, secure); | ||
| 1284 | }; | ||
| 1285 | }, | ||
| 1286 | wrappedBuiltIns, | ||
| 1287 | ); | ||
| 1288 | } | ||
| 1289 | } | ||
| 1218 | 1290 | ||
| 1219 | if (autoBreadcrumbs.xhr && 'XMLHttpRequest' in _window) { | 1291 | fill(_window, "setTimeout", wrapTimeFn, wrappedBuiltIns); |
| 1220 | var xhrproto = XMLHttpRequest.prototype; | 1292 | fill(_window, "setInterval", wrapTimeFn, wrappedBuiltIns); |
| 1221 | fill( | 1293 | if (_window.requestAnimationFrame) { |
| 1222 | xhrproto, | 1294 | fill( |
| 1223 | 'open', | 1295 | _window, |
| 1224 | function(origOpen) { | 1296 | "requestAnimationFrame", |
| 1225 | return function(method, url) { | 1297 | function (orig) { |
| 1226 | // preserve arity | 1298 | return function (cb) { |
| 1227 | 1299 | return orig(self.wrap(cb)); | |
| 1228 | // if Sentry key appears in URL, don't capture | 1300 | }; |
| 1229 | if (isString(url) && url.indexOf(self._globalKey) === -1) { | 1301 | }, |
| 1230 | this.__raven_xhr = { | 1302 | wrappedBuiltIns, |
| 1231 | method: method, | 1303 | ); |
| 1232 | url: url, | 1304 | } |
| 1233 | status_code: null | ||
| 1234 | }; | ||
| 1235 | } | ||
| 1236 | 1305 | ||
| 1237 | return origOpen.apply(this, arguments); | 1306 | // event targets borrowed from bugsnag-js: |
| 1238 | }; | 1307 | // https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L666 |
| 1239 | }, | 1308 | var eventTargets = [ |
| 1240 | wrappedBuiltIns | 1309 | "EventTarget", |
| 1241 | ); | 1310 | "Window", |
| 1242 | 1311 | "Node", | |
| 1243 | fill( | 1312 | "ApplicationCache", |
| 1244 | xhrproto, | 1313 | "AudioTrackList", |
| 1245 | 'send', | 1314 | "ChannelMergerNode", |
| 1246 | function(origSend) { | 1315 | "CryptoOperation", |
| 1247 | return function(data) { | 1316 | "EventSource", |
| 1248 | // preserve arity | 1317 | "FileReader", |
| 1249 | var xhr = this; | 1318 | "HTMLUnknownElement", |
| 1250 | 1319 | "IDBDatabase", | |
| 1251 | function onreadystatechangeHandler() { | 1320 | "IDBRequest", |
| 1252 | if (xhr.__raven_xhr && xhr.readyState === 4) { | 1321 | "IDBTransaction", |
| 1253 | try { | 1322 | "KeyOperation", |
| 1254 | // touching statusCode in some platforms throws | 1323 | "MediaController", |
| 1255 | // an exception | 1324 | "MessagePort", |
| 1256 | xhr.__raven_xhr.status_code = xhr.status; | 1325 | "ModalWindow", |
| 1257 | } catch (e) { | 1326 | "Notification", |
| 1258 | /* do nothing */ | 1327 | "SVGElementInstance", |
| 1328 | "Screen", | ||
| 1329 | "TextTrack", | ||
| 1330 | "TextTrackCue", | ||
| 1331 | "TextTrackList", | ||
| 1332 | "WebSocket", | ||
| 1333 | "WebSocketWorker", | ||
| 1334 | "Worker", | ||
| 1335 | "XMLHttpRequest", | ||
| 1336 | "XMLHttpRequestEventTarget", | ||
| 1337 | "XMLHttpRequestUpload", | ||
| 1338 | ]; | ||
| 1339 | for (var i = 0; i < eventTargets.length; i++) { | ||
| 1340 | wrapEventTarget(eventTargets[i]); | ||
| 1341 | } | ||
| 1342 | }, | ||
| 1343 | |||
| 1344 | /** | ||
| 1345 | * Instrument browser built-ins w/ breadcrumb capturing | ||
| 1346 | * - XMLHttpRequests | ||
| 1347 | * - DOM interactions (click/typing) | ||
| 1348 | * - window.location changes | ||
| 1349 | * - console | ||
| 1350 | * | ||
| 1351 | * Can be disabled or individually configured via the `autoBreadcrumbs` config option | ||
| 1352 | */ | ||
| 1353 | _instrumentBreadcrumbs: function () { | ||
| 1354 | var self = this; | ||
| 1355 | var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs; | ||
| 1356 | |||
| 1357 | var wrappedBuiltIns = self._wrappedBuiltIns; | ||
| 1358 | |||
| 1359 | function wrapProp(prop, xhr) { | ||
| 1360 | if (prop in xhr && isFunction(xhr[prop])) { | ||
| 1361 | fill(xhr, prop, function (orig) { | ||
| 1362 | return self.wrap(orig); | ||
| 1363 | }); // intentionally don't track filled methods on XHR instances | ||
| 1364 | } | ||
| 1259 | } | 1365 | } |
| 1260 | 1366 | ||
| 1261 | self.captureBreadcrumb({ | 1367 | if (autoBreadcrumbs.xhr && "XMLHttpRequest" in _window) { |
| 1262 | type: 'http', | 1368 | var xhrproto = XMLHttpRequest.prototype; |
| 1263 | category: 'xhr', | 1369 | fill( |
| 1264 | data: xhr.__raven_xhr | 1370 | xhrproto, |
| 1265 | }); | 1371 | "open", |
| 1266 | } | 1372 | function (origOpen) { |
| 1267 | } | 1373 | return function (method, url) { |
| 1374 | // preserve arity | ||
| 1375 | |||
| 1376 | // if Sentry key appears in URL, don't capture | ||
| 1377 | if ( | ||
| 1378 | isString(url) && | ||
| 1379 | url.indexOf(self._globalKey) === -1 | ||
| 1380 | ) { | ||
| 1381 | this.__raven_xhr = { | ||
| 1382 | method: method, | ||
| 1383 | url: url, | ||
| 1384 | status_code: null, | ||
| 1385 | }; | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | return origOpen.apply(this, arguments); | ||
| 1389 | }; | ||
| 1390 | }, | ||
| 1391 | wrappedBuiltIns, | ||
| 1392 | ); | ||
| 1393 | |||
| 1394 | fill( | ||
| 1395 | xhrproto, | ||
| 1396 | "send", | ||
| 1397 | function (origSend) { | ||
| 1398 | return function (data) { | ||
| 1399 | // preserve arity | ||
| 1400 | var xhr = this; | ||
| 1401 | |||
| 1402 | function onreadystatechangeHandler() { | ||
| 1403 | if (xhr.__raven_xhr && xhr.readyState === 4) { | ||
| 1404 | try { | ||
| 1405 | // touching statusCode in some platforms throws | ||
| 1406 | // an exception | ||
| 1407 | xhr.__raven_xhr.status_code = xhr.status; | ||
| 1408 | } catch (e) { | ||
| 1409 | /* do nothing */ | ||
| 1410 | } | ||
| 1411 | |||
| 1412 | self.captureBreadcrumb({ | ||
| 1413 | type: "http", | ||
| 1414 | category: "xhr", | ||
| 1415 | data: xhr.__raven_xhr, | ||
| 1416 | }); | ||
| 1417 | } | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | var props = ["onload", "onerror", "onprogress"]; | ||
| 1421 | for (var j = 0; j < props.length; j++) { | ||
| 1422 | wrapProp(props[j], xhr); | ||
| 1423 | } | ||
| 1424 | |||
| 1425 | if ( | ||
| 1426 | "onreadystatechange" in xhr && | ||
| 1427 | isFunction(xhr.onreadystatechange) | ||
| 1428 | ) { | ||
| 1429 | fill( | ||
| 1430 | xhr, | ||
| 1431 | "onreadystatechange", | ||
| 1432 | function (orig) { | ||
| 1433 | return self.wrap( | ||
| 1434 | orig, | ||
| 1435 | undefined, | ||
| 1436 | onreadystatechangeHandler, | ||
| 1437 | ); | ||
| 1438 | } /* intentionally don't track this instrumentation */, | ||
| 1439 | ); | ||
| 1440 | } else { | ||
| 1441 | // if onreadystatechange wasn't actually set by the page on this xhr, we | ||
| 1442 | // are free to set our own and capture the breadcrumb | ||
| 1443 | xhr.onreadystatechange = onreadystatechangeHandler; | ||
| 1444 | } | ||
| 1445 | |||
| 1446 | return origSend.apply(this, arguments); | ||
| 1447 | }; | ||
| 1448 | }, | ||
| 1449 | wrappedBuiltIns, | ||
| 1450 | ); | ||
| 1451 | } | ||
| 1268 | 1452 | ||
| 1269 | var props = ['onload', 'onerror', 'onprogress']; | 1453 | if (autoBreadcrumbs.xhr && "fetch" in _window) { |
| 1270 | for (var j = 0; j < props.length; j++) { | 1454 | fill( |
| 1271 | wrapProp(props[j], xhr); | 1455 | _window, |
| 1272 | } | 1456 | "fetch", |
| 1457 | function (origFetch) { | ||
| 1458 | return function (fn, t) { | ||
| 1459 | // preserve arity | ||
| 1460 | // Make a copy of the arguments to prevent deoptimization | ||
| 1461 | // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments | ||
| 1462 | var args = new Array(arguments.length); | ||
| 1463 | for (var i = 0; i < args.length; ++i) { | ||
| 1464 | args[i] = arguments[i]; | ||
| 1465 | } | ||
| 1466 | |||
| 1467 | var fetchInput = args[0]; | ||
| 1468 | var method = "GET"; | ||
| 1469 | var url; | ||
| 1470 | |||
| 1471 | if (typeof fetchInput === "string") { | ||
| 1472 | url = fetchInput; | ||
| 1473 | } else if ( | ||
| 1474 | "Request" in _window && | ||
| 1475 | fetchInput instanceof _window.Request | ||
| 1476 | ) { | ||
| 1477 | url = fetchInput.url; | ||
| 1478 | if (fetchInput.method) { | ||
| 1479 | method = fetchInput.method; | ||
| 1480 | } | ||
| 1481 | } else { | ||
| 1482 | url = "" + fetchInput; | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | if (args[1] && args[1].method) { | ||
| 1486 | method = args[1].method; | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | var fetchData = { | ||
| 1490 | method: method, | ||
| 1491 | url: url, | ||
| 1492 | status_code: null, | ||
| 1493 | }; | ||
| 1494 | |||
| 1495 | self.captureBreadcrumb({ | ||
| 1496 | type: "http", | ||
| 1497 | category: "fetch", | ||
| 1498 | data: fetchData, | ||
| 1499 | }); | ||
| 1500 | |||
| 1501 | return origFetch | ||
| 1502 | .apply(this, args) | ||
| 1503 | .then(function (response) { | ||
| 1504 | fetchData.status_code = response.status; | ||
| 1505 | |||
| 1506 | return response; | ||
| 1507 | }); | ||
| 1508 | }; | ||
| 1509 | }, | ||
| 1510 | wrappedBuiltIns, | ||
| 1511 | ); | ||
| 1512 | } | ||
| 1273 | 1513 | ||
| 1274 | if ('onreadystatechange' in xhr && isFunction(xhr.onreadystatechange)) { | 1514 | // Capture breadcrumbs from any click that is unhandled / bubbled up all the way |
| 1275 | fill( | 1515 | // to the document. Do this before we instrument addEventListener. |
| 1276 | xhr, | 1516 | if (autoBreadcrumbs.dom && this._hasDocument) { |
| 1277 | 'onreadystatechange', | 1517 | if (_document.addEventListener) { |
| 1278 | function(orig) { | 1518 | _document.addEventListener( |
| 1279 | return self.wrap(orig, undefined, onreadystatechangeHandler); | 1519 | "click", |
| 1280 | } /* intentionally don't track this instrumentation */ | 1520 | self._breadcrumbEventHandler("click"), |
| 1281 | ); | 1521 | false, |
| 1282 | } else { | 1522 | ); |
| 1283 | // if onreadystatechange wasn't actually set by the page on this xhr, we | 1523 | _document.addEventListener( |
| 1284 | // are free to set our own and capture the breadcrumb | 1524 | "keypress", |
| 1285 | xhr.onreadystatechange = onreadystatechangeHandler; | 1525 | self._keypressEventHandler(), |
| 1286 | } | 1526 | false, |
| 1527 | ); | ||
| 1528 | } else { | ||
| 1529 | // IE8 Compatibility | ||
| 1530 | _document.attachEvent( | ||
| 1531 | "onclick", | ||
| 1532 | self._breadcrumbEventHandler("click"), | ||
| 1533 | ); | ||
| 1534 | _document.attachEvent( | ||
| 1535 | "onkeypress", | ||
| 1536 | self._keypressEventHandler(), | ||
| 1537 | ); | ||
| 1538 | } | ||
| 1539 | } | ||
| 1287 | 1540 | ||
| 1288 | return origSend.apply(this, arguments); | 1541 | // record navigation (URL) changes |
| 1289 | }; | 1542 | // NOTE: in Chrome App environment, touching history.pushState, *even inside |
| 1290 | }, | 1543 | // a try/catch block*, will cause Chrome to output an error to console.error |
| 1291 | wrappedBuiltIns | 1544 | // borrowed from: https://github.com/angular/angular.js/pull/13945/files |
| 1292 | ); | 1545 | var chrome = _window.chrome; |
| 1293 | } | 1546 | var isChromePackagedApp = |
| 1547 | chrome && chrome.app && chrome.app.runtime; | ||
| 1548 | var hasPushAndReplaceState = | ||
| 1549 | !isChromePackagedApp && | ||
| 1550 | _window.history && | ||
| 1551 | history.pushState && | ||
| 1552 | history.replaceState; | ||
| 1553 | if (autoBreadcrumbs.location && hasPushAndReplaceState) { | ||
| 1554 | // TODO: remove onpopstate handler on uninstall() | ||
| 1555 | var oldOnPopState = _window.onpopstate; | ||
| 1556 | _window.onpopstate = function () { | ||
| 1557 | var currentHref = self._location.href; | ||
| 1558 | self._captureUrlChange(self._lastHref, currentHref); | ||
| 1559 | |||
| 1560 | if (oldOnPopState) { | ||
| 1561 | return oldOnPopState.apply(this, arguments); | ||
| 1562 | } | ||
| 1563 | }; | ||
| 1564 | |||
| 1565 | var historyReplacementFunction = function (origHistFunction) { | ||
| 1566 | // note history.pushState.length is 0; intentionally not declaring | ||
| 1567 | // params to preserve 0 arity | ||
| 1568 | return function (/* state, title, url */) { | ||
| 1569 | var url = arguments.length > 2 ? arguments[2] : undefined; | ||
| 1570 | |||
| 1571 | // url argument is optional | ||
| 1572 | if (url) { | ||
| 1573 | // coerce to string (this is what pushState does) | ||
| 1574 | self._captureUrlChange(self._lastHref, url + ""); | ||
| 1575 | } | ||
| 1576 | |||
| 1577 | return origHistFunction.apply(this, arguments); | ||
| 1578 | }; | ||
| 1579 | }; | ||
| 1580 | |||
| 1581 | fill( | ||
| 1582 | history, | ||
| 1583 | "pushState", | ||
| 1584 | historyReplacementFunction, | ||
| 1585 | wrappedBuiltIns, | ||
| 1586 | ); | ||
| 1587 | fill( | ||
| 1588 | history, | ||
| 1589 | "replaceState", | ||
| 1590 | historyReplacementFunction, | ||
| 1591 | wrappedBuiltIns, | ||
| 1592 | ); | ||
| 1593 | } | ||
| 1294 | 1594 | ||
| 1295 | if (autoBreadcrumbs.xhr && 'fetch' in _window) { | 1595 | if ( |
| 1296 | fill( | 1596 | autoBreadcrumbs.console && |
| 1297 | _window, | 1597 | "console" in _window && |
| 1298 | 'fetch', | 1598 | console.log |
| 1299 | function(origFetch) { | 1599 | ) { |
| 1300 | return function(fn, t) { | 1600 | // console |
| 1301 | // preserve arity | 1601 | var consoleMethodCallback = function (msg, data) { |
| 1302 | // Make a copy of the arguments to prevent deoptimization | 1602 | self.captureBreadcrumb({ |
| 1303 | // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments | 1603 | message: msg, |
| 1304 | var args = new Array(arguments.length); | 1604 | level: data.level, |
| 1305 | for (var i = 0; i < args.length; ++i) { | 1605 | category: "console", |
| 1306 | args[i] = arguments[i]; | 1606 | }); |
| 1307 | } | 1607 | }; |
| 1608 | |||
| 1609 | each( | ||
| 1610 | ["debug", "info", "warn", "error", "log"], | ||
| 1611 | function (_, level) { | ||
| 1612 | wrapConsoleMethod(console, level, consoleMethodCallback); | ||
| 1613 | }, | ||
| 1614 | ); | ||
| 1615 | } | ||
| 1616 | }, | ||
| 1308 | 1617 | ||
| 1309 | var fetchInput = args[0]; | 1618 | _restoreBuiltIns: function () { |
| 1310 | var method = 'GET'; | 1619 | // restore any wrapped builtins |
| 1311 | var url; | 1620 | var builtin; |
| 1621 | while (this._wrappedBuiltIns.length) { | ||
| 1622 | builtin = this._wrappedBuiltIns.shift(); | ||
| 1312 | 1623 | ||
| 1313 | if (typeof fetchInput === 'string') { | 1624 | var obj = builtin[0], |
| 1314 | url = fetchInput; | 1625 | name = builtin[1], |
| 1315 | } else if ('Request' in _window && fetchInput instanceof _window.Request) { | 1626 | orig = builtin[2]; |
| 1316 | url = fetchInput.url; | ||
| 1317 | if (fetchInput.method) { | ||
| 1318 | method = fetchInput.method; | ||
| 1319 | } | ||
| 1320 | } else { | ||
| 1321 | url = '' + fetchInput; | ||
| 1322 | } | ||
| 1323 | 1627 | ||
| 1324 | if (args[1] && args[1].method) { | 1628 | obj[name] = orig; |
| 1325 | method = args[1].method; | 1629 | } |
| 1326 | } | 1630 | }, |
| 1327 | 1631 | ||
| 1328 | var fetchData = { | 1632 | _drainPlugins: function () { |
| 1329 | method: method, | 1633 | var self = this; |
| 1330 | url: url, | ||
| 1331 | status_code: null | ||
| 1332 | }; | ||
| 1333 | 1634 | ||
| 1334 | self.captureBreadcrumb({ | 1635 | // FIX ME TODO |
| 1335 | type: 'http', | 1636 | each(this._plugins, function (_, plugin) { |
| 1336 | category: 'fetch', | 1637 | var installer = plugin[0]; |
| 1337 | data: fetchData | 1638 | var args = plugin[1]; |
| 1338 | }); | 1639 | installer.apply(self, [self].concat(args)); |
| 1640 | }); | ||
| 1641 | }, | ||
| 1339 | 1642 | ||
| 1340 | return origFetch.apply(this, args).then(function(response) { | 1643 | _parseDSN: function (str) { |
| 1341 | fetchData.status_code = response.status; | 1644 | var m = dsnPattern.exec(str), |
| 1645 | dsn = {}, | ||
| 1646 | i = 7; | ||
| 1342 | 1647 | ||
| 1343 | return response; | 1648 | try { |
| 1344 | }); | 1649 | while (i--) dsn[dsnKeys[i]] = m[i] || ""; |
| 1345 | }; | 1650 | } catch (e) { |
| 1346 | }, | 1651 | throw new RavenConfigError("Invalid DSN: " + str); |
| 1347 | wrappedBuiltIns | 1652 | } |
| 1348 | ); | ||
| 1349 | } | ||
| 1350 | 1653 | ||
| 1351 | // Capture breadcrumbs from any click that is unhandled / bubbled up all the way | 1654 | if (dsn.pass && !this._globalOptions.allowSecretKey) { |
| 1352 | // to the document. Do this before we instrument addEventListener. | 1655 | throw new RavenConfigError( |
| 1353 | if (autoBreadcrumbs.dom && this._hasDocument) { | 1656 | "Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key", |
| 1354 | if (_document.addEventListener) { | 1657 | ); |
| 1355 | _document.addEventListener('click', self._breadcrumbEventHandler('click'), false); | 1658 | } |
| 1356 | _document.addEventListener('keypress', self._keypressEventHandler(), false); | ||
| 1357 | } else { | ||
| 1358 | // IE8 Compatibility | ||
| 1359 | _document.attachEvent('onclick', self._breadcrumbEventHandler('click')); | ||
| 1360 | _document.attachEvent('onkeypress', self._keypressEventHandler()); | ||
| 1361 | } | ||
| 1362 | } | ||
| 1363 | 1659 | ||
| 1364 | // record navigation (URL) changes | 1660 | return dsn; |
| 1365 | // NOTE: in Chrome App environment, touching history.pushState, *even inside | 1661 | }, |
| 1366 | // a try/catch block*, will cause Chrome to output an error to console.error | ||
| 1367 | // borrowed from: https://github.com/angular/angular.js/pull/13945/files | ||
| 1368 | var chrome = _window.chrome; | ||
| 1369 | var isChromePackagedApp = chrome && chrome.app && chrome.app.runtime; | ||
| 1370 | var hasPushAndReplaceState = | ||
| 1371 | !isChromePackagedApp && | ||
| 1372 | _window.history && | ||
| 1373 | history.pushState && | ||
| 1374 | history.replaceState; | ||
| 1375 | if (autoBreadcrumbs.location && hasPushAndReplaceState) { | ||
| 1376 | // TODO: remove onpopstate handler on uninstall() | ||
| 1377 | var oldOnPopState = _window.onpopstate; | ||
| 1378 | _window.onpopstate = function() { | ||
| 1379 | var currentHref = self._location.href; | ||
| 1380 | self._captureUrlChange(self._lastHref, currentHref); | ||
| 1381 | |||
| 1382 | if (oldOnPopState) { | ||
| 1383 | return oldOnPopState.apply(this, arguments); | ||
| 1384 | } | ||
| 1385 | }; | ||
| 1386 | |||
| 1387 | var historyReplacementFunction = function(origHistFunction) { | ||
| 1388 | // note history.pushState.length is 0; intentionally not declaring | ||
| 1389 | // params to preserve 0 arity | ||
| 1390 | return function(/* state, title, url */) { | ||
| 1391 | var url = arguments.length > 2 ? arguments[2] : undefined; | ||
| 1392 | |||
| 1393 | // url argument is optional | ||
| 1394 | if (url) { | ||
| 1395 | // coerce to string (this is what pushState does) | ||
| 1396 | self._captureUrlChange(self._lastHref, url + ''); | ||
| 1397 | } | ||
| 1398 | 1662 | ||
| 1399 | return origHistFunction.apply(this, arguments); | 1663 | _getGlobalServer: function (uri) { |
| 1400 | }; | 1664 | // assemble the endpoint from the uri pieces |
| 1401 | }; | 1665 | var globalServer = |
| 1666 | "//" + uri.host + (uri.port ? ":" + uri.port : ""); | ||
| 1402 | 1667 | ||
| 1403 | fill(history, 'pushState', historyReplacementFunction, wrappedBuiltIns); | 1668 | if (uri.protocol) { |
| 1404 | fill(history, 'replaceState', historyReplacementFunction, wrappedBuiltIns); | 1669 | globalServer = uri.protocol + ":" + globalServer; |
| 1405 | } | 1670 | } |
| 1671 | return globalServer; | ||
| 1672 | }, | ||
| 1406 | 1673 | ||
| 1407 | if (autoBreadcrumbs.console && 'console' in _window && console.log) { | 1674 | _handleOnErrorStackInfo: function () { |
| 1408 | // console | 1675 | // if we are intentionally ignoring errors via onerror, bail out |
| 1409 | var consoleMethodCallback = function(msg, data) { | 1676 | if (!this._ignoreOnError) { |
| 1410 | self.captureBreadcrumb({ | 1677 | this._handleStackInfo.apply(this, arguments); |
| 1411 | message: msg, | 1678 | } |
| 1412 | level: data.level, | 1679 | }, |
| 1413 | category: 'console' | ||
| 1414 | }); | ||
| 1415 | }; | ||
| 1416 | |||
| 1417 | each(['debug', 'info', 'warn', 'error', 'log'], function(_, level) { | ||
| 1418 | wrapConsoleMethod(console, level, consoleMethodCallback); | ||
| 1419 | }); | ||
| 1420 | } | ||
| 1421 | }, | ||
| 1422 | 1680 | ||
| 1423 | _restoreBuiltIns: function() { | 1681 | _handleStackInfo: function (stackInfo, options) { |
| 1424 | // restore any wrapped builtins | 1682 | var frames = this._prepareFrames(stackInfo, options); |
| 1425 | var builtin; | ||
| 1426 | while (this._wrappedBuiltIns.length) { | ||
| 1427 | builtin = this._wrappedBuiltIns.shift(); | ||
| 1428 | 1683 | ||
| 1429 | var obj = builtin[0], | 1684 | this._triggerEvent("handle", { |
| 1430 | name = builtin[1], | 1685 | stackInfo: stackInfo, |
| 1431 | orig = builtin[2]; | 1686 | options: options, |
| 1687 | }); | ||
| 1432 | 1688 | ||
| 1433 | obj[name] = orig; | 1689 | this._processException( |
| 1434 | } | 1690 | stackInfo.name, |
| 1435 | }, | 1691 | stackInfo.message, |
| 1436 | 1692 | stackInfo.url, | |
| 1437 | _drainPlugins: function() { | 1693 | stackInfo.lineno, |
| 1438 | var self = this; | 1694 | frames, |
| 1439 | 1695 | options, | |
| 1440 | // FIX ME TODO | 1696 | ); |
| 1441 | each(this._plugins, function(_, plugin) { | 1697 | }, |
| 1442 | var installer = plugin[0]; | 1698 | |
| 1443 | var args = plugin[1]; | 1699 | _prepareFrames: function (stackInfo, options) { |
| 1444 | installer.apply(self, [self].concat(args)); | 1700 | var self = this; |
| 1445 | }); | 1701 | var frames = []; |
| 1446 | }, | 1702 | if (stackInfo.stack && stackInfo.stack.length) { |
| 1447 | 1703 | each(stackInfo.stack, function (i, stack) { | |
| 1448 | _parseDSN: function(str) { | 1704 | var frame = self._normalizeFrame(stack, stackInfo.url); |
| 1449 | var m = dsnPattern.exec(str), | 1705 | if (frame) { |
| 1450 | dsn = {}, | 1706 | frames.push(frame); |
| 1451 | i = 7; | 1707 | } |
| 1452 | 1708 | }); | |
| 1453 | try { | 1709 | |
| 1454 | while (i--) dsn[dsnKeys[i]] = m[i] || ''; | 1710 | // e.g. frames captured via captureMessage throw |
| 1455 | } catch (e) { | 1711 | if (options && options.trimHeadFrames) { |
| 1456 | throw new RavenConfigError('Invalid DSN: ' + str); | 1712 | for ( |
| 1457 | } | 1713 | var j = 0; |
| 1714 | j < options.trimHeadFrames && j < frames.length; | ||
| 1715 | j++ | ||
| 1716 | ) { | ||
| 1717 | frames[j].in_app = false; | ||
| 1718 | } | ||
| 1719 | } | ||
| 1720 | } | ||
| 1721 | frames = frames.slice(0, this._globalOptions.stackTraceLimit); | ||
| 1722 | return frames; | ||
| 1723 | }, | ||
| 1724 | |||
| 1725 | _normalizeFrame: function (frame, stackInfoUrl) { | ||
| 1726 | // normalize the frames data | ||
| 1727 | var normalized = { | ||
| 1728 | filename: frame.url, | ||
| 1729 | lineno: frame.line, | ||
| 1730 | colno: frame.column, | ||
| 1731 | function: frame.func || "?", | ||
| 1732 | }; | ||
| 1458 | 1733 | ||
| 1459 | if (dsn.pass && !this._globalOptions.allowSecretKey) { | 1734 | // Case when we don't have any information about the error |
| 1460 | throw new RavenConfigError( | 1735 | // E.g. throwing a string or raw object, instead of an `Error` in Firefox |
| 1461 | 'Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key' | 1736 | // Generating synthetic error doesn't add any value here |
| 1462 | ); | 1737 | // |
| 1463 | } | 1738 | // We should probably somehow let a user know that they should fix their code |
| 1739 | if (!frame.url) { | ||
| 1740 | normalized.filename = stackInfoUrl; // fallback to whole stacks url from onerror handler | ||
| 1741 | } | ||
| 1464 | 1742 | ||
| 1465 | return dsn; | 1743 | normalized.in_app = !( |
| 1466 | }, | 1744 | // determine if an exception came from outside of our app |
| 1745 | // first we check the global includePaths list. | ||
| 1746 | ( | ||
| 1747 | (!!this._globalOptions.includePaths.test && | ||
| 1748 | !this._globalOptions.includePaths.test( | ||
| 1749 | normalized.filename, | ||
| 1750 | )) || | ||
| 1751 | // Now we check for fun, if the function name is Raven or TraceKit | ||
| 1752 | /(Raven|TraceKit)\./.test(normalized["function"]) || | ||
| 1753 | // finally, we do a last ditch effort and check for raven.min.js | ||
| 1754 | /raven\.(min\.)?js$/.test(normalized.filename) | ||
| 1755 | ) | ||
| 1756 | ); | ||
| 1757 | |||
| 1758 | return normalized; | ||
| 1759 | }, | ||
| 1760 | |||
| 1761 | _processException: function ( | ||
| 1762 | type, | ||
| 1763 | message, | ||
| 1764 | fileurl, | ||
| 1765 | lineno, | ||
| 1766 | frames, | ||
| 1767 | options, | ||
| 1768 | ) { | ||
| 1769 | var prefixedMessage = | ||
| 1770 | (type ? type + ": " : "") + (message || ""); | ||
| 1771 | if ( | ||
| 1772 | !!this._globalOptions.ignoreErrors.test && | ||
| 1773 | (this._globalOptions.ignoreErrors.test(message) || | ||
| 1774 | this._globalOptions.ignoreErrors.test(prefixedMessage)) | ||
| 1775 | ) { | ||
| 1776 | return; | ||
| 1777 | } | ||
| 1467 | 1778 | ||
| 1468 | _getGlobalServer: function(uri) { | 1779 | var stacktrace; |
| 1469 | // assemble the endpoint from the uri pieces | 1780 | |
| 1470 | var globalServer = '//' + uri.host + (uri.port ? ':' + uri.port : ''); | 1781 | if (frames && frames.length) { |
| 1782 | fileurl = frames[0].filename || fileurl; | ||
| 1783 | // Sentry expects frames oldest to newest | ||
| 1784 | // and JS sends them as newest to oldest | ||
| 1785 | frames.reverse(); | ||
| 1786 | stacktrace = { frames: frames }; | ||
| 1787 | } else if (fileurl) { | ||
| 1788 | stacktrace = { | ||
| 1789 | frames: [ | ||
| 1790 | { | ||
| 1791 | filename: fileurl, | ||
| 1792 | lineno: lineno, | ||
| 1793 | in_app: true, | ||
| 1794 | }, | ||
| 1795 | ], | ||
| 1796 | }; | ||
| 1797 | } | ||
| 1471 | 1798 | ||
| 1472 | if (uri.protocol) { | 1799 | if ( |
| 1473 | globalServer = uri.protocol + ':' + globalServer; | 1800 | !!this._globalOptions.ignoreUrls.test && |
| 1474 | } | 1801 | this._globalOptions.ignoreUrls.test(fileurl) |
| 1475 | return globalServer; | 1802 | ) { |
| 1476 | }, | 1803 | return; |
| 1804 | } | ||
| 1477 | 1805 | ||
| 1478 | _handleOnErrorStackInfo: function() { | 1806 | if ( |
| 1479 | // if we are intentionally ignoring errors via onerror, bail out | 1807 | !!this._globalOptions.whitelistUrls.test && |
| 1480 | if (!this._ignoreOnError) { | 1808 | !this._globalOptions.whitelistUrls.test(fileurl) |
| 1481 | this._handleStackInfo.apply(this, arguments); | 1809 | ) { |
| 1482 | } | 1810 | return; |
| 1483 | }, | 1811 | } |
| 1484 | |||
| 1485 | _handleStackInfo: function(stackInfo, options) { | ||
| 1486 | var frames = this._prepareFrames(stackInfo, options); | ||
| 1487 | |||
| 1488 | this._triggerEvent('handle', { | ||
| 1489 | stackInfo: stackInfo, | ||
| 1490 | options: options | ||
| 1491 | }); | ||
| 1492 | |||
| 1493 | this._processException( | ||
| 1494 | stackInfo.name, | ||
| 1495 | stackInfo.message, | ||
| 1496 | stackInfo.url, | ||
| 1497 | stackInfo.lineno, | ||
| 1498 | frames, | ||
| 1499 | options | ||
| 1500 | ); | ||
| 1501 | }, | ||
| 1502 | |||
| 1503 | _prepareFrames: function(stackInfo, options) { | ||
| 1504 | var self = this; | ||
| 1505 | var frames = []; | ||
| 1506 | if (stackInfo.stack && stackInfo.stack.length) { | ||
| 1507 | each(stackInfo.stack, function(i, stack) { | ||
| 1508 | var frame = self._normalizeFrame(stack, stackInfo.url); | ||
| 1509 | if (frame) { | ||
| 1510 | frames.push(frame); | ||
| 1511 | } | ||
| 1512 | }); | ||
| 1513 | 1812 | ||
| 1514 | // e.g. frames captured via captureMessage throw | 1813 | var data = objectMerge( |
| 1515 | if (options && options.trimHeadFrames) { | 1814 | { |
| 1516 | for (var j = 0; j < options.trimHeadFrames && j < frames.length; j++) { | 1815 | // sentry.interfaces.Exception |
| 1517 | frames[j].in_app = false; | 1816 | exception: { |
| 1518 | } | 1817 | values: [ |
| 1519 | } | 1818 | { |
| 1520 | } | 1819 | type: type, |
| 1521 | frames = frames.slice(0, this._globalOptions.stackTraceLimit); | 1820 | value: message, |
| 1522 | return frames; | 1821 | stacktrace: stacktrace, |
| 1523 | }, | 1822 | }, |
| 1524 | 1823 | ], | |
| 1525 | _normalizeFrame: function(frame, stackInfoUrl) { | 1824 | }, |
| 1526 | // normalize the frames data | 1825 | culprit: fileurl, |
| 1527 | var normalized = { | 1826 | }, |
| 1528 | filename: frame.url, | 1827 | options, |
| 1529 | lineno: frame.line, | 1828 | ); |
| 1530 | colno: frame.column, | 1829 | |
| 1531 | function: frame.func || '?' | 1830 | // Fire away! |
| 1532 | }; | 1831 | this._send(data); |
| 1533 | 1832 | }, | |
| 1534 | // Case when we don't have any information about the error | 1833 | |
| 1535 | // E.g. throwing a string or raw object, instead of an `Error` in Firefox | 1834 | _trimPacket: function (data) { |
| 1536 | // Generating synthetic error doesn't add any value here | 1835 | // For now, we only want to truncate the two different messages |
| 1537 | // | 1836 | // but this could/should be expanded to just trim everything |
| 1538 | // We should probably somehow let a user know that they should fix their code | 1837 | var max = this._globalOptions.maxMessageLength; |
| 1539 | if (!frame.url) { | 1838 | if (data.message) { |
| 1540 | normalized.filename = stackInfoUrl; // fallback to whole stacks url from onerror handler | 1839 | data.message = truncate(data.message, max); |
| 1541 | } | 1840 | } |
| 1841 | if (data.exception) { | ||
| 1842 | var exception = data.exception.values[0]; | ||
| 1843 | exception.value = truncate(exception.value, max); | ||
| 1844 | } | ||
| 1542 | 1845 | ||
| 1543 | normalized.in_app = !// determine if an exception came from outside of our app | 1846 | var request = data.request; |
| 1544 | // first we check the global includePaths list. | 1847 | if (request) { |
| 1545 | ( | 1848 | if (request.url) { |
| 1546 | (!!this._globalOptions.includePaths.test && | 1849 | request.url = truncate( |
| 1547 | !this._globalOptions.includePaths.test(normalized.filename)) || | 1850 | request.url, |
| 1548 | // Now we check for fun, if the function name is Raven or TraceKit | 1851 | this._globalOptions.maxUrlLength, |
| 1549 | /(Raven|TraceKit)\./.test(normalized['function']) || | 1852 | ); |
| 1550 | // finally, we do a last ditch effort and check for raven.min.js | 1853 | } |
| 1551 | /raven\.(min\.)?js$/.test(normalized.filename) | 1854 | if (request.Referer) { |
| 1552 | ); | 1855 | request.Referer = truncate( |
| 1553 | 1856 | request.Referer, | |
| 1554 | return normalized; | 1857 | this._globalOptions.maxUrlLength, |
| 1555 | }, | 1858 | ); |
| 1556 | 1859 | } | |
| 1557 | _processException: function(type, message, fileurl, lineno, frames, options) { | 1860 | } |
| 1558 | var prefixedMessage = (type ? type + ': ' : '') + (message || ''); | ||
| 1559 | if ( | ||
| 1560 | !!this._globalOptions.ignoreErrors.test && | ||
| 1561 | (this._globalOptions.ignoreErrors.test(message) || | ||
| 1562 | this._globalOptions.ignoreErrors.test(prefixedMessage)) | ||
| 1563 | ) { | ||
| 1564 | return; | ||
| 1565 | } | ||
| 1566 | 1861 | ||
| 1567 | var stacktrace; | 1862 | if (data.breadcrumbs && data.breadcrumbs.values) |
| 1568 | 1863 | this._trimBreadcrumbs(data.breadcrumbs); | |
| 1569 | if (frames && frames.length) { | 1864 | |
| 1570 | fileurl = frames[0].filename || fileurl; | 1865 | return data; |
| 1571 | // Sentry expects frames oldest to newest | 1866 | }, |
| 1572 | // and JS sends them as newest to oldest | 1867 | |
| 1573 | frames.reverse(); | 1868 | /** |
| 1574 | stacktrace = {frames: frames}; | 1869 | * Truncate breadcrumb values (right now just URLs) |
| 1575 | } else if (fileurl) { | 1870 | */ |
| 1576 | stacktrace = { | 1871 | _trimBreadcrumbs: function (breadcrumbs) { |
| 1577 | frames: [ | 1872 | // known breadcrumb properties with urls |
| 1578 | { | 1873 | // TODO: also consider arbitrary prop values that start with (https?)?:// |
| 1579 | filename: fileurl, | 1874 | var urlProps = ["to", "from", "url"], |
| 1580 | lineno: lineno, | 1875 | urlProp, |
| 1581 | in_app: true | 1876 | crumb, |
| 1582 | } | 1877 | data; |
| 1583 | ] | 1878 | |
| 1584 | }; | 1879 | for (var i = 0; i < breadcrumbs.values.length; ++i) { |
| 1585 | } | 1880 | crumb = breadcrumbs.values[i]; |
| 1881 | if ( | ||
| 1882 | !crumb.hasOwnProperty("data") || | ||
| 1883 | !isObject(crumb.data) || | ||
| 1884 | objectFrozen(crumb.data) | ||
| 1885 | ) | ||
| 1886 | continue; | ||
| 1887 | |||
| 1888 | data = objectMerge({}, crumb.data); | ||
| 1889 | for (var j = 0; j < urlProps.length; ++j) { | ||
| 1890 | urlProp = urlProps[j]; | ||
| 1891 | if (data.hasOwnProperty(urlProp) && data[urlProp]) { | ||
| 1892 | data[urlProp] = truncate( | ||
| 1893 | data[urlProp], | ||
| 1894 | this._globalOptions.maxUrlLength, | ||
| 1895 | ); | ||
| 1896 | } | ||
| 1897 | } | ||
| 1898 | breadcrumbs.values[i].data = data; | ||
| 1899 | } | ||
| 1900 | }, | ||
| 1586 | 1901 | ||
| 1587 | if ( | 1902 | _getHttpData: function () { |
| 1588 | !!this._globalOptions.ignoreUrls.test && | 1903 | if (!this._hasNavigator && !this._hasDocument) return; |
| 1589 | this._globalOptions.ignoreUrls.test(fileurl) | 1904 | var httpData = {}; |
| 1590 | ) { | ||
| 1591 | return; | ||
| 1592 | } | ||
| 1593 | 1905 | ||
| 1594 | if ( | 1906 | if (this._hasNavigator && _navigator.userAgent) { |
| 1595 | !!this._globalOptions.whitelistUrls.test && | 1907 | httpData.headers = { |
| 1596 | !this._globalOptions.whitelistUrls.test(fileurl) | 1908 | "User-Agent": navigator.userAgent, |
| 1597 | ) { | 1909 | }; |
| 1598 | return; | 1910 | } |
| 1599 | } | ||
| 1600 | 1911 | ||
| 1601 | var data = objectMerge( | 1912 | if (this._hasDocument) { |
| 1602 | { | 1913 | if (_document.location && _document.location.href) { |
| 1603 | // sentry.interfaces.Exception | 1914 | httpData.url = _document.location.href; |
| 1604 | exception: { | 1915 | } |
| 1605 | values: [ | 1916 | if (_document.referrer) { |
| 1606 | { | 1917 | if (!httpData.headers) httpData.headers = {}; |
| 1607 | type: type, | 1918 | httpData.headers.Referer = _document.referrer; |
| 1608 | value: message, | 1919 | } |
| 1609 | stacktrace: stacktrace | 1920 | } |
| 1610 | } | ||
| 1611 | ] | ||
| 1612 | }, | ||
| 1613 | culprit: fileurl | ||
| 1614 | }, | ||
| 1615 | options | ||
| 1616 | ); | ||
| 1617 | |||
| 1618 | // Fire away! | ||
| 1619 | this._send(data); | ||
| 1620 | }, | ||
| 1621 | |||
| 1622 | _trimPacket: function(data) { | ||
| 1623 | // For now, we only want to truncate the two different messages | ||
| 1624 | // but this could/should be expanded to just trim everything | ||
| 1625 | var max = this._globalOptions.maxMessageLength; | ||
| 1626 | if (data.message) { | ||
| 1627 | data.message = truncate(data.message, max); | ||
| 1628 | } | ||
| 1629 | if (data.exception) { | ||
| 1630 | var exception = data.exception.values[0]; | ||
| 1631 | exception.value = truncate(exception.value, max); | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | var request = data.request; | ||
| 1635 | if (request) { | ||
| 1636 | if (request.url) { | ||
| 1637 | request.url = truncate(request.url, this._globalOptions.maxUrlLength); | ||
| 1638 | } | ||
| 1639 | if (request.Referer) { | ||
| 1640 | request.Referer = truncate(request.Referer, this._globalOptions.maxUrlLength); | ||
| 1641 | } | ||
| 1642 | } | ||
| 1643 | |||
| 1644 | if (data.breadcrumbs && data.breadcrumbs.values) | ||
| 1645 | this._trimBreadcrumbs(data.breadcrumbs); | ||
| 1646 | |||
| 1647 | return data; | ||
| 1648 | }, | ||
| 1649 | |||
| 1650 | /** | ||
| 1651 | * Truncate breadcrumb values (right now just URLs) | ||
| 1652 | */ | ||
| 1653 | _trimBreadcrumbs: function(breadcrumbs) { | ||
| 1654 | // known breadcrumb properties with urls | ||
| 1655 | // TODO: also consider arbitrary prop values that start with (https?)?:// | ||
| 1656 | var urlProps = ['to', 'from', 'url'], | ||
| 1657 | urlProp, | ||
| 1658 | crumb, | ||
| 1659 | data; | ||
| 1660 | |||
| 1661 | for (var i = 0; i < breadcrumbs.values.length; ++i) { | ||
| 1662 | crumb = breadcrumbs.values[i]; | ||
| 1663 | if ( | ||
| 1664 | !crumb.hasOwnProperty('data') || | ||
| 1665 | !isObject(crumb.data) || | ||
| 1666 | objectFrozen(crumb.data) | ||
| 1667 | ) | ||
| 1668 | continue; | ||
| 1669 | |||
| 1670 | data = objectMerge({}, crumb.data); | ||
| 1671 | for (var j = 0; j < urlProps.length; ++j) { | ||
| 1672 | urlProp = urlProps[j]; | ||
| 1673 | if (data.hasOwnProperty(urlProp) && data[urlProp]) { | ||
| 1674 | data[urlProp] = truncate(data[urlProp], this._globalOptions.maxUrlLength); | ||
| 1675 | } | ||
| 1676 | } | ||
| 1677 | breadcrumbs.values[i].data = data; | ||
| 1678 | } | ||
| 1679 | }, | ||
| 1680 | |||
| 1681 | _getHttpData: function() { | ||
| 1682 | if (!this._hasNavigator && !this._hasDocument) return; | ||
| 1683 | var httpData = {}; | ||
| 1684 | |||
| 1685 | if (this._hasNavigator && _navigator.userAgent) { | ||
| 1686 | httpData.headers = { | ||
| 1687 | 'User-Agent': navigator.userAgent | ||
| 1688 | }; | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | if (this._hasDocument) { | ||
| 1692 | if (_document.location && _document.location.href) { | ||
| 1693 | httpData.url = _document.location.href; | ||
| 1694 | } | ||
| 1695 | if (_document.referrer) { | ||
| 1696 | if (!httpData.headers) httpData.headers = {}; | ||
| 1697 | httpData.headers.Referer = _document.referrer; | ||
| 1698 | } | ||
| 1699 | } | ||
| 1700 | |||
| 1701 | return httpData; | ||
| 1702 | }, | ||
| 1703 | |||
| 1704 | _resetBackoff: function() { | ||
| 1705 | this._backoffDuration = 0; | ||
| 1706 | this._backoffStart = null; | ||
| 1707 | }, | ||
| 1708 | |||
| 1709 | _shouldBackoff: function() { | ||
| 1710 | return this._backoffDuration && now() - this._backoffStart < this._backoffDuration; | ||
| 1711 | }, | ||
| 1712 | |||
| 1713 | /** | ||
| 1714 | * Returns true if the in-process data payload matches the signature | ||
| 1715 | * of the previously-sent data | ||
| 1716 | * | ||
| 1717 | * NOTE: This has to be done at this level because TraceKit can generate | ||
| 1718 | * data from window.onerror WITHOUT an exception object (IE8, IE9, | ||
| 1719 | * other old browsers). This can take the form of an "exception" | ||
| 1720 | * data object with a single frame (derived from the onerror args). | ||
| 1721 | */ | ||
| 1722 | _isRepeatData: function(current) { | ||
| 1723 | var last = this._lastData; | ||
| 1724 | |||
| 1725 | if ( | ||
| 1726 | !last || | ||
| 1727 | current.message !== last.message || // defined for captureMessage | ||
| 1728 | current.culprit !== last.culprit // defined for captureException/onerror | ||
| 1729 | ) | ||
| 1730 | return false; | ||
| 1731 | |||
| 1732 | // Stacktrace interface (i.e. from captureMessage) | ||
| 1733 | if (current.stacktrace || last.stacktrace) { | ||
| 1734 | return isSameStacktrace(current.stacktrace, last.stacktrace); | ||
| 1735 | } else if (current.exception || last.exception) { | ||
| 1736 | // Exception interface (i.e. from captureException/onerror) | ||
| 1737 | return isSameException(current.exception, last.exception); | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | return true; | ||
| 1741 | }, | ||
| 1742 | |||
| 1743 | _setBackoffState: function(request) { | ||
| 1744 | // If we are already in a backoff state, don't change anything | ||
| 1745 | if (this._shouldBackoff()) { | ||
| 1746 | return; | ||
| 1747 | } | ||
| 1748 | |||
| 1749 | var status = request.status; | ||
| 1750 | |||
| 1751 | // 400 - project_id doesn't exist or some other fatal | ||
| 1752 | // 401 - invalid/revoked dsn | ||
| 1753 | // 429 - too many requests | ||
| 1754 | if (!(status === 400 || status === 401 || status === 429)) return; | ||
| 1755 | |||
| 1756 | var retry; | ||
| 1757 | try { | ||
| 1758 | // If Retry-After is not in Access-Control-Expose-Headers, most | ||
| 1759 | // browsers will throw an exception trying to access it | ||
| 1760 | retry = request.getResponseHeader('Retry-After'); | ||
| 1761 | retry = parseInt(retry, 10) * 1000; // Retry-After is returned in seconds | ||
| 1762 | } catch (e) { | ||
| 1763 | /* eslint no-empty:0 */ | ||
| 1764 | } | ||
| 1765 | |||
| 1766 | this._backoffDuration = retry | ||
| 1767 | ? // If Sentry server returned a Retry-After value, use it | ||
| 1768 | retry | ||
| 1769 | : // Otherwise, double the last backoff duration (starts at 1 sec) | ||
| 1770 | this._backoffDuration * 2 || 1000; | ||
| 1771 | 1921 | ||
| 1772 | this._backoffStart = now(); | 1922 | return httpData; |
| 1773 | }, | 1923 | }, |
| 1924 | |||
| 1925 | _resetBackoff: function () { | ||
| 1926 | this._backoffDuration = 0; | ||
| 1927 | this._backoffStart = null; | ||
| 1928 | }, | ||
| 1929 | |||
| 1930 | _shouldBackoff: function () { | ||
| 1931 | return ( | ||
| 1932 | this._backoffDuration && | ||
| 1933 | now() - this._backoffStart < this._backoffDuration | ||
| 1934 | ); | ||
| 1935 | }, | ||
| 1936 | |||
| 1937 | /** | ||
| 1938 | * Returns true if the in-process data payload matches the signature | ||
| 1939 | * of the previously-sent data | ||
| 1940 | * | ||
| 1941 | * NOTE: This has to be done at this level because TraceKit can generate | ||
| 1942 | * data from window.onerror WITHOUT an exception object (IE8, IE9, | ||
| 1943 | * other old browsers). This can take the form of an "exception" | ||
| 1944 | * data object with a single frame (derived from the onerror args). | ||
| 1945 | */ | ||
| 1946 | _isRepeatData: function (current) { | ||
| 1947 | var last = this._lastData; | ||
| 1948 | |||
| 1949 | if ( | ||
| 1950 | !last || | ||
| 1951 | current.message !== last.message || // defined for captureMessage | ||
| 1952 | current.culprit !== last.culprit // defined for captureException/onerror | ||
| 1953 | ) | ||
| 1954 | return false; | ||
| 1955 | |||
| 1956 | // Stacktrace interface (i.e. from captureMessage) | ||
| 1957 | if (current.stacktrace || last.stacktrace) { | ||
| 1958 | return isSameStacktrace(current.stacktrace, last.stacktrace); | ||
| 1959 | } else if (current.exception || last.exception) { | ||
| 1960 | // Exception interface (i.e. from captureException/onerror) | ||
| 1961 | return isSameException(current.exception, last.exception); | ||
| 1962 | } | ||
| 1774 | 1963 | ||
| 1775 | _send: function(data) { | 1964 | return true; |
| 1776 | var globalOptions = this._globalOptions; | 1965 | }, |
| 1777 | 1966 | ||
| 1778 | var baseData = { | 1967 | _setBackoffState: function (request) { |
| 1779 | project: this._globalProject, | 1968 | // If we are already in a backoff state, don't change anything |
| 1780 | logger: globalOptions.logger, | 1969 | if (this._shouldBackoff()) { |
| 1781 | platform: 'javascript' | 1970 | return; |
| 1782 | }, | 1971 | } |
| 1783 | httpData = this._getHttpData(); | ||
| 1784 | 1972 | ||
| 1785 | if (httpData) { | 1973 | var status = request.status; |
| 1786 | baseData.request = httpData; | ||
| 1787 | } | ||
| 1788 | 1974 | ||
| 1789 | // HACK: delete `trimHeadFrames` to prevent from appearing in outbound payload | 1975 | // 400 - project_id doesn't exist or some other fatal |
| 1790 | if (data.trimHeadFrames) delete data.trimHeadFrames; | 1976 | // 401 - invalid/revoked dsn |
| 1977 | // 429 - too many requests | ||
| 1978 | if (!(status === 400 || status === 401 || status === 429)) | ||
| 1979 | return; | ||
| 1791 | 1980 | ||
| 1792 | data = objectMerge(baseData, data); | 1981 | var retry; |
| 1982 | try { | ||
| 1983 | // If Retry-After is not in Access-Control-Expose-Headers, most | ||
| 1984 | // browsers will throw an exception trying to access it | ||
| 1985 | retry = request.getResponseHeader("Retry-After"); | ||
| 1986 | retry = parseInt(retry, 10) * 1000; // Retry-After is returned in seconds | ||
| 1987 | } catch (e) { | ||
| 1988 | /* eslint no-empty:0 */ | ||
| 1989 | } | ||
| 1793 | 1990 | ||
| 1794 | // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge | 1991 | this._backoffDuration = retry |
| 1795 | data.tags = objectMerge(objectMerge({}, this._globalContext.tags), data.tags); | 1992 | ? // If Sentry server returned a Retry-After value, use it |
| 1796 | data.extra = objectMerge(objectMerge({}, this._globalContext.extra), data.extra); | 1993 | retry |
| 1994 | : // Otherwise, double the last backoff duration (starts at 1 sec) | ||
| 1995 | this._backoffDuration * 2 || 1000; | ||
| 1797 | 1996 | ||
| 1798 | // Send along our own collected metadata with extra | 1997 | this._backoffStart = now(); |
| 1799 | data.extra['session:duration'] = now() - this._startTime; | 1998 | }, |
| 1800 | 1999 | ||
| 1801 | if (this._breadcrumbs && this._breadcrumbs.length > 0) { | 2000 | _send: function (data) { |
| 1802 | // intentionally make shallow copy so that additions | 2001 | var globalOptions = this._globalOptions; |
| 1803 | // to breadcrumbs aren't accidentally sent in this request | ||
| 1804 | data.breadcrumbs = { | ||
| 1805 | values: [].slice.call(this._breadcrumbs, 0) | ||
| 1806 | }; | ||
| 1807 | } | ||
| 1808 | 2002 | ||
| 1809 | // If there are no tags/extra, strip the key from the payload alltogther. | 2003 | var baseData = { |
| 1810 | if (isEmptyObject(data.tags)) delete data.tags; | 2004 | project: this._globalProject, |
| 2005 | logger: globalOptions.logger, | ||
| 2006 | platform: "javascript", | ||
| 2007 | }, | ||
| 2008 | httpData = this._getHttpData(); | ||
| 1811 | 2009 | ||
| 1812 | if (this._globalContext.user) { | 2010 | if (httpData) { |
| 1813 | // sentry.interfaces.User | 2011 | baseData.request = httpData; |
| 1814 | data.user = this._globalContext.user; | 2012 | } |
| 1815 | } | ||
| 1816 | 2013 | ||
| 1817 | // Include the environment if it's defined in globalOptions | 2014 | // HACK: delete `trimHeadFrames` to prevent from appearing in outbound payload |
| 1818 | if (globalOptions.environment) data.environment = globalOptions.environment; | 2015 | if (data.trimHeadFrames) delete data.trimHeadFrames; |
| 2016 | |||
| 2017 | data = objectMerge(baseData, data); | ||
| 2018 | |||
| 2019 | // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge | ||
| 2020 | data.tags = objectMerge( | ||
| 2021 | objectMerge({}, this._globalContext.tags), | ||
| 2022 | data.tags, | ||
| 2023 | ); | ||
| 2024 | data.extra = objectMerge( | ||
| 2025 | objectMerge({}, this._globalContext.extra), | ||
| 2026 | data.extra, | ||
| 2027 | ); | ||
| 2028 | |||
| 2029 | // Send along our own collected metadata with extra | ||
| 2030 | data.extra["session:duration"] = now() - this._startTime; | ||
| 2031 | |||
| 2032 | if (this._breadcrumbs && this._breadcrumbs.length > 0) { | ||
| 2033 | // intentionally make shallow copy so that additions | ||
| 2034 | // to breadcrumbs aren't accidentally sent in this request | ||
| 2035 | data.breadcrumbs = { | ||
| 2036 | values: [].slice.call(this._breadcrumbs, 0), | ||
| 2037 | }; | ||
| 2038 | } | ||
| 1819 | 2039 | ||
| 1820 | // Include the release if it's defined in globalOptions | 2040 | // If there are no tags/extra, strip the key from the payload alltogther. |
| 1821 | if (globalOptions.release) data.release = globalOptions.release; | 2041 | if (isEmptyObject(data.tags)) delete data.tags; |
| 1822 | 2042 | ||
| 1823 | // Include server_name if it's defined in globalOptions | 2043 | if (this._globalContext.user) { |
| 1824 | if (globalOptions.serverName) data.server_name = globalOptions.serverName; | 2044 | // sentry.interfaces.User |
| 2045 | data.user = this._globalContext.user; | ||
| 2046 | } | ||
| 1825 | 2047 | ||
| 1826 | if (isFunction(globalOptions.dataCallback)) { | 2048 | // Include the environment if it's defined in globalOptions |
| 1827 | data = globalOptions.dataCallback(data) || data; | 2049 | if (globalOptions.environment) |
| 1828 | } | 2050 | data.environment = globalOptions.environment; |
| 1829 | 2051 | ||
| 1830 | // Why?????????? | 2052 | // Include the release if it's defined in globalOptions |
| 1831 | if (!data || isEmptyObject(data)) { | 2053 | if (globalOptions.release) data.release = globalOptions.release; |
| 1832 | return; | ||
| 1833 | } | ||
| 1834 | 2054 | ||
| 1835 | // Check if the request should be filtered or not | 2055 | // Include server_name if it's defined in globalOptions |
| 1836 | if ( | 2056 | if (globalOptions.serverName) |
| 1837 | isFunction(globalOptions.shouldSendCallback) && | 2057 | data.server_name = globalOptions.serverName; |
| 1838 | !globalOptions.shouldSendCallback(data) | ||
| 1839 | ) { | ||
| 1840 | return; | ||
| 1841 | } | ||
| 1842 | 2058 | ||
| 1843 | // Backoff state: Sentry server previously responded w/ an error (e.g. 429 - too many requests), | 2059 | if (isFunction(globalOptions.dataCallback)) { |
| 1844 | // so drop requests until "cool-off" period has elapsed. | 2060 | data = globalOptions.dataCallback(data) || data; |
| 1845 | if (this._shouldBackoff()) { | 2061 | } |
| 1846 | this._logDebug('warn', 'Raven dropped error due to backoff: ', data); | ||
| 1847 | return; | ||
| 1848 | } | ||
| 1849 | 2062 | ||
| 1850 | if (typeof globalOptions.sampleRate === 'number') { | 2063 | // Why?????????? |
| 1851 | if (Math.random() < globalOptions.sampleRate) { | 2064 | if (!data || isEmptyObject(data)) { |
| 1852 | this._sendProcessedPayload(data); | 2065 | return; |
| 1853 | } | 2066 | } |
| 1854 | } else { | ||
| 1855 | this._sendProcessedPayload(data); | ||
| 1856 | } | ||
| 1857 | }, | ||
| 1858 | 2067 | ||
| 1859 | _getUuid: function() { | 2068 | // Check if the request should be filtered or not |
| 1860 | return uuid4(); | 2069 | if ( |
| 1861 | }, | 2070 | isFunction(globalOptions.shouldSendCallback) && |
| 2071 | !globalOptions.shouldSendCallback(data) | ||
| 2072 | ) { | ||
| 2073 | return; | ||
| 2074 | } | ||
| 1862 | 2075 | ||
| 1863 | _sendProcessedPayload: function(data, callback) { | 2076 | // Backoff state: Sentry server previously responded w/ an error (e.g. 429 - too many requests), |
| 1864 | var self = this; | 2077 | // so drop requests until "cool-off" period has elapsed. |
| 1865 | var globalOptions = this._globalOptions; | 2078 | if (this._shouldBackoff()) { |
| 2079 | this._logDebug( | ||
| 2080 | "warn", | ||
| 2081 | "Raven dropped error due to backoff: ", | ||
| 2082 | data, | ||
| 2083 | ); | ||
| 2084 | return; | ||
| 2085 | } | ||
| 1866 | 2086 | ||
| 1867 | if (!this.isSetup()) return; | 2087 | if (typeof globalOptions.sampleRate === "number") { |
| 2088 | if (Math.random() < globalOptions.sampleRate) { | ||
| 2089 | this._sendProcessedPayload(data); | ||
| 2090 | } | ||
| 2091 | } else { | ||
| 2092 | this._sendProcessedPayload(data); | ||
| 2093 | } | ||
| 2094 | }, | ||
| 2095 | |||
| 2096 | _getUuid: function () { | ||
| 2097 | return uuid4(); | ||
| 2098 | }, | ||
| 2099 | |||
| 2100 | _sendProcessedPayload: function (data, callback) { | ||
| 2101 | var self = this; | ||
| 2102 | var globalOptions = this._globalOptions; | ||
| 2103 | |||
| 2104 | if (!this.isSetup()) return; | ||
| 2105 | |||
| 2106 | // Try and clean up the packet before sending by truncating long values | ||
| 2107 | data = this._trimPacket(data); | ||
| 2108 | |||
| 2109 | // ideally duplicate error testing should occur *before* dataCallback/shouldSendCallback, | ||
| 2110 | // but this would require copying an un-truncated copy of the data packet, which can be | ||
| 2111 | // arbitrarily deep (extra_data) -- could be worthwhile? will revisit | ||
| 2112 | if ( | ||
| 2113 | !this._globalOptions.allowDuplicates && | ||
| 2114 | this._isRepeatData(data) | ||
| 2115 | ) { | ||
| 2116 | this._logDebug("warn", "Raven dropped repeat event: ", data); | ||
| 2117 | return; | ||
| 2118 | } | ||
| 1868 | 2119 | ||
| 1869 | // Try and clean up the packet before sending by truncating long values | 2120 | // Send along an event_id if not explicitly passed. |
| 1870 | data = this._trimPacket(data); | 2121 | // This event_id can be used to reference the error within Sentry itself. |
| 2122 | // Set lastEventId after we know the error should actually be sent | ||
| 2123 | this._lastEventId = | ||
| 2124 | data.event_id || (data.event_id = this._getUuid()); | ||
| 1871 | 2125 | ||
| 1872 | // ideally duplicate error testing should occur *before* dataCallback/shouldSendCallback, | 2126 | // Store outbound payload after trim |
| 1873 | // but this would require copying an un-truncated copy of the data packet, which can be | 2127 | this._lastData = data; |
| 1874 | // arbitrarily deep (extra_data) -- could be worthwhile? will revisit | ||
| 1875 | if (!this._globalOptions.allowDuplicates && this._isRepeatData(data)) { | ||
| 1876 | this._logDebug('warn', 'Raven dropped repeat event: ', data); | ||
| 1877 | return; | ||
| 1878 | } | ||
| 1879 | 2128 | ||
| 1880 | // Send along an event_id if not explicitly passed. | 2129 | this._logDebug("debug", "Raven about to send:", data); |
| 1881 | // This event_id can be used to reference the error within Sentry itself. | ||
| 1882 | // Set lastEventId after we know the error should actually be sent | ||
| 1883 | this._lastEventId = data.event_id || (data.event_id = this._getUuid()); | ||
| 1884 | 2130 | ||
| 1885 | // Store outbound payload after trim | 2131 | var auth = { |
| 1886 | this._lastData = data; | 2132 | sentry_version: "7", |
| 2133 | sentry_client: "raven-js/" + this.VERSION, | ||
| 2134 | sentry_key: this._globalKey, | ||
| 2135 | }; | ||
| 1887 | 2136 | ||
| 1888 | this._logDebug('debug', 'Raven about to send:', data); | 2137 | if (this._globalSecret) { |
| 2138 | auth.sentry_secret = this._globalSecret; | ||
| 2139 | } | ||
| 1889 | 2140 | ||
| 1890 | var auth = { | 2141 | var exception = data.exception && data.exception.values[0]; |
| 1891 | sentry_version: '7', | 2142 | |
| 1892 | sentry_client: 'raven-js/' + this.VERSION, | 2143 | // only capture 'sentry' breadcrumb is autoBreadcrumbs is truthy |
| 1893 | sentry_key: this._globalKey | 2144 | if ( |
| 1894 | }; | 2145 | this._globalOptions.autoBreadcrumbs && |
| 2146 | this._globalOptions.autoBreadcrumbs.sentry | ||
| 2147 | ) { | ||
| 2148 | this.captureBreadcrumb({ | ||
| 2149 | category: "sentry", | ||
| 2150 | message: exception | ||
| 2151 | ? (exception.type ? exception.type + ": " : "") + | ||
| 2152 | exception.value | ||
| 2153 | : data.message, | ||
| 2154 | event_id: data.event_id, | ||
| 2155 | level: data.level || "error", // presume error unless specified | ||
| 2156 | }); | ||
| 2157 | } | ||
| 1895 | 2158 | ||
| 1896 | if (this._globalSecret) { | 2159 | var url = this._globalEndpoint; |
| 1897 | auth.sentry_secret = this._globalSecret; | 2160 | (globalOptions.transport || this._makeRequest).call(this, { |
| 1898 | } | 2161 | url: url, |
| 2162 | auth: auth, | ||
| 2163 | data: data, | ||
| 2164 | options: globalOptions, | ||
| 2165 | onSuccess: function success() { | ||
| 2166 | self._resetBackoff(); | ||
| 2167 | |||
| 2168 | self._triggerEvent("success", { | ||
| 2169 | data: data, | ||
| 2170 | src: url, | ||
| 2171 | }); | ||
| 2172 | callback && callback(); | ||
| 2173 | }, | ||
| 2174 | onError: function failure(error) { | ||
| 2175 | self._logDebug( | ||
| 2176 | "error", | ||
| 2177 | "Raven transport failed to send: ", | ||
| 2178 | error, | ||
| 2179 | ); | ||
| 2180 | |||
| 2181 | if (error.request) { | ||
| 2182 | self._setBackoffState(error.request); | ||
| 2183 | } | ||
| 2184 | |||
| 2185 | self._triggerEvent("failure", { | ||
| 2186 | data: data, | ||
| 2187 | src: url, | ||
| 2188 | }); | ||
| 2189 | error = | ||
| 2190 | error || | ||
| 2191 | new Error( | ||
| 2192 | "Raven send failed (no additional details provided)", | ||
| 2193 | ); | ||
| 2194 | callback && callback(error); | ||
| 2195 | }, | ||
| 2196 | }); | ||
| 2197 | }, | ||
| 2198 | |||
| 2199 | _makeRequest: function (opts) { | ||
| 2200 | var request = | ||
| 2201 | _window.XMLHttpRequest && new _window.XMLHttpRequest(); | ||
| 2202 | if (!request) return; | ||
| 2203 | |||
| 2204 | // if browser doesn't support CORS (e.g. IE7), we are out of luck | ||
| 2205 | var hasCORS = | ||
| 2206 | "withCredentials" in request || | ||
| 2207 | typeof XDomainRequest !== "undefined"; | ||
| 2208 | |||
| 2209 | if (!hasCORS) return; | ||
| 2210 | |||
| 2211 | var url = opts.url; | ||
| 2212 | |||
| 2213 | if ("withCredentials" in request) { | ||
| 2214 | request.onreadystatechange = function () { | ||
| 2215 | if (request.readyState !== 4) { | ||
| 2216 | return; | ||
| 2217 | } else if (request.status === 200) { | ||
| 2218 | opts.onSuccess && opts.onSuccess(); | ||
| 2219 | } else if (opts.onError) { | ||
| 2220 | var err = new Error( | ||
| 2221 | "Sentry error code: " + request.status, | ||
| 2222 | ); | ||
| 2223 | err.request = request; | ||
| 2224 | opts.onError(err); | ||
| 2225 | } | ||
| 2226 | }; | ||
| 2227 | } else { | ||
| 2228 | request = new XDomainRequest(); | ||
| 2229 | // xdomainrequest cannot go http -> https (or vice versa), | ||
| 2230 | // so always use protocol relative | ||
| 2231 | url = url.replace(/^https?:/, ""); | ||
| 2232 | |||
| 2233 | // onreadystatechange not supported by XDomainRequest | ||
| 2234 | if (opts.onSuccess) { | ||
| 2235 | request.onload = opts.onSuccess; | ||
| 2236 | } | ||
| 2237 | if (opts.onError) { | ||
| 2238 | request.onerror = function () { | ||
| 2239 | var err = new Error("Sentry error code: XDomainRequest"); | ||
| 2240 | err.request = request; | ||
| 2241 | opts.onError(err); | ||
| 2242 | }; | ||
| 2243 | } | ||
| 2244 | } | ||
| 1899 | 2245 | ||
| 1900 | var exception = data.exception && data.exception.values[0]; | 2246 | // NOTE: auth is intentionally sent as part of query string (NOT as custom |
| 1901 | 2247 | // HTTP header) so as to avoid preflight CORS requests | |
| 1902 | // only capture 'sentry' breadcrumb is autoBreadcrumbs is truthy | 2248 | request.open("POST", url + "?" + urlencode(opts.auth)); |
| 1903 | if ( | 2249 | request.send(stringify(opts.data)); |
| 1904 | this._globalOptions.autoBreadcrumbs && | 2250 | }, |
| 1905 | this._globalOptions.autoBreadcrumbs.sentry | 2251 | |
| 1906 | ) { | 2252 | _logDebug: function (level) { |
| 1907 | this.captureBreadcrumb({ | 2253 | if (this._originalConsoleMethods[level] && this.debug) { |
| 1908 | category: 'sentry', | 2254 | // In IE<10 console methods do not have their own 'apply' method |
| 1909 | message: exception | 2255 | Function.prototype.apply.call( |
| 1910 | ? (exception.type ? exception.type + ': ' : '') + exception.value | 2256 | this._originalConsoleMethods[level], |
| 1911 | : data.message, | 2257 | this._originalConsole, |
| 1912 | event_id: data.event_id, | 2258 | [].slice.call(arguments, 1), |
| 1913 | level: data.level || 'error' // presume error unless specified | 2259 | ); |
| 1914 | }); | 2260 | } |
| 1915 | } | 2261 | }, |
| 2262 | |||
| 2263 | _mergeContext: function (key, context) { | ||
| 2264 | if (isUndefined(context)) { | ||
| 2265 | delete this._globalContext[key]; | ||
| 2266 | } else { | ||
| 2267 | this._globalContext[key] = objectMerge( | ||
| 2268 | this._globalContext[key] || {}, | ||
| 2269 | context, | ||
| 2270 | ); | ||
| 2271 | } | ||
| 2272 | }, | ||
| 2273 | }; | ||
| 1916 | 2274 | ||
| 1917 | var url = this._globalEndpoint; | 2275 | // Deprecations |
| 1918 | (globalOptions.transport || this._makeRequest).call(this, { | 2276 | Raven.prototype.setUser = Raven.prototype.setUserContext; |
| 1919 | url: url, | 2277 | Raven.prototype.setReleaseContext = Raven.prototype.setRelease; |
| 1920 | auth: auth, | 2278 | |
| 1921 | data: data, | 2279 | module.exports = Raven; |
| 1922 | options: globalOptions, | 2280 | }).call( |
| 1923 | onSuccess: function success() { | 2281 | this, |
| 1924 | self._resetBackoff(); | 2282 | typeof global !== "undefined" |
| 1925 | 2283 | ? global | |
| 1926 | self._triggerEvent('success', { | 2284 | : typeof self !== "undefined" |
| 1927 | data: data, | 2285 | ? self |
| 1928 | src: url | 2286 | : typeof window !== "undefined" |
| 1929 | }); | 2287 | ? window |
| 1930 | callback && callback(); | 2288 | : {}, |
| 1931 | }, | 2289 | ); |
| 1932 | onError: function failure(error) { | 2290 | }, |
| 1933 | self._logDebug('error', 'Raven transport failed to send: ', error); | 2291 | { 1: 1, 2: 2, 5: 5, 6: 6, 7: 7 }, |
| 1934 | 2292 | ], | |
| 1935 | if (error.request) { | 2293 | 4: [ |
| 1936 | self._setBackoffState(error.request); | 2294 | function (_dereq_, module, exports) { |
| 1937 | } | 2295 | (function (global) { |
| 2296 | /** | ||
| 2297 | * Enforces a single instance of the Raven client, and the | ||
| 2298 | * main entry point for Raven. If you are a consumer of the | ||
| 2299 | * Raven library, you SHOULD load this file (vs raven.js). | ||
| 2300 | **/ | ||
| 2301 | |||
| 2302 | var RavenConstructor = _dereq_(3); | ||
| 2303 | |||
| 2304 | // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) | ||
| 2305 | var _window = | ||
| 2306 | typeof window !== "undefined" | ||
| 2307 | ? window | ||
| 2308 | : typeof global !== "undefined" | ||
| 2309 | ? global | ||
| 2310 | : typeof self !== "undefined" | ||
| 2311 | ? self | ||
| 2312 | : {}; | ||
| 2313 | var _Raven = _window.Raven; | ||
| 2314 | |||
| 2315 | var Raven = new RavenConstructor(); | ||
| 2316 | |||
| 2317 | /* | ||
| 2318 | * Allow multiple versions of Raven to be installed. | ||
| 2319 | * Strip Raven from the global context and returns the instance. | ||
| 2320 | * | ||
| 2321 | * @return {Raven} | ||
| 2322 | */ | ||
| 2323 | Raven.noConflict = function () { | ||
| 2324 | _window.Raven = _Raven; | ||
| 2325 | return Raven; | ||
| 2326 | }; | ||
| 1938 | 2327 | ||
| 1939 | self._triggerEvent('failure', { | 2328 | Raven.afterLoad(); |
| 1940 | data: data, | 2329 | |
| 1941 | src: url | 2330 | module.exports = Raven; |
| 1942 | }); | 2331 | }).call( |
| 1943 | error = error || new Error('Raven send failed (no additional details provided)'); | 2332 | this, |
| 1944 | callback && callback(error); | 2333 | typeof global !== "undefined" |
| 1945 | } | 2334 | ? global |
| 1946 | }); | 2335 | : typeof self !== "undefined" |
| 1947 | }, | 2336 | ? self |
| 1948 | 2337 | : typeof window !== "undefined" | |
| 1949 | _makeRequest: function(opts) { | 2338 | ? window |
| 1950 | var request = _window.XMLHttpRequest && new _window.XMLHttpRequest(); | 2339 | : {}, |
| 1951 | if (!request) return; | 2340 | ); |
| 1952 | 2341 | }, | |
| 1953 | // if browser doesn't support CORS (e.g. IE7), we are out of luck | 2342 | { 3: 3 }, |
| 1954 | var hasCORS = 'withCredentials' in request || typeof XDomainRequest !== 'undefined'; | 2343 | ], |
| 1955 | 2344 | 5: [ | |
| 1956 | if (!hasCORS) return; | 2345 | function (_dereq_, module, exports) { |
| 1957 | 2346 | (function (global) { | |
| 1958 | var url = opts.url; | 2347 | var _window = |
| 1959 | 2348 | typeof window !== "undefined" | |
| 1960 | if ('withCredentials' in request) { | 2349 | ? window |
| 1961 | request.onreadystatechange = function() { | 2350 | : typeof global !== "undefined" |
| 1962 | if (request.readyState !== 4) { | 2351 | ? global |
| 1963 | return; | 2352 | : typeof self !== "undefined" |
| 1964 | } else if (request.status === 200) { | 2353 | ? self |
| 1965 | opts.onSuccess && opts.onSuccess(); | 2354 | : {}; |
| 1966 | } else if (opts.onError) { | 2355 | |
| 1967 | var err = new Error('Sentry error code: ' + request.status); | 2356 | function isObject(what) { |
| 1968 | err.request = request; | 2357 | return typeof what === "object" && what !== null; |
| 1969 | opts.onError(err); | 2358 | } |
| 1970 | } | ||
| 1971 | }; | ||
| 1972 | } else { | ||
| 1973 | request = new XDomainRequest(); | ||
| 1974 | // xdomainrequest cannot go http -> https (or vice versa), | ||
| 1975 | // so always use protocol relative | ||
| 1976 | url = url.replace(/^https?:/, ''); | ||
| 1977 | |||
| 1978 | // onreadystatechange not supported by XDomainRequest | ||
| 1979 | if (opts.onSuccess) { | ||
| 1980 | request.onload = opts.onSuccess; | ||
| 1981 | } | ||
| 1982 | if (opts.onError) { | ||
| 1983 | request.onerror = function() { | ||
| 1984 | var err = new Error('Sentry error code: XDomainRequest'); | ||
| 1985 | err.request = request; | ||
| 1986 | opts.onError(err); | ||
| 1987 | }; | ||
| 1988 | } | ||
| 1989 | } | ||
| 1990 | 2359 | ||
| 1991 | // NOTE: auth is intentionally sent as part of query string (NOT as custom | 2360 | // Yanked from https://git.io/vS8DV re-used under CC0 |
| 1992 | // HTTP header) so as to avoid preflight CORS requests | 2361 | // with some tiny modifications |
| 1993 | request.open('POST', url + '?' + urlencode(opts.auth)); | 2362 | function isError(value) { |
| 1994 | request.send(stringify(opts.data)); | 2363 | switch ({}.toString.call(value)) { |
| 1995 | }, | 2364 | case "[object Error]": |
| 1996 | 2365 | return true; | |
| 1997 | _logDebug: function(level) { | 2366 | case "[object Exception]": |
| 1998 | if (this._originalConsoleMethods[level] && this.debug) { | 2367 | return true; |
| 1999 | // In IE<10 console methods do not have their own 'apply' method | 2368 | case "[object DOMException]": |
| 2000 | Function.prototype.apply.call( | 2369 | return true; |
| 2001 | this._originalConsoleMethods[level], | 2370 | default: |
| 2002 | this._originalConsole, | 2371 | return value instanceof Error; |
| 2003 | [].slice.call(arguments, 1) | 2372 | } |
| 2004 | ); | 2373 | } |
| 2005 | } | ||
| 2006 | }, | ||
| 2007 | 2374 | ||
| 2008 | _mergeContext: function(key, context) { | 2375 | function isErrorEvent(value) { |
| 2009 | if (isUndefined(context)) { | 2376 | return ( |
| 2010 | delete this._globalContext[key]; | 2377 | supportsErrorEvent() && |
| 2011 | } else { | 2378 | {}.toString.call(value) === "[object ErrorEvent]" |
| 2012 | this._globalContext[key] = objectMerge(this._globalContext[key] || {}, context); | 2379 | ); |
| 2013 | } | 2380 | } |
| 2014 | } | ||
| 2015 | }; | ||
| 2016 | 2381 | ||
| 2017 | // Deprecations | 2382 | function isUndefined(what) { |
| 2018 | Raven.prototype.setUser = Raven.prototype.setUserContext; | 2383 | return what === void 0; |
| 2019 | Raven.prototype.setReleaseContext = Raven.prototype.setRelease; | 2384 | } |
| 2020 | 2385 | ||
| 2021 | module.exports = Raven; | 2386 | function isFunction(what) { |
| 2387 | return typeof what === "function"; | ||
| 2388 | } | ||
| 2022 | 2389 | ||
| 2023 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | 2390 | function isString(what) { |
| 2024 | },{"1":1,"2":2,"5":5,"6":6,"7":7}],4:[function(_dereq_,module,exports){ | 2391 | return Object.prototype.toString.call(what) === "[object String]"; |
| 2025 | (function (global){ | 2392 | } |
| 2026 | /** | ||
| 2027 | * Enforces a single instance of the Raven client, and the | ||
| 2028 | * main entry point for Raven. If you are a consumer of the | ||
| 2029 | * Raven library, you SHOULD load this file (vs raven.js). | ||
| 2030 | **/ | ||
| 2031 | 2393 | ||
| 2032 | var RavenConstructor = _dereq_(3); | 2394 | function isArray(what) { |
| 2395 | return Object.prototype.toString.call(what) === "[object Array]"; | ||
| 2396 | } | ||
| 2033 | 2397 | ||
| 2034 | // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) | 2398 | function isEmptyObject(what) { |
| 2035 | var _window = | 2399 | for (var _ in what) { |
| 2036 | typeof window !== 'undefined' | 2400 | if (what.hasOwnProperty(_)) { |
| 2037 | ? window | 2401 | return false; |
| 2038 | : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | 2402 | } |
| 2039 | var _Raven = _window.Raven; | 2403 | } |
| 2404 | return true; | ||
| 2405 | } | ||
| 2040 | 2406 | ||
| 2041 | var Raven = new RavenConstructor(); | 2407 | function supportsErrorEvent() { |
| 2408 | try { | ||
| 2409 | new ErrorEvent(""); // eslint-disable-line no-new | ||
| 2410 | return true; | ||
| 2411 | } catch (e) { | ||
| 2412 | return false; | ||
| 2413 | } | ||
| 2414 | } | ||
| 2042 | 2415 | ||
| 2043 | /* | 2416 | function wrappedCallback(callback) { |
| 2044 | * Allow multiple versions of Raven to be installed. | 2417 | function dataCallback(data, original) { |
| 2045 | * Strip Raven from the global context and returns the instance. | 2418 | var normalizedData = callback(data) || data; |
| 2046 | * | 2419 | if (original) { |
| 2047 | * @return {Raven} | 2420 | return original(normalizedData) || normalizedData; |
| 2048 | */ | 2421 | } |
| 2049 | Raven.noConflict = function() { | 2422 | return normalizedData; |
| 2050 | _window.Raven = _Raven; | 2423 | } |
| 2051 | return Raven; | ||
| 2052 | }; | ||
| 2053 | |||
| 2054 | Raven.afterLoad(); | ||
| 2055 | |||
| 2056 | module.exports = Raven; | ||
| 2057 | |||
| 2058 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | ||
| 2059 | },{"3":3}],5:[function(_dereq_,module,exports){ | ||
| 2060 | (function (global){ | ||
| 2061 | var _window = | ||
| 2062 | typeof window !== 'undefined' | ||
| 2063 | ? window | ||
| 2064 | : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | ||
| 2065 | |||
| 2066 | function isObject(what) { | ||
| 2067 | return typeof what === 'object' && what !== null; | ||
| 2068 | } | ||
| 2069 | |||
| 2070 | // Yanked from https://git.io/vS8DV re-used under CC0 | ||
| 2071 | // with some tiny modifications | ||
| 2072 | function isError(value) { | ||
| 2073 | switch ({}.toString.call(value)) { | ||
| 2074 | case '[object Error]': | ||
| 2075 | return true; | ||
| 2076 | case '[object Exception]': | ||
| 2077 | return true; | ||
| 2078 | case '[object DOMException]': | ||
| 2079 | return true; | ||
| 2080 | default: | ||
| 2081 | return value instanceof Error; | ||
| 2082 | } | ||
| 2083 | } | ||
| 2084 | 2424 | ||
| 2085 | function isErrorEvent(value) { | 2425 | return dataCallback; |
| 2086 | return supportsErrorEvent() && {}.toString.call(value) === '[object ErrorEvent]'; | 2426 | } |
| 2087 | } | ||
| 2088 | 2427 | ||
| 2089 | function isUndefined(what) { | 2428 | function each(obj, callback) { |
| 2090 | return what === void 0; | 2429 | var i, j; |
| 2091 | } | ||
| 2092 | 2430 | ||
| 2093 | function isFunction(what) { | 2431 | if (isUndefined(obj.length)) { |
| 2094 | return typeof what === 'function'; | 2432 | for (i in obj) { |
| 2095 | } | 2433 | if (hasKey(obj, i)) { |
| 2434 | callback.call(null, i, obj[i]); | ||
| 2435 | } | ||
| 2436 | } | ||
| 2437 | } else { | ||
| 2438 | j = obj.length; | ||
| 2439 | if (j) { | ||
| 2440 | for (i = 0; i < j; i++) { | ||
| 2441 | callback.call(null, i, obj[i]); | ||
| 2442 | } | ||
| 2443 | } | ||
| 2444 | } | ||
| 2445 | } | ||
| 2096 | 2446 | ||
| 2097 | function isString(what) { | 2447 | function objectMerge(obj1, obj2) { |
| 2098 | return Object.prototype.toString.call(what) === '[object String]'; | 2448 | if (!obj2) { |
| 2099 | } | 2449 | return obj1; |
| 2450 | } | ||
| 2451 | each(obj2, function (key, value) { | ||
| 2452 | obj1[key] = value; | ||
| 2453 | }); | ||
| 2454 | return obj1; | ||
| 2455 | } | ||
| 2100 | 2456 | ||
| 2101 | function isArray(what) { | 2457 | /** |
| 2102 | return Object.prototype.toString.call(what) === '[object Array]'; | 2458 | * This function is only used for react-native. |
| 2103 | } | 2459 | * react-native freezes object that have already been sent over the |
| 2460 | * js bridge. We need this function in order to check if the object is frozen. | ||
| 2461 | * So it's ok that objectFrozen returns false if Object.isFrozen is not | ||
| 2462 | * supported because it's not relevant for other "platforms". See related issue: | ||
| 2463 | * https://github.com/getsentry/react-native-sentry/issues/57 | ||
| 2464 | */ | ||
| 2465 | function objectFrozen(obj) { | ||
| 2466 | if (!Object.isFrozen) { | ||
| 2467 | return false; | ||
| 2468 | } | ||
| 2469 | return Object.isFrozen(obj); | ||
| 2470 | } | ||
| 2104 | 2471 | ||
| 2105 | function isEmptyObject(what) { | 2472 | function truncate(str, max) { |
| 2106 | for (var _ in what) { | 2473 | return !max || str.length <= max |
| 2107 | if (what.hasOwnProperty(_)) { | 2474 | ? str |
| 2108 | return false; | 2475 | : str.substr(0, max) + "\u2026"; |
| 2109 | } | 2476 | } |
| 2110 | } | ||
| 2111 | return true; | ||
| 2112 | } | ||
| 2113 | |||
| 2114 | function supportsErrorEvent() { | ||
| 2115 | try { | ||
| 2116 | new ErrorEvent(''); // eslint-disable-line no-new | ||
| 2117 | return true; | ||
| 2118 | } catch (e) { | ||
| 2119 | return false; | ||
| 2120 | } | ||
| 2121 | } | ||
| 2122 | 2477 | ||
| 2123 | function wrappedCallback(callback) { | 2478 | /** |
| 2124 | function dataCallback(data, original) { | 2479 | * hasKey, a better form of hasOwnProperty |
| 2125 | var normalizedData = callback(data) || data; | 2480 | * Example: hasKey(MainHostObject, property) === true/false |
| 2126 | if (original) { | 2481 | * |
| 2127 | return original(normalizedData) || normalizedData; | 2482 | * @param {Object} host object to check property |
| 2128 | } | 2483 | * @param {string} key to check |
| 2129 | return normalizedData; | 2484 | */ |
| 2130 | } | 2485 | function hasKey(object, key) { |
| 2486 | return Object.prototype.hasOwnProperty.call(object, key); | ||
| 2487 | } | ||
| 2131 | 2488 | ||
| 2132 | return dataCallback; | 2489 | function joinRegExp(patterns) { |
| 2133 | } | 2490 | // Combine an array of regular expressions and strings into one large regexp |
| 2491 | // Be mad. | ||
| 2492 | var sources = [], | ||
| 2493 | i = 0, | ||
| 2494 | len = patterns.length, | ||
| 2495 | pattern; | ||
| 2496 | |||
| 2497 | for (; i < len; i++) { | ||
| 2498 | pattern = patterns[i]; | ||
| 2499 | if (isString(pattern)) { | ||
| 2500 | // If it's a string, we need to escape it | ||
| 2501 | // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions | ||
| 2502 | sources.push( | ||
| 2503 | pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"), | ||
| 2504 | ); | ||
| 2505 | } else if (pattern && pattern.source) { | ||
| 2506 | // If it's a regexp already, we want to extract the source | ||
| 2507 | sources.push(pattern.source); | ||
| 2508 | } | ||
| 2509 | // Intentionally skip other cases | ||
| 2510 | } | ||
| 2511 | return new RegExp(sources.join("|"), "i"); | ||
| 2512 | } | ||
| 2134 | 2513 | ||
| 2135 | function each(obj, callback) { | 2514 | function urlencode(o) { |
| 2136 | var i, j; | 2515 | var pairs = []; |
| 2516 | each(o, function (key, value) { | ||
| 2517 | pairs.push( | ||
| 2518 | encodeURIComponent(key) + "=" + encodeURIComponent(value), | ||
| 2519 | ); | ||
| 2520 | }); | ||
| 2521 | return pairs.join("&"); | ||
| 2522 | } | ||
| 2137 | 2523 | ||
| 2138 | if (isUndefined(obj.length)) { | 2524 | // borrowed from https://datatracker.ietf.org/doc/html/rfc3986#appendix-B |
| 2139 | for (i in obj) { | 2525 | // intentionally using regex and not <a/> href parsing trick because React Native and other |
| 2140 | if (hasKey(obj, i)) { | 2526 | // environments where DOM might not be available |
| 2141 | callback.call(null, i, obj[i]); | 2527 | function parseUrl(url) { |
| 2142 | } | 2528 | var match = url.match( |
| 2143 | } | 2529 | /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/, |
| 2144 | } else { | 2530 | ); |
| 2145 | j = obj.length; | 2531 | if (!match) return {}; |
| 2146 | if (j) { | 2532 | |
| 2147 | for (i = 0; i < j; i++) { | 2533 | // coerce to undefined values to empty string so we don't get 'undefined' |
| 2148 | callback.call(null, i, obj[i]); | 2534 | var query = match[6] || ""; |
| 2149 | } | 2535 | var fragment = match[8] || ""; |
| 2150 | } | 2536 | return { |
| 2151 | } | 2537 | protocol: match[2], |
| 2152 | } | 2538 | host: match[4], |
| 2539 | path: match[5], | ||
| 2540 | relative: match[5] + query + fragment, // everything minus origin | ||
| 2541 | }; | ||
| 2542 | } | ||
| 2543 | function uuid4() { | ||
| 2544 | var crypto = _window.crypto || _window.msCrypto; | ||
| 2545 | |||
| 2546 | if (!isUndefined(crypto) && crypto.getRandomValues) { | ||
| 2547 | // Use window.crypto API if available | ||
| 2548 | // eslint-disable-next-line no-undef | ||
| 2549 | var arr = new Uint16Array(8); | ||
| 2550 | crypto.getRandomValues(arr); | ||
| 2551 | |||
| 2552 | // set 4 in byte 7 | ||
| 2553 | arr[3] = (arr[3] & 0xfff) | 0x4000; | ||
| 2554 | // set 2 most significant bits of byte 9 to '10' | ||
| 2555 | arr[4] = (arr[4] & 0x3fff) | 0x8000; | ||
| 2556 | |||
| 2557 | var pad = function (num) { | ||
| 2558 | var v = num.toString(16); | ||
| 2559 | while (v.length < 4) { | ||
| 2560 | v = "0" + v; | ||
| 2561 | } | ||
| 2562 | return v; | ||
| 2563 | }; | ||
| 2153 | 2564 | ||
| 2154 | function objectMerge(obj1, obj2) { | 2565 | return ( |
| 2155 | if (!obj2) { | 2566 | pad(arr[0]) + |
| 2156 | return obj1; | 2567 | pad(arr[1]) + |
| 2157 | } | 2568 | pad(arr[2]) + |
| 2158 | each(obj2, function(key, value) { | 2569 | pad(arr[3]) + |
| 2159 | obj1[key] = value; | 2570 | pad(arr[4]) + |
| 2160 | }); | 2571 | pad(arr[5]) + |
| 2161 | return obj1; | 2572 | pad(arr[6]) + |
| 2162 | } | 2573 | pad(arr[7]) |
| 2163 | 2574 | ); | |
| 2164 | /** | 2575 | } else { |
| 2165 | * This function is only used for react-native. | 2576 | // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523 |
| 2166 | * react-native freezes object that have already been sent over the | 2577 | return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace( |
| 2167 | * js bridge. We need this function in order to check if the object is frozen. | 2578 | /[xy]/g, |
| 2168 | * So it's ok that objectFrozen returns false if Object.isFrozen is not | 2579 | function (c) { |
| 2169 | * supported because it's not relevant for other "platforms". See related issue: | 2580 | var r = (Math.random() * 16) | 0, |
| 2170 | * https://github.com/getsentry/react-native-sentry/issues/57 | 2581 | v = c === "x" ? r : (r & 0x3) | 0x8; |
| 2171 | */ | 2582 | return v.toString(16); |
| 2172 | function objectFrozen(obj) { | 2583 | }, |
| 2173 | if (!Object.isFrozen) { | 2584 | ); |
| 2174 | return false; | 2585 | } |
| 2175 | } | 2586 | } |
| 2176 | return Object.isFrozen(obj); | ||
| 2177 | } | ||
| 2178 | 2587 | ||
| 2179 | function truncate(str, max) { | 2588 | /** |
| 2180 | return !max || str.length <= max ? str : str.substr(0, max) + '\u2026'; | 2589 | * Given a child DOM element, returns a query-selector statement describing that |
| 2181 | } | 2590 | * and its ancestors |
| 2591 | * e.g. [HTMLElement] => body > div > input#foo.btn[name=baz] | ||
| 2592 | * @param elem | ||
| 2593 | * @returns {string} | ||
| 2594 | */ | ||
| 2595 | function htmlTreeAsString(elem) { | ||
| 2596 | /* eslint no-extra-parens:0*/ | ||
| 2597 | var MAX_TRAVERSE_HEIGHT = 5, | ||
| 2598 | MAX_OUTPUT_LEN = 80, | ||
| 2599 | out = [], | ||
| 2600 | height = 0, | ||
| 2601 | len = 0, | ||
| 2602 | separator = " > ", | ||
| 2603 | sepLength = separator.length, | ||
| 2604 | nextStr; | ||
| 2605 | |||
| 2606 | while (elem && height++ < MAX_TRAVERSE_HEIGHT) { | ||
| 2607 | nextStr = htmlElementAsString(elem); | ||
| 2608 | // bail out if | ||
| 2609 | // - nextStr is the 'html' element | ||
| 2610 | // - the length of the string that would be created exceeds MAX_OUTPUT_LEN | ||
| 2611 | // (ignore this limit if we are on the first iteration) | ||
| 2612 | if ( | ||
| 2613 | nextStr === "html" || | ||
| 2614 | (height > 1 && | ||
| 2615 | len + out.length * sepLength + nextStr.length >= | ||
| 2616 | MAX_OUTPUT_LEN) | ||
| 2617 | ) { | ||
| 2618 | break; | ||
| 2619 | } | ||
| 2182 | 2620 | ||
| 2183 | /** | 2621 | out.push(nextStr); |
| 2184 | * hasKey, a better form of hasOwnProperty | ||
| 2185 | * Example: hasKey(MainHostObject, property) === true/false | ||
| 2186 | * | ||
| 2187 | * @param {Object} host object to check property | ||
| 2188 | * @param {string} key to check | ||
| 2189 | */ | ||
| 2190 | function hasKey(object, key) { | ||
| 2191 | return Object.prototype.hasOwnProperty.call(object, key); | ||
| 2192 | } | ||
| 2193 | |||
| 2194 | function joinRegExp(patterns) { | ||
| 2195 | // Combine an array of regular expressions and strings into one large regexp | ||
| 2196 | // Be mad. | ||
| 2197 | var sources = [], | ||
| 2198 | i = 0, | ||
| 2199 | len = patterns.length, | ||
| 2200 | pattern; | ||
| 2201 | |||
| 2202 | for (; i < len; i++) { | ||
| 2203 | pattern = patterns[i]; | ||
| 2204 | if (isString(pattern)) { | ||
| 2205 | // If it's a string, we need to escape it | ||
| 2206 | // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions | ||
| 2207 | sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1')); | ||
| 2208 | } else if (pattern && pattern.source) { | ||
| 2209 | // If it's a regexp already, we want to extract the source | ||
| 2210 | sources.push(pattern.source); | ||
| 2211 | } | ||
| 2212 | // Intentionally skip other cases | ||
| 2213 | } | ||
| 2214 | return new RegExp(sources.join('|'), 'i'); | ||
| 2215 | } | ||
| 2216 | |||
| 2217 | function urlencode(o) { | ||
| 2218 | var pairs = []; | ||
| 2219 | each(o, function(key, value) { | ||
| 2220 | pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); | ||
| 2221 | }); | ||
| 2222 | return pairs.join('&'); | ||
| 2223 | } | ||
| 2224 | |||
| 2225 | // borrowed from https://datatracker.ietf.org/doc/html/rfc3986#appendix-B | ||
| 2226 | // intentionally using regex and not <a/> href parsing trick because React Native and other | ||
| 2227 | // environments where DOM might not be available | ||
| 2228 | function parseUrl(url) { | ||
| 2229 | var match = url.match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/); | ||
| 2230 | if (!match) return {}; | ||
| 2231 | |||
| 2232 | // coerce to undefined values to empty string so we don't get 'undefined' | ||
| 2233 | var query = match[6] || ''; | ||
| 2234 | var fragment = match[8] || ''; | ||
| 2235 | return { | ||
| 2236 | protocol: match[2], | ||
| 2237 | host: match[4], | ||
| 2238 | path: match[5], | ||
| 2239 | relative: match[5] + query + fragment // everything minus origin | ||
| 2240 | }; | ||
| 2241 | } | ||
| 2242 | function uuid4() { | ||
| 2243 | var crypto = _window.crypto || _window.msCrypto; | ||
| 2244 | |||
| 2245 | if (!isUndefined(crypto) && crypto.getRandomValues) { | ||
| 2246 | // Use window.crypto API if available | ||
| 2247 | // eslint-disable-next-line no-undef | ||
| 2248 | var arr = new Uint16Array(8); | ||
| 2249 | crypto.getRandomValues(arr); | ||
| 2250 | |||
| 2251 | // set 4 in byte 7 | ||
| 2252 | arr[3] = (arr[3] & 0xfff) | 0x4000; | ||
| 2253 | // set 2 most significant bits of byte 9 to '10' | ||
| 2254 | arr[4] = (arr[4] & 0x3fff) | 0x8000; | ||
| 2255 | |||
| 2256 | var pad = function(num) { | ||
| 2257 | var v = num.toString(16); | ||
| 2258 | while (v.length < 4) { | ||
| 2259 | v = '0' + v; | ||
| 2260 | } | ||
| 2261 | return v; | ||
| 2262 | }; | ||
| 2263 | |||
| 2264 | return ( | ||
| 2265 | pad(arr[0]) + | ||
| 2266 | pad(arr[1]) + | ||
| 2267 | pad(arr[2]) + | ||
| 2268 | pad(arr[3]) + | ||
| 2269 | pad(arr[4]) + | ||
| 2270 | pad(arr[5]) + | ||
| 2271 | pad(arr[6]) + | ||
| 2272 | pad(arr[7]) | ||
| 2273 | ); | ||
| 2274 | } else { | ||
| 2275 | // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523 | ||
| 2276 | return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) { | ||
| 2277 | var r = (Math.random() * 16) | 0, | ||
| 2278 | v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
| 2279 | return v.toString(16); | ||
| 2280 | }); | ||
| 2281 | } | ||
| 2282 | } | ||
| 2283 | |||
| 2284 | /** | ||
| 2285 | * Given a child DOM element, returns a query-selector statement describing that | ||
| 2286 | * and its ancestors | ||
| 2287 | * e.g. [HTMLElement] => body > div > input#foo.btn[name=baz] | ||
| 2288 | * @param elem | ||
| 2289 | * @returns {string} | ||
| 2290 | */ | ||
| 2291 | function htmlTreeAsString(elem) { | ||
| 2292 | /* eslint no-extra-parens:0*/ | ||
| 2293 | var MAX_TRAVERSE_HEIGHT = 5, | ||
| 2294 | MAX_OUTPUT_LEN = 80, | ||
| 2295 | out = [], | ||
| 2296 | height = 0, | ||
| 2297 | len = 0, | ||
| 2298 | separator = ' > ', | ||
| 2299 | sepLength = separator.length, | ||
| 2300 | nextStr; | ||
| 2301 | |||
| 2302 | while (elem && height++ < MAX_TRAVERSE_HEIGHT) { | ||
| 2303 | nextStr = htmlElementAsString(elem); | ||
| 2304 | // bail out if | ||
| 2305 | // - nextStr is the 'html' element | ||
| 2306 | // - the length of the string that would be created exceeds MAX_OUTPUT_LEN | ||
| 2307 | // (ignore this limit if we are on the first iteration) | ||
| 2308 | if ( | ||
| 2309 | nextStr === 'html' || | ||
| 2310 | (height > 1 && len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN) | ||
| 2311 | ) { | ||
| 2312 | break; | ||
| 2313 | } | ||
| 2314 | 2622 | ||
| 2315 | out.push(nextStr); | 2623 | len += nextStr.length; |
| 2624 | elem = elem.parentNode; | ||
| 2625 | } | ||
| 2316 | 2626 | ||
| 2317 | len += nextStr.length; | 2627 | return out.reverse().join(separator); |
| 2318 | elem = elem.parentNode; | 2628 | } |
| 2319 | } | ||
| 2320 | 2629 | ||
| 2321 | return out.reverse().join(separator); | 2630 | /** |
| 2322 | } | 2631 | * Returns a simple, query-selector representation of a DOM element |
| 2632 | * e.g. [HTMLElement] => input#foo.btn[name=baz] | ||
| 2633 | * @param HTMLElement | ||
| 2634 | * @returns {string} | ||
| 2635 | */ | ||
| 2636 | function htmlElementAsString(elem) { | ||
| 2637 | var out = [], | ||
| 2638 | className, | ||
| 2639 | classes, | ||
| 2640 | key, | ||
| 2641 | attr, | ||
| 2642 | i; | ||
| 2643 | |||
| 2644 | if (!elem || !elem.tagName) { | ||
| 2645 | return ""; | ||
| 2646 | } | ||
| 2323 | 2647 | ||
| 2324 | /** | 2648 | out.push(elem.tagName.toLowerCase()); |
| 2325 | * Returns a simple, query-selector representation of a DOM element | 2649 | if (elem.id) { |
| 2326 | * e.g. [HTMLElement] => input#foo.btn[name=baz] | 2650 | out.push("#" + elem.id); |
| 2327 | * @param HTMLElement | 2651 | } |
| 2328 | * @returns {string} | ||
| 2329 | */ | ||
| 2330 | function htmlElementAsString(elem) { | ||
| 2331 | var out = [], | ||
| 2332 | className, | ||
| 2333 | classes, | ||
| 2334 | key, | ||
| 2335 | attr, | ||
| 2336 | i; | ||
| 2337 | |||
| 2338 | if (!elem || !elem.tagName) { | ||
| 2339 | return ''; | ||
| 2340 | } | ||
| 2341 | 2652 | ||
| 2342 | out.push(elem.tagName.toLowerCase()); | 2653 | className = elem.className; |
| 2343 | if (elem.id) { | 2654 | if (className && isString(className)) { |
| 2344 | out.push('#' + elem.id); | 2655 | classes = className.split(/\s+/); |
| 2345 | } | 2656 | for (i = 0; i < classes.length; i++) { |
| 2657 | out.push("." + classes[i]); | ||
| 2658 | } | ||
| 2659 | } | ||
| 2660 | var attrWhitelist = ["type", "name", "title", "alt"]; | ||
| 2661 | for (i = 0; i < attrWhitelist.length; i++) { | ||
| 2662 | key = attrWhitelist[i]; | ||
| 2663 | attr = elem.getAttribute(key); | ||
| 2664 | if (attr) { | ||
| 2665 | out.push("[" + key + '="' + attr + '"]'); | ||
| 2666 | } | ||
| 2667 | } | ||
| 2668 | return out.join(""); | ||
| 2669 | } | ||
| 2346 | 2670 | ||
| 2347 | className = elem.className; | 2671 | /** |
| 2348 | if (className && isString(className)) { | 2672 | * Returns true if either a OR b is truthy, but not both |
| 2349 | classes = className.split(/\s+/); | 2673 | */ |
| 2350 | for (i = 0; i < classes.length; i++) { | 2674 | function isOnlyOneTruthy(a, b) { |
| 2351 | out.push('.' + classes[i]); | 2675 | return !!(!!a ^ !!b); |
| 2352 | } | 2676 | } |
| 2353 | } | ||
| 2354 | var attrWhitelist = ['type', 'name', 'title', 'alt']; | ||
| 2355 | for (i = 0; i < attrWhitelist.length; i++) { | ||
| 2356 | key = attrWhitelist[i]; | ||
| 2357 | attr = elem.getAttribute(key); | ||
| 2358 | if (attr) { | ||
| 2359 | out.push('[' + key + '="' + attr + '"]'); | ||
| 2360 | } | ||
| 2361 | } | ||
| 2362 | return out.join(''); | ||
| 2363 | } | ||
| 2364 | 2677 | ||
| 2365 | /** | 2678 | /** |
| 2366 | * Returns true if either a OR b is truthy, but not both | 2679 | * Returns true if the two input exception interfaces have the same content |
| 2367 | */ | 2680 | */ |
| 2368 | function isOnlyOneTruthy(a, b) { | 2681 | function isSameException(ex1, ex2) { |
| 2369 | return !!(!!a ^ !!b); | 2682 | if (isOnlyOneTruthy(ex1, ex2)) return false; |
| 2370 | } | ||
| 2371 | 2683 | ||
| 2372 | /** | 2684 | ex1 = ex1.values[0]; |
| 2373 | * Returns true if the two input exception interfaces have the same content | 2685 | ex2 = ex2.values[0]; |
| 2374 | */ | ||
| 2375 | function isSameException(ex1, ex2) { | ||
| 2376 | if (isOnlyOneTruthy(ex1, ex2)) return false; | ||
| 2377 | 2686 | ||
| 2378 | ex1 = ex1.values[0]; | 2687 | if (ex1.type !== ex2.type || ex1.value !== ex2.value) |
| 2379 | ex2 = ex2.values[0]; | 2688 | return false; |
| 2380 | 2689 | ||
| 2381 | if (ex1.type !== ex2.type || ex1.value !== ex2.value) return false; | 2690 | return isSameStacktrace(ex1.stacktrace, ex2.stacktrace); |
| 2691 | } | ||
| 2382 | 2692 | ||
| 2383 | return isSameStacktrace(ex1.stacktrace, ex2.stacktrace); | 2693 | /** |
| 2384 | } | 2694 | * Returns true if the two input stack trace interfaces have the same content |
| 2695 | */ | ||
| 2696 | function isSameStacktrace(stack1, stack2) { | ||
| 2697 | if (isOnlyOneTruthy(stack1, stack2)) return false; | ||
| 2698 | |||
| 2699 | var frames1 = stack1.frames; | ||
| 2700 | var frames2 = stack2.frames; | ||
| 2701 | |||
| 2702 | // Exit early if frame count differs | ||
| 2703 | if (frames1.length !== frames2.length) return false; | ||
| 2704 | |||
| 2705 | // Iterate through every frame; bail out if anything differs | ||
| 2706 | var a, b; | ||
| 2707 | for (var i = 0; i < frames1.length; i++) { | ||
| 2708 | a = frames1[i]; | ||
| 2709 | b = frames2[i]; | ||
| 2710 | if ( | ||
| 2711 | a.filename !== b.filename || | ||
| 2712 | a.lineno !== b.lineno || | ||
| 2713 | a.colno !== b.colno || | ||
| 2714 | a["function"] !== b["function"] | ||
| 2715 | ) | ||
| 2716 | return false; | ||
| 2717 | } | ||
| 2718 | return true; | ||
| 2719 | } | ||
| 2385 | 2720 | ||
| 2386 | /** | 2721 | /** |
| 2387 | * Returns true if the two input stack trace interfaces have the same content | 2722 | * Polyfill a method |
| 2388 | */ | 2723 | * @param obj object e.g. `document` |
| 2389 | function isSameStacktrace(stack1, stack2) { | 2724 | * @param name method name present on object e.g. `addEventListener` |
| 2390 | if (isOnlyOneTruthy(stack1, stack2)) return false; | 2725 | * @param replacement replacement function |
| 2391 | 2726 | * @param track {optional} record instrumentation to an array | |
| 2392 | var frames1 = stack1.frames; | 2727 | */ |
| 2393 | var frames2 = stack2.frames; | 2728 | function fill(obj, name, replacement, track) { |
| 2394 | 2729 | var orig = obj[name]; | |
| 2395 | // Exit early if frame count differs | 2730 | obj[name] = replacement(orig); |
| 2396 | if (frames1.length !== frames2.length) return false; | 2731 | obj[name].__raven__ = true; |
| 2397 | 2732 | obj[name].__orig__ = orig; | |
| 2398 | // Iterate through every frame; bail out if anything differs | 2733 | if (track) { |
| 2399 | var a, b; | ||
| 2400 | for (var i = 0; i < frames1.length; i++) { | ||
| 2401 | a = frames1[i]; | ||
| 2402 | b = frames2[i]; | ||
| 2403 | if ( | ||
| 2404 | a.filename !== b.filename || | ||
| 2405 | a.lineno !== b.lineno || | ||
| 2406 | a.colno !== b.colno || | ||
| 2407 | a['function'] !== b['function'] | ||
| 2408 | ) | ||
| 2409 | return false; | ||
| 2410 | } | ||
| 2411 | return true; | ||
| 2412 | } | ||
| 2413 | |||
| 2414 | /** | ||
| 2415 | * Polyfill a method | ||
| 2416 | * @param obj object e.g. `document` | ||
| 2417 | * @param name method name present on object e.g. `addEventListener` | ||
| 2418 | * @param replacement replacement function | ||
| 2419 | * @param track {optional} record instrumentation to an array | ||
| 2420 | */ | ||
| 2421 | function fill(obj, name, replacement, track) { | ||
| 2422 | var orig = obj[name]; | ||
| 2423 | obj[name] = replacement(orig); | ||
| 2424 | obj[name].__raven__ = true; | ||
| 2425 | obj[name].__orig__ = orig; | ||
| 2426 | if (track) { | ||
| 2427 | track.push([obj, name, orig]); | ||
| 2428 | } | ||
| 2429 | } | ||
| 2430 | |||
| 2431 | module.exports = { | ||
| 2432 | isObject: isObject, | ||
| 2433 | isError: isError, | ||
| 2434 | isErrorEvent: isErrorEvent, | ||
| 2435 | isUndefined: isUndefined, | ||
| 2436 | isFunction: isFunction, | ||
| 2437 | isString: isString, | ||
| 2438 | isArray: isArray, | ||
| 2439 | isEmptyObject: isEmptyObject, | ||
| 2440 | supportsErrorEvent: supportsErrorEvent, | ||
| 2441 | wrappedCallback: wrappedCallback, | ||
| 2442 | each: each, | ||
| 2443 | objectMerge: objectMerge, | ||
| 2444 | truncate: truncate, | ||
| 2445 | objectFrozen: objectFrozen, | ||
| 2446 | hasKey: hasKey, | ||
| 2447 | joinRegExp: joinRegExp, | ||
| 2448 | urlencode: urlencode, | ||
| 2449 | uuid4: uuid4, | ||
| 2450 | htmlTreeAsString: htmlTreeAsString, | ||
| 2451 | htmlElementAsString: htmlElementAsString, | ||
| 2452 | isSameException: isSameException, | ||
| 2453 | isSameStacktrace: isSameStacktrace, | ||
| 2454 | parseUrl: parseUrl, | ||
| 2455 | fill: fill | ||
| 2456 | }; | ||
| 2457 | |||
| 2458 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | ||
| 2459 | },{}],6:[function(_dereq_,module,exports){ | ||
| 2460 | (function (global){ | ||
| 2461 | var utils = _dereq_(5); | ||
1440 more lines aren’t shown here.
| @@ -32,51 +32,46 @@ const Cls = (app.views.Content = class Content extends app.View { · 31 unmodified lines | |||
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | static initClass() { | 34 | static initClass() { |
| 35 | this.el = '._content'; | 35 | this.el = "._content"; |
| 36 | this.loadingClass = '_content-loading'; | 36 | this.loadingClass = "_content-loading"; |
| 37 | 37 | ||
| 38 | this.events = | 38 | this.events = { click: "onClick" }; |
| 39 | {click: 'onClick'}; | 39 | |
| 40 | |||
| 41 | this.shortcuts = { | 40 | this.shortcuts = { |
| 42 | altUp: 'scrollStepUp', | 41 | altUp: "scrollStepUp", |
| 43 | altDown: 'scrollStepDown', | 42 | altDown: "scrollStepDown", |
| 44 | pageUp: 'scrollPageUp', | 43 | pageUp: "scrollPageUp", |
| 45 | pageDown: 'scrollPageDown', | 44 | pageDown: "scrollPageDown", |
| 46 | pageTop: 'scrollToTop', | 45 | pageTop: "scrollToTop", |
| 47 | pageBottom: 'scrollToBottom', | 46 | pageBottom: "scrollToBottom", |
| 48 | altF: 'onAltF' | 47 | altF: "onAltF", |
| 49 | }; | 48 | }; |
| 50 | 49 | ||
| 51 | this.routes = { | 50 | this.routes = { |
| 52 | before: 'beforeRoute', | 51 | before: "beforeRoute", |
| 53 | after: 'afterRoute' | 52 | after: "afterRoute", |
| 54 | }; | 53 | }; |
| 55 | } | 54 | } |
| 56 | 55 | ||
| 57 | init() { | 56 | init() { |
| 58 | this.scrollEl = app.isMobile() ? | 57 | this.scrollEl = app.isMobile() |
| 59 | (document.scrollingElement || document.body) | 58 | ? document.scrollingElement || document.body |
| 60 | : | 59 | : this.el; |
| 61 | this.el; | ||
| 62 | this.scrollMap = {}; | 60 | this.scrollMap = {}; |
| 63 | this.scrollStack = []; | 61 | this.scrollStack = []; |
| 64 | 62 | ||
| 65 | this.rootPage = new app.views.RootPage; | 63 | this.rootPage = new app.views.RootPage(); |
| 66 | this.staticPage = new app.views.StaticPage; | 64 | this.staticPage = new app.views.StaticPage(); |
| 67 | this.settingsPage = new app.views.SettingsPage; | 65 | this.settingsPage = new app.views.SettingsPage(); |
| 68 | this.offlinePage = new app.views.OfflinePage; | 66 | this.offlinePage = new app.views.OfflinePage(); |
| 69 | this.typePage = new app.views.TypePage; | 67 | this.typePage = new app.views.TypePage(); |
| 70 | this.entryPage = new app.views.EntryPage; | 68 | this.entryPage = new app.views.EntryPage(); |
| 71 | 69 | ||
| 72 | this.entryPage | 70 | this.entryPage |
| 73 | .on('loading', this.onEntryLoading) | 71 | .on("loading", this.onEntryLoading) |
| 74 | .on('loaded', this.onEntryLoaded); | 72 | .on("loaded", this.onEntryLoaded); |
| 75 | |||
| 76 | app | ||
| 77 | .on('ready', this.onReady) | ||
| 78 | .on('bootError', this.onBootError); | ||
| 79 | 73 | ||
| 74 | app.on("ready", this.onReady).on("bootError", this.onBootError); | ||
| 80 | } | 75 | } |
| 81 | 76 | ||
| 82 | show(view) { | 77 | show(view) { |
| @@ -85,7 +80,7 @@ const Cls = (app.views.Content = class Content extends app.View { · 2 unmodified lines | |||
| 85 | if (this.view != null) { | 80 | if (this.view != null) { |
| 86 | this.view.deactivate(); | 81 | this.view.deactivate(); |
| 87 | } | 82 | } |
| 88 | this.html(this.view = view); | 83 | this.html((this.view = view)); |
| 89 | this.view.activate(); | 84 | this.view.activate(); |
| 90 | } | 85 | } |
| 91 | } | 86 | } |
| @@ -107,7 +102,7 @@ const Cls = (app.views.Content = class Content extends app.View { · 15 unmodified lines | |||
| 107 | } | 102 | } |
| 108 | 103 | ||
| 109 | smoothScrollTo(value) { | 104 | smoothScrollTo(value) { |
| 110 | if (app.settings.get('fastScroll')) { | 105 | if (app.settings.get("fastScroll")) { |
| 111 | this.scrollTo(value); | 106 | this.scrollTo(value); |
| 112 | } else { | 107 | } else { |
| 113 | $.smoothScroll(this.scrollEl, value || 0); | 108 | $.smoothScroll(this.scrollEl, value || 0); |
| @@ -144,10 +139,14 @@ const Cls = (app.views.Content = class Content extends app.View { · 30 unmodified lines | |||
| 144 | 139 | ||
| 145 | scrollToTarget() { | 140 | scrollToTarget() { |
| 146 | let el; | 141 | let el; |
| 147 | if (this.routeCtx.hash && (el = this.findTargetByHash(this.routeCtx.hash))) { | 142 | if ( |
| 148 | $.scrollToWithImageLock(el, this.scrollEl, 'top', | 143 | this.routeCtx.hash && |
| 149 | {margin: this.scrollEl === this.el ? 0 : $.offset(this.el).top}); | 144 | (el = this.findTargetByHash(this.routeCtx.hash)) |
| 150 | $.highlight(el, {className: '_highlight'}); | 145 | ) { |
| 146 | $.scrollToWithImageLock(el, this.scrollEl, "top", { | ||
| 147 | margin: this.scrollEl === this.el ? 0 : $.offset(this.el).top, | ||
| 148 | }); | ||
| 149 | $.highlight(el, { className: "_highlight" }); | ||
| 151 | } else { | 150 | } else { |
| 152 | this.scrollTo(this.scrollMap[this.routeCtx.state.id]); | 151 | this.scrollTo(this.scrollMap[this.routeCtx.state.id]); |
| 153 | } | 152 | } |
| @@ -159,7 +158,7 @@ const Cls = (app.views.Content = class Content extends app.View { · 5 unmodified lines | |||
| 159 | 158 | ||
| 160 | onBootError() { | 159 | onBootError() { |
| 161 | this.hideLoading(); | 160 | this.hideLoading(); |
| 162 | this.html(this.tmpl('bootError')); | 161 | this.html(this.tmpl("bootError")); |
| 163 | } | 162 | } |
| 164 | 163 | ||
| 165 | onEntryLoading() { | 164 | onEntryLoading() { |
| @@ -186,8 +185,12 @@ const Cls = (app.views.Content = class Content extends app.View { · 20 unmodified lines | |||
| 186 | } | 185 | } |
| 187 | 186 | ||
| 188 | cacheScrollPosition() { | 187 | cacheScrollPosition() { |
| 189 | if (!this.routeCtx || this.routeCtx.hash) { return; } | 188 | if (!this.routeCtx || this.routeCtx.hash) { |
| 190 | if (this.routeCtx.path === '/') { return; } | 189 | return; |
| 190 | } | ||
| 191 | if (this.routeCtx.path === "/") { | ||
| 192 | return; | ||
| 193 | } | ||
| 191 | 194 | ||
| 192 | if (this.scrollMap[this.routeCtx.state.id] == null) { | 195 | if (this.scrollMap[this.routeCtx.state.id] == null) { |
| 193 | this.scrollStack.push(this.routeCtx.state.id); | 196 | this.scrollStack.push(this.routeCtx.state.id); |
| @@ -200,24 +203,24 @@ const Cls = (app.views.Content = class Content extends app.View { · 6 unmodified lines | |||
| 200 | } | 203 | } |
| 201 | 204 | ||
| 202 | afterRoute(route, context) { | 205 | afterRoute(route, context) { |
| 203 | if ((route !== 'entry') && (route !== 'type')) { | 206 | if (route !== "entry" && route !== "type") { |
| 204 | resetFavicon(); | 207 | resetFavicon(); |
| 205 | } | 208 | } |
| 206 | 209 | ||
| 207 | switch (route) { | 210 | switch (route) { |
| 208 | case 'root': | 211 | case "root": |
| 209 | this.show(this.rootPage); | 212 | this.show(this.rootPage); |
| 210 | break; | 213 | break; |
| 211 | case 'entry': | 214 | case "entry": |
| 212 | this.show(this.entryPage); | 215 | this.show(this.entryPage); |
| 213 | break; | 216 | break; |
| 214 | case 'type': | 217 | case "type": |
| 215 | this.show(this.typePage); | 218 | this.show(this.typePage); |
| 216 | break; | 219 | break; |
| 217 | case 'settings': | 220 | case "settings": |
| 218 | this.show(this.settingsPage); | 221 | this.show(this.settingsPage); |
| 219 | break; | 222 | break; |
| 220 | case 'offline': | 223 | case "offline": |
| 221 | this.show(this.offlinePage); | 224 | this.show(this.offlinePage); |
| 222 | break; | 225 | break; |
| 223 | default: | 226 | default: |
| @@ -225,37 +228,59 @@ const Cls = (app.views.Content = class Content extends app.View { · 1 unmodified line | |||
| 225 | } | 228 | } |
| 226 | 229 | ||
| 227 | this.view.onRoute(context); | 230 | this.view.onRoute(context); |
| 228 | app.document.setTitle(typeof this.view.getTitle === 'function' ? this.view.getTitle() : undefined); | 231 | app.document.setTitle( |
| 232 | typeof this.view.getTitle === "function" | ||
| 233 | ? this.view.getTitle() | ||
| 234 | : undefined, | ||
| 235 | ); | ||
| 229 | } | 236 | } |
| 230 | 237 | ||
| 231 | onClick(event) { | 238 | onClick(event) { |
| 232 | const link = $.closestLink($.eventTarget(event), this.el); | 239 | const link = $.closestLink($.eventTarget(event), this.el); |
| 233 | if (link && this.isExternalUrl(link.getAttribute('href'))) { | 240 | if (link && this.isExternalUrl(link.getAttribute("href"))) { |
| 234 | $.stopEvent(event); | 241 | $.stopEvent(event); |
| 235 | $.popup(link); | 242 | $.popup(link); |
| 236 | } | 243 | } |
| 237 | } | 244 | } |
| 238 | 245 | ||
| 239 | onAltF(event) { | 246 | onAltF(event) { |
| 240 | if (!document.activeElement || !$.hasChild(this.el, document.activeElement)) { | 247 | if ( |
| 241 | __guard__(this.find('a:not(:empty)'), x => x.focus()); | 248 | !document.activeElement || |
| 249 | !$.hasChild(this.el, document.activeElement) | ||
| 250 | ) { | ||
| 251 | __guard__(this.find("a:not(:empty)"), (x) => x.focus()); | ||
| 242 | return $.stopEvent(event); | 252 | return $.stopEvent(event); |
| 243 | } | 253 | } |
| 244 | } | 254 | } |
| 245 | 255 | ||
| 246 | findTargetByHash(hash) { | 256 | findTargetByHash(hash) { |
| 247 | let el = (() => { try { return $.id(decodeURIComponent(hash)); } catch (error) {} })(); | 257 | let el = (() => { |
| 248 | if (!el) { el = (() => { try { return $.id(hash); } catch (error1) {} })(); } | 258 | try { |
| 259 | return $.id(decodeURIComponent(hash)); | ||
| 260 | } catch (error) {} | ||
| 261 | })(); | ||
| 262 | if (!el) { | ||
| 263 | el = (() => { | ||
| 264 | try { | ||
| 265 | return $.id(hash); | ||
| 266 | } catch (error1) {} | ||
| 267 | })(); | ||
| 268 | } | ||
| 249 | return el; | 269 | return el; |
| 250 | } | 270 | } |
| 251 | 271 | ||
| 252 | isExternalUrl(url) { | 272 | isExternalUrl(url) { |
| 253 | let needle; | 273 | let needle; |
| 254 | return (needle = __guard__(url, x => x.slice(0, 6)), ['http:/', 'https:'].includes(needle)); | 274 | return ( |
| 275 | (needle = __guard__(url, (x) => x.slice(0, 6))), | ||
| 276 | ["http:/", "https:"].includes(needle) | ||
| 277 | ); | ||
| 255 | } | 278 | } |
| 256 | }); | 279 | }); |
| 257 | Cls.initClass(); | 280 | Cls.initClass(); |
| 258 | 281 | ||
| 259 | function __guard__(value, transform) { | 282 | function __guard__(value, transform) { |
| 260 | return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; | 283 | return typeof value !== "undefined" && value !== null |
| 261 | } | 284 | ? transform(value) |
| 285 | : undefined; | ||
| 286 | } | ||
| @@ -10,7 +10,7 @@ · 9 unmodified lines | |||
| 10 | * DS207: Consider shorter variations of null checks | 10 | * DS207: Consider shorter variations of null checks |
| 11 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 11 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 12 | */ | 12 | */ |
| 13 | (function() { | 13 | (function () { |
| 14 | let LINKS = undefined; | 14 | let LINKS = undefined; |
| 15 | const Cls = (app.views.EntryPage = class EntryPage extends app.View { | 15 | const Cls = (app.views.EntryPage = class EntryPage extends app.View { |
| 16 | constructor(...args) { | 16 | constructor(...args) { |
| @@ -24,23 +24,21 @@ · 7 unmodified lines | |||
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | static initClass() { | 26 | static initClass() { |
| 27 | this.className = '_page'; | 27 | this.className = "_page"; |
| 28 | this.errorClass = '_page-error'; | 28 | this.errorClass = "_page-error"; |
| 29 | 29 | ||
| 30 | this.events = | 30 | this.events = { click: "onClick" }; |
| 31 | {click: 'onClick'}; | 31 | |
| 32 | |||
| 33 | this.shortcuts = { | 32 | this.shortcuts = { |
| 34 | altC: 'onAltC', | 33 | altC: "onAltC", |
| 35 | altO: 'onAltO' | 34 | altO: "onAltO", |
| 36 | }; | 35 | }; |
| 37 | 36 | ||
| 38 | this.routes = | 37 | this.routes = { before: "beforeRoute" }; |
| 39 | {before: 'beforeRoute'}; | 38 | |
| 40 | |||
| 41 | LINKS = { | 39 | LINKS = { |
| 42 | home: 'Homepage', | 40 | home: "Homepage", |
| 43 | code: 'Source code' | 41 | code: "Source code", |
| 44 | }; | 42 | }; |
| 45 | } | 43 | } |
| 46 | 44 | ||
| @@ -58,61 +56,84 @@ · 11 unmodified lines | |||
| 58 | 56 | ||
| 59 | loading() { | 57 | loading() { |
| 60 | this.empty(); | 58 | this.empty(); |
| 61 | this.trigger('loading'); | 59 | this.trigger("loading"); |
| 62 | } | 60 | } |
| 63 | 61 | ||
| 64 | render(content, fromCache) { | 62 | render(content, fromCache) { |
| 65 | if (content == null) { content = ''; } | 63 | if (content == null) { |
| 66 | if (fromCache == null) { fromCache = false; } | 64 | content = ""; |
| 67 | if (!this.activated) { return; } | 65 | } |
| 66 | if (fromCache == null) { | ||
| 67 | fromCache = false; | ||
| 68 | } | ||
| 69 | if (!this.activated) { | ||
| 70 | return; | ||
| 71 | } | ||
| 68 | this.empty(); | 72 | this.empty(); |
| 69 | this.subview = new (this.subViewClass())(this.el, this.entry); | 73 | this.subview = new (this.subViewClass())(this.el, this.entry); |
| 70 | 74 | ||
| 71 | $.batchUpdate(this.el, () => { | 75 | $.batchUpdate(this.el, () => { |
| 72 | this.subview.render(content, fromCache); | 76 | this.subview.render(content, fromCache); |
| 73 | if (!fromCache) { this.addCopyButtons(); } | 77 | if (!fromCache) { |
| 78 | this.addCopyButtons(); | ||
| 79 | } | ||
| 74 | }); | 80 | }); |
| 75 | 81 | ||
| 76 | if (app.disabledDocs.findBy('slug', this.entry.doc.slug)) { | 82 | if (app.disabledDocs.findBy("slug", this.entry.doc.slug)) { |
| 77 | this.hiddenView = new app.views.HiddenPage(this.el, this.entry); | 83 | this.hiddenView = new app.views.HiddenPage(this.el, this.entry); |
| 78 | } | 84 | } |
| 79 | 85 | ||
| 80 | setFaviconForDoc(this.entry.doc); | 86 | setFaviconForDoc(this.entry.doc); |
| 81 | this.delay(this.polyfillMathML); | 87 | this.delay(this.polyfillMathML); |
| 82 | this.trigger('loaded'); | 88 | this.trigger("loaded"); |
| 83 | } | 89 | } |
| 84 | 90 | ||
| 85 | addCopyButtons() { | 91 | addCopyButtons() { |
| 86 | if (!this.copyButton) { | 92 | if (!this.copyButton) { |
| 87 | this.copyButton = document.createElement('button'); | 93 | this.copyButton = document.createElement("button"); |
| 88 | this.copyButton.innerHTML = '<svg><use xlink:href="#icon-copy"/></svg>'; | 94 | this.copyButton.innerHTML = '<svg><use xlink:href="#icon-copy"/></svg>'; |
| 89 | this.copyButton.type = 'button'; | 95 | this.copyButton.type = "button"; |
| 90 | this.copyButton.className = '_pre-clip'; | 96 | this.copyButton.className = "_pre-clip"; |
| 91 | this.copyButton.title = 'Copy to clipboard'; | 97 | this.copyButton.title = "Copy to clipboard"; |
| 92 | this.copyButton.setAttribute('aria-label', 'Copy to clipboard'); | 98 | this.copyButton.setAttribute("aria-label", "Copy to clipboard"); |
| 99 | } | ||
| 100 | for (var el of Array.from(this.findAllByTag("pre"))) { | ||
| 101 | el.appendChild(this.copyButton.cloneNode(true)); | ||
| 93 | } | 102 | } |
| 94 | for (var el of Array.from(this.findAllByTag('pre'))) { el.appendChild(this.copyButton.cloneNode(true)); } | ||
| 95 | } | 103 | } |
| 96 | 104 | ||
| 97 | polyfillMathML() { | 105 | polyfillMathML() { |
| 98 | if ((window.supportsMathML !== false) || !!this.polyfilledMathML || !this.findByTag('math')) { return; } | 106 | if ( |
| 107 | window.supportsMathML !== false || | ||
| 108 | !!this.polyfilledMathML || | ||
| 109 | !this.findByTag("math") | ||
| 110 | ) { | ||
| 111 | return; | ||
| 112 | } | ||
| 99 | this.polyfilledMathML = true; | 113 | this.polyfilledMathML = true; |
| 100 | $.append(document.head, `<link rel="stylesheet" href="${app.config.mathml_stylesheet}">`); | 114 | $.append( |
| 115 | document.head, | ||
| 116 | `<link rel="stylesheet" href="${app.config.mathml_stylesheet}">`, | ||
| 117 | ); | ||
| 101 | } | 118 | } |
| 102 | 119 | ||
| 103 | prepareContent(content) { | 120 | prepareContent(content) { |
| 104 | if (!this.entry.isIndex() || !this.entry.doc.links) { return content; } | 121 | if (!this.entry.isIndex() || !this.entry.doc.links) { |
| 122 | return content; | ||
| 123 | } | ||
| 105 | 124 | ||
| 106 | const links = (() => { | 125 | const links = (() => { |
| 107 | const result = []; | 126 | const result = []; |
| 108 | for (var link in this.entry.doc.links) { | 127 | for (var link in this.entry.doc.links) { |
| 109 | var url = this.entry.doc.links[link]; | 128 | var url = this.entry.doc.links[link]; |
| 110 | result.push(`<a href="${url}" class="_links-link">${LINKS[link]}</a>`); | 129 | result.push( |
| 130 | `<a href="${url}" class="_links-link">${LINKS[link]}</a>`, | ||
| 131 | ); | ||
| 111 | } | 132 | } |
| 112 | return result; | 133 | return result; |
| 113 | })(); | 134 | })(); |
| 114 | 135 | ||
| 115 | return `<p class="_links">${links.join('')}</p>${content}`; | 136 | return `<p class="_links">${links.join("")}</p>${content}`; |
| 116 | } | 137 | } |
| 117 | 138 | ||
| 118 | empty() { | 139 | empty() { |
| @@ -131,11 +152,17 @@ · 12 unmodified lines | |||
| 131 | } | 152 | } |
| 132 | 153 | ||
| 133 | subViewClass() { | 154 | subViewClass() { |
| 134 | return app.views[`${$.classify(this.entry.doc.type)}Page`] || app.views.BasePage; | 155 | return ( |
| 156 | app.views[`${$.classify(this.entry.doc.type)}Page`] || | ||
| 157 | app.views.BasePage | ||
| 158 | ); | ||
| 135 | } | 159 | } |
| 136 | 160 | ||
| 137 | getTitle() { | 161 | getTitle() { |
| 138 | return this.entry.doc.fullName + (this.entry.isIndex() ? ' documentation' : ` / ${this.entry.name}`); | 162 | return ( |
| 163 | this.entry.doc.fullName + | ||
| 164 | (this.entry.isIndex() ? " documentation" : ` / ${this.entry.name}`) | ||
| 165 | ); | ||
| 139 | } | 166 | } |
| 140 | 167 | ||
| 141 | beforeRoute() { | 168 | beforeRoute() { |
| @@ -144,9 +171,13 @@ · 2 unmodified lines | |||
| 144 | } | 171 | } |
| 145 | 172 | ||
| 146 | onRoute(context) { | 173 | onRoute(context) { |
| 147 | const isSameFile = context.entry.filePath() === (this.entry != null ? this.entry.filePath() : undefined); | 174 | const isSameFile = |
| 175 | context.entry.filePath() === | ||
| 176 | (this.entry != null ? this.entry.filePath() : undefined); | ||
| 148 | this.entry = context.entry; | 177 | this.entry = context.entry; |
| 149 | if (!isSameFile) { this.restore() || this.load(); } | 178 | if (!isSameFile) { |
| 179 | this.restore() || this.load(); | ||
| 180 | } | ||
| 150 | } | 181 | } |
| 151 | 182 | ||
| 152 | load() { | 183 | load() { |
| @@ -157,19 +188,21 @@ · 4 unmodified lines | |||
| 157 | abort() { | 188 | abort() { |
| 158 | if (this.xhr) { | 189 | if (this.xhr) { |
| 159 | this.xhr.abort(); | 190 | this.xhr.abort(); |
| 160 | this.xhr = (this.entry = null); | 191 | this.xhr = this.entry = null; |
| 161 | } | 192 | } |
| 162 | } | 193 | } |
| 163 | 194 | ||
| 164 | onSuccess(response) { | 195 | onSuccess(response) { |
| 165 | if (!this.activated) { return; } | 196 | if (!this.activated) { |
| 197 | return; | ||
| 198 | } | ||
| 166 | this.xhr = null; | 199 | this.xhr = null; |
| 167 | this.render(this.prepareContent(response)); | 200 | this.render(this.prepareContent(response)); |
| 168 | } | 201 | } |
| 169 | 202 | ||
| 170 | onError() { | 203 | onError() { |
| 171 | this.xhr = null; | 204 | this.xhr = null; |
| 172 | this.render(this.tmpl('pageLoadError')); | 205 | this.render(this.tmpl("pageLoadError")); |
| 173 | this.resetClass(); | 206 | this.resetClass(); |
| 174 | this.addClass(this.constructor.errorClass); | 207 | this.addClass(this.constructor.errorClass); |
| 175 | if (app.serviceWorker != null) { | 208 | if (app.serviceWorker != null) { |
| @@ -179,7 +212,13 @@ · 3 unmodified lines | |||
| 179 | 212 | ||
| 180 | cache() { | 213 | cache() { |
| 181 | let path; | 214 | let path; |
| 182 | if (this.xhr || !this.entry || this.cacheMap[(path = this.entry.filePath())]) { return; } | 215 | if ( |
| 216 | this.xhr || | ||
| 217 | !this.entry || | ||
| 218 | this.cacheMap[(path = this.entry.filePath())] | ||
| 219 | ) { | ||
| 220 | return; | ||
| 221 | } | ||
| 183 | 222 | ||
| 184 | this.cacheMap[path] = this.el.innerHTML; | 223 | this.cacheMap[path] = this.el.innerHTML; |
| 185 | this.cacheStack.push(path); | 224 | this.cacheStack.push(path); |
| @@ -199,26 +238,34 @@ · 13 unmodified lines | |||
| 199 | 238 | ||
| 200 | onClick(event) { | 239 | onClick(event) { |
| 201 | const target = $.eventTarget(event); | 240 | const target = $.eventTarget(event); |
| 202 | if (target.hasAttribute('data-retry')) { | 241 | if (target.hasAttribute("data-retry")) { |
| 203 | $.stopEvent(event); | 242 | $.stopEvent(event); |
| 204 | this.load(); | 243 | this.load(); |
| 205 | } else if (target.classList.contains('_pre-clip')) { | 244 | } else if (target.classList.contains("_pre-clip")) { |
| 206 | $.stopEvent(event); | 245 | $.stopEvent(event); |
| 207 | target.classList.add($.copyToClipboard(target.parentNode.textContent) ? '_pre-clip-success' : '_pre-clip-error'); | 246 | target.classList.add( |
| 208 | setTimeout((() => target.className = '_pre-clip'), 2000); | 247 | $.copyToClipboard(target.parentNode.textContent) |
| 248 | ? "_pre-clip-success" | ||
| 249 | : "_pre-clip-error", | ||
| 250 | ); | ||
| 251 | setTimeout(() => (target.className = "_pre-clip"), 2000); | ||
| 209 | } | 252 | } |
| 210 | } | 253 | } |
| 211 | 254 | ||
| 212 | onAltC() { | 255 | onAltC() { |
| 213 | let link; | 256 | let link; |
| 214 | if (!(link = this.find('._attribution:last-child ._attribution-link'))) { return; } | 257 | if (!(link = this.find("._attribution:last-child ._attribution-link"))) { |
| 258 | return; | ||
| 259 | } | ||
| 215 | console.log(link.href + location.hash); | 260 | console.log(link.href + location.hash); |
| 216 | navigator.clipboard.writeText(link.href + location.hash); | 261 | navigator.clipboard.writeText(link.href + location.hash); |
| 217 | } | 262 | } |
| 218 | 263 | ||
| 219 | onAltO() { | 264 | onAltO() { |
| 220 | let link; | 265 | let link; |
| 221 | if (!(link = this.find('._attribution:last-child ._attribution-link'))) { return; } | 266 | if (!(link = this.find("._attribution:last-child ._attribution-link"))) { |
| 267 | return; | ||
| 268 | } | ||
| 222 | this.delay(() => $.popup(link.href + location.hash)); | 269 | this.delay(() => $.popup(link.href + location.hash)); |
| 223 | } | 270 | } |
| 224 | }); | 271 | }); |
| @@ -15,11 +15,11 @@ const Cls = (app.views.OfflinePage = class OfflinePage extends app.View { · 14 unmodified lines | |||
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | static initClass() { | 17 | static initClass() { |
| 18 | this.className = '_static'; | 18 | this.className = "_static"; |
| 19 | 19 | ||
| 20 | this.events = { | 20 | this.events = { |
| 21 | click: 'onClick', | 21 | click: "onClick", |
| 22 | change: 'onChange' | 22 | change: "onChange", |
| 23 | }; | 23 | }; |
| 24 | } | 24 | } |
| 25 | 25 | ||
| @@ -31,41 +31,49 @@ const Cls = (app.views.OfflinePage = class OfflinePage extends app.View { · 5 unmodified lines | |||
| 31 | 31 | ||
| 32 | render() { | 32 | render() { |
| 33 | if (app.cookieBlocked) { | 33 | if (app.cookieBlocked) { |
| 34 | this.html(this.tmpl('offlineError', 'cookie_blocked')); | 34 | this.html(this.tmpl("offlineError", "cookie_blocked")); |
| 35 | return; | 35 | return; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | app.docs.getInstallStatuses(statuses => { | 38 | app.docs.getInstallStatuses((statuses) => { |
| 39 | if (!this.activated) { return; } | 39 | if (!this.activated) { |
| 40 | return; | ||
| 41 | } | ||
| 40 | if (statuses === false) { | 42 | if (statuses === false) { |
| 41 | this.html(this.tmpl('offlineError', app.db.reason, app.db.error)); | 43 | this.html(this.tmpl("offlineError", app.db.reason, app.db.error)); |
| 42 | } else { | 44 | } else { |
| 43 | let html = ''; | 45 | let html = ""; |
| 44 | for (var doc of Array.from(app.docs.all())) { html += this.renderDoc(doc, statuses[doc.slug]); } | 46 | for (var doc of Array.from(app.docs.all())) { |
| 45 | this.html(this.tmpl('offlinePage', html)); | 47 | html += this.renderDoc(doc, statuses[doc.slug]); |
| 48 | } | ||
| 49 | this.html(this.tmpl("offlinePage", html)); | ||
| 46 | this.refreshLinks(); | 50 | this.refreshLinks(); |
| 47 | } | 51 | } |
| 48 | }); | 52 | }); |
| 49 | } | 53 | } |
| 50 | 54 | ||
| 51 | renderDoc(doc, status) { | 55 | renderDoc(doc, status) { |
| 52 | return app.templates.render('offlineDoc', doc, status); | 56 | return app.templates.render("offlineDoc", doc, status); |
| 53 | } | 57 | } |
| 54 | 58 | ||
| 55 | getTitle() { | 59 | getTitle() { |
| 56 | return 'Offline'; | 60 | return "Offline"; |
| 57 | } | 61 | } |
| 58 | 62 | ||
| 59 | refreshLinks() { | 63 | refreshLinks() { |
| 60 | for (var action of ['install', 'update', 'uninstall']) { | 64 | for (var action of ["install", "update", "uninstall"]) { |
| 61 | this.find(`[data-action-all='${action}']`).classList[this.find(`[data-action='${action}']`) ? 'add' : 'remove']('_show'); | 65 | this.find(`[data-action-all='${action}']`).classList[ |
| 66 | this.find(`[data-action='${action}']`) ? "add" : "remove" | ||
| 67 | ]("_show"); | ||
| 62 | } | 68 | } |
| 63 | } | 69 | } |
| 64 | 70 | ||
| 65 | docByEl(el) { | 71 | docByEl(el) { |
| 66 | let slug; | 72 | let slug; |
| 67 | while (!(slug = el.getAttribute('data-slug'))) { el = el.parentNode; } | 73 | while (!(slug = el.getAttribute("data-slug"))) { |
| 68 | return app.docs.findBy('slug', slug); | 74 | el = el.parentNode; |
| 75 | } | ||
| 76 | return app.docs.findBy("slug", slug); | ||
| 69 | } | 77 | } |
| 70 | 78 | ||
| 71 | docEl(doc) { | 79 | docEl(doc) { |
| @@ -79,26 +87,44 @@ const Cls = (app.views.OfflinePage = class OfflinePage extends app.View { · 7 unmodified lines | |||
| 79 | onClick(event) { | 87 | onClick(event) { |
| 80 | let action; | 88 | let action; |
| 81 | let el = $.eventTarget(event); | 89 | let el = $.eventTarget(event); |
| 82 | if (action = el.getAttribute('data-action')) { | 90 | if ((action = el.getAttribute("data-action"))) { |
| 83 | const doc = this.docByEl(el); | 91 | const doc = this.docByEl(el); |
| 84 | if (action === 'update') { action = 'install'; } | 92 | if (action === "update") { |
| 85 | doc[action](this.onInstallSuccess.bind(this, doc), this.onInstallError.bind(this, doc), this.onInstallProgress.bind(this, doc)); | 93 | action = "install"; |
| 86 | el.parentNode.innerHTML = `${el.textContent.replace(/e$/, '')}ing…`; | 94 | } |
| 87 | } else if (action = el.getAttribute('data-action-all') || el.parentElement.getAttribute('data-action-all')) { | 95 | doc[action]( |
| 88 | if ((action === 'uninstall') && !window.confirm('Uninstall all docs?')) { return; } | 96 | this.onInstallSuccess.bind(this, doc), |
| 97 | this.onInstallError.bind(this, doc), | ||
| 98 | this.onInstallProgress.bind(this, doc), | ||
| 99 | ); | ||
| 100 | el.parentNode.innerHTML = `${el.textContent.replace(/e$/, "")}ing…`; | ||
| 101 | } else if ( | ||
| 102 | (action = | ||
| 103 | el.getAttribute("data-action-all") || | ||
| 104 | el.parentElement.getAttribute("data-action-all")) | ||
| 105 | ) { | ||
| 106 | if (action === "uninstall" && !window.confirm("Uninstall all docs?")) { | ||
| 107 | return; | ||
| 108 | } | ||
| 89 | app.db.migrate(); | 109 | app.db.migrate(); |
| 90 | for (el of Array.from(this.findAll(`[data-action='${action}']`))) { $.click(el); } | 110 | for (el of Array.from(this.findAll(`[data-action='${action}']`))) { |
| 111 | $.click(el); | ||
| 112 | } | ||
| 91 | } | 113 | } |
| 92 | } | 114 | } |
| 93 | 115 | ||
| 94 | onInstallSuccess(doc) { | 116 | onInstallSuccess(doc) { |
| 95 | if (!this.activated) { return; } | 117 | if (!this.activated) { |
| 96 | doc.getInstallStatus(status => { | 118 | return; |
| 119 | } | ||
| 120 | doc.getInstallStatus((status) => { | ||
| 97 | let el; | 121 | let el; |
| 98 | if (!this.activated) { return; } | 122 | if (!this.activated) { |
| 99 | if (el = this.docEl(doc)) { | 123 | return; |
| 124 | } | ||
| 125 | if ((el = this.docEl(doc))) { | ||
| 100 | el.outerHTML = this.renderDoc(doc, status); | 126 | el.outerHTML = this.renderDoc(doc, status); |
| 101 | $.highlight(el, {className: '_highlight'}); | 127 | $.highlight(el, { className: "_highlight" }); |
| 102 | this.refreshLinks(); | 128 | this.refreshLinks(); |
| 103 | } | 129 | } |
| 104 | }); | 130 | }); |
| @@ -106,24 +132,31 @@ const Cls = (app.views.OfflinePage = class OfflinePage extends app.View { · 1 unmodified line | |||
| 106 | 132 | ||
| 107 | onInstallError(doc) { | 133 | onInstallError(doc) { |
| 108 | let el; | 134 | let el; |
| 109 | if (!this.activated) { return; } | 135 | if (!this.activated) { |
| 110 | if (el = this.docEl(doc)) { | 136 | return; |
| 111 | el.lastElementChild.textContent = 'Error'; | 137 | } |
| 138 | if ((el = this.docEl(doc))) { | ||
| 139 | el.lastElementChild.textContent = "Error"; | ||
| 112 | } | 140 | } |
| 113 | } | 141 | } |
| 114 | 142 | ||
| 115 | onInstallProgress(doc, event) { | 143 | onInstallProgress(doc, event) { |
| 116 | let el; | 144 | let el; |
| 117 | if (!this.activated || !event.lengthComputable) { return; } | 145 | if (!this.activated || !event.lengthComputable) { |
| 118 | if (el = this.docEl(doc)) { | 146 | return; |
| 147 | } | ||
| 148 | if ((el = this.docEl(doc))) { | ||
| 119 | const percentage = Math.round((event.loaded * 100) / event.total); | 149 | const percentage = Math.round((event.loaded * 100) / event.total); |
| 120 | el.lastElementChild.textContent = el.lastElementChild.textContent.replace(/(\s.+)?$/, ` (${percentage}%)`); | 150 | el.lastElementChild.textContent = el.lastElementChild.textContent.replace( |
| 151 | /(\s.+)?$/, | ||
| 152 | ` (${percentage}%)`, | ||
| 153 | ); | ||
| 121 | } | 154 | } |
| 122 | } | 155 | } |
| 123 | 156 | ||
| 124 | onChange(event) { | 157 | onChange(event) { |
| 125 | if (event.target.name === 'autoUpdate') { | 158 | if (event.target.name === "autoUpdate") { |
| 126 | app.settings.set('manualUpdate', !event.target.checked); | 159 | app.settings.set("manualUpdate", !event.target.checked); |
| 127 | } | 160 | } |
| 128 | } | 161 | } |
| 129 | }); | 162 | }); |
| @@ -14,26 +14,26 @@ const Cls = (app.views.RootPage = class RootPage extends app.View { · 13 unmodified lines | |||
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | static initClass() { | 16 | static initClass() { |
| 17 | this.events = | 17 | this.events = { click: "onClick" }; |
| 18 | {click: 'onClick'}; | ||
| 19 | } | 18 | } |
| 20 | 19 | ||
| 21 | init() { | 20 | init() { |
| 22 | if (!this.isHidden()) { this.setHidden(false); } // reserve space in local storage | 21 | if (!this.isHidden()) { |
| 22 | this.setHidden(false); | ||
| 23 | } // reserve space in local storage | ||
| 23 | this.render(); | 24 | this.render(); |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | render() { | 27 | render() { |
| 27 | this.empty(); | 28 | this.empty(); |
| 28 | 29 | ||
| 29 | const tmpl = app.isAndroidWebview() ? | 30 | const tmpl = app.isAndroidWebview() |
| 30 | 'androidWarning' | 31 | ? "androidWarning" |
| 31 | : this.isHidden() ? | 32 | : this.isHidden() |
| 32 | 'splash' | 33 | ? "splash" |
| 33 | : app.isMobile() ? | 34 | : app.isMobile() |
| 34 | 'mobileIntro' | 35 | ? "mobileIntro" |
| 35 | : | 36 | : "intro"; |
| 36 | 'intro'; | ||
| 37 | 37 | ||
| 38 | this.append(this.tmpl(tmpl)); | 38 | this.append(this.tmpl(tmpl)); |
| 39 | } | 39 | } |
| @@ -44,17 +44,17 @@ const Cls = (app.views.RootPage = class RootPage extends app.View { · 4 unmodified lines | |||
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | setHidden(value) { | 46 | setHidden(value) { |
| 47 | app.settings.set('hideIntro', value); | 47 | app.settings.set("hideIntro", value); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | isHidden() { | 50 | isHidden() { |
| 51 | return app.isSingleDoc() || app.settings.get('hideIntro'); | 51 | return app.isSingleDoc() || app.settings.get("hideIntro"); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | onRoute() {} | 54 | onRoute() {} |
| 55 | 55 | ||
| 56 | onClick(event) { | 56 | onClick(event) { |
| 57 | if ($.eventTarget(event).hasAttribute('data-hide-intro')) { | 57 | if ($.eventTarget(event).hasAttribute("data-hide-intro")) { |
| 58 | $.stopEvent(event); | 58 | $.stopEvent(event); |
| 59 | this.hideIntro(); | 59 | this.hideIntro(); |
| 60 | } | 60 | } |
| @@ -17,39 +17,41 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View { · 16 unmodified lines | |||
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | static initClass() { | 19 | static initClass() { |
| 20 | this.className = '_static'; | 20 | this.className = "_static"; |
| 21 | 21 | ||
| 22 | this.events = { | 22 | this.events = { |
| 23 | click: 'onClick', | 23 | click: "onClick", |
| 24 | change: 'onChange' | 24 | change: "onChange", |
| 25 | }; | 25 | }; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | render() { | 28 | render() { |
| 29 | this.html(this.tmpl('settingsPage', this.currentSettings())); | 29 | this.html(this.tmpl("settingsPage", this.currentSettings())); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | currentSettings() { | 32 | currentSettings() { |
| 33 | const settings = {}; | 33 | const settings = {}; |
| 34 | settings.theme = app.settings.get('theme'); | 34 | settings.theme = app.settings.get("theme"); |
| 35 | settings.smoothScroll = !app.settings.get('fastScroll'); | 35 | settings.smoothScroll = !app.settings.get("fastScroll"); |
| 36 | settings.arrowScroll = app.settings.get('arrowScroll'); | 36 | settings.arrowScroll = app.settings.get("arrowScroll"); |
| 37 | settings.noAutofocus = app.settings.get('noAutofocus'); | 37 | settings.noAutofocus = app.settings.get("noAutofocus"); |
| 38 | settings.autoInstall = app.settings.get('autoInstall'); | 38 | settings.autoInstall = app.settings.get("autoInstall"); |
| 39 | settings.analyticsConsent = app.settings.get('analyticsConsent'); | 39 | settings.analyticsConsent = app.settings.get("analyticsConsent"); |
| 40 | settings.spaceScroll = app.settings.get('spaceScroll'); | 40 | settings.spaceScroll = app.settings.get("spaceScroll"); |
| 41 | settings.spaceTimeout = app.settings.get('spaceTimeout'); | 41 | settings.spaceTimeout = app.settings.get("spaceTimeout"); |
| 42 | settings.autoSupported = app.settings.autoSupported; | 42 | settings.autoSupported = app.settings.autoSupported; |
| 43 | for (var layout of Array.from(app.settings.LAYOUTS)) { settings[layout] = app.settings.hasLayout(layout); } | 43 | for (var layout of Array.from(app.settings.LAYOUTS)) { |
| 44 | settings[layout] = app.settings.hasLayout(layout); | ||
| 45 | } | ||
| 44 | return settings; | 46 | return settings; |
| 45 | } | 47 | } |
| 46 | 48 | ||
| 47 | getTitle() { | 49 | getTitle() { |
| 48 | return 'Preferences'; | 50 | return "Preferences"; |
| 49 | } | 51 | } |
| 50 | 52 | ||
| 51 | setTheme(value) { | 53 | setTheme(value) { |
| 52 | app.settings.set('theme', value); | 54 | app.settings.set("theme", value); |
| 53 | } | 55 | } |
| 54 | 56 | ||
| 55 | toggleLayout(layout, enable) { | 57 | toggleLayout(layout, enable) { |
| @@ -57,20 +59,22 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View { · 1 unmodified line | |||
| 57 | } | 59 | } |
| 58 | 60 | ||
| 59 | toggleSmoothScroll(enable) { | 61 | toggleSmoothScroll(enable) { |
| 60 | app.settings.set('fastScroll', !enable); | 62 | app.settings.set("fastScroll", !enable); |
| 61 | } | 63 | } |
| 62 | 64 | ||
| 63 | toggleAnalyticsConsent(enable) { | 65 | toggleAnalyticsConsent(enable) { |
| 64 | app.settings.set('analyticsConsent', enable ? '1' : '0'); | 66 | app.settings.set("analyticsConsent", enable ? "1" : "0"); |
| 65 | if (!enable) { resetAnalytics(); } | 67 | if (!enable) { |
| 68 | resetAnalytics(); | ||
| 69 | } | ||
| 66 | } | 70 | } |
| 67 | 71 | ||
| 68 | toggleSpaceScroll(enable) { | 72 | toggleSpaceScroll(enable) { |
| 69 | app.settings.set('spaceScroll', enable ? 1 : 0); | 73 | app.settings.set("spaceScroll", enable ? 1 : 0); |
| 70 | } | 74 | } |
| 71 | 75 | ||
| 72 | setScrollTimeout(value) { | 76 | setScrollTimeout(value) { |
| 73 | return app.settings.set('spaceTimeout', value); | 77 | return app.settings.set("spaceTimeout", value); |
| 74 | } | 78 | } |
| 75 | 79 | ||
| 76 | toggle(name, enable) { | 80 | toggle(name, enable) { |
| @@ -78,31 +82,37 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View { · 1 unmodified line | |||
| 78 | } | 82 | } |
| 79 | 83 | ||
| 80 | export() { | 84 | export() { |
| 81 | const data = new Blob([JSON.stringify(app.settings.export())], {type: 'application/json'}); | 85 | const data = new Blob([JSON.stringify(app.settings.export())], { |
| 82 | const link = document.createElement('a'); | 86 | type: "application/json", |
| 87 | }); | ||
| 88 | const link = document.createElement("a"); | ||
| 83 | link.href = URL.createObjectURL(data); | 89 | link.href = URL.createObjectURL(data); |
| 84 | link.download = 'devdocs.json'; | 90 | link.download = "devdocs.json"; |
| 85 | link.style.display = 'none'; | 91 | link.style.display = "none"; |
| 86 | document.body.appendChild(link); | 92 | document.body.appendChild(link); |
| 87 | link.click(); | 93 | link.click(); |
| 88 | document.body.removeChild(link); | 94 | document.body.removeChild(link); |
| 89 | } | 95 | } |
| 90 | 96 | ||
| 91 | import(file, input) { | 97 | import(file, input) { |
| 92 | if (!file || (file.type !== 'application/json')) { | 98 | if (!file || file.type !== "application/json") { |
| 93 | new app.views.Notif('ImportInvalid', {autoHide: false}); | 99 | new app.views.Notif("ImportInvalid", { autoHide: false }); |
| 94 | return; | 100 | return; |
| 95 | } | 101 | } |
| 96 | 102 | ||
| 97 | const reader = new FileReader(); | 103 | const reader = new FileReader(); |
| 98 | reader.onloadend = function() { | 104 | reader.onloadend = function () { |
| 99 | const data = (() => { try { return JSON.parse(reader.result); } catch (error) {} })(); | 105 | const data = (() => { |
| 100 | if (!data || (data.constructor !== Object)) { | 106 | try { |
| 101 | new app.views.Notif('ImportInvalid', {autoHide: false}); | 107 | return JSON.parse(reader.result); |
| 108 | } catch (error) {} | ||
| 109 | })(); | ||
| 110 | if (!data || data.constructor !== Object) { | ||
| 111 | new app.views.Notif("ImportInvalid", { autoHide: false }); | ||
| 102 | return; | 112 | return; |
| 103 | } | 113 | } |
| 104 | app.settings.import(data); | 114 | app.settings.import(data); |
| 105 | $.trigger(input.form, 'import'); | 115 | $.trigger(input.form, "import"); |
| 106 | }; | 116 | }; |
| 107 | reader.readAsText(file); | 117 | reader.readAsText(file); |
| 108 | } | 118 | } |
| @@ -110,25 +120,25 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View { · 1 unmodified line | |||
| 110 | onChange(event) { | 120 | onChange(event) { |
| 111 | const input = event.target; | 121 | const input = event.target; |
| 112 | switch (input.name) { | 122 | switch (input.name) { |
| 113 | case 'theme': | 123 | case "theme": |
| 114 | this.setTheme(input.value); | 124 | this.setTheme(input.value); |
| 115 | break; | 125 | break; |
| 116 | case 'layout': | 126 | case "layout": |
| 117 | this.toggleLayout(input.value, input.checked); | 127 | this.toggleLayout(input.value, input.checked); |
| 118 | break; | 128 | break; |
| 119 | case 'smoothScroll': | 129 | case "smoothScroll": |
| 120 | this.toggleSmoothScroll(input.checked); | 130 | this.toggleSmoothScroll(input.checked); |
| 121 | break; | 131 | break; |
| 122 | case 'import': | 132 | case "import": |
| 123 | this.import(input.files[0], input); | 133 | this.import(input.files[0], input); |
| 124 | break; | 134 | break; |
| 125 | case 'analyticsConsent': | 135 | case "analyticsConsent": |
| 126 | this.toggleAnalyticsConsent(input.checked); | 136 | this.toggleAnalyticsConsent(input.checked); |
| 127 | break; | 137 | break; |
| 128 | case 'spaceScroll': | 138 | case "spaceScroll": |
| 129 | this.toggleSpaceScroll(input.checked); | 139 | this.toggleSpaceScroll(input.checked); |
| 130 | break; | 140 | break; |
| 131 | case 'spaceTimeout': | 141 | case "spaceTimeout": |
| 132 | this.setScrollTimeout(input.value); | 142 | this.setScrollTimeout(input.value); |
| 133 | break; | 143 | break; |
| 134 | default: | 144 | default: |
| @@ -138,8 +148,8 @@ const Cls = (app.views.SettingsPage = class SettingsPage extends app.View { · 3 unmodified lines | |||
| 138 | 148 | ||
| 139 | onClick(event) { | 149 | onClick(event) { |
| 140 | const target = $.eventTarget(event); | 150 | const target = $.eventTarget(event); |
| 141 | switch (target.getAttribute('data-action')) { | 151 | switch (target.getAttribute("data-action")) { |
| 142 | case 'export': | 152 | case "export": |
| 143 | $.stopEvent(event); | 153 | $.stopEvent(event); |
| 144 | this.export(); | 154 | this.export(); |
| 145 | break; | 155 | break; |
| @@ -8,13 +8,13 @@ · 7 unmodified lines | |||
| 8 | */ | 8 | */ |
| 9 | const Cls = (app.views.StaticPage = class StaticPage extends app.View { | 9 | const Cls = (app.views.StaticPage = class StaticPage extends app.View { |
| 10 | static initClass() { | 10 | static initClass() { |
| 11 | this.className = '_static'; | 11 | this.className = "_static"; |
| 12 | 12 | ||
| 13 | this.titles = { | 13 | this.titles = { |
| 14 | about: 'About', | 14 | about: "About", |
| 15 | news: 'News', | 15 | news: "News", |
| 16 | help: 'User Guide', | 16 | help: "User Guide", |
| 17 | notFound: '404' | 17 | notFound: "404", |
| 18 | }; | 18 | }; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| @@ -35,7 +35,7 @@ const Cls = (app.views.StaticPage = class StaticPage extends app.View { · 14 unmodified lines | |||
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | onRoute(context) { | 37 | onRoute(context) { |
| 38 | this.render(context.page || 'notFound'); | 38 | this.render(context.page || "notFound"); |
| 39 | } | 39 | } |
| 40 | }); | 40 | }); |
| 41 | Cls.initClass(); | 41 | Cls.initClass(); |
| @@ -8,7 +8,7 @@ · 7 unmodified lines | |||
| 8 | */ | 8 | */ |
| 9 | const Cls = (app.views.TypePage = class TypePage extends app.View { | 9 | const Cls = (app.views.TypePage = class TypePage extends app.View { |
| 10 | static initClass() { | 10 | static initClass() { |
| 11 | this.className = '_page'; | 11 | this.className = "_page"; |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | deactivate() { | 14 | deactivate() { |
| @@ -20,7 +20,7 @@ const Cls = (app.views.TypePage = class TypePage extends app.View { · 5 unmodified lines | |||
| 20 | 20 | ||
| 21 | render(type) { | 21 | render(type) { |
| 22 | this.type = type; | 22 | this.type = type; |
| 23 | this.html(this.tmpl('typePage', this.type)); | 23 | this.html(this.tmpl("typePage", this.type)); |
| 24 | setFaviconForDoc(this.type.doc); | 24 | setFaviconForDoc(this.type.doc); |
| 25 | } | 25 | } |
| 26 | 26 | ||
| @@ -17,41 +17,49 @@ const Cls = (app.views.Document = class Document extends app.View { · 16 unmodified lines | |||
| 17 | 17 | ||
| 18 | static initClass() { | 18 | static initClass() { |
| 19 | this.el = document; | 19 | this.el = document; |
| 20 | 20 | ||
| 21 | this.events = | 21 | this.events = { visibilitychange: "onVisibilityChange" }; |
| 22 | {visibilitychange: 'onVisibilityChange'}; | 22 | |
| 23 | |||
| 24 | this.shortcuts = { | 23 | this.shortcuts = { |
| 25 | help: 'onHelp', | 24 | help: "onHelp", |
| 26 | preferences: 'onPreferences', | 25 | preferences: "onPreferences", |
| 27 | escape: 'onEscape', | 26 | escape: "onEscape", |
| 28 | superLeft: 'onBack', | 27 | superLeft: "onBack", |
| 29 | superRight: 'onForward' | 28 | superRight: "onForward", |
| 30 | }; | 29 | }; |
| 31 | 30 | ||
| 32 | this.routes = | 31 | this.routes = { after: "afterRoute" }; |
| 33 | {after: 'afterRoute'}; | ||
| 34 | } | 32 | } |
| 35 | 33 | ||
| 36 | init() { | 34 | init() { |
| 37 | this.addSubview((this.menu = new app.views.Menu), | 35 | this.addSubview( |
| 38 | this.addSubview(this.sidebar = new app.views.Sidebar)); | 36 | (this.menu = new app.views.Menu()), |
| 39 | if (app.views.Resizer.isSupported()) { this.addSubview(this.resizer = new app.views.Resizer); } | 37 | this.addSubview((this.sidebar = new app.views.Sidebar())), |
| 40 | this.addSubview(this.content = new app.views.Content); | 38 | ); |
| 41 | if (!app.isSingleDoc() && !app.isMobile()) { this.addSubview(this.path = new app.views.Path); } | 39 | if (app.views.Resizer.isSupported()) { |
| 42 | if (!app.isSingleDoc()) { this.settings = new app.views.Settings; } | 40 | this.addSubview((this.resizer = new app.views.Resizer())); |
| 41 | } | ||
| 42 | this.addSubview((this.content = new app.views.Content())); | ||
| 43 | if (!app.isSingleDoc() && !app.isMobile()) { | ||
| 44 | this.addSubview((this.path = new app.views.Path())); | ||
| 45 | } | ||
| 46 | if (!app.isSingleDoc()) { | ||
| 47 | this.settings = new app.views.Settings(); | ||
| 48 | } | ||
| 43 | 49 | ||
| 44 | $.on(document.body, 'click', this.onClick); | 50 | $.on(document.body, "click", this.onClick); |
| 45 | 51 | ||
| 46 | this.activate(); | 52 | this.activate(); |
| 47 | } | 53 | } |
| 48 | 54 | ||
| 49 | setTitle(title) { | 55 | setTitle(title) { |
| 50 | return this.el.title = title ? `${title} — DevDocs` : 'DevDocs API Documentation'; | 56 | return (this.el.title = title |
| 57 | ? `${title} — DevDocs` | ||
| 58 | : "DevDocs API Documentation"); | ||
| 51 | } | 59 | } |
| 52 | 60 | ||
| 53 | afterRoute(route) { | 61 | afterRoute(route) { |
| 54 | if (route === 'settings') { | 62 | if (route === "settings") { |
| 55 | if (this.settings != null) { | 63 | if (this.settings != null) { |
| 56 | this.settings.activate(); | 64 | this.settings.activate(); |
| 57 | } | 65 | } |
| @@ -63,26 +71,29 @@ const Cls = (app.views.Document = class Document extends app.View { · 5 unmodified lines | |||
| 63 | } | 71 | } |
| 64 | 72 | ||
| 65 | onVisibilityChange() { | 73 | onVisibilityChange() { |
| 66 | if (this.el.visibilityState !== 'visible') { return; } | 74 | if (this.el.visibilityState !== "visible") { |
| 67 | this.delay(function() { | 75 | return; |
| 68 | if (app.isMobile() !== app.views.Mobile.detect()) { location.reload(); } | ||
| 69 | } | 76 | } |
| 70 | , 300); | 77 | this.delay(function () { |
| 78 | if (app.isMobile() !== app.views.Mobile.detect()) { | ||
| 79 | location.reload(); | ||
| 80 | } | ||
| 81 | }, 300); | ||
| 71 | } | 82 | } |
| 72 | 83 | ||
| 73 | onHelp() { | 84 | onHelp() { |
| 74 | app.router.show('/help#shortcuts'); | 85 | app.router.show("/help#shortcuts"); |
| 75 | } | 86 | } |
| 76 | 87 | ||
| 77 | onPreferences() { | 88 | onPreferences() { |
| 78 | app.router.show('/settings'); | 89 | app.router.show("/settings"); |
| 79 | } | 90 | } |
| 80 | 91 | ||
| 81 | onEscape() { | 92 | onEscape() { |
| 82 | const path = !app.isSingleDoc() || (location.pathname === app.doc.fullPath()) ? | 93 | const path = |
| 83 | '/' | 94 | !app.isSingleDoc() || location.pathname === app.doc.fullPath() |
| 84 | : | 95 | ? "/" |
| 85 | app.doc.fullPath(); | 96 | : app.doc.fullPath(); |
| 86 | 97 | ||
| 87 | app.router.show(path); | 98 | app.router.show(path); |
| 88 | } | 99 | } |
| @@ -97,16 +108,34 @@ const Cls = (app.views.Document = class Document extends app.View { · 8 unmodified lines | |||
| 97 | 108 | ||
| 98 | onClick(event) { | 109 | onClick(event) { |
| 99 | const target = $.eventTarget(event); | 110 | const target = $.eventTarget(event); |
| 100 | if (!target.hasAttribute('data-behavior')) { return; } | 111 | if (!target.hasAttribute("data-behavior")) { |
| 112 | return; | ||
| 113 | } | ||
| 101 | $.stopEvent(event); | 114 | $.stopEvent(event); |
| 102 | switch (target.getAttribute('data-behavior')) { | 115 | switch (target.getAttribute("data-behavior")) { |
| 103 | case 'back': history.back(); break; | 116 | case "back": |
| 104 | case 'reload': window.location.reload(); break; | 117 | history.back(); |
| 105 | case 'reboot': app.reboot(); break; | 118 | break; |
| 106 | case 'hard-reload': app.reload(); break; | 119 | case "reload": |
| 107 | case 'reset': if (confirm('Are you sure you want to reset DevDocs?')) { app.reset(); } break; | 120 | window.location.reload(); |
| 108 | case 'accept-analytics': Cookies.set('analyticsConsent', '1', {expires: 1e8}) && app.reboot(); break; | 121 | break; |
| 109 | case 'decline-analytics': Cookies.set('analyticsConsent', '0', {expires: 1e8}) && app.reboot(); break; | 122 | case "reboot": |
| 123 | app.reboot(); | ||
| 124 | break; | ||
| 125 | case "hard-reload": | ||
| 126 | app.reload(); | ||
| 127 | break; | ||
| 128 | case "reset": | ||
| 129 | if (confirm("Are you sure you want to reset DevDocs?")) { | ||
| 130 | app.reset(); | ||
| 131 | } | ||
| 132 | break; | ||
| 133 | case "accept-analytics": | ||
| 134 | Cookies.set("analyticsConsent", "1", { expires: 1e8 }) && app.reboot(); | ||
| 135 | break; | ||
| 136 | case "decline-analytics": | ||
| 137 | Cookies.set("analyticsConsent", "0", { expires: 1e8 }) && app.reboot(); | ||
| 138 | break; | ||
| 110 | } | 139 | } |
| 111 | } | 140 | } |
| 112 | }); | 141 | }); |
| @@ -13,25 +13,32 @@ const Cls = (app.views.Menu = class Menu extends app.View { · 12 unmodified lines | |||
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | static initClass() { | 15 | static initClass() { |
| 16 | this.el = '._menu'; | 16 | this.el = "._menu"; |
| 17 | this.activeClass = 'active'; | 17 | this.activeClass = "active"; |
| 18 | 18 | ||
| 19 | this.events = | 19 | this.events = { click: "onClick" }; |
| 20 | {click: 'onClick'}; | ||
| 21 | } | 20 | } |
| 22 | 21 | ||
| 23 | init() { | 22 | init() { |
| 24 | $.on(document.body, 'click', this.onGlobalClick); | 23 | $.on(document.body, "click", this.onGlobalClick); |
| 25 | } | 24 | } |
| 26 | 25 | ||
| 27 | onClick(event) { | 26 | onClick(event) { |
| 28 | const target = $.eventTarget(event); | 27 | const target = $.eventTarget(event); |
| 29 | if (target.tagName === 'A') { target.blur(); } | 28 | if (target.tagName === "A") { |
| 29 | target.blur(); | ||
| 30 | } | ||
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | onGlobalClick(event) { | 33 | onGlobalClick(event) { |
| 33 | if (event.which !== 1) { return; } | 34 | if (event.which !== 1) { |
| 34 | if (typeof event.target.hasAttribute === 'function' ? event.target.hasAttribute('data-toggle-menu') : undefined) { | 35 | return; |
| 36 | } | ||
| 37 | if ( | ||
| 38 | typeof event.target.hasAttribute === "function" | ||
| 39 | ? event.target.hasAttribute("data-toggle-menu") | ||
| 40 | : undefined | ||
| 41 | ) { | ||
| 35 | this.toggleClass(this.constructor.activeClass); | 42 | this.toggleClass(this.constructor.activeClass); |
| 36 | } else if (this.hasClass(this.constructor.activeClass)) { | 43 | } else if (this.hasClass(this.constructor.activeClass)) { |
| 37 | this.removeClass(this.constructor.activeClass); | 44 | this.removeClass(this.constructor.activeClass); |
| @@ -10,34 +10,36 @@ · 9 unmodified lines | |||
| 10 | */ | 10 | */ |
| 11 | const Cls = (app.views.Mobile = class Mobile extends app.View { | 11 | const Cls = (app.views.Mobile = class Mobile extends app.View { |
| 12 | static initClass() { | 12 | static initClass() { |
| 13 | this.className = '_mobile'; | 13 | this.className = "_mobile"; |
| 14 | 14 | ||
| 15 | this.elements = { | 15 | this.elements = { |
| 16 | body: 'body', | 16 | body: "body", |
| 17 | content: '._container', | 17 | content: "._container", |
| 18 | sidebar: '._sidebar', | 18 | sidebar: "._sidebar", |
| 19 | docPicker: '._settings ._sidebar' | 19 | docPicker: "._settings ._sidebar", |
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| 22 | this.shortcuts = | 22 | this.shortcuts = { escape: "onEscape" }; |
| 23 | {escape: 'onEscape'}; | 23 | |
| 24 | 24 | this.routes = { after: "afterRoute" }; | |
| 25 | this.routes = | ||
| 26 | {after: 'afterRoute'}; | ||
| 27 | } | 25 | } |
| 28 | 26 | ||
| 29 | static detect() { | 27 | static detect() { |
| 30 | if (Cookies.get('override-mobile-detect') != null) { | 28 | if (Cookies.get("override-mobile-detect") != null) { |
| 31 | return JSON.parse(Cookies.get('override-mobile-detect')); | 29 | return JSON.parse(Cookies.get("override-mobile-detect")); |
| 32 | } | 30 | } |
| 33 | try { | 31 | try { |
| 34 | return (window.matchMedia('(max-width: 480px)').matches) || | 32 | return ( |
| 35 | (window.matchMedia('(max-width: 767px)').matches) || | 33 | window.matchMedia("(max-width: 480px)").matches || |
| 36 | (window.matchMedia('(max-height: 767px) and (max-width: 1024px)').matches) || | 34 | window.matchMedia("(max-width: 767px)").matches || |
| 37 | // Need to sniff the user agent because some Android and Windows Phone devices don't take | 35 | window.matchMedia("(max-height: 767px) and (max-width: 1024px)") |
| 38 | // resolution (dpi) into account when reporting device width/height. | 36 | .matches || |
| 39 | ((navigator.userAgent.indexOf('Android') !== -1) && (navigator.userAgent.indexOf('Mobile') !== -1)) || | 37 | // Need to sniff the user agent because some Android and Windows Phone devices don't take |
| 40 | (navigator.userAgent.indexOf('IEMobile') !== -1); | 38 | // resolution (dpi) into account when reporting device width/height. |
| 39 | (navigator.userAgent.indexOf("Android") !== -1 && | ||
| 40 | navigator.userAgent.indexOf("Mobile") !== -1) || | ||
| 41 | navigator.userAgent.indexOf("IEMobile") !== -1 | ||
| 42 | ); | ||
| 41 | } catch (error) { | 43 | } catch (error) { |
| 42 | return false; | 44 | return false; |
| 43 | } | 45 | } |
| @@ -67,30 +69,29 @@ const Cls = (app.views.Mobile = class Mobile extends app.View { · 23 unmodified lines | |||
| 67 | } | 69 | } |
| 68 | 70 | ||
| 69 | init() { | 71 | init() { |
| 70 | $.on($('._search'), 'touchend', this.onTapSearch); | 72 | $.on($("._search"), "touchend", this.onTapSearch); |
| 71 | 73 | ||
| 72 | this.toggleSidebar = $('button[data-toggle-sidebar]'); | 74 | this.toggleSidebar = $("button[data-toggle-sidebar]"); |
| 73 | this.toggleSidebar.removeAttribute('hidden'); | 75 | this.toggleSidebar.removeAttribute("hidden"); |
| 74 | $.on(this.toggleSidebar, 'click', this.onClickToggleSidebar); | 76 | $.on(this.toggleSidebar, "click", this.onClickToggleSidebar); |
| 75 | 77 | ||
| 76 | this.back = $('button[data-back]'); | 78 | this.back = $("button[data-back]"); |
| 77 | this.back.removeAttribute('hidden'); | 79 | this.back.removeAttribute("hidden"); |
| 78 | $.on(this.back, 'click', this.onClickBack); | 80 | $.on(this.back, "click", this.onClickBack); |
| 79 | 81 | ||
| 80 | this.forward = $('button[data-forward]'); | 82 | this.forward = $("button[data-forward]"); |
| 81 | this.forward.removeAttribute('hidden'); | 83 | this.forward.removeAttribute("hidden"); |
| 82 | $.on(this.forward, 'click', this.onClickForward); | 84 | $.on(this.forward, "click", this.onClickForward); |
| 83 | 85 | ||
| 84 | this.docPickerTab = $('button[data-tab="doc-picker"]'); | 86 | this.docPickerTab = $('button[data-tab="doc-picker"]'); |
| 85 | this.docPickerTab.removeAttribute('hidden'); | 87 | this.docPickerTab.removeAttribute("hidden"); |
| 86 | $.on(this.docPickerTab, 'click', this.onClickDocPickerTab); | 88 | $.on(this.docPickerTab, "click", this.onClickDocPickerTab); |
| 87 | 89 | ||
| 88 | this.settingsTab = $('button[data-tab="settings"]'); | 90 | this.settingsTab = $('button[data-tab="settings"]'); |
| 89 | this.settingsTab.removeAttribute('hidden'); | 91 | this.settingsTab.removeAttribute("hidden"); |
| 90 | $.on(this.settingsTab, 'click', this.onClickSettingsTab); | 92 | $.on(this.settingsTab, "click", this.onClickSettingsTab); |
| 91 | 93 | ||
| 92 | app.document.sidebar.search | 94 | app.document.sidebar.search.on("searching", this.showSidebar); |
| 93 | .on('searching', this.showSidebar); | ||
| 94 | 95 | ||
| 95 | this.activate(); | 96 | this.activate(); |
| 96 | } | 97 | } |
| @@ -103,27 +104,36 @@ const Cls = (app.views.Mobile = class Mobile extends app.View { · 6 unmodified lines | |||
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | this.contentTop = window.scrollY; | 106 | this.contentTop = window.scrollY; |
| 106 | this.content.style.display = 'none'; | 107 | this.content.style.display = "none"; |
| 107 | this.sidebar.style.display = 'block'; | 108 | this.sidebar.style.display = "block"; |
| 108 | 109 | ||
| 109 | if (selection = this.findByClass(app.views.ListSelect.activeClass)) { | 110 | if ((selection = this.findByClass(app.views.ListSelect.activeClass))) { |
| 110 | const scrollContainer = window.scrollY === this.body.scrollTop ? this.body : document.documentElement; | 111 | const scrollContainer = |
| 111 | $.scrollTo(selection, scrollContainer, 'center'); | 112 | window.scrollY === this.body.scrollTop |
| 113 | ? this.body | ||
| 114 | : document.documentElement; | ||
| 115 | $.scrollTo(selection, scrollContainer, "center"); | ||
| 112 | } else { | 116 | } else { |
| 113 | window.scrollTo(0, (this.findByClass(app.views.ListFold.activeClass) && this.sidebarTop) || 0); | 117 | window.scrollTo( |
| 118 | 0, | ||
| 119 | (this.findByClass(app.views.ListFold.activeClass) && this.sidebarTop) || | ||
| 120 | 0, | ||
| 121 | ); | ||
| 114 | } | 122 | } |
| 115 | } | 123 | } |
| 116 | 124 | ||
| 117 | hideSidebar() { | 125 | hideSidebar() { |
| 118 | if (!this.isSidebarShown()) { return; } | 126 | if (!this.isSidebarShown()) { |
| 127 | return; | ||
| 128 | } | ||
| 119 | this.sidebarTop = window.scrollY; | 129 | this.sidebarTop = window.scrollY; |
| 120 | this.sidebar.style.display = 'none'; | 130 | this.sidebar.style.display = "none"; |
| 121 | this.content.style.display = 'block'; | 131 | this.content.style.display = "block"; |
| 122 | window.scrollTo(0, this.contentTop || 0); | 132 | window.scrollTo(0, this.contentTop || 0); |
| 123 | } | 133 | } |
| 124 | 134 | ||
| 125 | isSidebarShown() { | 135 | isSidebarShown() { |
| 126 | return this.sidebar.style.display !== 'none'; | 136 | return this.sidebar.style.display !== "none"; |
| 127 | } | 137 | } |
| 128 | 138 | ||
| 129 | onClickBack() { | 139 | onClickBack() { |
| @@ -135,7 +145,11 @@ const Cls = (app.views.Mobile = class Mobile extends app.View { · 5 unmodified lines | |||
| 135 | } | 145 | } |
| 136 | 146 | ||
| 137 | onClickToggleSidebar() { | 147 | onClickToggleSidebar() { |
| 138 | if (this.isSidebarShown()) { this.hideSidebar(); } else { this.showSidebar(); } | 148 | if (this.isSidebarShown()) { |
| 149 | this.hideSidebar(); | ||
| 150 | } else { | ||
| 151 | this.showSidebar(); | ||
| 152 | } | ||
| 139 | } | 153 | } |
| 140 | 154 | ||
| 141 | onClickDocPickerTab(event) { | 155 | onClickDocPickerTab(event) { |
| @@ -150,18 +164,18 @@ const Cls = (app.views.Mobile = class Mobile extends app.View { · 8 unmodified lines | |||
| 150 | 164 | ||
| 151 | showDocPicker() { | 165 | showDocPicker() { |
| 152 | window.scrollTo(0, 0); | 166 | window.scrollTo(0, 0); |
| 153 | this.docPickerTab.classList.add('active'); | 167 | this.docPickerTab.classList.add("active"); |
| 154 | this.settingsTab.classList.remove('active'); | 168 | this.settingsTab.classList.remove("active"); |
| 155 | this.docPicker.style.display = 'block'; | 169 | this.docPicker.style.display = "block"; |
| 156 | this.content.style.display = 'none'; | 170 | this.content.style.display = "none"; |
| 157 | } | 171 | } |
| 158 | 172 | ||
| 159 | showSettings() { | 173 | showSettings() { |
| 160 | window.scrollTo(0, 0); | 174 | window.scrollTo(0, 0); |
| 161 | this.docPickerTab.classList.remove('active'); | 175 | this.docPickerTab.classList.remove("active"); |
| 162 | this.settingsTab.classList.add('active'); | 176 | this.settingsTab.classList.add("active"); |
| 163 | this.docPicker.style.display = 'none'; | 177 | this.docPicker.style.display = "none"; |
| 164 | this.content.style.display = 'block'; | 178 | this.content.style.display = "block"; |
| 165 | } | 179 | } |
| 166 | 180 | ||
| 167 | onTapSearch() { | 181 | onTapSearch() { |
| @@ -175,22 +189,22 @@ const Cls = (app.views.Mobile = class Mobile extends app.View { · 7 unmodified lines | |||
| 175 | afterRoute(route) { | 189 | afterRoute(route) { |
| 176 | this.hideSidebar(); | 190 | this.hideSidebar(); |
| 177 | 191 | ||
| 178 | if (route === 'settings') { | 192 | if (route === "settings") { |
| 179 | this.showDocPicker(); | 193 | this.showDocPicker(); |
| 180 | } else { | 194 | } else { |
| 181 | this.content.style.display = 'block'; | 195 | this.content.style.display = "block"; |
| 182 | } | 196 | } |
| 183 | 197 | ||
| 184 | if (page.canGoBack()) { | 198 | if (page.canGoBack()) { |
| 185 | this.back.removeAttribute('disabled'); | 199 | this.back.removeAttribute("disabled"); |
| 186 | } else { | 200 | } else { |
| 187 | this.back.setAttribute('disabled', 'disabled'); | 201 | this.back.setAttribute("disabled", "disabled"); |
| 188 | } | 202 | } |
| 189 | 203 | ||
| 190 | if (page.canGoForward()) { | 204 | if (page.canGoForward()) { |
| 191 | this.forward.removeAttribute('disabled'); | 205 | this.forward.removeAttribute("disabled"); |
| 192 | } else { | 206 | } else { |
| 193 | this.forward.setAttribute('disabled', 'disabled'); | 207 | this.forward.setAttribute("disabled", "disabled"); |
| 194 | } | 208 | } |
| 195 | } | 209 | } |
| 196 | }); | 210 | }); |
| @@ -15,33 +15,36 @@ const Cls = (app.views.Path = class Path extends app.View { · 14 unmodified lines | |||
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | static initClass() { | 17 | static initClass() { |
| 18 | this.className = '_path'; | 18 | this.className = "_path"; |
| 19 | this.attributes = | 19 | this.attributes = { role: "complementary" }; |
| 20 | {role: 'complementary'}; | 20 | |
| 21 | 21 | this.events = { click: "onClick" }; | |
| 22 | this.events = | 22 | |
| 23 | {click: 'onClick'}; | 23 | this.routes = { after: "afterRoute" }; |
| 24 | |||
| 25 | this.routes = | ||
| 26 | {after: 'afterRoute'}; | ||
| 27 | } | 24 | } |
| 28 | 25 | ||
| 29 | render(...args) { | 26 | render(...args) { |
| 30 | this.html(this.tmpl('path', ...Array.from(args))); | 27 | this.html(this.tmpl("path", ...Array.from(args))); |
| 31 | this.show(); | 28 | this.show(); |
| 32 | } | 29 | } |
| 33 | 30 | ||
| 34 | show() { | 31 | show() { |
| 35 | if (!this.el.parentNode) { this.prependTo(app.el); } | 32 | if (!this.el.parentNode) { |
| 33 | this.prependTo(app.el); | ||
| 34 | } | ||
| 36 | } | 35 | } |
| 37 | 36 | ||
| 38 | hide() { | 37 | hide() { |
| 39 | if (this.el.parentNode) { $.remove(this.el); } | 38 | if (this.el.parentNode) { |
| 39 | $.remove(this.el); | ||
| 40 | } | ||
| 40 | } | 41 | } |
| 41 | 42 | ||
| 42 | onClick(event) { | 43 | onClick(event) { |
| 43 | let link; | 44 | let link; |
| 44 | if (link = $.closestLink(event.target, this.el)) { this.clicked = true; } | 45 | if ((link = $.closestLink(event.target, this.el))) { |
| 46 | this.clicked = true; | ||
| 47 | } | ||
| 45 | } | 48 | } |
| 46 | 49 | ||
| 47 | afterRoute(route, context) { | 50 | afterRoute(route, context) { |
| @@ -7,7 +7,7 @@ · 6 unmodified lines | |||
| 7 | * DS206: Consider reworking classes to avoid initClass | 7 | * DS206: Consider reworking classes to avoid initClass |
| 8 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 8 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 9 | */ | 9 | */ |
| 10 | (function() { | 10 | (function () { |
| 11 | let MIN = undefined; | 11 | let MIN = undefined; |
| 12 | let MAX = undefined; | 12 | let MAX = undefined; |
| 13 | const Cls = (app.views.Resizer = class Resizer extends app.View { | 13 | const Cls = (app.views.Resizer = class Resizer extends app.View { |
| @@ -19,54 +19,66 @@ · 5 unmodified lines | |||
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | static initClass() { | 21 | static initClass() { |
| 22 | this.className = '_resizer'; | 22 | this.className = "_resizer"; |
| 23 | 23 | ||
| 24 | this.events = { | 24 | this.events = { |
| 25 | dragstart: 'onDragStart', | 25 | dragstart: "onDragStart", |
| 26 | dragend: 'onDragEnd' | 26 | dragend: "onDragEnd", |
| 27 | }; | 27 | }; |
| 28 | 28 | ||
| 29 | MIN = 260; | 29 | MIN = 260; |
| 30 | MAX = 600; | 30 | MAX = 600; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | static isSupported() { | 33 | static isSupported() { |
| 34 | return 'ondragstart' in document.createElement('div') && !app.isMobile(); | 34 | return "ondragstart" in document.createElement("div") && !app.isMobile(); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | init() { | 37 | init() { |
| 38 | this.el.setAttribute('draggable', 'true'); | 38 | this.el.setAttribute("draggable", "true"); |
| 39 | this.appendTo($('._app')); | 39 | this.appendTo($("._app")); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | resize(value, save) { | 42 | resize(value, save) { |
| 43 | value -= app.el.offsetLeft; | 43 | value -= app.el.offsetLeft; |
| 44 | if (!(value > 0)) { return; } | 44 | if (!(value > 0)) { |
| 45 | return; | ||
| 46 | } | ||
| 45 | value = Math.min(Math.max(Math.round(value), MIN), MAX); | 47 | value = Math.min(Math.max(Math.round(value), MIN), MAX); |
| 46 | const newSize = `${value}px`; | 48 | const newSize = `${value}px`; |
| 47 | document.documentElement.style.setProperty('--sidebarWidth', newSize); | 49 | document.documentElement.style.setProperty("--sidebarWidth", newSize); |
| 48 | if (save) { app.settings.setSize(value); } | 50 | if (save) { |
| 51 | app.settings.setSize(value); | ||
| 52 | } | ||
| 49 | } | 53 | } |
| 50 | 54 | ||
| 51 | onDragStart(event) { | 55 | onDragStart(event) { |
| 52 | event.dataTransfer.effectAllowed = 'link'; | 56 | event.dataTransfer.effectAllowed = "link"; |
| 53 | event.dataTransfer.setData('Text', ''); | 57 | event.dataTransfer.setData("Text", ""); |
| 54 | $.on(window, 'dragover', this.onDrag); | 58 | $.on(window, "dragover", this.onDrag); |
| 55 | } | 59 | } |
| 56 | 60 | ||
| 57 | onDrag(event) { | 61 | onDrag(event) { |
| 58 | const value = event.pageX; | 62 | const value = event.pageX; |
| 59 | if (!(value > 0)) { return; } | 63 | if (!(value > 0)) { |
| 64 | return; | ||
| 65 | } | ||
| 60 | this.lastDragValue = value; | 66 | this.lastDragValue = value; |
| 61 | if (this.lastDrag && (this.lastDrag > (Date.now() - 50))) { return; } | 67 | if (this.lastDrag && this.lastDrag > Date.now() - 50) { |
| 68 | return; | ||
| 69 | } | ||
| 62 | this.lastDrag = Date.now(); | 70 | this.lastDrag = Date.now(); |
| 63 | this.resize(value, false); | 71 | this.resize(value, false); |
| 64 | } | 72 | } |
| 65 | 73 | ||
| 66 | onDragEnd(event) { | 74 | onDragEnd(event) { |
| 67 | $.off(window, 'dragover', this.onDrag); | 75 | $.off(window, "dragover", this.onDrag); |
| 68 | let value = event.pageX || (event.screenX - window.screenX); | 76 | let value = event.pageX || event.screenX - window.screenX; |
| 69 | if (this.lastDragValue && !(this.lastDragValue - 5 < value && value < this.lastDragValue + 5)) { // https://github.com/freeCodeCamp/devdocs/issues/265 | 77 | if ( |
| 78 | this.lastDragValue && | ||
| 79 | !(this.lastDragValue - 5 < value && value < this.lastDragValue + 5) | ||
| 80 | ) { | ||
| 81 | // https://github.com/freeCodeCamp/devdocs/issues/265 | ||
| 70 | value = this.lastDragValue; | 82 | value = this.lastDragValue; |
| 71 | } | 83 | } |
| 72 | this.resize(value, true); | 84 | this.resize(value, true); |
| @@ -10,7 +10,7 @@ · 9 unmodified lines | |||
| 10 | * DS207: Consider shorter variations of null checks | 10 | * DS207: Consider shorter variations of null checks |
| 11 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 11 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 12 | */ | 12 | */ |
| 13 | (function() { | 13 | (function () { |
| 14 | let SIDEBAR_HIDDEN_LAYOUT = undefined; | 14 | let SIDEBAR_HIDDEN_LAYOUT = undefined; |
| 15 | const Cls = (app.views.Settings = class Settings extends app.View { | 15 | const Cls = (app.views.Settings = class Settings extends app.View { |
| 16 | constructor(...args) { | 16 | constructor(...args) { |
| @@ -23,29 +23,28 @@ · 6 unmodified lines | |||
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | static initClass() { | 25 | static initClass() { |
| 26 | SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden'; | 26 | SIDEBAR_HIDDEN_LAYOUT = "_sidebar-hidden"; |
| 27 | 27 | ||
| 28 | this.el = '._settings'; | 28 | this.el = "._settings"; |
| 29 | 29 | ||
| 30 | this.elements = { | 30 | this.elements = { |
| 31 | sidebar: '._sidebar', | 31 | sidebar: "._sidebar", |
| 32 | saveBtn: 'button[type="submit"]', | 32 | saveBtn: 'button[type="submit"]', |
| 33 | backBtn: 'button[data-back]' | 33 | backBtn: "button[data-back]", |
| 34 | }; | 34 | }; |
| 35 | 35 | ||
| 36 | this.events = { | 36 | this.events = { |
| 37 | import: 'onImport', | 37 | import: "onImport", |
| 38 | change: 'onChange', | 38 | change: "onChange", |
| 39 | submit: 'onSubmit', | 39 | submit: "onSubmit", |
| 40 | click: 'onClick' | 40 | click: "onClick", |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | this.shortcuts = | 43 | this.shortcuts = { enter: "onEnter" }; |
| 44 | {enter: 'onEnter'}; | ||
| 45 | } | 44 | } |
| 46 | 45 | ||
| 47 | init() { | 46 | init() { |
| 48 | this.addSubview(this.docPicker = new app.views.DocPicker); | 47 | this.addSubview((this.docPicker = new app.views.DocPicker())); |
| 49 | } | 48 | } |
| 50 | 49 | ||
| 51 | activate() { | 50 | activate() { |
| @@ -59,18 +58,22 @@ · 7 unmodified lines | |||
| 59 | if (super.deactivate(...arguments)) { | 58 | if (super.deactivate(...arguments)) { |
| 60 | this.resetClass(); | 59 | this.resetClass(); |
| 61 | this.docPicker.detach(); | 60 | this.docPicker.detach(); |
| 62 | if (app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)) { document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT); } | 61 | if (app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)) { |
| 62 | document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT); | ||
| 63 | } | ||
| 63 | } | 64 | } |
| 64 | } | 65 | } |
| 65 | 66 | ||
| 66 | render() { | 67 | render() { |
| 67 | this.docPicker.appendTo(this.sidebar); | 68 | this.docPicker.appendTo(this.sidebar); |
| 68 | this.refreshElements(); | 69 | this.refreshElements(); |
| 69 | this.addClass('_in'); | 70 | this.addClass("_in"); |
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | save(options) { | 73 | save(options) { |
| 73 | if (options == null) { options = {}; } | 74 | if (options == null) { |
| 75 | options = {}; | ||
| 76 | } | ||
| 74 | if (!this.saving) { | 77 | if (!this.saving) { |
| 75 | let docs; | 78 | let docs; |
| 76 | this.saving = true; | 79 | this.saving = true; |
| @@ -82,16 +85,19 @@ · 5 unmodified lines | |||
| 82 | app.settings.setDocs(docs); | 85 | app.settings.setDocs(docs); |
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | this.saveBtn.textContent = 'Saving\u2026'; | 88 | this.saveBtn.textContent = "Saving\u2026"; |
| 86 | const disabledDocs = new app.collections.Docs((() => { | 89 | const disabledDocs = new app.collections.Docs( |
| 87 | const result = []; | 90 | (() => { |
| 88 | for (var doc of Array.from(app.docs.all())) { if (docs.indexOf(doc.slug) === -1) { | 91 | const result = []; |
| 89 | result.push(doc); | 92 | for (var doc of Array.from(app.docs.all())) { |
| 93 | if (docs.indexOf(doc.slug) === -1) { | ||
| 94 | result.push(doc); | ||
| 95 | } | ||
| 90 | } | 96 | } |
| 91 | } | 97 | return result; |
| 92 | return result; | 98 | })(), |
| 93 | })()); | 99 | ); |
| 94 | disabledDocs.uninstall(function() { | 100 | disabledDocs.uninstall(function () { |
| 95 | app.db.migrate(); | 101 | app.db.migrate(); |
| 96 | return app.reload(); | 102 | return app.reload(); |
| 97 | }); | 103 | }); |
| @@ -99,7 +105,7 @@ · 1 unmodified line | |||
| 99 | } | 105 | } |
| 100 | 106 | ||
| 101 | onChange() { | 107 | onChange() { |
| 102 | this.addClass('_dirty'); | 108 | this.addClass("_dirty"); |
| 103 | } | 109 | } |
| 104 | 110 | ||
| 105 | onEnter() { | 111 | onEnter() { |
| @@ -112,15 +118,17 @@ · 6 unmodified lines | |||
| 112 | } | 118 | } |
| 113 | 119 | ||
| 114 | onImport() { | 120 | onImport() { |
| 115 | this.addClass('_dirty'); | 121 | this.addClass("_dirty"); |
| 116 | this.save({import: true}); | 122 | this.save({ import: true }); |
| 117 | } | 123 | } |
| 118 | 124 | ||
| 119 | onClick(event) { | 125 | onClick(event) { |
| 120 | if (event.which !== 1) { return; } | 126 | if (event.which !== 1) { |
| 127 | return; | ||
| 128 | } | ||
| 121 | if (event.target === this.backBtn) { | 129 | if (event.target === this.backBtn) { |
| 122 | $.stopEvent(event); | 130 | $.stopEvent(event); |
| 123 | app.router.show('/'); | 131 | app.router.show("/"); |
| 124 | } | 132 | } |
| 125 | } | 133 | } |
| 126 | }); | 134 | }); |
| @@ -10,18 +10,17 @@ · 9 unmodified lines | |||
| 10 | */ | 10 | */ |
| 11 | const Cls = (app.views.ListFocus = class ListFocus extends app.View { | 11 | const Cls = (app.views.ListFocus = class ListFocus extends app.View { |
| 12 | static initClass() { | 12 | static initClass() { |
| 13 | this.activeClass = 'focus'; | 13 | this.activeClass = "focus"; |
| 14 | 14 | ||
| 15 | this.events = | 15 | this.events = { click: "onClick" }; |
| 16 | {click: 'onClick'}; | 16 | |
| 17 | |||
| 18 | this.shortcuts = { | 17 | this.shortcuts = { |
| 19 | up: 'onUp', | 18 | up: "onUp", |
| 20 | down: 'onDown', | 19 | down: "onDown", |
| 21 | left: 'onLeft', | 20 | left: "onLeft", |
| 22 | enter: 'onEnter', | 21 | enter: "onEnter", |
| 23 | superEnter: 'onSuperEnter', | 22 | superEnter: "onSuperEnter", |
| 24 | escape: 'blur' | 23 | escape: "blur", |
| 25 | }; | 24 | }; |
| 26 | } | 25 | } |
| 27 | 26 | ||
| @@ -39,41 +38,51 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View { · 11 unmodified lines | |||
| 39 | } | 38 | } |
| 40 | 39 | ||
| 41 | focus(el, options) { | 40 | focus(el, options) { |
| 42 | if (options == null) { options = {}; } | 41 | if (options == null) { |
| 42 | options = {}; | ||
| 43 | } | ||
| 43 | if (el && !el.classList.contains(this.constructor.activeClass)) { | 44 | if (el && !el.classList.contains(this.constructor.activeClass)) { |
| 44 | this.blur(); | 45 | this.blur(); |
| 45 | el.classList.add(this.constructor.activeClass); | 46 | el.classList.add(this.constructor.activeClass); |
| 46 | if (options.silent !== true) { $.trigger(el, 'focus'); } | 47 | if (options.silent !== true) { |
| 48 | $.trigger(el, "focus"); | ||
| 49 | } | ||
| 47 | } | 50 | } |
| 48 | } | 51 | } |
| 49 | 52 | ||
| 50 | blur() { | 53 | blur() { |
| 51 | let cursor; | 54 | let cursor; |
| 52 | if (cursor = this.getCursor()) { | 55 | if ((cursor = this.getCursor())) { |
| 53 | cursor.classList.remove(this.constructor.activeClass); | 56 | cursor.classList.remove(this.constructor.activeClass); |
| 54 | $.trigger(cursor, 'blur'); | 57 | $.trigger(cursor, "blur"); |
| 55 | } | 58 | } |
| 56 | } | 59 | } |
| 57 | 60 | ||
| 58 | getCursor() { | 61 | getCursor() { |
| 59 | return this.findByClass(this.constructor.activeClass) || this.findByClass(app.views.ListSelect.activeClass); | 62 | return ( |
| 63 | this.findByClass(this.constructor.activeClass) || | ||
| 64 | this.findByClass(app.views.ListSelect.activeClass) | ||
| 65 | ); | ||
| 60 | } | 66 | } |
| 61 | 67 | ||
| 62 | findNext(cursor) { | 68 | findNext(cursor) { |
| 63 | let next; | 69 | let next; |
| 64 | if (next = cursor.nextSibling) { | 70 | if ((next = cursor.nextSibling)) { |
| 65 | if (next.tagName === 'A') { | 71 | if (next.tagName === "A") { |
| 66 | return next; | 72 | return next; |
| 67 | } else if (next.tagName === 'SPAN') { // pagination link | 73 | } else if (next.tagName === "SPAN") { |
| 74 | // pagination link | ||
| 68 | $.click(next); | 75 | $.click(next); |
| 69 | return this.findNext(cursor); | 76 | return this.findNext(cursor); |
| 70 | } else if (next.tagName === 'DIV') { // sub-list | 77 | } else if (next.tagName === "DIV") { |
| 71 | if (cursor.className.indexOf(' open') >= 0) { | 78 | // sub-list |
| 79 | if (cursor.className.indexOf(" open") >= 0) { | ||
| 72 | return this.findFirst(next) || this.findNext(next); | 80 | return this.findFirst(next) || this.findNext(next); |
| 73 | } else { | 81 | } else { |
| 74 | return this.findNext(next); | 82 | return this.findNext(next); |
| 75 | } | 83 | } |
| 76 | } else if (next.tagName === 'H6') { // title | 84 | } else if (next.tagName === "H6") { |
| 85 | // title | ||
| 77 | return this.findNext(next); | 86 | return this.findNext(next); |
| 78 | } | 87 | } |
| 79 | } else if (cursor.parentNode !== this.el) { | 88 | } else if (cursor.parentNode !== this.el) { |
| @@ -83,11 +92,14 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View { · 3 unmodified lines | |||
| 83 | 92 | ||
| 84 | findFirst(cursor) { | 93 | findFirst(cursor) { |
| 85 | let first; | 94 | let first; |
| 86 | if (!(first = cursor.firstChild)) { return; } | 95 | if (!(first = cursor.firstChild)) { |
| 96 | return; | ||
| 97 | } | ||
| 87 | 98 | ||
| 88 | if (first.tagName === 'A') { | 99 | if (first.tagName === "A") { |
| 89 | return first; | 100 | return first; |
| 90 | } else if (first.tagName === 'SPAN') { // pagination link | 101 | } else if (first.tagName === "SPAN") { |
| 102 | // pagination link | ||
| 91 | $.click(first); | 103 | $.click(first); |
| 92 | return this.findFirst(cursor); | 104 | return this.findFirst(cursor); |
| 93 | } | 105 | } |
| @@ -95,19 +107,22 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View { · 1 unmodified line | |||
| 95 | 107 | ||
| 96 | findPrev(cursor) { | 108 | findPrev(cursor) { |
| 97 | let prev; | 109 | let prev; |
| 98 | if (prev = cursor.previousSibling) { | 110 | if ((prev = cursor.previousSibling)) { |
| 99 | if (prev.tagName === 'A') { | 111 | if (prev.tagName === "A") { |
| 100 | return prev; | 112 | return prev; |
| 101 | } else if (prev.tagName === 'SPAN') { // pagination link | 113 | } else if (prev.tagName === "SPAN") { |
| 114 | // pagination link | ||
| 102 | $.click(prev); | 115 | $.click(prev); |
| 103 | return this.findPrev(cursor); | 116 | return this.findPrev(cursor); |
| 104 | } else if (prev.tagName === 'DIV') { // sub-list | 117 | } else if (prev.tagName === "DIV") { |
| 105 | if (prev.previousSibling.className.indexOf('open') >= 0) { | 118 | // sub-list |
| 119 | if (prev.previousSibling.className.indexOf("open") >= 0) { | ||
| 106 | return this.findLast(prev) || this.findPrev(prev); | 120 | return this.findLast(prev) || this.findPrev(prev); |
| 107 | } else { | 121 | } else { |
| 108 | return this.findPrev(prev); | 122 | return this.findPrev(prev); |
| 109 | } | 123 | } |
| 110 | } else if (prev.tagName === 'H6') { // title | 124 | } else if (prev.tagName === "H6") { |
| 125 | // title | ||
| 111 | return this.findPrev(prev); | 126 | return this.findPrev(prev); |
| 112 | } | 127 | } |
| 113 | } else if (cursor.parentNode !== this.el) { | 128 | } else if (cursor.parentNode !== this.el) { |
| @@ -117,13 +132,17 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View { · 3 unmodified lines | |||
| 117 | 132 | ||
| 118 | findLast(cursor) { | 133 | findLast(cursor) { |
| 119 | let last; | 134 | let last; |
| 120 | if (!(last = cursor.lastChild)) { return; } | 135 | if (!(last = cursor.lastChild)) { |
| 136 | return; | ||
| 137 | } | ||
| 121 | 138 | ||
| 122 | if (last.tagName === 'A') { | 139 | if (last.tagName === "A") { |
| 123 | return last; | 140 | return last; |
| 124 | } else if ((last.tagName === 'SPAN') || (last.tagName === 'H6')) { // pagination link or title | 141 | } else if (last.tagName === "SPAN" || last.tagName === "H6") { |
| 142 | // pagination link or title | ||
| 125 | return this.findPrev(last); | 143 | return this.findPrev(last); |
| 126 | } else if (last.tagName === 'DIV') { // sub-list | 144 | } else if (last.tagName === "DIV") { |
| 145 | // sub-list | ||
| 127 | return this.findLast(last); | 146 | return this.findLast(last); |
| 128 | } | 147 | } |
| 129 | } | 148 | } |
| @@ -133,7 +152,7 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View { · 3 unmodified lines | |||
| 133 | if ((cursor = this.getCursor())) { | 152 | if ((cursor = this.getCursor())) { |
| 134 | this.focusOnNextFrame(this.findNext(cursor)); | 153 | this.focusOnNextFrame(this.findNext(cursor)); |
| 135 | } else { | 154 | } else { |
| 136 | this.focusOnNextFrame(this.findByTag('a')); | 155 | this.focusOnNextFrame(this.findByTag("a")); |
| 137 | } | 156 | } |
| 138 | } | 157 | } |
| 139 | 158 | ||
| @@ -142,37 +161,45 @@ const Cls = (app.views.ListFocus = class ListFocus extends app.View { · 2 unmodified lines | |||
| 142 | if ((cursor = this.getCursor())) { | 161 | if ((cursor = this.getCursor())) { |
| 143 | this.focusOnNextFrame(this.findPrev(cursor)); | 162 | this.focusOnNextFrame(this.findPrev(cursor)); |
| 144 | } else { | 163 | } else { |
| 145 | this.focusOnNextFrame(this.findLastByTag('a')); | 164 | this.focusOnNextFrame(this.findLastByTag("a")); |
| 146 | } | 165 | } |
| 147 | } | 166 | } |
| 148 | 167 | ||
| 149 | onLeft() { | 168 | onLeft() { |
| 150 | const cursor = this.getCursor(); | 169 | const cursor = this.getCursor(); |
| 151 | if (cursor && !cursor.classList.contains(app.views.ListFold.activeClass) && (cursor.parentNode !== this.el)) { | 170 | if ( |
| 171 | cursor && | ||
| 172 | !cursor.classList.contains(app.views.ListFold.activeClass) && | ||
| 173 | cursor.parentNode !== this.el | ||
| 174 | ) { | ||
| 152 | const prev = cursor.parentNode.previousSibling; | 175 | const prev = cursor.parentNode.previousSibling; |
| 153 | if (prev && prev.classList.contains(app.views.ListFold.targetClass)) { this.focusOnNextFrame(cursor.parentNode.previousSibling); } | 176 | if (prev && prev.classList.contains(app.views.ListFold.targetClass)) { |
| 177 | this.focusOnNextFrame(cursor.parentNode.previousSibling); | ||
| 178 | } | ||
| 154 | } | 179 | } |
| 155 | } | 180 | } |
| 156 | 181 | ||
| 157 | onEnter() { | 182 | onEnter() { |
| 158 | let cursor; | 183 | let cursor; |
| 159 | if (cursor = this.getCursor()) { | 184 | if ((cursor = this.getCursor())) { |
| 160 | $.click(cursor); | 185 | $.click(cursor); |
| 161 | } | 186 | } |
| 162 | } | 187 | } |
| 163 | 188 | ||
| 164 | onSuperEnter() { | 189 | onSuperEnter() { |
| 165 | let cursor; | 190 | let cursor; |
| 166 | if (cursor = this.getCursor()) { | 191 | if ((cursor = this.getCursor())) { |
| 167 | $.popup(cursor); | 192 | $.popup(cursor); |
| 168 | } | 193 | } |
| 169 | } | 194 | } |
| 170 | 195 | ||
| 171 | onClick(event) { | 196 | onClick(event) { |
| 172 | if ((event.which !== 1) || event.metaKey || event.ctrlKey) { return; } | 197 | if (event.which !== 1 || event.metaKey || event.ctrlKey) { |
| 198 | return; | ||
| 199 | } | ||
| 173 | const target = $.eventTarget(event); | 200 | const target = $.eventTarget(event); |
| 174 | if (target.tagName === 'A') { | 201 | if (target.tagName === "A") { |
| 175 | this.focus(target, {silent: true}); | 202 | this.focus(target, { silent: true }); |
| 176 | } | 203 | } |
| 177 | } | 204 | } |
| 178 | }); | 205 | }); |
| @@ -10,32 +10,37 @@ · 9 unmodified lines | |||
| 10 | */ | 10 | */ |
| 11 | const Cls = (app.views.ListFold = class ListFold extends app.View { | 11 | const Cls = (app.views.ListFold = class ListFold extends app.View { |
| 12 | static initClass() { | 12 | static initClass() { |
| 13 | this.targetClass = '_list-dir'; | 13 | this.targetClass = "_list-dir"; |
| 14 | this.handleClass = '_list-arrow'; | 14 | this.handleClass = "_list-arrow"; |
| 15 | this.activeClass = 'open'; | 15 | this.activeClass = "open"; |
| 16 | 16 | ||
| 17 | this.events = | 17 | this.events = { click: "onClick" }; |
| 18 | {click: 'onClick'}; | 18 | |
| 19 | |||
| 20 | this.shortcuts = { | 19 | this.shortcuts = { |
| 21 | left: 'onLeft', | 20 | left: "onLeft", |
| 22 | right: 'onRight' | 21 | right: "onRight", |
| 23 | }; | 22 | }; |
| 24 | } | 23 | } |
| 25 | 24 | ||
| 26 | constructor(el) { this.onLeft = this.onLeft.bind(this); this.onRight = this.onRight.bind(this); this.onClick = this.onClick.bind(this); this.el = el; super(...arguments); } | 25 | constructor(el) { |
| 26 | this.onLeft = this.onLeft.bind(this); | ||
| 27 | this.onRight = this.onRight.bind(this); | ||
| 28 | this.onClick = this.onClick.bind(this); | ||
| 29 | this.el = el; | ||
| 30 | super(...arguments); | ||
| 31 | } | ||
| 27 | 32 | ||
| 28 | open(el) { | 33 | open(el) { |
| 29 | if (el && !el.classList.contains(this.constructor.activeClass)) { | 34 | if (el && !el.classList.contains(this.constructor.activeClass)) { |
| 30 | el.classList.add(this.constructor.activeClass); | 35 | el.classList.add(this.constructor.activeClass); |
| 31 | $.trigger(el, 'open'); | 36 | $.trigger(el, "open"); |
| 32 | } | 37 | } |
| 33 | } | 38 | } |
| 34 | 39 | ||
| 35 | close(el) { | 40 | close(el) { |
| 36 | if (el && el.classList.contains(this.constructor.activeClass)) { | 41 | if (el && el.classList.contains(this.constructor.activeClass)) { |
| 37 | el.classList.remove(this.constructor.activeClass); | 42 | el.classList.remove(this.constructor.activeClass); |
| 38 | $.trigger(el, 'close'); | 43 | $.trigger(el, "close"); |
| 39 | } | 44 | } |
| 40 | } | 45 | } |
| 41 | 46 | ||
| @@ -55,36 +60,55 @@ const Cls = (app.views.ListFold = class ListFold extends app.View { · 13 unmodified lines | |||
| 55 | } | 60 | } |
| 56 | 61 | ||
| 57 | getCursor() { | 62 | getCursor() { |
| 58 | return this.findByClass(app.views.ListFocus.activeClass) || this.findByClass(app.views.ListSelect.activeClass); | 63 | return ( |
| 64 | this.findByClass(app.views.ListFocus.activeClass) || | ||
| 65 | this.findByClass(app.views.ListSelect.activeClass) | ||
| 66 | ); | ||
| 59 | } | 67 | } |
| 60 | 68 | ||
| 61 | onLeft() { | 69 | onLeft() { |
| 62 | const cursor = this.getCursor(); | 70 | const cursor = this.getCursor(); |
| 63 | if (cursor != null ? cursor.classList.contains(this.constructor.activeClass) : undefined) { | 71 | if ( |
| 72 | cursor != null | ||
| 73 | ? cursor.classList.contains(this.constructor.activeClass) | ||
| 74 | : undefined | ||
| 75 | ) { | ||
| 64 | this.close(cursor); | 76 | this.close(cursor); |
| 65 | } | 77 | } |
| 66 | } | 78 | } |
| 67 | 79 | ||
| 68 | onRight() { | 80 | onRight() { |
| 69 | const cursor = this.getCursor(); | 81 | const cursor = this.getCursor(); |
| 70 | if (cursor != null ? cursor.classList.contains(this.constructor.targetClass) : undefined) { | 82 | if ( |
| 83 | cursor != null | ||
| 84 | ? cursor.classList.contains(this.constructor.targetClass) | ||
| 85 | : undefined | ||
| 86 | ) { | ||
| 71 | this.open(cursor); | 87 | this.open(cursor); |
| 72 | } | 88 | } |
| 73 | } | 89 | } |
| 74 | 90 | ||
| 75 | onClick(event) { | 91 | onClick(event) { |
| 76 | if ((event.which !== 1) || event.metaKey || event.ctrlKey) { return; } | 92 | if (event.which !== 1 || event.metaKey || event.ctrlKey) { |
| 77 | if (!event.pageY) { return; } // ignore fabricated clicks | 93 | return; |
| 94 | } | ||
| 95 | if (!event.pageY) { | ||
| 96 | return; | ||
| 97 | } // ignore fabricated clicks | ||
| 78 | let el = $.eventTarget(event); | 98 | let el = $.eventTarget(event); |
| 79 | if (el.parentNode.tagName.toUpperCase() === 'SVG') { el = el.parentNode; } | 99 | if (el.parentNode.tagName.toUpperCase() === "SVG") { |
| 100 | el = el.parentNode; | ||
| 101 | } | ||
| 80 | 102 | ||
| 81 | if (el.classList.contains(this.constructor.handleClass)) { | 103 | if (el.classList.contains(this.constructor.handleClass)) { |
| 82 | $.stopEvent(event); | 104 | $.stopEvent(event); |
| 83 | this.toggle(el.parentNode); | 105 | this.toggle(el.parentNode); |
| 84 | } else if (el.classList.contains(this.constructor.targetClass)) { | 106 | } else if (el.classList.contains(this.constructor.targetClass)) { |
| 85 | if (el.hasAttribute('href')) { | 107 | if (el.hasAttribute("href")) { |
| 86 | if (el.classList.contains(this.constructor.activeClass)) { | 108 | if (el.classList.contains(this.constructor.activeClass)) { |
| 87 | if (el.classList.contains(app.views.ListSelect.activeClass)) { this.close(el); } | 109 | if (el.classList.contains(app.views.ListSelect.activeClass)) { |
| 110 | this.close(el); | ||
| 111 | } | ||
| 88 | } else { | 112 | } else { |
| 89 | this.open(el); | 113 | this.open(el); |
| 90 | } | 114 | } |
| @@ -10,36 +10,43 @@ · 9 unmodified lines | |||
| 10 | */ | 10 | */ |
| 11 | const Cls = (app.views.ListSelect = class ListSelect extends app.View { | 11 | const Cls = (app.views.ListSelect = class ListSelect extends app.View { |
| 12 | static initClass() { | 12 | static initClass() { |
| 13 | this.activeClass = 'active'; | 13 | this.activeClass = "active"; |
| 14 | 14 | ||
| 15 | this.events = | 15 | this.events = { click: "onClick" }; |
| 16 | {click: 'onClick'}; | ||
| 17 | } | 16 | } |
| 18 | 17 | ||
| 19 | constructor(el) { this.onClick = this.onClick.bind(this); this.el = el; super(...arguments); } | 18 | constructor(el) { |
| 19 | this.onClick = this.onClick.bind(this); | ||
| 20 | this.el = el; | ||
| 21 | super(...arguments); | ||
| 22 | } | ||
| 20 | 23 | ||
| 21 | deactivate() { | 24 | deactivate() { |
| 22 | if (super.deactivate(...arguments)) { this.deselect(); } | 25 | if (super.deactivate(...arguments)) { |
| 26 | this.deselect(); | ||
| 27 | } | ||
| 23 | } | 28 | } |
| 24 | 29 | ||
| 25 | select(el) { | 30 | select(el) { |
| 26 | this.deselect(); | 31 | this.deselect(); |
| 27 | if (el) { | 32 | if (el) { |
| 28 | el.classList.add(this.constructor.activeClass); | 33 | el.classList.add(this.constructor.activeClass); |
| 29 | $.trigger(el, 'select'); | 34 | $.trigger(el, "select"); |
| 30 | } | 35 | } |
| 31 | } | 36 | } |
| 32 | 37 | ||
| 33 | deselect() { | 38 | deselect() { |
| 34 | let selection; | 39 | let selection; |
| 35 | if (selection = this.getSelection()) { | 40 | if ((selection = this.getSelection())) { |
| 36 | selection.classList.remove(this.constructor.activeClass); | 41 | selection.classList.remove(this.constructor.activeClass); |
| 37 | $.trigger(selection, 'deselect'); | 42 | $.trigger(selection, "deselect"); |
| 38 | } | 43 | } |
| 39 | } | 44 | } |
| 40 | 45 | ||
| 41 | selectByHref(href) { | 46 | selectByHref(href) { |
| 42 | if (__guard__(this.getSelection(), x => x.getAttribute('href')) !== href) { | 47 | if ( |
| 48 | __guard__(this.getSelection(), (x) => x.getAttribute("href")) !== href | ||
| 49 | ) { | ||
| 43 | this.select(this.find(`a[href='${href}']`)); | 50 | this.select(this.find(`a[href='${href}']`)); |
| 44 | } | 51 | } |
| 45 | } | 52 | } |
| @@ -53,9 +60,11 @@ const Cls = (app.views.ListSelect = class ListSelect extends app.View { · 7 unmodified lines | |||
| 53 | } | 60 | } |
| 54 | 61 | ||
| 55 | onClick(event) { | 62 | onClick(event) { |
| 56 | if ((event.which !== 1) || event.metaKey || event.ctrlKey) { return; } | 63 | if (event.which !== 1 || event.metaKey || event.ctrlKey) { |
| 64 | return; | ||
| 65 | } | ||
| 57 | const target = $.eventTarget(event); | 66 | const target = $.eventTarget(event); |
| 58 | if (target.tagName === 'A') { | 67 | if (target.tagName === "A") { |
| 59 | this.select(target); | 68 | this.select(target); |
| 60 | } | 69 | } |
| 61 | } | 70 | } |
| @@ -63,5 +72,7 @@ const Cls = (app.views.ListSelect = class ListSelect extends app.View { · 1 unmodified line | |||
| 63 | Cls.initClass(); | 72 | Cls.initClass(); |
| 64 | 73 | ||
| 65 | function __guard__(value, transform) { | 74 | function __guard__(value, transform) { |
| 66 | return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; | 75 | return typeof value !== "undefined" && value !== null |
| 67 | } | 76 | ? transform(value) |
| 77 | : undefined; | ||
| 78 | } | ||
| @@ -10,7 +10,7 @@ · 9 unmodified lines | |||
| 10 | * DS207: Consider shorter variations of null checks | 10 | * DS207: Consider shorter variations of null checks |
| 11 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 11 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 12 | */ | 12 | */ |
| 13 | (function() { | 13 | (function () { |
| 14 | let PER_PAGE = undefined; | 14 | let PER_PAGE = undefined; |
| 15 | const Cls = (app.views.PaginatedList = class PaginatedList extends app.View { | 15 | const Cls = (app.views.PaginatedList = class PaginatedList extends app.View { |
| 16 | static initClass() { | 16 | static initClass() { |
| @@ -21,7 +21,12 @@ · 4 unmodified lines | |||
| 21 | let base; | 21 | let base; |
| 22 | this.onClick = this.onClick.bind(this); | 22 | this.onClick = this.onClick.bind(this); |
| 23 | this.data = data; | 23 | this.data = data; |
| 24 | if (((base = this.constructor.events || (this.constructor.events = {}))).click == null) { base.click = 'onClick'; } | 24 | if ( |
| 25 | (base = this.constructor.events || (this.constructor.events = {})) | ||
| 26 | .click == null | ||
| 27 | ) { | ||
| 28 | base.click = "onClick"; | ||
| 29 | } | ||
| 25 | super(...arguments); | 30 | super(...arguments); |
| 26 | } | 31 | } |
| 27 | 32 | ||
| @@ -42,11 +47,13 @@ · 14 unmodified lines | |||
| 42 | } | 47 | } |
| 43 | 48 | ||
| 44 | renderPage(page) { | 49 | renderPage(page) { |
| 45 | return this.render(this.data.slice(((page - 1) * PER_PAGE), (page * PER_PAGE))); | 50 | return this.render( |
| 51 | this.data.slice((page - 1) * PER_PAGE, page * PER_PAGE), | ||
| 52 | ); | ||
| 46 | } | 53 | } |
| 47 | 54 | ||
| 48 | renderPageLink(count) { | 55 | renderPageLink(count) { |
| 49 | return this.tmpl('sidebarPageLink', count); | 56 | return this.tmpl("sidebarPageLink", count); |
| 50 | } | 57 | } |
| 51 | 58 | ||
| 52 | renderPrevLink(page) { | 59 | renderPrevLink(page) { |
| @@ -54,7 +61,7 @@ · 1 unmodified line | |||
| 54 | } | 61 | } |
| 55 | 62 | ||
| 56 | renderNextLink(page) { | 63 | renderNextLink(page) { |
| 57 | return this.renderPageLink(this.data.length - (page * PER_PAGE)); | 64 | return this.renderPageLink(this.data.length - page * PER_PAGE); |
| 58 | } | 65 | } |
| 59 | 66 | ||
| 60 | totalPages() { | 67 | totalPages() { |
| @@ -64,17 +71,27 @@ · 3 unmodified lines | |||
| 64 | paginate(link) { | 71 | paginate(link) { |
| 65 | $.lockScroll(link.nextSibling || link.previousSibling, () => { | 72 | $.lockScroll(link.nextSibling || link.previousSibling, () => { |
| 66 | $.batchUpdate(this.el, () => { | 73 | $.batchUpdate(this.el, () => { |
| 67 | if (link.nextSibling) { this.paginatePrev(link); } else { this.paginateNext(link); } | 74 | if (link.nextSibling) { |
| 75 | this.paginatePrev(link); | ||
| 76 | } else { | ||
| 77 | this.paginateNext(link); | ||
| 78 | } | ||
| 68 | }); | 79 | }); |
| 69 | }); | 80 | }); |
| 70 | } | 81 | } |
| 71 | 82 | ||
| 72 | paginateNext() { | 83 | paginateNext() { |
| 73 | if (this.el.lastChild) { this.remove(this.el.lastChild); } // remove link | 84 | if (this.el.lastChild) { |
| 74 | if (this.page >= 2) { this.hideTopPage(); } // keep previous page into view | 85 | this.remove(this.el.lastChild); |
| 86 | } // remove link | ||
| 87 | if (this.page >= 2) { | ||
| 88 | this.hideTopPage(); | ||
| 89 | } // keep previous page into view | ||
| 75 | this.page++; | 90 | this.page++; |
| 76 | this.append(this.renderPage(this.page)); | 91 | this.append(this.renderPage(this.page)); |
| 77 | if (this.page < this.totalPages()) { this.append(this.renderNextLink(this.page)); } | 92 | if (this.page < this.totalPages()) { |
| 93 | this.append(this.renderNextLink(this.page)); | ||
| 94 | } | ||
| 78 | } | 95 | } |
| 79 | 96 | ||
| 80 | paginatePrev() { | 97 | paginatePrev() { |
| @@ -82,37 +99,55 @@ · 1 unmodified line | |||
| 82 | this.hideBottomPage(); | 99 | this.hideBottomPage(); |
| 83 | this.page--; | 100 | this.page--; |
| 84 | this.prepend(this.renderPage(this.page - 1)); // previous page is offset by one | 101 | this.prepend(this.renderPage(this.page - 1)); // previous page is offset by one |
| 85 | if (this.page >= 3) { this.prepend(this.renderPrevLink(this.page - 1)); } | 102 | if (this.page >= 3) { |
| 103 | this.prepend(this.renderPrevLink(this.page - 1)); | ||
| 104 | } | ||
| 86 | } | 105 | } |
| 87 | 106 | ||
| 88 | paginateTo(object) { | 107 | paginateTo(object) { |
| 89 | const index = this.data.indexOf(object); | 108 | const index = this.data.indexOf(object); |
| 90 | if (index >= PER_PAGE) { | 109 | if (index >= PER_PAGE) { |
| 91 | for (let i = 0, end = Math.floor(index / PER_PAGE), asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { this.paginateNext(); } | 110 | for ( |
| 111 | let i = 0, end = Math.floor(index / PER_PAGE), asc = 0 <= end; | ||
| 112 | asc ? i < end : i > end; | ||
| 113 | asc ? i++ : i-- | ||
| 114 | ) { | ||
| 115 | this.paginateNext(); | ||
| 116 | } | ||
| 92 | } | 117 | } |
| 93 | } | 118 | } |
| 94 | 119 | ||
| 95 | hideTopPage() { | 120 | hideTopPage() { |
| 96 | const n = this.page <= 2 ? | 121 | const n = this.page <= 2 ? PER_PAGE : PER_PAGE + 1; // remove link |
| 97 | PER_PAGE | 122 | for ( |
| 98 | : | 123 | let i = 0, end = n, asc = 0 <= end; |
| 99 | PER_PAGE + 1; // remove link | 124 | asc ? i < end : i > end; |
| 100 | for (let i = 0, end = n, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { this.remove(this.el.firstChild); } | 125 | asc ? i++ : i-- |
| 126 | ) { | ||
| 127 | this.remove(this.el.firstChild); | ||
| 128 | } | ||
| 101 | this.prepend(this.renderPrevLink(this.page)); | 129 | this.prepend(this.renderPrevLink(this.page)); |
| 102 | } | 130 | } |
| 103 | 131 | ||
| 104 | hideBottomPage() { | 132 | hideBottomPage() { |
| 105 | const n = this.page === this.totalPages() ? | 133 | const n = |
| 106 | (this.data.length % PER_PAGE) || PER_PAGE | 134 | this.page === this.totalPages() |
| 107 | : | 135 | ? this.data.length % PER_PAGE || PER_PAGE |
| 108 | PER_PAGE + 1; // remove link | 136 | : PER_PAGE + 1; // remove link |
| 109 | for (let i = 0, end = n, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { this.remove(this.el.lastChild); } | 137 | for ( |
| 138 | let i = 0, end = n, asc = 0 <= end; | ||
| 139 | asc ? i < end : i > end; | ||
| 140 | asc ? i++ : i-- | ||
| 141 | ) { | ||
| 142 | this.remove(this.el.lastChild); | ||
| 143 | } | ||
| 110 | this.append(this.renderNextLink(this.page - 1)); | 144 | this.append(this.renderNextLink(this.page - 1)); |
| 111 | } | 145 | } |
| 112 | 146 | ||
| 113 | onClick(event) { | 147 | onClick(event) { |
| 114 | const target = $.eventTarget(event); | 148 | const target = $.eventTarget(event); |
| 115 | if (target.tagName === 'SPAN') { // link | 149 | if (target.tagName === "SPAN") { |
| 150 | // link | ||
| 116 | $.stopEvent(event); | 151 | $.stopEvent(event); |
| 117 | this.paginate(target); | 152 | this.paginate(target); |
| 118 | } | 153 | } |
| @@ -12,15 +12,16 @@ · 11 unmodified lines | |||
| 12 | 12 | ||
| 13 | const Cls = (app.views.News = class News extends app.views.Notif { | 13 | const Cls = (app.views.News = class News extends app.views.Notif { |
| 14 | static initClass() { | 14 | static initClass() { |
| 15 | this.className += ' _notif-news'; | 15 | this.className += " _notif-news"; |
| 16 | 16 | ||
| 17 | this.defautOptions = | 17 | this.defautOptions = { autoHide: 30000 }; |
| 18 | {autoHide: 30000}; | ||
| 19 | } | 18 | } |
| 20 | 19 | ||
| 21 | init() { | 20 | init() { |
| 22 | this.unreadNews = this.getUnreadNews(); | 21 | this.unreadNews = this.getUnreadNews(); |
| 23 | if (this.unreadNews.length) { this.show(); } | 22 | if (this.unreadNews.length) { |
| 23 | this.show(); | ||
| 24 | } | ||
| 24 | this.markAllAsRead(); | 25 | this.markAllAsRead(); |
| 25 | } | 26 | } |
| 26 | 27 | ||
| @@ -30,12 +31,16 @@ const Cls = (app.views.News = class News extends app.views.Notif { · 3 unmodified lines | |||
| 30 | 31 | ||
| 31 | getUnreadNews() { | 32 | getUnreadNews() { |
| 32 | let time; | 33 | let time; |
| 33 | if (!(time = this.getLastReadTime())) { return []; } | 34 | if (!(time = this.getLastReadTime())) { |
| 35 | return []; | ||
| 36 | } | ||
| 34 | 37 | ||
| 35 | return (() => { | 38 | return (() => { |
| 36 | const result = []; | 39 | const result = []; |
| 37 | for (var news of Array.from(app.news)) { | 40 | for (var news of Array.from(app.news)) { |
| 38 | if (new Date(news[0]).getTime() <= time) { break; } | 41 | if (new Date(news[0]).getTime() <= time) { |
| 42 | break; | ||
| 43 | } | ||
| 39 | result.push(news); | 44 | result.push(news); |
| 40 | } | 45 | } |
| 41 | return result; | 46 | return result; |
| @@ -47,11 +52,11 @@ const Cls = (app.views.News = class News extends app.views.Notif { · 5 unmodified lines | |||
| 47 | } | 52 | } |
| 48 | 53 | ||
| 49 | getLastReadTime() { | 54 | getLastReadTime() { |
| 50 | return app.settings.get('news'); | 55 | return app.settings.get("news"); |
| 51 | } | 56 | } |
| 52 | 57 | ||
| 53 | markAllAsRead() { | 58 | markAllAsRead() { |
| 54 | app.settings.set('news', this.getLastNewsTime()); | 59 | app.settings.set("news", this.getLastNewsTime()); |
| 55 | } | 60 | } |
| 56 | }); | 61 | }); |
| 57 | Cls.initClass(); | 62 | Cls.initClass(); |
| @@ -9,23 +9,30 @@ · 8 unmodified lines | |||
| 9 | */ | 9 | */ |
| 10 | const Cls = (app.views.Notice = class Notice extends app.View { | 10 | const Cls = (app.views.Notice = class Notice extends app.View { |
| 11 | static initClass() { | 11 | static initClass() { |
| 12 | this.className = '_notice'; | 12 | this.className = "_notice"; |
| 13 | this.attributes = | 13 | this.attributes = { role: "alert" }; |
| 14 | {role: 'alert'}; | ||
| 15 | } | 14 | } |
| 16 | 15 | ||
| 17 | constructor(type, ...rest) { this.type = type; [...this.args] = Array.from(rest); super(...arguments); } | 16 | constructor(type, ...rest) { |
| 17 | this.type = type; | ||
| 18 | [...this.args] = Array.from(rest); | ||
| 19 | super(...arguments); | ||
| 20 | } | ||
| 18 | 21 | ||
| 19 | init() { | 22 | init() { |
| 20 | this.activate(); | 23 | this.activate(); |
| 21 | } | 24 | } |
| 22 | 25 | ||
| 23 | activate() { | 26 | activate() { |
| 24 | if (super.activate(...arguments)) { this.show(); } | 27 | if (super.activate(...arguments)) { |
| 28 | this.show(); | ||
| 29 | } | ||
| 25 | } | 30 | } |
| 26 | 31 | ||
| 27 | deactivate() { | 32 | deactivate() { |
| 28 | if (super.deactivate(...arguments)) { this.hide(); } | 33 | if (super.deactivate(...arguments)) { |
| 34 | this.hide(); | ||
| 35 | } | ||
| 29 | } | 36 | } |
| 30 | 37 | ||
| 31 | show() { | 38 | show() { |
| @@ -9,22 +9,21 @@ · 8 unmodified lines | |||
| 9 | */ | 9 | */ |
| 10 | const Cls = (app.views.Notif = class Notif extends app.View { | 10 | const Cls = (app.views.Notif = class Notif extends app.View { |
| 11 | static initClass() { | 11 | static initClass() { |
| 12 | this.className = '_notif'; | 12 | this.className = "_notif"; |
| 13 | this.activeClass = '_in'; | 13 | this.activeClass = "_in"; |
| 14 | this.attributes = | 14 | this.attributes = { role: "alert" }; |
| 15 | {role: 'alert'}; | 15 | |
| 16 | 16 | this.defautOptions = { autoHide: 15000 }; | |
| 17 | this.defautOptions = | 17 | |
| 18 | {autoHide: 15000}; | 18 | this.events = { click: "onClick" }; |
| 19 | |||
| 20 | this.events = | ||
| 21 | {click: 'onClick'}; | ||
| 22 | } | 19 | } |
| 23 | 20 | ||
| 24 | constructor(type, options) { | 21 | constructor(type, options) { |
| 25 | this.onClick = this.onClick.bind(this); | 22 | this.onClick = this.onClick.bind(this); |
| 26 | this.type = type; | 23 | this.type = type; |
| 27 | if (options == null) { options = {}; } | 24 | if (options == null) { |
| 25 | options = {}; | ||
| 26 | } | ||
| 28 | this.options = options; | 27 | this.options = options; |
| 29 | this.options = $.extend({}, this.constructor.defautOptions, this.options); | 28 | this.options = $.extend({}, this.constructor.defautOptions, this.options); |
| 30 | super(...arguments); | 29 | super(...arguments); |
| @@ -45,7 +44,9 @@ const Cls = (app.views.Notif = class Notif extends app.View { · 14 unmodified lines | |||
| 45 | this.appendTo(document.body); | 44 | this.appendTo(document.body); |
| 46 | this.el.offsetWidth; // force reflow | 45 | this.el.offsetWidth; // force reflow |
| 47 | this.addClass(this.constructor.activeClass); | 46 | this.addClass(this.constructor.activeClass); |
| 48 | if (this.options.autoHide) { this.timeout = this.delay(this.hide, this.options.autoHide); } | 47 | if (this.options.autoHide) { |
| 48 | this.timeout = this.delay(this.hide, this.options.autoHide); | ||
| 49 | } | ||
| 49 | } | 50 | } |
| 50 | } | 51 | } |
| 51 | 52 | ||
| @@ -63,15 +64,20 @@ const Cls = (app.views.Notif = class Notif extends app.View { · 11 unmodified lines | |||
| 63 | const notifications = $$(`.${app.views.Notif.className}`); | 64 | const notifications = $$(`.${app.views.Notif.className}`); |
| 64 | if (notifications.length) { | 65 | if (notifications.length) { |
| 65 | const lastNotif = notifications[notifications.length - 1]; | 66 | const lastNotif = notifications[notifications.length - 1]; |
| 66 | this.el.style.top = lastNotif.offsetTop + lastNotif.offsetHeight + 16 + 'px'; | 67 | this.el.style.top = |
| 68 | lastNotif.offsetTop + lastNotif.offsetHeight + 16 + "px"; | ||
| 67 | } | 69 | } |
| 68 | } | 70 | } |
| 69 | 71 | ||
| 70 | onClick(event) { | 72 | onClick(event) { |
| 71 | if (event.which !== 1) { return; } | 73 | if (event.which !== 1) { |
| 74 | return; | ||
| 75 | } | ||
| 72 | const target = $.eventTarget(event); | 76 | const target = $.eventTarget(event); |
| 73 | if (target.hasAttribute('data-behavior')) { return; } | 77 | if (target.hasAttribute("data-behavior")) { |
| 74 | if ((target.tagName !== 'A') || target.classList.contains('_notif-close')) { | 78 | return; |
| 79 | } | ||
| 80 | if (target.tagName !== "A" || target.classList.contains("_notif-close")) { | ||
| 75 | $.stopEvent(event); | 81 | $.stopEvent(event); |
| 76 | this.hide(); | 82 | this.hide(); |
| 77 | } | 83 | } |
| @@ -9,10 +9,9 @@ · 8 unmodified lines | |||
| 9 | 9 | ||
| 10 | const Cls = (app.views.Tip = class Tip extends app.views.Notif { | 10 | const Cls = (app.views.Tip = class Tip extends app.views.Notif { |
| 11 | static initClass() { | 11 | static initClass() { |
| 12 | this.className = '_notif _notif-tip'; | 12 | this.className = "_notif _notif-tip"; |
| 13 | 13 | ||
| 14 | this.defautOptions = | 14 | this.defautOptions = { autoHide: false }; |
| 15 | {autoHide: false}; | ||
| 16 | } | 15 | } |
| 17 | 16 | ||
| 18 | render() { | 17 | render() { |
| @@ -12,34 +12,47 @@ · 11 unmodified lines | |||
| 12 | 12 | ||
| 13 | const Cls = (app.views.Updates = class Updates extends app.views.Notif { | 13 | const Cls = (app.views.Updates = class Updates extends app.views.Notif { |
| 14 | static initClass() { | 14 | static initClass() { |
| 15 | this.className += ' _notif-news'; | 15 | this.className += " _notif-news"; |
| 16 | 16 | ||
| 17 | this.defautOptions = | 17 | this.defautOptions = { autoHide: 30000 }; |
| 18 | {autoHide: 30000}; | ||
| 19 | } | 18 | } |
| 20 | 19 | ||
| 21 | init() { | 20 | init() { |
| 22 | this.lastUpdateTime = this.getLastUpdateTime(); | 21 | this.lastUpdateTime = this.getLastUpdateTime(); |
| 23 | this.updatedDocs = this.getUpdatedDocs(); | 22 | this.updatedDocs = this.getUpdatedDocs(); |
| 24 | this.updatedDisabledDocs = this.getUpdatedDisabledDocs(); | 23 | this.updatedDisabledDocs = this.getUpdatedDisabledDocs(); |
| 25 | if ((this.updatedDocs.length > 0) || (this.updatedDisabledDocs.length > 0)) { this.show(); } | 24 | if (this.updatedDocs.length > 0 || this.updatedDisabledDocs.length > 0) { |
| 25 | this.show(); | ||
| 26 | } | ||
| 26 | this.markAllAsRead(); | 27 | this.markAllAsRead(); |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | render() { | 30 | render() { |
| 30 | this.html(app.templates.notifUpdates(this.updatedDocs, this.updatedDisabledDocs)); | 31 | this.html( |
| 32 | app.templates.notifUpdates(this.updatedDocs, this.updatedDisabledDocs), | ||
| 33 | ); | ||
| 31 | } | 34 | } |
| 32 | 35 | ||
| 33 | getUpdatedDocs() { | 36 | getUpdatedDocs() { |
| 34 | if (!this.lastUpdateTime) { return []; } | 37 | if (!this.lastUpdateTime) { |
| 35 | return Array.from(app.docs.all()).filter((doc) => doc.mtime > this.lastUpdateTime); | 38 | return []; |
| 39 | } | ||
| 40 | return Array.from(app.docs.all()).filter( | ||
| 41 | (doc) => doc.mtime > this.lastUpdateTime, | ||
| 42 | ); | ||
| 36 | } | 43 | } |
| 37 | 44 | ||
| 38 | getUpdatedDisabledDocs() { | 45 | getUpdatedDisabledDocs() { |
| 39 | if (!this.lastUpdateTime) { return []; } | 46 | if (!this.lastUpdateTime) { |
| 47 | return []; | ||
| 48 | } | ||
| 40 | return (() => { | 49 | return (() => { |
| 41 | const result = []; | 50 | const result = []; |
| 42 | for (var doc of Array.from(app.disabledDocs.all())) { if ((doc.mtime > this.lastUpdateTime) && app.docs.findBy('slug_without_version', doc.slug_without_version)) { | 51 | for (var doc of Array.from(app.disabledDocs.all())) { |
| 52 | if ( | ||
| 53 | doc.mtime > this.lastUpdateTime && | ||
| 54 | app.docs.findBy("slug_without_version", doc.slug_without_version) | ||
| 55 | ) { | ||
| 43 | result.push(doc); | 56 | result.push(doc); |
| 44 | } | 57 | } |
| 45 | } | 58 | } |
| @@ -48,11 +61,16 @@ const Cls = (app.views.Updates = class Updates extends app.views.Notif { · 2 unmodified lines | |||
| 48 | } | 61 | } |
| 49 | 62 | ||
| 50 | getLastUpdateTime() { | 63 | getLastUpdateTime() { |
| 51 | return app.settings.get('version'); | 64 | return app.settings.get("version"); |
| 52 | } | 65 | } |
| 53 | 66 | ||
| 54 | markAllAsRead() { | 67 | markAllAsRead() { |
| 55 | app.settings.set('version', app.config.env === 'production' ? app.config.version : Math.floor(Date.now() / 1000)); | 68 | app.settings.set( |
| 69 | "version", | ||
| 70 | app.config.env === "production" | ||
| 71 | ? app.config.version | ||
| 72 | : Math.floor(Date.now() / 1000), | ||
| 73 | ); | ||
| 56 | } | 74 | } |
| 57 | }); | 75 | }); |
| 58 | Cls.initClass(); | 76 | Cls.initClass(); |
| @@ -9,31 +9,44 @@ · 8 unmodified lines | |||
| 9 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 9 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 10 | */ | 10 | */ |
| 11 | app.views.BasePage = class BasePage extends app.View { | 11 | app.views.BasePage = class BasePage extends app.View { |
| 12 | constructor(el, entry) { this.paintCode = this.paintCode.bind(this); this.el = el; this.entry = entry; super(...arguments); } | 12 | constructor(el, entry) { |
| 13 | this.paintCode = this.paintCode.bind(this); | ||
| 14 | this.el = el; | ||
| 15 | this.entry = entry; | ||
| 16 | super(...arguments); | ||
| 17 | } | ||
| 13 | 18 | ||
| 14 | deactivate() { | 19 | deactivate() { |
| 15 | if (super.deactivate(...arguments)) { | 20 | if (super.deactivate(...arguments)) { |
| 16 | return this.highlightNodes = []; | 21 | return (this.highlightNodes = []); |
| 17 | } | 22 | } |
| 18 | } | 23 | } |
| 19 | 24 | ||
| 20 | render(content, fromCache) { | 25 | render(content, fromCache) { |
| 21 | if (fromCache == null) { fromCache = false; } | 26 | if (fromCache == null) { |
| 27 | fromCache = false; | ||
| 28 | } | ||
| 22 | this.highlightNodes = []; | 29 | this.highlightNodes = []; |
| 23 | this.previousTiming = null; | 30 | this.previousTiming = null; |
| 24 | if (!this.constructor.className) { this.addClass(`_${this.entry.doc.type}`); } | 31 | if (!this.constructor.className) { |
| 32 | this.addClass(`_${this.entry.doc.type}`); | ||
| 33 | } | ||
| 25 | this.html(content); | 34 | this.html(content); |
| 26 | if (!fromCache) { this.highlightCode(); } | 35 | if (!fromCache) { |
| 36 | this.highlightCode(); | ||
| 37 | } | ||
| 27 | this.activate(); | 38 | this.activate(); |
| 28 | if (this.afterRender) { this.delay(this.afterRender); } | 39 | if (this.afterRender) { |
| 40 | this.delay(this.afterRender); | ||
| 41 | } | ||
| 29 | if (this.highlightNodes.length > 0) { | 42 | if (this.highlightNodes.length > 0) { |
| 30 | $.requestAnimationFrame(() => $.requestAnimationFrame(this.paintCode)); | 43 | $.requestAnimationFrame(() => $.requestAnimationFrame(this.paintCode)); |
| 31 | } | 44 | } |
| 32 | } | 45 | } |
| 33 | 46 | ||
| 34 | highlightCode() { | 47 | highlightCode() { |
| 35 | for (var el of Array.from(this.findAll('pre[data-language]'))) { | 48 | for (var el of Array.from(this.findAll("pre[data-language]"))) { |
| 36 | var language = el.getAttribute('data-language'); | 49 | var language = el.getAttribute("data-language"); |
| 37 | el.classList.add(`language-${language}`); | 50 | el.classList.add(`language-${language}`); |
| 38 | this.highlightNodes.push(el); | 51 | this.highlightNodes.push(el); |
| 39 | } | 52 | } |
| @@ -41,23 +54,34 @@ app.views.BasePage = class BasePage extends app.View { · 1 unmodified line | |||
| 41 | 54 | ||
| 42 | paintCode(timing) { | 55 | paintCode(timing) { |
| 43 | if (this.previousTiming) { | 56 | if (this.previousTiming) { |
| 44 | if (Math.round(1000 / (timing - this.previousTiming)) > 50) { // fps | 57 | if (Math.round(1000 / (timing - this.previousTiming)) > 50) { |
| 45 | this.nodesPerFrame = Math.round(Math.min(this.nodesPerFrame * 1.25, 50)); | 58 | // fps |
| 59 | this.nodesPerFrame = Math.round( | ||
| 60 | Math.min(this.nodesPerFrame * 1.25, 50), | ||
| 61 | ); | ||
| 46 | } else { | 62 | } else { |
| 47 | this.nodesPerFrame = Math.round(Math.max(this.nodesPerFrame * .8, 10)); | 63 | this.nodesPerFrame = Math.round(Math.max(this.nodesPerFrame * 0.8, 10)); |
| 48 | } | 64 | } |
| 49 | } else { | 65 | } else { |
| 50 | this.nodesPerFrame = 10; | 66 | this.nodesPerFrame = 10; |
| 51 | } | 67 | } |
| 52 | 68 | ||
| 53 | for (var el of Array.from(this.highlightNodes.splice(0, this.nodesPerFrame))) { | 69 | for (var el of Array.from( |
| 70 | this.highlightNodes.splice(0, this.nodesPerFrame), | ||
| 71 | )) { | ||
| 54 | var clipEl; | 72 | var clipEl; |
| 55 | if (clipEl = el.lastElementChild) { $.remove(clipEl); } | 73 | if ((clipEl = el.lastElementChild)) { |
| 74 | $.remove(clipEl); | ||
| 75 | } | ||
| 56 | Prism.highlightElement(el); | 76 | Prism.highlightElement(el); |
| 57 | if (clipEl) { $.append(el, clipEl); } | 77 | if (clipEl) { |
| 78 | $.append(el, clipEl); | ||
| 79 | } | ||
| 58 | } | 80 | } |
| 59 | 81 | ||
| 60 | if (this.highlightNodes.length > 0) { $.requestAnimationFrame(this.paintCode); } | 82 | if (this.highlightNodes.length > 0) { |
| 83 | $.requestAnimationFrame(this.paintCode); | ||
| 84 | } | ||
| 61 | this.previousTiming = timing; | 85 | this.previousTiming = timing; |
| 62 | } | 86 | } |
| 63 | }; | 87 | }; |
| @@ -8,20 +8,24 @@ · 7 unmodified lines | |||
| 8 | */ | 8 | */ |
| 9 | const Cls = (app.views.HiddenPage = class HiddenPage extends app.View { | 9 | const Cls = (app.views.HiddenPage = class HiddenPage extends app.View { |
| 10 | static initClass() { | 10 | static initClass() { |
| 11 | this.events = | 11 | this.events = { click: "onClick" }; |
| 12 | {click: 'onClick'}; | ||
| 13 | } | 12 | } |
| 14 | 13 | ||
| 15 | constructor(el, entry) { this.onClick = this.onClick.bind(this); this.el = el; this.entry = entry; super(...arguments); } | 14 | constructor(el, entry) { |
| 15 | this.onClick = this.onClick.bind(this); | ||
| 16 | this.el = el; | ||
| 17 | this.entry = entry; | ||
| 18 | super(...arguments); | ||
| 19 | } | ||
| 16 | 20 | ||
| 17 | init() { | 21 | init() { |
| 18 | this.addSubview(this.notice = new app.views.Notice('disabledDoc')); | 22 | this.addSubview((this.notice = new app.views.Notice("disabledDoc"))); |
| 19 | this.activate(); | 23 | this.activate(); |
| 20 | } | 24 | } |
| 21 | 25 | ||
| 22 | onClick(event) { | 26 | onClick(event) { |
| 23 | let link; | 27 | let link; |
| 24 | if (link = $.closestLink(event.target, this.el)) { | 28 | if ((link = $.closestLink(event.target, this.el))) { |
| 25 | $.stopEvent(event); | 29 | $.stopEvent(event); |
| 26 | $.popup(link); | 30 | $.popup(link); |
| 27 | } | 31 | } |
| @@ -10,46 +10,54 @@ · 9 unmodified lines | |||
| 10 | */ | 10 | */ |
| 11 | //= require views/pages/base | 11 | //= require views/pages/base |
| 12 | 12 | ||
| 13 | const Cls = (app.views.JqueryPage = class JqueryPage extends app.views.BasePage { | 13 | const Cls = (app.views.JqueryPage = class JqueryPage extends ( |
| 14 | app.views.BasePage | ||
| 15 | ) { | ||
| 14 | constructor(...args) { | 16 | constructor(...args) { |
| 15 | this.onIframeLoaded = this.onIframeLoaded.bind(this); | 17 | this.onIframeLoaded = this.onIframeLoaded.bind(this); |
| 16 | super(...args); | 18 | super(...args); |
| 17 | } | 19 | } |
| 18 | 20 | ||
| 19 | static initClass() { | 21 | static initClass() { |
| 20 | this.demoClassName = '_jquery-demo'; | 22 | this.demoClassName = "_jquery-demo"; |
| 21 | } | 23 | } |
| 22 | 24 | ||
| 23 | afterRender() { | 25 | afterRender() { |
| 24 | // Prevent jQuery Mobile's demo iframes from scrolling the page | 26 | // Prevent jQuery Mobile's demo iframes from scrolling the page |
| 25 | for (var iframe of Array.from(this.findAllByTag('iframe'))) { | 27 | for (var iframe of Array.from(this.findAllByTag("iframe"))) { |
| 26 | iframe.style.display = 'none'; | 28 | iframe.style.display = "none"; |
| 27 | $.on(iframe, 'load', this.onIframeLoaded); | 29 | $.on(iframe, "load", this.onIframeLoaded); |
| 28 | } | 30 | } |
| 29 | 31 | ||
| 30 | return this.runExamples(); | 32 | return this.runExamples(); |
| 31 | } | 33 | } |
| 32 | 34 | ||
| 33 | onIframeLoaded(event) { | 35 | onIframeLoaded(event) { |
| 34 | event.target.style.display = ''; | 36 | event.target.style.display = ""; |
| 35 | $.off(event.target, 'load', this.onIframeLoaded); | 37 | $.off(event.target, "load", this.onIframeLoaded); |
| 36 | } | 38 | } |
| 37 | 39 | ||
| 38 | runExamples() { | 40 | runExamples() { |
| 39 | for (var el of Array.from(this.findAllByClass('entry-example'))) { | 41 | for (var el of Array.from(this.findAllByClass("entry-example"))) { |
| 40 | try { this.runExample(el); } catch (error) {} | 42 | try { |
| 43 | this.runExample(el); | ||
| 44 | } catch (error) {} | ||
| 41 | } | 45 | } |
| 42 | } | 46 | } |
| 43 | 47 | ||
| 44 | runExample(el) { | 48 | runExample(el) { |
| 45 | let iframe; | 49 | let iframe; |
| 46 | const source = el.getElementsByClassName('syntaxhighlighter')[0]; | 50 | const source = el.getElementsByClassName("syntaxhighlighter")[0]; |
| 47 | if (!source || (source.innerHTML.indexOf('!doctype') === -1)) { return; } | 51 | if (!source || source.innerHTML.indexOf("!doctype") === -1) { |
| 52 | return; | ||
| 53 | } | ||
| 48 | 54 | ||
| 49 | if (!(iframe = el.getElementsByClassName(this.constructor.demoClassName)[0])) { | 55 | if ( |
| 50 | iframe = document.createElement('iframe'); | 56 | !(iframe = el.getElementsByClassName(this.constructor.demoClassName)[0]) |
| 57 | ) { | ||
| 58 | iframe = document.createElement("iframe"); | ||
| 51 | iframe.className = this.constructor.demoClassName; | 59 | iframe.className = this.constructor.demoClassName; |
| 52 | iframe.width = '100%'; | 60 | iframe.width = "100%"; |
| 53 | iframe.height = 200; | 61 | iframe.height = 200; |
| 54 | el.appendChild(iframe); | 62 | el.appendChild(iframe); |
| 55 | } | 63 | } |
| @@ -60,8 +68,13 @@ const Cls = (app.views.JqueryPage = class JqueryPage extends app.views.BasePage · 4 unmodified lines | |||
| 60 | } | 68 | } |
| 61 | 69 | ||
| 62 | fixIframeSource(source) { | 70 | fixIframeSource(source) { |
| 63 | source = source.replace('"/resources/', '"https://api.jquery.com/resources/'); // attr(), keydown() | 71 | source = source.replace( |
| 64 | source = source.replace('</head>', `\ | 72 | '"/resources/', |
| 73 | '"https://api.jquery.com/resources/', | ||
| 74 | ); // attr(), keydown() | ||
| 75 | source = source.replace( | ||
| 76 | "</head>", | ||
| 77 | `\ | ||
| 65 | <style> | 78 | <style> |
| 66 | html, body { border: 0; margin: 0; padding: 0; } | 79 | html, body { border: 0; margin: 0; padding: 0; } |
| 67 | body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; } | 80 | body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; } |
| @@ -75,7 +88,7 @@ const Cls = (app.views.JqueryPage = class JqueryPage extends app.views.BasePage · 7 unmodified lines | |||
| 75 | }); | 88 | }); |
| 76 | </script> | 89 | </script> |
| 77 | </head>\ | 90 | </head>\ |
| 78 | ` | 91 | `, |
| 79 | ); | 92 | ); |
| 80 | return source.replace(/<script>/gi, '<script nonce="devdocs">'); | 93 | return source.replace(/<script>/gi, '<script nonce="devdocs">'); |
| 81 | } | 94 | } |
| @@ -10,19 +10,23 @@ · 9 unmodified lines | |||
| 10 | 10 | ||
| 11 | const Cls = (app.views.RdocPage = class RdocPage extends app.views.BasePage { | 11 | const Cls = (app.views.RdocPage = class RdocPage extends app.views.BasePage { |
| 12 | static initClass() { | 12 | static initClass() { |
| 13 | this.events = | 13 | this.events = { click: "onClick" }; |
| 14 | {click: 'onClick'}; | ||
| 15 | } | 14 | } |
| 16 | 15 | ||
| 17 | onClick(event) { | 16 | onClick(event) { |
| 18 | if (!event.target.classList.contains('method-click-advice')) { return; } | 17 | if (!event.target.classList.contains("method-click-advice")) { |
| 18 | return; | ||
| 19 | } | ||
| 19 | $.stopEvent(event); | 20 | $.stopEvent(event); |
| 20 | 21 | ||
| 21 | const source = $('.method-source-code', event.target.closest('.method-detail')); | 22 | const source = $( |
| 22 | const isShown = source.style.display === 'block'; | 23 | ".method-source-code", |
| 24 | event.target.closest(".method-detail"), | ||
| 25 | ); | ||
| 26 | const isShown = source.style.display === "block"; | ||
| 23 | 27 | ||
| 24 | source.style.display = isShown ? 'none' : 'block'; | 28 | source.style.display = isShown ? "none" : "block"; |
| 25 | return event.target.textContent = isShown ? 'Show source' : 'Hide source'; | 29 | return (event.target.textContent = isShown ? "Show source" : "Hide source"); |
| 26 | } | 30 | } |
| 27 | }); | 31 | }); |
| 28 | Cls.initClass(); | 32 | Cls.initClass(); |
| @@ -8,28 +8,33 @@ · 7 unmodified lines | |||
| 8 | */ | 8 | */ |
| 9 | //= require views/pages/base | 9 | //= require views/pages/base |
| 10 | 10 | ||
| 11 | const Cls = (app.views.SqlitePage = class SqlitePage extends app.views.BasePage { | 11 | const Cls = (app.views.SqlitePage = class SqlitePage extends ( |
| 12 | app.views.BasePage | ||
| 13 | ) { | ||
| 12 | constructor(...args) { | 14 | constructor(...args) { |
| 13 | this.onClick = this.onClick.bind(this); | 15 | this.onClick = this.onClick.bind(this); |
| 14 | super(...args); | 16 | super(...args); |
| 15 | } | 17 | } |
| 16 | 18 | ||
| 17 | static initClass() { | 19 | static initClass() { |
| 18 | this.events = | 20 | this.events = { click: "onClick" }; |
| 19 | {click: 'onClick'}; | ||
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | onClick(event) { | 23 | onClick(event) { |
| 23 | let el, id; | 24 | let el, id; |
| 24 | if (!(id = event.target.getAttribute('data-toggle'))) { return; } | 25 | if (!(id = event.target.getAttribute("data-toggle"))) { |
| 25 | if (!(el = this.find(`#${id}`))) { return; } | 26 | return; |
| 27 | } | ||
| 28 | if (!(el = this.find(`#${id}`))) { | ||
| 29 | return; | ||
| 30 | } | ||
| 26 | $.stopEvent(event); | 31 | $.stopEvent(event); |
| 27 | if (el.style.display === 'none') { | 32 | if (el.style.display === "none") { |
| 28 | el.style.display = 'block'; | 33 | el.style.display = "block"; |
| 29 | event.target.textContent = 'hide'; | 34 | event.target.textContent = "hide"; |
| 30 | } else { | 35 | } else { |
| 31 | el.style.display = 'none'; | 36 | el.style.display = "none"; |
| 32 | event.target.textContent = 'show'; | 37 | event.target.textContent = "show"; |
| 33 | } | 38 | } |
| 34 | } | 39 | } |
| 35 | }); | 40 | }); |
| @@ -7,19 +7,24 @@ · 6 unmodified lines | |||
| 7 | */ | 7 | */ |
| 8 | //= require views/pages/base | 8 | //= require views/pages/base |
| 9 | 9 | ||
| 10 | const Cls = (app.views.SupportTablesPage = class SupportTablesPage extends app.views.BasePage { | 10 | const Cls = (app.views.SupportTablesPage = class SupportTablesPage extends ( |
| 11 | app.views.BasePage | ||
| 12 | ) { | ||
| 11 | static initClass() { | 13 | static initClass() { |
| 12 | this.events = | 14 | this.events = { click: "onClick" }; |
| 13 | {click: 'onClick'}; | ||
| 14 | } | 15 | } |
| 15 | 16 | ||
| 16 | onClick(event) { | 17 | onClick(event) { |
| 17 | if (!event.target.classList.contains('show-all')) { return; } | 18 | if (!event.target.classList.contains("show-all")) { |
| 19 | return; | ||
| 20 | } | ||
| 18 | $.stopEvent(event); | 21 | $.stopEvent(event); |
| 19 | 22 | ||
| 20 | let el = event.target; | 23 | let el = event.target; |
| 21 | while (el.tagName !== 'TABLE') { el = el.parentNode; } | 24 | while (el.tagName !== "TABLE") { |
| 22 | el.classList.add('show-all'); | 25 | el = el.parentNode; |
| 26 | } | ||
| 27 | el.classList.add("show-all"); | ||
| 23 | } | 28 | } |
| 24 | }); | 29 | }); |
| 25 | Cls.initClass(); | 30 | Cls.initClass(); |
| @@ -9,7 +9,7 @@ · 8 unmodified lines | |||
| 9 | * DS207: Consider shorter variations of null checks | 9 | * DS207: Consider shorter variations of null checks |
| 10 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 10 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 11 | */ | 11 | */ |
| 12 | (function() { | 12 | (function () { |
| 13 | let SEARCH_PARAM = undefined; | 13 | let SEARCH_PARAM = undefined; |
| 14 | let HASH_RGX = undefined; | 14 | let HASH_RGX = undefined; |
| 15 | const Cls = (app.views.Search = class Search extends app.View { | 15 | const Cls = (app.views.Search = class Search extends app.View { |
| @@ -33,86 +33,106 @@ · 17 unmodified lines | |||
| 33 | 33 | ||
| 34 | static initClass() { | 34 | static initClass() { |
| 35 | SEARCH_PARAM = app.config.search_param; | 35 | SEARCH_PARAM = app.config.search_param; |
| 36 | 36 | ||
| 37 | this.el = '._search'; | 37 | this.el = "._search"; |
| 38 | this.activeClass = '_search-active'; | 38 | this.activeClass = "_search-active"; |
| 39 | 39 | ||
| 40 | this.elements = { | 40 | this.elements = { |
| 41 | input: '._search-input', | 41 | input: "._search-input", |
| 42 | resetLink: '._search-clear' | 42 | resetLink: "._search-clear", |
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | this.events = { | 45 | this.events = { |
| 46 | input: 'onInput', | 46 | input: "onInput", |
| 47 | click: 'onClick', | 47 | click: "onClick", |
| 48 | submit: 'onSubmit' | 48 | submit: "onSubmit", |
| 49 | }; | 49 | }; |
| 50 | 50 | ||
| 51 | this.shortcuts = { | 51 | this.shortcuts = { |
| 52 | typing: 'focus', | 52 | typing: "focus", |
| 53 | altG: 'google', | 53 | altG: "google", |
| 54 | altS: 'stackoverflow', | 54 | altS: "stackoverflow", |
| 55 | altD: 'duckduckgo' | 55 | altD: "duckduckgo", |
| 56 | }; | 56 | }; |
| 57 | 57 | ||
| 58 | this.routes = | 58 | this.routes = { after: "afterRoute" }; |
| 59 | {after: 'afterRoute'}; | 59 | |
| 60 | |||
| 61 | HASH_RGX = new RegExp(`^#${SEARCH_PARAM}=(.*)`); | 60 | HASH_RGX = new RegExp(`^#${SEARCH_PARAM}=(.*)`); |
| 62 | } | 61 | } |
| 63 | 62 | ||
| 64 | init() { | 63 | init() { |
| 65 | this.addSubview(this.scope = new app.views.SearchScope(this.el)); | 64 | this.addSubview((this.scope = new app.views.SearchScope(this.el))); |
| 66 | 65 | ||
| 67 | this.searcher = new app.Searcher; | 66 | this.searcher = new app.Searcher(); |
| 68 | this.searcher | 67 | this.searcher.on("results", this.onResults).on("end", this.onEnd); |
| 69 | .on('results', this.onResults) | ||
| 70 | .on('end', this.onEnd); | ||
| 71 | 68 | ||
| 72 | this.scope | 69 | this.scope.on("change", this.onScopeChange); |
| 73 | .on('change', this.onScopeChange); | ||
| 74 | 70 | ||
| 75 | app.on('ready', this.onReady); | 71 | app.on("ready", this.onReady); |
| 76 | $.on(window, 'hashchange', this.searchUrl); | 72 | $.on(window, "hashchange", this.searchUrl); |
| 77 | $.on(window, 'focus', this.onWindowFocus); | 73 | $.on(window, "focus", this.onWindowFocus); |
| 78 | } | 74 | } |
| 79 | 75 | ||
| 80 | focus() { | 76 | focus() { |
| 81 | if (document.activeElement === this.input) { return; } | 77 | if (document.activeElement === this.input) { |
| 82 | if (app.settings.get('noAutofocus')) { return; } | 78 | return; |
| 83 | this.input.focus(); | 79 | } |
| 80 | if (app.settings.get("noAutofocus")) { | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | this.input.focus(); | ||
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | autoFocus() { | 86 | autoFocus() { |
| 87 | if (app.isMobile() || $.isAndroid() || $.isIOS()) { return; } | 87 | if (app.isMobile() || $.isAndroid() || $.isIOS()) { |
| 88 | if ((document.activeElement != null ? document.activeElement.tagName : undefined) === 'INPUT') { return; } | 88 | return; |
| 89 | if (app.settings.get('noAutofocus')) { return; } | 89 | } |
| 90 | if ( | ||
| 91 | (document.activeElement != null | ||
| 92 | ? document.activeElement.tagName | ||
| 93 | : undefined) === "INPUT" | ||
| 94 | ) { | ||
| 95 | return; | ||
| 96 | } | ||
| 97 | if (app.settings.get("noAutofocus")) { | ||
| 98 | return; | ||
| 99 | } | ||
| 90 | this.input.focus(); | 100 | this.input.focus(); |
| 91 | } | 101 | } |
| 92 | 102 | ||
| 93 | onWindowFocus(event) { | 103 | onWindowFocus(event) { |
| 94 | if (event.target === window) { return this.autoFocus(); } | 104 | if (event.target === window) { |
| 105 | return this.autoFocus(); | ||
| 106 | } | ||
| 95 | } | 107 | } |
| 96 | 108 | ||
| 97 | getScopeDoc() { | 109 | getScopeDoc() { |
| 98 | if (this.scope.isActive()) { return this.scope.getScope(); } | 110 | if (this.scope.isActive()) { |
| 111 | return this.scope.getScope(); | ||
| 112 | } | ||
| 99 | } | 113 | } |
| 100 | 114 | ||
| 101 | reset(force) { | 115 | reset(force) { |
| 102 | if (force || !this.input.value) { this.scope.reset(); } | 116 | if (force || !this.input.value) { |
| 117 | this.scope.reset(); | ||
| 118 | } | ||
| 103 | this.el.reset(); | 119 | this.el.reset(); |
| 104 | this.onInput(); | 120 | this.onInput(); |
| 105 | this.autoFocus(); | 121 | this.autoFocus(); |
| 106 | } | 122 | } |
| 107 | 123 | ||
| 108 | onReady() { | 124 | onReady() { |
| 109 | this.value = ''; | 125 | this.value = ""; |
| 110 | this.delay(this.onInput); | 126 | this.delay(this.onInput); |
| 111 | } | 127 | } |
| 112 | 128 | ||
| 113 | onInput() { | 129 | onInput() { |
| 114 | if ((this.value == null) || // ignore events pre-"ready" | 130 | if ( |
| 115 | (this.value === this.input.value)) { return; } | 131 | this.value == null || // ignore events pre-"ready" |
| 132 | this.value === this.input.value | ||
| 133 | ) { | ||
| 134 | return; | ||
| 135 | } | ||
| 116 | this.value = this.input.value; | 136 | this.value = this.input.value; |
| 117 | 137 | ||
| 118 | if (this.value.length) { | 138 | if (this.value.length) { |
| @@ -123,25 +143,33 @@ · 4 unmodified lines | |||
| 123 | } | 143 | } |
| 124 | 144 | ||
| 125 | search(url) { | 145 | search(url) { |
| 126 | if (url == null) { url = false; } | 146 | if (url == null) { |
| 147 | url = false; | ||
| 148 | } | ||
| 127 | this.addClass(this.constructor.activeClass); | 149 | this.addClass(this.constructor.activeClass); |
| 128 | this.trigger('searching'); | 150 | this.trigger("searching"); |
| 129 | 151 | ||
| 130 | this.hasResults = null; | 152 | this.hasResults = null; |
| 131 | this.flags = {urlSearch: url, initialResults: true}; | 153 | this.flags = { urlSearch: url, initialResults: true }; |
| 132 | this.searcher.find(this.scope.getScope().entries.all(), 'text', this.value); | 154 | this.searcher.find( |
| 155 | this.scope.getScope().entries.all(), | ||
| 156 | "text", | ||
| 157 | this.value, | ||
| 158 | ); | ||
| 133 | } | 159 | } |
| 134 | 160 | ||
| 135 | searchUrl() { | 161 | searchUrl() { |
| 136 | let value; | 162 | let value; |
| 137 | if (location.pathname === '/') { | 163 | if (location.pathname === "/") { |
| 138 | this.scope.searchUrl(); | 164 | this.scope.searchUrl(); |
| 139 | } else if (!app.router.isIndex()) { | 165 | } else if (!app.router.isIndex()) { |
| 140 | return; | 166 | return; |
| 141 | } | 167 | } |
| 142 | 168 | ||
| 143 | if (!(value = this.extractHashValue())) { return; } | 169 | if (!(value = this.extractHashValue())) { |
| 144 | this.input.value = (this.value = value); | 170 | return; |
| 171 | } | ||
| 172 | this.input.value = this.value = value; | ||
| 145 | this.input.setSelectionRange(value.length, value.length); | 173 | this.input.setSelectionRange(value.length, value.length); |
| 146 | this.search(true); | 174 | this.search(true); |
| 147 | return true; | 175 | return true; |
| @@ -149,13 +177,15 @@ · 1 unmodified line | |||
| 149 | 177 | ||
| 150 | clear() { | 178 | clear() { |
| 151 | this.removeClass(this.constructor.activeClass); | 179 | this.removeClass(this.constructor.activeClass); |
| 152 | this.trigger('clear'); | 180 | this.trigger("clear"); |
| 153 | } | 181 | } |
| 154 | 182 | ||
| 155 | externalSearch(url) { | 183 | externalSearch(url) { |
| 156 | let value; | 184 | let value; |
| 157 | if (value = this.value) { | 185 | if ((value = this.value)) { |
| 158 | if (this.scope.name()) { value = `${this.scope.name()} ${value}`; } | 186 | if (this.scope.name()) { |
| 187 | value = `${this.scope.name()} ${value}`; | ||
| 188 | } | ||
| 159 | $.popup(`${url}${encodeURIComponent(value)}`); | 189 | $.popup(`${url}${encodeURIComponent(value)}`); |
| 160 | this.reset(); | 190 | this.reset(); |
| 161 | } | 191 | } |
| @@ -174,13 +204,17 @@ · 12 unmodified lines | |||
| 174 | } | 204 | } |
| 175 | 205 | ||
| 176 | onResults(results) { | 206 | onResults(results) { |
| 177 | if (results.length) { this.hasResults = true; } | 207 | if (results.length) { |
| 178 | this.trigger('results', results, this.flags); | 208 | this.hasResults = true; |
| 209 | } | ||
| 210 | this.trigger("results", results, this.flags); | ||
| 179 | this.flags.initialResults = false; | 211 | this.flags.initialResults = false; |
| 180 | } | 212 | } |
| 181 | 213 | ||
| 182 | onEnd() { | 214 | onEnd() { |
| 183 | if (!this.hasResults) { this.trigger('noresults'); } | 215 | if (!this.hasResults) { |
| 216 | this.trigger("noresults"); | ||
| 217 | } | ||
| 184 | } | 218 | } |
| 185 | 219 | ||
| 186 | onClick(event) { | 220 | onClick(event) { |
| @@ -195,14 +229,24 @@ · 8 unmodified lines | |||
| 195 | } | 229 | } |
| 196 | 230 | ||
| 197 | onScopeChange() { | 231 | onScopeChange() { |
| 198 | this.value = ''; | 232 | this.value = ""; |
| 199 | this.onInput(); | 233 | this.onInput(); |
| 200 | } | 234 | } |
| 201 | 235 | ||
| 202 | afterRoute(name, context) { | 236 | afterRoute(name, context) { |
| 203 | if ((app.shortcuts.eventInProgress != null ? app.shortcuts.eventInProgress.name : undefined) === 'escape') { return; } | 237 | if ( |
| 204 | if (!context.init && app.router.isIndex()) { this.reset(true); } | 238 | (app.shortcuts.eventInProgress != null |
| 205 | if (context.hash) { this.delay(this.searchUrl); } | 239 | ? app.shortcuts.eventInProgress.name |
| 240 | : undefined) === "escape" | ||
| 241 | ) { | ||
| 242 | return; | ||
| 243 | } | ||
| 244 | if (!context.init && app.router.isIndex()) { | ||
| 245 | this.reset(true); | ||
| 246 | } | ||
| 247 | if (context.hash) { | ||
| 248 | this.delay(this.searchUrl); | ||
| 249 | } | ||
| 206 | $.requestAnimationFrame(this.autoFocus); | 250 | $.requestAnimationFrame(this.autoFocus); |
| 207 | } | 251 | } |
| 208 | 252 | ||
| @@ -215,7 +259,12 @@ · 6 unmodified lines | |||
| 215 | } | 259 | } |
| 216 | 260 | ||
| 217 | getHashValue() { | 261 | getHashValue() { |
| 218 | try { return __guard__(HASH_RGX.exec($.urlDecode(location.hash)), x => x[1]); } catch (error) {} | 262 | try { |
| 263 | return __guard__( | ||
| 264 | HASH_RGX.exec($.urlDecode(location.hash)), | ||
| 265 | (x) => x[1], | ||
| 266 | ); | ||
| 267 | } catch (error) {} | ||
| 219 | } | 268 | } |
| 220 | }); | 269 | }); |
| 221 | Cls.initClass(); | 270 | Cls.initClass(); |
| @@ -223,5 +272,7 @@ · 1 unmodified line | |||
| 223 | })(); | 272 | })(); |
| 224 | 273 | ||
| 225 | function __guard__(value, transform) { | 274 | function __guard__(value, transform) { |
| 226 | return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; | 275 | return typeof value !== "undefined" && value !== null |
| 227 | } | 276 | ? transform(value) |
| 277 | : undefined; | ||
| 278 | } | ||
| @@ -9,41 +9,49 @@ · 8 unmodified lines | |||
| 9 | * DS207: Consider shorter variations of null checks | 9 | * DS207: Consider shorter variations of null checks |
| 10 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md | 10 | * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md |
| 11 | */ | 11 | */ |
| 12 | (function() { | 12 | (function () { |
| 13 | let SEARCH_PARAM = undefined; | 13 | let SEARCH_PARAM = undefined; |
| 14 | let HASH_RGX = undefined; | 14 | let HASH_RGX = undefined; |
| 15 | const Cls = (app.views.SearchScope = class SearchScope extends app.View { | 15 | const Cls = (app.views.SearchScope = class SearchScope extends app.View { |
| 16 | static initClass() { | 16 | static initClass() { |
| 17 | SEARCH_PARAM = app.config.search_param; | 17 | SEARCH_PARAM = app.config.search_param; |
| 18 | 18 | ||
| 19 | this.elements = { | 19 | this.elements = { |
| 20 | input: '._search-input', | 20 | input: "._search-input", |
| 21 | tag: '._search-tag' | 21 | tag: "._search-tag", |
| 22 | }; | 22 | }; |
| 23 | 23 | ||
| 24 | this.events = { | 24 | this.events = { |
| 25 | click: 'onClick', | 25 | click: "onClick", |
| 26 | keydown: 'onKeydown', | 26 | keydown: "onKeydown", |
| 27 | textInput: 'onTextInput' | 27 | textInput: "onTextInput", |
| 28 | }; | 28 | }; |
| 29 | 29 | ||
| 30 | this.routes = | 30 | this.routes = { after: "afterRoute" }; |
| 31 | {after: 'afterRoute'}; | 31 | |
| 32 | |||
| 33 | HASH_RGX = new RegExp(`^#${SEARCH_PARAM}=(.+?) .`); | 32 | HASH_RGX = new RegExp(`^#${SEARCH_PARAM}=(.+?) .`); |
| 34 | } | 33 | } |
| 35 | 34 | ||
| 36 | constructor(el) { this.onResults = this.onResults.bind(this); this.reset = this.reset.bind(this); this.doScopeSearch = this.doScopeSearch.bind(this); this.onClick = this.onClick.bind(this); this.onKeydown = this.onKeydown.bind(this); this.onTextInput = this.onTextInput.bind(this); this.afterRoute = this.afterRoute.bind(this); this.el = el; super(...arguments); } | 35 | constructor(el) { |
| 36 | this.onResults = this.onResults.bind(this); | ||
| 37 | this.reset = this.reset.bind(this); | ||
| 38 | this.doScopeSearch = this.doScopeSearch.bind(this); | ||
| 39 | this.onClick = this.onClick.bind(this); | ||
| 40 | this.onKeydown = this.onKeydown.bind(this); | ||
| 41 | this.onTextInput = this.onTextInput.bind(this); | ||
| 42 | this.afterRoute = this.afterRoute.bind(this); | ||
| 43 | this.el = el; | ||
| 44 | super(...arguments); | ||
| 45 | } | ||
| 37 | 46 | ||
| 38 | init() { | 47 | init() { |
| 39 | this.placeholder = this.input.getAttribute('placeholder'); | 48 | this.placeholder = this.input.getAttribute("placeholder"); |
| 40 | 49 | ||
| 41 | this.searcher = new app.SynchronousSearcher({ | 50 | this.searcher = new app.SynchronousSearcher({ |
| 42 | fuzzy_min_length: 2, | 51 | fuzzy_min_length: 2, |
| 43 | max_results: 1 | 52 | max_results: 1, |
| 44 | }); | 53 | }); |
| 45 | this.searcher.on('results', this.onResults); | 54 | this.searcher.on("results", this.onResults); |
| 46 | |||
| 47 | } | 55 | } |
| 48 | 56 | ||
| 49 | getScope() { | 57 | getScope() { |
| @@ -55,26 +63,34 @@ · 5 unmodified lines | |||
| 55 | } | 63 | } |
| 56 | 64 | ||
| 57 | name() { | 65 | name() { |
| 58 | return (this.doc != null ? this.doc.name : undefined); | 66 | return this.doc != null ? this.doc.name : undefined; |
| 59 | } | 67 | } |
| 60 | 68 | ||
| 61 | search(value, searchDisabled) { | 69 | search(value, searchDisabled) { |
| 62 | if (searchDisabled == null) { searchDisabled = false; } | 70 | if (searchDisabled == null) { |
| 63 | if (this.doc) { return; } | 71 | searchDisabled = false; |
| 64 | this.searcher.find(app.docs.all(), 'text', value); | 72 | } |
| 65 | if (!this.doc && searchDisabled) { this.searcher.find(app.disabledDocs.all(), 'text', value); } | 73 | if (this.doc) { |
| 74 | return; | ||
| 75 | } | ||
| 76 | this.searcher.find(app.docs.all(), "text", value); | ||
| 77 | if (!this.doc && searchDisabled) { | ||
| 78 | this.searcher.find(app.disabledDocs.all(), "text", value); | ||
| 79 | } | ||
| 66 | } | 80 | } |
| 67 | 81 | ||
| 68 | searchUrl() { | 82 | searchUrl() { |
| 69 | let value; | 83 | let value; |
| 70 | if (value = this.extractHashValue()) { | 84 | if ((value = this.extractHashValue())) { |
| 71 | this.search(value, true); | 85 | this.search(value, true); |
| 72 | } | 86 | } |
| 73 | } | 87 | } |
| 74 | 88 | ||
| 75 | onResults(results) { | 89 | onResults(results) { |
| 76 | let doc; | 90 | let doc; |
| 77 | if (!(doc = results[0])) { return; } | 91 | if (!(doc = results[0])) { |
| 92 | return; | ||
| 93 | } | ||
| 78 | if (app.docs.contains(doc)) { | 94 | if (app.docs.contains(doc)) { |
| 79 | this.selectDoc(doc); | 95 | this.selectDoc(doc); |
| 80 | } else { | 96 | } else { |
| @@ -84,45 +100,49 @@ · 3 unmodified lines | |||
| 84 | 100 | ||
| 85 | selectDoc(doc) { | 101 | selectDoc(doc) { |
| 86 | const previousDoc = this.doc; | 102 | const previousDoc = this.doc; |
| 87 | if (doc === previousDoc) { return; } | 103 | if (doc === previousDoc) { |
| 104 | return; | ||
| 105 | } | ||
| 88 | this.doc = doc; | 106 | this.doc = doc; |
| 89 | 107 | ||
| 90 | this.tag.textContent = doc.fullName; | 108 | this.tag.textContent = doc.fullName; |
| 91 | this.tag.style.display = 'block'; | 109 | this.tag.style.display = "block"; |
| 92 | 110 | ||
| 93 | this.input.removeAttribute('placeholder'); | 111 | this.input.removeAttribute("placeholder"); |
| 94 | this.input.value = this.input.value.slice(this.input.selectionStart); | 112 | this.input.value = this.input.value.slice(this.input.selectionStart); |
| 95 | this.input.style.paddingLeft = this.tag.offsetWidth + 10 + 'px'; | 113 | this.input.style.paddingLeft = this.tag.offsetWidth + 10 + "px"; |
| 96 | 114 | ||
| 97 | $.trigger(this.input, 'input'); | 115 | $.trigger(this.input, "input"); |
| 98 | this.trigger('change', this.doc, previousDoc); | 116 | this.trigger("change", this.doc, previousDoc); |
| 99 | } | 117 | } |
| 100 | 118 | ||
| 101 | redirectToDoc(doc) { | 119 | redirectToDoc(doc) { |
| 102 | const { | 120 | const { hash } = location; |
| 103 | hash | 121 | app.router.replaceHash(""); |
| 104 | } = location; | ||
| 105 | app.router.replaceHash(''); | ||
| 106 | location.assign(doc.fullPath() + hash); | 122 | location.assign(doc.fullPath() + hash); |
| 107 | } | 123 | } |
| 108 | 124 | ||
| 109 | reset() { | 125 | reset() { |
| 110 | if (!this.doc) { return; } | 126 | if (!this.doc) { |
| 127 | return; | ||
| 128 | } | ||
| 111 | const previousDoc = this.doc; | 129 | const previousDoc = this.doc; |
| 112 | this.doc = null; | 130 | this.doc = null; |
| 113 | 131 | ||
| 114 | this.tag.textContent = ''; | 132 | this.tag.textContent = ""; |
| 115 | this.tag.style.display = 'none'; | 133 | this.tag.style.display = "none"; |
| 116 | 134 | ||
| 117 | this.input.setAttribute('placeholder', this.placeholder); | 135 | this.input.setAttribute("placeholder", this.placeholder); |
| 118 | this.input.style.paddingLeft = ''; | 136 | this.input.style.paddingLeft = ""; |
| 119 | 137 | ||
| 120 | this.trigger('change', null, previousDoc); | 138 | this.trigger("change", null, previousDoc); |
| 121 | } | 139 | } |
| 122 | 140 | ||
| 123 | doScopeSearch(event) { | 141 | doScopeSearch(event) { |
| 124 | this.search(this.input.value.slice(0, this.input.selectionStart)); | 142 | this.search(this.input.value.slice(0, this.input.selectionStart)); |
| 125 | if (this.doc) { $.stopEvent(event); } | 143 | if (this.doc) { |
| 144 | $.stopEvent(event); | ||
| 145 | } | ||
| 126 | } | 146 | } |
| 127 | 147 | ||
| 128 | onClick(event) { | 148 | onClick(event) { |
| @@ -133,38 +153,54 @@ · 4 unmodified lines | |||
| 133 | } | 153 | } |
| 134 | 154 | ||
| 135 | onKeydown(event) { | 155 | onKeydown(event) { |
| 136 | if (event.which === 8) { // backspace | 156 | if (event.which === 8) { |
| 137 | if (this.doc && (this.input.selectionEnd === 0)) { | 157 | // backspace |
| 158 | if (this.doc && this.input.selectionEnd === 0) { | ||
| 138 | this.reset(); | 159 | this.reset(); |
| 139 | $.stopEvent(event); | 160 | $.stopEvent(event); |
| 140 | } | 161 | } |
| 141 | } else if (!this.doc && this.input.value && !$.isChromeForAndroid()) { | 162 | } else if (!this.doc && this.input.value && !$.isChromeForAndroid()) { |
| 142 | if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) { return; } | 163 | if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) { |
| 143 | if ((event.which === 9) || // tab | 164 | return; |
| 144 | ((event.which === 32) && app.isMobile())) { // space | 165 | } |
| 166 | if ( | ||
| 167 | event.which === 9 || // tab | ||
| 168 | (event.which === 32 && app.isMobile()) | ||
| 169 | ) { | ||
| 170 | // space | ||
| 145 | this.doScopeSearch(event); | 171 | this.doScopeSearch(event); |
| 146 | } | 172 | } |
| 147 | } | 173 | } |
| 148 | } | 174 | } |
| 149 | 175 | ||
| 150 | onTextInput(event) { | 176 | onTextInput(event) { |
| 151 | if (!$.isChromeForAndroid()) { return; } | 177 | if (!$.isChromeForAndroid()) { |
| 152 | if (!this.doc && this.input.value && (event.data === ' ')) { | 178 | return; |
| 179 | } | ||
| 180 | if (!this.doc && this.input.value && event.data === " ") { | ||
| 153 | this.doScopeSearch(event); | 181 | this.doScopeSearch(event); |
| 154 | } | 182 | } |
| 155 | } | 183 | } |
| 156 | 184 | ||
| 157 | extractHashValue() { | 185 | extractHashValue() { |
| 158 | let value; | 186 | let value; |
| 159 | if (value = this.getHashValue()) { | 187 | if ((value = this.getHashValue())) { |
| 160 | const newHash = $.urlDecode(location.hash).replace(`#${SEARCH_PARAM}=${value} `, `#${SEARCH_PARAM}=`); | 188 | const newHash = $.urlDecode(location.hash).replace( |
| 189 | `#${SEARCH_PARAM}=${value} `, | ||
| 190 | `#${SEARCH_PARAM}=`, | ||
| 191 | ); | ||
| 161 | app.router.replaceHash(newHash); | 192 | app.router.replaceHash(newHash); |
| 162 | return value; | 193 | return value; |
| 163 | } | 194 | } |
| 164 | } | 195 | } |
| 165 | 196 | ||
| 166 | getHashValue() { | 197 | getHashValue() { |
| 167 | try { return __guard__(HASH_RGX.exec($.urlDecode(location.hash)), x => x[1]); } catch (error) {} | 198 | try { |
| 199 | return __guard__( | ||
| 200 | HASH_RGX.exec($.urlDecode(location.hash)), | ||
| 201 | (x) => x[1], | ||
| 202 | ); | ||
| 203 | } catch (error) {} | ||
| 168 | } | 204 | } |
| 169 | 205 | ||
| 170 | afterRoute(name, context) { | 206 | afterRoute(name, context) { |
| @@ -178,5 +214,7 @@ · 7 unmodified lines | |||
| 178 | })(); | 214 | })(); |
| 179 | 215 | ||
| 180 | function __guard__(value, transform) { | 216 | function __guard__(value, transform) { |
| 181 | return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; | 217 | return typeof value !== "undefined" && value !== null |
| 182 | } | 218 | ? transform(value) |
| 219 | : undefined; | ||
| 220 | } | ||
| @@ -20,65 +20,75 @@ const Cls = (app.views.DocList = class DocList extends app.View { · 19 unmodified lines | |||
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | static initClass() { | 22 | static initClass() { |
| 23 | this.className = '_list'; | 23 | this.className = "_list"; |
| 24 | this.attributes = | 24 | this.attributes = { role: "navigation" }; |
| 25 | {role: 'navigation'}; | 25 | |
| 26 | |||
| 27 | this.events = { | 26 | this.events = { |
| 28 | open: 'onOpen', | 27 | open: "onOpen", |
| 29 | close: 'onClose', | 28 | close: "onClose", |
| 30 | click: 'onClick' | 29 | click: "onClick", |
| 31 | }; | 30 | }; |
| 32 | 31 | ||
| 33 | this.routes = | 32 | this.routes = { after: "afterRoute" }; |
| 34 | {after: 'afterRoute'}; | 33 | |
| 35 | |||
| 36 | this.elements = { | 34 | this.elements = { |
| 37 | disabledTitle: '._list-title', | 35 | disabledTitle: "._list-title", |
| 38 | disabledList: '._disabled-list' | 36 | disabledList: "._disabled-list", |
| 39 | }; | 37 | }; |
| 40 | } | 38 | } |
| 41 | 39 | ||
| 42 | init() { | 40 | init() { |
| 43 | this.lists = {}; | 41 | this.lists = {}; |
| 44 | 42 | ||
| 45 | this.addSubview(this.listFocus = new app.views.ListFocus(this.el)); | 43 | this.addSubview((this.listFocus = new app.views.ListFocus(this.el))); |
| 46 | this.addSubview(this.listFold = new app.views.ListFold(this.el)); | 44 | this.addSubview((this.listFold = new app.views.ListFold(this.el))); |
| 47 | this.addSubview(this.listSelect = new app.views.ListSelect(this.el)); | 45 | this.addSubview((this.listSelect = new app.views.ListSelect(this.el))); |
| 48 | 46 | ||
| 49 | app.on('ready', this.render); | 47 | app.on("ready", this.render); |
| 50 | } | 48 | } |
| 51 | 49 | ||
| 52 | activate() { | 50 | activate() { |
| 53 | if (super.activate(...arguments)) { | 51 | if (super.activate(...arguments)) { |
| 54 | for (var slug in this.lists) { var list = this.lists[slug]; list.activate(); } | 52 | for (var slug in this.lists) { |
| 53 | var list = this.lists[slug]; | ||
| 54 | list.activate(); | ||
| 55 | } | ||
| 55 | this.listSelect.selectCurrent(); | 56 | this.listSelect.selectCurrent(); |
| 56 | } | 57 | } |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | deactivate() { | 60 | deactivate() { |
| 60 | if (super.deactivate(...arguments)) { | 61 | if (super.deactivate(...arguments)) { |
| 61 | for (var slug in this.lists) { var list = this.lists[slug]; list.deactivate(); } | 62 | for (var slug in this.lists) { |
| 63 | var list = this.lists[slug]; | ||
| 64 | list.deactivate(); | ||
| 65 | } | ||
| 62 | } | 66 | } |
| 63 | } | 67 | } |
| 64 | 68 | ||
| 65 | render() { | 69 | render() { |
| 66 | let html = ''; | 70 | let html = ""; |
| 67 | for (var doc of Array.from(app.docs.all())) { | 71 | for (var doc of Array.from(app.docs.all())) { |
| 68 | html += this.tmpl('sidebarDoc', doc, {fullName: app.docs.countAllBy('name', doc.name) > 1}); | 72 | html += this.tmpl("sidebarDoc", doc, { |
| 73 | fullName: app.docs.countAllBy("name", doc.name) > 1, | ||
| 74 | }); | ||
| 69 | } | 75 | } |
| 70 | this.html(html); | 76 | this.html(html); |
| 71 | if (!app.isSingleDoc() && (app.disabledDocs.size() !== 0)) { this.renderDisabled(); } | 77 | if (!app.isSingleDoc() && app.disabledDocs.size() !== 0) { |
| 78 | this.renderDisabled(); | ||
| 79 | } | ||
| 72 | } | 80 | } |
| 73 | 81 | ||
| 74 | renderDisabled() { | 82 | renderDisabled() { |
| 75 | this.append(this.tmpl('sidebarDisabled', {count: app.disabledDocs.size()})); | 83 | this.append( |
| 84 | this.tmpl("sidebarDisabled", { count: app.disabledDocs.size() }), | ||
| 85 | ); | ||
| 76 | this.refreshElements(); | 86 | this.refreshElements(); |
| 77 | this.renderDisabledList(); | 87 | this.renderDisabledList(); |
| 78 | } | 88 | } |
| 79 | 89 | ||
| 80 | renderDisabledList() { | 90 | renderDisabledList() { |
| 81 | if (app.settings.get('hideDisabled')) { | 91 | if (app.settings.get("hideDisabled")) { |
| 82 | this.removeDisabledList(); | 92 | this.removeDisabledList(); |
| 83 | } else { | 93 | } else { |
| 84 | this.appendDisabledList(); | 94 | this.appendDisabledList(); |
| @@ -87,60 +97,67 @@ const Cls = (app.views.DocList = class DocList extends app.View { · 2 unmodified lines | |||
| 87 | 97 | ||
| 88 | appendDisabledList() { | 98 | appendDisabledList() { |
| 89 | let doc; | 99 | let doc; |
| 90 | let html = ''; | 100 | let html = ""; |
| 91 | const docs = [].concat(...Array.from(app.disabledDocs.all() || [])); | 101 | const docs = [].concat(...Array.from(app.disabledDocs.all() || [])); |
| 92 | 102 | ||
| 93 | while ((doc = docs.shift())) { | 103 | while ((doc = docs.shift())) { |
| 94 | if (doc.version != null) { | 104 | if (doc.version != null) { |
| 95 | var versions = ''; | 105 | var versions = ""; |
| 96 | while (true) { | 106 | while (true) { |
| 97 | versions += this.tmpl('sidebarDoc', doc, {disabled: true}); | 107 | versions += this.tmpl("sidebarDoc", doc, { disabled: true }); |
| 98 | if ((docs[0] != null ? docs[0].name : undefined) !== doc.name) { break; } | 108 | if ((docs[0] != null ? docs[0].name : undefined) !== doc.name) { |
| 109 | break; | ||
| 110 | } | ||
| 99 | doc = docs.shift(); | 111 | doc = docs.shift(); |
| 100 | } | 112 | } |
| 101 | html += this.tmpl('sidebarDisabledVersionedDoc', doc, versions); | 113 | html += this.tmpl("sidebarDisabledVersionedDoc", doc, versions); |
| 102 | } else { | 114 | } else { |
| 103 | html += this.tmpl('sidebarDoc', doc, {disabled: true}); | 115 | html += this.tmpl("sidebarDoc", doc, { disabled: true }); |
| 104 | } | 116 | } |
| 105 | } | 117 | } |
| 106 | 118 | ||
| 107 | this.append(this.tmpl('sidebarDisabledList', html)); | 119 | this.append(this.tmpl("sidebarDisabledList", html)); |
| 108 | this.disabledTitle.classList.add('open-title'); | 120 | this.disabledTitle.classList.add("open-title"); |
| 109 | this.refreshElements(); | 121 | this.refreshElements(); |
| 110 | } | 122 | } |
| 111 | 123 | ||
| 112 | removeDisabledList() { | 124 | removeDisabledList() { |
| 113 | if (this.disabledList) { $.remove(this.disabledList); } | 125 | if (this.disabledList) { |
| 114 | this.disabledTitle.classList.remove('open-title'); | 126 | $.remove(this.disabledList); |
| 127 | } | ||
| 128 | this.disabledTitle.classList.remove("open-title"); | ||
| 115 | this.refreshElements(); | 129 | this.refreshElements(); |
| 116 | } | 130 | } |
| 117 | 131 | ||
| 118 | reset(options) { | 132 | reset(options) { |
| 119 | if (options == null) { options = {}; } | 133 | if (options == null) { |
| 134 | options = {}; | ||
| 135 | } | ||
| 120 | this.listSelect.deselect(); | 136 | this.listSelect.deselect(); |
| 121 | if (this.listFocus != null) { | 137 | if (this.listFocus != null) { |
| 122 | this.listFocus.blur(); | 138 | this.listFocus.blur(); |
| 123 | } | 139 | } |
| 124 | this.listFold.reset(); | 140 | this.listFold.reset(); |
| 125 | if (options.revealCurrent || app.isSingleDoc()) { this.revealCurrent(); } | 141 | if (options.revealCurrent || app.isSingleDoc()) { |
| 142 | this.revealCurrent(); | ||
| 143 | } | ||
| 126 | } | 144 | } |
125 more lines aren’t shown here.
This diff is large: its 160 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 126 lines aren’t shown here.
This diff is large: its 216 lines aren’t shown here.
This diff is large: its 126 lines aren’t shown here.
This diff is large: its 100 lines aren’t shown here.
This diff is large: its 162 lines aren’t shown here.