|
| 1 | +/* globals CHAT, StackExchange, jQuery */ |
1 | 2 | (function(sox, $) { |
2 | 3 | 'use strict'; |
3 | 4 | const SOX_SETTINGS = 'SOXSETTINGS'; |
|
49 | 50 | let Stack; |
50 | 51 | if (location.href.indexOf('github.com') === -1) { //need this so it works on FF -- CSP blocks window.eval() it seems |
51 | 52 | Chat = (typeof window.CHAT === 'undefined' ? window.eval('typeof CHAT != \'undefined\' ? CHAT : undefined') : CHAT); |
52 | | - Stack = (typeof Chat === 'undefined' ? (typeof StackExchange === 'undefined' ? window.eval('if (typeof StackExchange != "undefined") StackExchange') : (StackExchange || window.StackExchange)) : undefined); |
| 53 | + Stack = (typeof Chat === 'undefined' |
| 54 | + ? (typeof StackExchange === 'undefined' |
| 55 | + ? window.eval('if (typeof StackExchange != "undefined") StackExchange') |
| 56 | + : (StackExchange || window.StackExchange)) |
| 57 | + : undefined); |
53 | 58 | } |
54 | 59 |
|
55 | 60 | sox.Stack = Stack; |
|
91 | 96 | return settings === undefined ? undefined : JSON.parse(settings); |
92 | 97 | }, |
93 | 98 | save: function(settings) { |
94 | | - GM_setValue(SOX_SETTINGS, typeof settings === 'string' ? settings : JSON.stringify(settings)); //if importing, it will already be a string so don't stringify the string! |
| 99 | + // If importing, it will already be a string so there's no need to stringify it |
| 100 | + GM_setValue(SOX_SETTINGS, typeof settings === 'string' ? settings : JSON.stringify(settings)); |
95 | 101 | }, |
96 | 102 | reset: function() { |
97 | 103 | const keys = GM_listValues(); |
98 | 104 | sox.debug(keys); |
99 | | - for (let i = 0; i < keys.length; i++) { |
100 | | - const key = keys[i]; |
101 | | - GM_deleteValue(key); |
102 | | - } |
| 105 | + keys.forEach(key => GM_deleteValue(key)); |
103 | 106 | }, |
104 | 107 | get accessToken() { |
105 | 108 | const accessToken = GM_getValue('SOX-accessToken', false); |
|
149 | 152 | use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', `#sox_${name}`); |
150 | 153 | svg.appendChild(use); |
151 | 154 |
|
152 | | - if (css) $(svg).css(css); |
153 | 155 | svg.classList.add('sox-sprite'); |
154 | 156 | svg.classList.add(`sox-sprite-${name}`); |
155 | | - return $(svg); |
| 157 | + return svg; |
156 | 158 | }, |
157 | 159 | }; |
158 | 160 |
|
|
257 | 259 | } |
258 | 260 | sox.debug(`API: Sending request to URL: '${queryURL}'`); |
259 | 261 |
|
260 | | - $.ajax({ |
261 | | - type: 'get', |
262 | | - url: queryURL, |
263 | | - success: function(d) { |
264 | | - if (d.backoff) { |
265 | | - sox.error('SOX Error: BACKOFF: ' + d.backoff); |
266 | | - } else if (d.error_id == 502) { |
267 | | - sox.error('THROTTLE VIOLATION', d); |
268 | | - } else if (d.error_id == 403) { |
269 | | - sox.warn('Access token invalid! Opening window to get new one'); |
270 | | - window.open('https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/'); |
271 | | - alert('Your access token is no longer valid. A window has been opened to request a new one.'); |
| 262 | + fetch(queryURL).then(apiResponse => apiResponse.json()).then(responseJson => { |
| 263 | + if (responseJson.backoff) { |
| 264 | + sox.error('SOX Error: BACKOFF: ' + responseJson.backoff); |
| 265 | + } else if (responseJson.error_id == 502) { |
| 266 | + sox.error('THROTTLE VIOLATION', responseJson); |
| 267 | + } else if (responseJson.error_id == 403) { |
| 268 | + sox.warn('Access token invalid! Opening window to get new one'); |
| 269 | + window.open('https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/'); |
| 270 | + alert('Your access token is no longer valid. A window has been opened to request a new one.'); |
| 271 | + } else { |
| 272 | + if (useCache) { |
| 273 | + responseJson.items.forEach(item => { |
| 274 | + item.sox_request_time = new Date().getTime(); |
| 275 | + finalItems.push(item); |
| 276 | + endpointCache.push(item); |
| 277 | + }); |
| 278 | + GM_setValue('SOX-apiCache', JSON.stringify(apiCache)); |
| 279 | + sox.debug('API: saving new cache', apiCache); |
272 | 280 | } else { |
273 | | - if (useCache) { |
274 | | - d.items.forEach(item => { |
275 | | - item.sox_request_time = new Date().getTime(); |
276 | | - finalItems.push(item); |
277 | | - endpointCache.push(item); |
278 | | - }); |
279 | | - GM_setValue('SOX-apiCache', JSON.stringify(apiCache)); |
280 | | - sox.debug('API: saving new cache', apiCache); |
281 | | - } else { |
282 | | - finalItems = d.items; |
283 | | - } |
284 | | - callback(finalItems); |
| 281 | + finalItems = responseJson.items; |
285 | 282 | } |
286 | | - }, |
287 | | - error: function(a, b, c) { |
288 | | - sox.error('SOX Error: ' + b + ' ' + c); |
289 | | - }, |
| 283 | + callback(finalItems); |
| 284 | + } |
290 | 285 | }); |
291 | 286 | }, |
292 | 287 | observe: function (targets, elements, callback) { |
|
384 | 379 | return siteMatch ? siteMatch[1] : null; |
385 | 380 | }, |
386 | 381 | createModal: function (params) { |
387 | | - const $dialog = $('<aside/>', { |
388 | | - 'class': 's-modal js-modal-overlay js-modal-close js-stacks-managed-popup js-fades-with-aria-hidden sox-custom-dialog', |
389 | | - 'role': 'dialog', |
390 | | - 'aria-hidden': false, |
391 | | - }); |
392 | | - if (params.css) $dialog.css(params.css); |
393 | | - if (params.id) $dialog.attr('id', params.id); |
394 | | - const $dialogInnerContainer = $('<div/>', { |
395 | | - 'class': 's-modal--dialog js-modal-dialog ', |
396 | | - 'style': 'min-width: 568px;',// top: 227.736px; left: 312.653px;', |
397 | | - }); |
398 | | - if (params.css) $dialogInnerContainer.css(params.css); |
399 | | - const $header = $('<h1/>', { |
400 | | - 'class': 's-modal--header fs-headline1 fw-bold mr48 js-first-tabbable sox-custom-dialog-header', |
401 | | - 'html': params.header, |
402 | | - }); |
403 | | - const $mainContent = $('<div/>', { |
404 | | - 'class': 's-modal--body sox-custom-dialog-content', |
405 | | - }); |
406 | | - if (params.html) $mainContent.html(params.html); |
407 | | - const $closeButton = $('<button/>', { |
408 | | - 'class': 's-modal--close s-btn s-btn__muted js-modal-close js-last-tabbable', |
409 | | - 'click': () => $('.sox-custom-dialog').remove(), |
410 | | - }).append($('<svg aria-hidden="true" class="svg-icon m0 iconClearSm" width="14" height="14" viewBox="0 0 14 14"><path d="M12 3.41L10.59 2 7 5.59 3.41 2 2 3.41 5.59 7 2 10.59 3.41 12 7 8.41 10.59 12 12 10.59 8.41 7z"></path></svg>')); |
411 | | - |
412 | | - $dialogInnerContainer.append($header).append($mainContent).append($closeButton); |
413 | | - $dialog.append($dialogInnerContainer); |
414 | | - |
415 | | - return $dialog; |
| 382 | + const closeButtonSvg = `<svg aria-hidden="true" class="svg-icon m0 iconClearSm" width="14" height="14" viewBox="0 0 14 14"> |
| 383 | + <path d="M12 3.41L10.59 2 7 5.59 3.41 2 2 3.41 5.59 7 2 10.59 3.41 12 7 8.41 10.59 12 12 10.59 8.41 7z"></path> |
| 384 | + </svg>`; |
| 385 | + |
| 386 | + const dialog = document.createElement('aside'); |
| 387 | + dialog.className = 's-modal js-modal-overlay js-modal-close js-stacks-managed-popup js-fades-with-aria-hidden sox-custom-dialog'; |
| 388 | + dialog.role = 'dialog'; |
| 389 | + dialog.ariaHidden = false; |
| 390 | + if (params.id) dialog.id = params.id; |
| 391 | + |
| 392 | + const dialogInnerContainer = document.createElement('div'); |
| 393 | + dialogInnerContainer.className = 's-modal--dialog js-modal-dialog'; |
| 394 | + dialogInnerContainer.style.minWidth = '568px'; // top: 227.736px; left: 312.653px; |
| 395 | + |
| 396 | + // if (params.css) $dialog.css(params.css) |
| 397 | + // if (params.css) $dialogInnerContainer.css(params.css); |
| 398 | + |
| 399 | + const header = document.createElement('h1'); |
| 400 | + header.className = 's-modal--header fs-headline1 fw-bold mr48 js-first-tabbable sox-custom-dialog-header'; |
| 401 | + header.innerHTML = params.header; |
| 402 | + const mainContent = document.createElement('div'); |
| 403 | + mainContent.className = 's-modal--body sox-custom-dialog-content'; |
| 404 | + if (params.html) mainContent.innerHTML = params.html; |
| 405 | + |
| 406 | + const closeButton = document.createElement('button'); |
| 407 | + closeButton.className = 's-modal--close s-btn s-btn__muted js-modal-close js-last-tabbable'; |
| 408 | + closeButton.onclick = () => document.querySelector('.sox-custom-dialog').remove(); |
| 409 | + closeButton.insertAdjacentHTML('beforeend', closeButtonSvg); |
| 410 | + |
| 411 | + dialogInnerContainer.appendChild(header); |
| 412 | + dialogInnerContainer.appendChild(mainContent); |
| 413 | + dialogInnerContainer.appendChild(closeButton); |
| 414 | + dialog.appendChild(dialogInnerContainer); |
| 415 | + |
| 416 | + return dialog; |
416 | 417 | }, |
417 | 418 | addButtonToHelpMenu: function (params) { |
418 | | - const $li = $('<li/>'); |
419 | | - const $a = $('<a/>', { |
420 | | - 'href': 'javascript:void(0)', |
421 | | - 'id': params.id, |
422 | | - 'text': `SOX: ${params.linkText}`, |
423 | | - }); |
424 | | - const $span = $('<span/>', { |
425 | | - 'class': 'item-summary', |
426 | | - 'text': params.summary, |
427 | | - }); |
428 | | - $li.on('click', params.click); |
429 | | - $li.append($a.append($span)); |
430 | | - $('.topbar-dialog.help-dialog.js-help-dialog > .modal-content ul').append($li); |
| 419 | + const liElement = document.createElement('li'); |
| 420 | + const anchor = document.createElement('a'); |
| 421 | + anchor.href = 'javascript:void(0)'; |
| 422 | + anchor.id = params.id; |
| 423 | + anchor.innerText = `SOX: ${params.linkText}`; |
| 424 | + const span = document.createElement('span'); |
| 425 | + span.className = 'item-summary'; |
| 426 | + span.innerText = params.summary; |
| 427 | + |
| 428 | + liElement.addEventListener('click', params.click); |
| 429 | + anchor.appendChild(span); |
| 430 | + liElement.appendChild(anchor); |
| 431 | + document.querySelector('.topbar-dialog.help-dialog.js-help-dialog .modal-content ul').appendChild(liElement); |
431 | 432 | }, |
432 | 433 | surroundSelectedText: function(textarea, start, end) { |
433 | 434 | // same wrapper code on either side (`$...$`) |
|
480 | 481 |
|
481 | 482 | sox.Stack.MarkdownEditor.refreshAllPreviews(); |
482 | 483 | }, |
| 484 | + getCssProperty: function(element, propertyValue) { |
| 485 | + return window.getComputedStyle(element).getPropertyValue(propertyValue); |
| 486 | + }, |
483 | 487 | }; |
484 | 488 |
|
485 | 489 | sox.site = { |
|
493 | 497 | currentApiParameter: sox.helpers.getSiteNameFromLink(location.href), |
494 | 498 | get name() { |
495 | 499 | if (Chat) { |
496 | | - return $('#footer-logo a').attr('title'); |
| 500 | + return document.querySelector('#footer-logo a').title; |
497 | 501 | } else { //using StackExchange object doesn't give correct name (eg. `Biology` is called `Biology Stack Exchange` in the object) |
498 | | - return $('.js-topbar-dialog-corral .modal-content.current-site-container .current-site-link div').attr('title'); |
| 502 | + return document.querySelector('.js-topbar-dialog-corral .modal-content.current-site-container .current-site-link div').title; |
499 | 503 | } |
500 | 504 | }, |
501 | 505 |
|
|
507 | 511 | return this.types.meta; |
508 | 512 | } else { |
509 | 513 | // check if site is in beta or graduated |
510 | | - if ($('.beta-title').length > 0) { |
| 514 | + if (document.querySelector('.beta-title')) { |
511 | 515 | return this.types.beta; |
512 | 516 | } else { |
513 | 517 | return this.types.main; |
|
517 | 521 | return null; |
518 | 522 | }, |
519 | 523 | get icon() { |
520 | | - return 'favicon-' + $('.current-site a:not([href*=\'meta\']) .site-icon').attr('class').split('favicon-')[1]; |
| 524 | + return 'favicon-' + document.querySelector('.current-site a:not([href*=\'meta\']) .site-icon').className.split('favicon-')[1]; |
521 | 525 | }, |
522 | | - url: location.hostname, //e.g. "meta.stackexchange.com" |
523 | | - href: location.href, //e.g. "https://meta.stackexchange.com/questions/blah/blah" |
| 526 | + url: location.hostname, // e.g. "meta.stackexchange.com" |
| 527 | + href: location.href, // e.g. "https://meta.stackexchange.com/questions/blah/blah" |
524 | 528 | }; |
525 | 529 |
|
526 | 530 | sox.location = { |
|
537 | 541 | matchWithPattern: function(pattern, urlToMatchWith) { //commented version @ https://jsfiddle.net/shub01/t90kx2dv/ |
538 | 542 | if (pattern == 'SE1.0') { //SE.com && Area51.SE.com special checking |
539 | 543 | if (urlToMatchWith) { |
540 | | - if (urlToMatchWith.match(/https?:\/\/stackexchange\.com\/?/) || (sox.location.matchWithPattern('*://area51.stackexchange.com/*') && sox.site.href.indexOf('.meta.') === -1)) return true; |
| 544 | + if (urlToMatchWith.match(/https?:\/\/stackexchange\.com\/?/) |
| 545 | + || (sox.location.matchWithPattern('*://area51.stackexchange.com/*') && sox.site.href.indexOf('.meta.') === -1)) return true; |
541 | 546 | } else { |
542 | | - if (location.href.match(/https?:\/\/stackexchange\.com\/?/) || (sox.location.matchWithPattern('*://area51.stackexchange.com/*') && sox.site.href.indexOf('.meta.') === -1)) return true; |
| 547 | + if (location.href.match(/https?:\/\/stackexchange\.com\/?/) || |
| 548 | + (sox.location.matchWithPattern('*://area51.stackexchange.com/*') && sox.site.href.indexOf('.meta.') === -1)) return true; |
543 | 549 | } |
544 | 550 | return false; |
545 | 551 | } |
|
590 | 596 | if (sox.site.type == sox.site.types.chat) { |
591 | 597 | return Chat.RoomUsers.current().name; |
592 | 598 | } else { |
593 | | - const $uname = $('.top-bar div.gravatar-wrapper-24'); //used to be $('body > div.topbar > div > div.topbar-links > a > div.gravatar-wrapper-24'); |
594 | | - return ($uname.length ? $uname.attr('title') : false); |
| 599 | + const username = document.querySelector('.top-bar div.gravatar-wrapper-24'); // used to be 'body div.topbar div div.topbar-links a div.gravatar-wrapper-24' |
| 600 | + return (username ? username.title : false); |
595 | 601 | } |
596 | 602 | }, |
597 | 603 | get loggedIn() { |
|
0 commit comments