Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- multiple command key shortcuts#4 24-Feb-2019
- Function:
- command + s to save the post.
- control + command + h Toggles between showing the underlying html and back to normal.
- Note: Apple filters the html.
- [ You should use this for view only. Making changes is at your own risk. ]
- control + command + p for pick picture
- control + command + PageUp to decrease edit box size
- control + command + PageDown to increase edit box size
- control + command + t for quote
- If a reply page, make preferred community the default community
- Avoids use of jquery. jquery is not working for me at this time.
- interesting
- https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
- https://discussions.apple.com/thread/250079673?page=1
- from Greasemonkey hacks: tips & Tools for Remixing the web with Firefox
- by Julien Couvreur
- Code from book with his gracious permission
- Javascript: The Definitive Guide 6th Edition
- https://stackoverflow.com/questions/2878983/capture-key-press-without-placing-an-input-element-on-the-page
- window.addEventListener('keydown', function (e) {
- if (e.ctrlKey && e.keyCode == 90) {
- // Ctrl + z pressed
- }
- });
- Assigns each level of a brace to a different color.
- http://www.balancebraces.com/
- show utf character codes:
- https://mothereff.in/js-escapes#1%E6%BC%A2%E5%AD%97
- getting and setting css variables
- https://davidwalsh.name/css-variables-javascript
- How to initialize
- :root {
- --my-variable-name: #999999;
- }
- getComputedStyle(document.documentElement)
- .getPropertyValue('--my-variable-name'); // #999999
- document.documentElement.style
- .setProperty('--my-variable-name', 'pink');
- window.location.href returns the href (URL) of the current page.
- What html characters do we need to escape?
- < (<)
- > (>)
- & (&)
- " double-quote (")
- ' single quote (')
- https://www.w3.org/International/questions/qa-escapes#use
- Support for full regular expressions in include and exclude rules is also available.
- If the rule both starts and ends with a forward-slash (/) character,
- the contents inside those slashes are interpreted as a regular expression.
- two types of url's we need to handle:
- https://discussions.apple.com/thread/250075737
- https://discussions.apple.com/create/question?communityId=2206020
- https://discussions.apple.com/create/userTip?communityId=2206020
- https://discussions.apple.com/docs/DOC-250000368
- */
- /* eslint */
- /* https://eslint.org/docs/rules/no-multi-spaces */
- /* Most of these rule adjustment don't work in */
- /* this browser :-(. */
- /* eslint "ignoreEOLComments": "off", "ignoreComments": "off" */
- // /*eslint no-caller: "error"*/
- // /* eslint no-multi-spaces: ["error", { ignoreEOLComments: true }] */
- // ==UserScript==
- // @name multiple command key shortcuts#5 1-Apr-2019
- // @namespace bubo-bubo/gmscripts
- // @description Implement save and html edit shortcut.
- // @include /https://discussions\.apple\.com/+thread/+.*/
- // @include /https://discussions\.apple\.com/create/question.*/
- // @include /https://discussions\.apple\.com/create/userTip.*/
- // @include /https://discussions\.apple\.com/docs/.*/
- // @version 19Jan2019
- // @grant none
- // ==/UserScript==
- var debug = 2; // 0 -- no debug
- // 1 -- normal debug
- // 2 -- intense debug
- // 3 -- extremely intense
- // Keeps tracks of what stage we are in our HTML edit.
- // 0 is not editing
- // 1 is editing. Thereby displaying actually html.
- var trackHTMLEdit = false;
- // Is event listner load deployed?
- var deployListnerLoad = false;
- let aDate = new Date();
- console.log ("====> starting command key shortcuts. " + aDate + " <====");
- // Setup once page is loaded.
- // This program monitors incoming keypress. When a known key is observed, this program takes the
- // assigned action.
- /* Most likely will be interactive.
- The script will run after the main page is loaded, but before other resources
- (images, style sheets, etc.) have loaded.
- https://wiki.greasespot.net/Metadata_Block#.40run-at */
- if (debug) console.log("document.readyState is " + document.readyState);
- // ready to roll? More stuff for later?
- if (document.readyState=="complete") {
- console.log("rolling along")
- // rolling along
- pageLoadComplete()
- }
- else {
- if (debug>=2) console.log('adding listeners')
- // wait out page loading.
- window.addEventListener("load",
- pageLoadComplete,
- true);
- deployListnerLoad = true;
- }
- // register event listeners
- // nothing like starting at the end ;-)
- window.addEventListener('unload',
- function(e)
- {
- if (debug) console.log('unloading.');
- // may not be needed. Wonder if that is bad?
- if (deployListnerLoad) {
- window.removeEventListener("load",
- pageLoadComplete,
- true);
- deployListnerLoad = false;
- }
- window.removeEventListener("keypress",
- processKey,
- false);
- if (debug) console.log('removing e.type is '+ e.type + " arg is " + arguments.callee);
- // remove anonymous unload. [ Thus unload ]
- window.removeEventListener(e.type,
- arguments.callee,
- true);
- },
- true);
- // last set value is returned as return code.
- var done;
- done = 0;
- // -----------------------------------------------------------------------------
- function pageLoadComplete() {
- if (debug) console.log ("--> pageLoadComplete. ");
- // find how many reply buttons there are. Not used for nothing.
- if (debug>=2){
- console.log("All page resources finished loading!")
- const theNodes = document.querySelectorAll(
- "button.button.button-black:not(.hidden):not(.button-checked)")
- spew ("theNodes",theNodes)
- }
- /* Let's get started. */
- processPage()
- } // end function pageLoadComplete
- /* --------------------------------------------------------------------- */
- /* main line code */
- function processPage() {
- if (debug) console.log ("--> processPage. ");
- // See if this is a reply page which we will then click on preferred link to make default
- // https://discussions.apple.com/create/question?communityId=2140020
- // Skip:
- // https://discussions.apple.com/profile/rccharles/subscriptions
- const currentPage = window.location.href // returns the href (URL) of the current page.
- console.log ("currentPage is ", currentPage)
- if ( currentPage.search(/https:\/\/discussions.apple.com\/create\/question\?communityId=/)>=0) {
- // Default post from perferred community
- let saveRevealIcon = document.querySelector('div.create-post-topic div.create-post-selection-list div.selector-item button.selector-choice');
- if (debug>=2) console.log ("saveRevealIcon is ",saveRevealIcon)
- if (saveRevealIcon) {
- //clickAnchorTag(saveRevealIcon)
- }
- }
- /* Who knows, we might have to wait a bit */
- /* <div class="editor-section"> */
- //var theNode = document.querySelector("div.editor-section")
- var theNode = document.querySelector("div#main-content")
- console.log("theNode is ",theNode)
- theNode.addEventListener("keypress",
- processKey,
- false);
- if (debug) console.log ("done with processPage ");
- } // end of processPage;
- // -----------------------------------------;
- function processKey(event) {
- if (debug) console.log("in processKey with event input event",event);
- if (debug>=2) {
- console.log(" event.altKey is " + event.altKey);
- console.log(" event.ctrlKey is " + event.ctrlKey);
- console.log(" event.metaKey is " + event.metaKey);
- console.log(" event.shiftKey is " + event.shiftKey);
- console.log(" event.charCode is " + event.charCode + " event.key is " + event.key);
- } // end if debug;
- // Check modifier keys;
- /*
- command + s for save post
- control + command + h toggle for html viewing
- [ You should use this for view only. Making changes is at your own risk. ]
- control + command + p for pick picture
- control + command + PageUp to decrease edit box size
- control + command + PageDown to increase edit box size
- control + command + t for quote
- */
- if (event.altKey) {
- // skip;
- if (debug>=2) console.log (" skipping altKey! no actions assigned.");
- }
- else if (event.shiftKey) {
- // skip;
- if (debug>=2) console.log (" skipping shiftKey! no actions assigned.");
- }
- // meta key is required for save, html edit, quote and select picture;
- else if (event.metaKey) {
- if (debug>=2) console.log (" metaKey found!");
- // control key is required for html edit, quote, select picture etc.
- if (event.ctrlKey) {
- if (debug>=2) console.log (" ctrlKey found!");
- // could be control + command + h for html edit;
- if (event.charCode === 104) {
- if (debug>=2) console.log (" command + control + h!");
- htmlEdit(event);
- }
- // could be control + command + p for pick picture;
- else if (event.charCode === 112) {
- if (debug>=2) console.log (" command + control + p!");
- findPicture(event);
- if (debug>=2) console.log (" processed image");
- }
- // could be control + command + PageUp to decrease edit box size;
- else if (event.key === "PageUp") {
- if (debug>=2) console.log (" command + control + PageUp!");
- changeEditBoxSize(event,-100);
- if (debug>=2) console.log (" processed PageUp");
- }
- // could be control + command + PageDown to increase edit box size;
- else if (event.key === "PageDown" ) {
- if (debug>=2) console.log (" command + control + PageDown!");
- changeEditBoxSize(event,100);
- if (debug>=2) console.log (" processed PageDown");
- }
- // could be control + command + t to input a quote
- else if (event.key === "t" ) {
- if (debug>=2) console.log (" command + control + t!");
- makeQuote(event);
- if (debug>=2) console.log (" processed quote");
- }
- } // end of ctrlKey;
- else {
- if (debug>=2) console.log (" processing metaKey!");
- // could be command + s for save post
- if (event.charCode === 115 ){
- if (debug>=2) console.log (" found command + s!");
- // <div class="editor-section hidden">;
- // ... clipped ...
- // <button class="button" data-action="submit-post">Post</button>
- // same for repy and edit.
- // div.editor-section
- // let saveButton = document.querySelector('button.button[data-action="submit-post"]')
- // noticed after reply, reminants are left hidden :-(. So, avoid hidden.;
- let saveButton = document.querySelector(
- 'div.editor-section:not(.hidden) button.button[data-action="submit-post"], section.create-post div.create-post-buttons button.button.create-post-button');
- if (debug>=2) console.log ("saveButton is ",saveButton);
- if (saveButton) {
- clickAnchorTag(saveButton);
- // we will take control of command+s, so don't pass up to other event handlers.;
- event.preventDefault();
- }
- else {
- if (debug) console.log("We have no reply to save; bubble up.");
- }
- } // 115 or s;
- } // end of else not control key;
- } // metaKey;
- //GetDescriptionFor(e," ");
- if (debug>=1) console.log(" done with processKey");
- } // end of processKey;
- /* --------------------------------------------------------------------- */
- function htmlEdit(event)
- {
- /*
- This html is for the reply and edit reply. There is a slightly different format for
- a new post and a user tip.
- <div class="editor-section">
- <div id="editor-placeholder" ... >
- <div class="editor ql-container" ... >
- <div class="ql-editor"
- data-gramm="false"
- data-placeholder="Tell us what’s on your mind."
- contenteditable="true">
- */
- // find the editor section.
- // Reply and Edit Reply catch first selector.
- // Create new post and new user tip catch on second selector.
- let saveTextNode = document.querySelector(
- 'div.editor-section:not(.hidden) div#editor-placeholder div.ql-editor, section.create-post div.editor.ql-container div.ql-editor')
- if (debug>=2) console.log("saveTextNode is ",saveTextNode)
- // is the user really doing an edit?
- if (saveTextNode) {
- // should we begin the HTML edit?
- if (trackHTMLEdit===false) {
- // request to edit.
- let savedInnerHTML = saveTextNode.innerHTML
- displayHexString(savedInnerHTML," ")
- if (debug>=2) console.log("savedInnerHTML is ",savedInnerHTML)
- // Convert display to HTML
- // amazing.
- // https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript
- saveTextNode.innerText = savedInnerHTML
- // toggle
- trackHTMLEdit = true
- // we will take control of control + command + h, so don't pass up
- // to other event handlers.
- event.preventDefault()
- }
- else {
- // end HTML edit. Convert to regular text.
- if (debug>=2) console.log("displaying html. Convert back. ")
- // already doing HTML edit.
- let displayedText=saveTextNode.innerText
- if (debug>=2) console.log("displayedText is ",displayedText)
- saveTextNode.innerHTML = displayedText
- // toggle
- trackHTMLEdit = false
- // we will take control of control + command + h, so don't pass up
- // to other event handlers.
- event.preventDefault()
- }
- } // end doing an edit found "right" node
- else {
- if (debug) console.log("reply edit note not found.")
- }
- } // end of function htmlEdit
- /* --------------------------------------------------------------------- */
- function findPicture(event) {
- if (debug>=1) console.log ("in findPicture")
- var savePicture = document.querySelector('div.toolbar div.row section.buttons-group:not(.hide-mobile) button.rte-button.rte-hl-bg-active:first-child')
- if (debug>=2) console.log ("savePicture is ",savePicture)
- if (savePicture) {
- clickAnchorTag(savePicture)
- // we will take control of command+s, so don't pass up to other event handlers.
- event.preventDefault();
- }
- } // end of findPicture
- /* --------------------------------------------------------------------- */
- /*
- // Set multiple styles in a single statement
- elt.style.cssText = "color: blue; border: 1px solid black";
- // Or
- elt.setAttribute("style", "color:red; border: 1px solid blue;");
- // Set specific style while leaving other inline style values untouched
- elt.style.color = "blue";
- https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style
- */
- function changeEditBoxSize(event,changeSize) {
- if (debug>=1) console.log ("in changeEditBoxSize")
- // if we find a messed up style, continue on our merry way.
- try {
- var saveEdit = document.querySelector('div.editor.ql-container');
- if (debug>=2) console.log ("saveEdit is ",saveEdit);
- if (saveEdit) {
- let allStyle = saveEdit.getAttribute("style")
- if (debug>=2) console.log("allStyle are ",allStyle);
- // Have we been through here before?
- if ( allStyle.search(/!important;/)>=0) {
- // been there done that
- editorHeight=saveEdit.style.getPropertyValue('--editor-height');
- console.log("editorHeight via html is ",editorHeight)
- }
- else {
- // need to take from composite style
- editorHeight = window.getComputedStyle(saveEdit).getPropertyValue('--editor-height')
- console.log("editorHeight via getComputedStyle is ",editorHeight)
- }
- /* https://stackoverflow.com/questions/8811027/
- get-elements-custom-css-properties-mystyle-using-javascript/36609605#36609605 */
- // fyi: returns an array
- editorHeightNumber = editorHeight.split("px")
- console.log("editorHeightNumber is ",editorHeightNumber)
- let count = Number(editorHeightNumber[0])
- count+=changeSize
- if (count <= 100){ count = Math.abs(changeSize) }
- console.log("count is ",count)
- const injectionString = count.toString()+"px !important;"
- console.log("injectionString is ",injectionString, "typeof injectionString is "
- + typeof injectionString)
- console.log("saveEdit.attributes are now ",saveEdit.attributes);
- console.log("~1~style is ",saveEdit.getAttribute("style"));
- // Up cascade priority
- // setting css variable, --editor-height, drops off second word, "!important;". :-(.
- editorHeightHTML=saveEdit.style.getPropertyValue('--editor-height');
- console.log ("editorHeightHTML is ", editorHeightHTML)
- const alteredString = allStyle.replace(editorHeightHTML,injectionString)
- console.log (" alteredString is ", alteredString)
- saveEdit.style.cssText = alteredString
- console.log("~2~style is ",saveEdit.getAttribute("style"));
- if (debug>=2) console.log ("eat character")
- // we will take control of command+s, so don't pass up to other event handlers.
- event.preventDefault()
- } // end of if (saveEdit)
- else {
- console.log("\nCould not find edit box!!!\n")
- }
- if (debug>=2) console.log ("bye from changeEditBoxSize")
- //Object.keys(inputObj).forEach(e => console.log(`key=${e} value=${inputObj[e]}`));
- } // end of try
- catch (err) {
- console.log ("Don't understand style format.\"" + err.message + "\"" )
- } // end catch
- } // end function changeEditBoxSize
- /* --------------------------------------------------------------------- */
- // Click on the quote icon.
- function makeQuote(event) {
- if (debug>=1) console.log ("in makeQuote")
- try {
- // Click on the reveal secondary icons. Aa icon
- let saveRevealIcon = document.querySelector('div.toolbar div.row section.buttons-group button.rte-button[data-rte="to-formats"]');
- if (debug>=2) console.log ("saveRevealIcon is ",saveRevealIcon)
- if (saveRevealIcon) {
- clickAnchorTag(saveRevealIcon)
- // Now, click on the quote icon
- let saveQuoteIcon = document.querySelector('div.toolbar div.row.sub-row div.sub-row-wrapper section.buttons-group button.rte-button.rte-hl-bg-active[data-rte="blockquote"]');
- if (debug>=2) console.log ("saveQuoteIcon is ",saveQuoteIcon)
- if (saveQuoteIcon) {
- clickAnchorTag(saveQuoteIcon)
- // Well, we quoted the stuff added to the input area.
- // seems good enough to count as processed request.
- // we will take control of control + command+t,
- // so don't pass up to other event handlers.
- event.preventDefault();
- // Finally, click on the close secondary icon line. X
- let saveQuitIcon = document.querySelector('div.toolbar div.row.sub-row button.rte-close-button');
- if (debug>=2) console.log ("saveQuitIcon is ",saveQuitIcon)
- if (saveQuitIcon) {
- clickAnchorTag(saveQuitIcon)
- } // end of saveQuitIcon
- else {
- console.log ("saveQuitIcon not found!!! ")
- }
- } // end of saveQuoteIcon
- } // end of saveRevealIcon
- } // end of try
- catch (err) {
- console.log ("makeQuote got confused. \"" + err.message + "\"" )
- } // end catch
- } // end of function makeQuote
- /* --------------------------------------------------------------------- */
- function GetDescriptionFor(e,inDent)
- {
- if (debug>=2) {
- console.log (inDent + "in GetDescriptionFor")
- console.log (inDent + "e.keyCode is " + e.keyCode )
- console.log (inDent + "e.keyCode in hex is " +toHexString(e.keyCode,inDent+" ") )
- }
- var result, code;
- if ((e.charCode) && (e.keyCode==0))
- {
- result = "charCode: " + e.charCode;
- code = e.charCode;
- } else {
- result = "keyCode: " + e.keyCode;
- code = e.keyCode;
- }
- if (code == 8) result += " BKSP"
- else if (code == 9) result += " TAB"
- else if (code == 46) result += " DEL"
- else if ((code >= 41) && (code <=126)) result += " '" + String.fromCharCode(code) + "'";
- if (e.altKey) result += " alt";
- if (e.ctrlKey) result += " ctrl";
- if (e.metaKey) result += " meta";
- if (e.shiftKey) result += " shift";
- console.log (inDent + "result is " + result)
- return result;
- } // end of GetDescriptionFor
- /* --------------------------------------------------------------------- */
- /* print out objects:
- https://stackoverflow.com/questions/957537/how-can-i-display-a-javascript-object
- https://code-maven.com/logging-javascript-objects
- */
- function spew (objName,inputObj) {
- if (debug>=1) console.log ("starting spew")
- if (debug>=2) {
- console.log (objName + " is: ", inputObj)
- console.log ("inputObj.length is " + inputObj.length)
- console.log ("typeof inputObj is " + typeof inputObj)
- //this will show object gandalf IN ALL BROWSERS!
- console.log(JSON.stringify(inputObj));
- //this will show object gandalf IN ALL BROWSERS! with beautiful indent
- console.log(JSON.stringify(inputObj, null, 4));
- console.log("keys",Object.keys(inputObj));
- console.log("lenght is " + Object.keys(inputObj).length)
- console.log(Object.values(inputObj));
- //There is also this new option if you're using ECMAScript 2016 or newer:
- try {
- Object.keys(inputObj).forEach(e => console.log(`key=${e} value=${inputObj[e]}`));
- }
- catch (err) {
- console.log (" You must need ECMAScript 2016 or newer \""+ err.message + "\"" )
- } // end catch
- } // end of if debug printing code
- if (debug>=1) console.log ("ending spew")
- } // end of function spew
- // -----------------------------------------
- // demo
- // http://mothereff.in/js-escapes#1%E6%BC%A2%E5%AD%97
- //;
- function displayHexString(value,inDent) {
- if (debug>=1) console.log("in function displayHexString");
- var hexString = value.hexEncode();
- if (debug>=2) console.log (inDent + "hexString is " + hexString);
- if (hexString.length % 2) {
- hexString = '0' + hexString;
- }
- const increment = 16;
- var prefixNumber = 0;
- var prefixNumberSpan = increment;
- // print groups of sixteen;
- while( hexString.length > increment ) {
- let prefixHex = prefixNumber.toString(increment);
- if (prefixHex.length % 2) {
- prefixHex = '0' + prefixHex;
- } // end of if;
- // Print out on console.;
- // four hex digits per one printable.;
- console.log(prefixHex + " " + hexString.substring(0,increment) + " " +
- value.substring(prefixNumber/4,prefixNumberSpan/4) );
- hexString = hexString.substring(increment);
- if (debug>=3) console.log( "hexString is "+ hexString);
- prefixNumber += increment;
- prefixNumberSpan += increment;
- if (debug>=3) console.log("prefixNumber is "+ prefixNumber + " prefixNumberSpan is " + prefixNumberSpan);
- } // end of while;
- if (debug>=3) console.log("done with groups of " + increment);
- if ( hexString.length ) {
- let prefixHex = prefixNumber.toString(increment);
- if (prefixHex.length % 2) {
- prefixHex = '0' + prefixHex;
- } // end of if
- // Print out on console.
- console.log(prefixHex + " " + hexString);
- } // end of if;
- } // end of toHexString;
- /* --------------------------------------------------------------------- */
- /*
- https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style
- <!DOCTYPE HTML>
- <html>
- <body style="font-weight:bold;">
- <div style="color:red" id="myElement">..</div>
- </body>
- </html>
- The style property is not useful for completely learning about the styles applied on the element,
- since it represents only the CSS declarations set in the element's inline style attribute, not
- those that come from style rules elsewhere, such as style rules in the <head> section, or
- external style sheets. To get the values of all CSS properties for an element you should use
- Window.getComputedStyle() instead.
- The output would be something like:
- ...
- fontWeight = '' > 'bold'
- color = 'red' > 'rgb(255, 0, 0)'
- ...
- Note the presence of the value "bold" for font-weight in the computed style and the absence
- of it in the element's style property
- */
- function printCombinedStyle(element) {
- //var element = document.getElementById("myElement");
- var out = "";
- var elementStyle = element.style;
- var computedStyle = window.getComputedStyle(element, null);
- for (prop in elementStyle) {
- if (elementStyle.hasOwnProperty(prop)) {
- out += " " + prop + " = '" + elementStyle[prop] + "' > '" + computedStyle[prop] + "'\n";
- }
- }
- console.log(out)
- } // end of function printCombinedStyle
- /* --------------------------------------------------------------------- */
- // https://stackoverflow.com/questions/21647928/javascript-unicode-string-to-hex
- String.prototype.hexEncode = function(){
- var hex, i;
- var result = "";
- for (i=0; i<this.length; i++) {
- hex = this.charCodeAt(i).toString(16);
- result += ("000"+hex).slice(-4);
- }
- return result
- }
- /* --------------------------------------------------------------------- */
- /* based on:
- https://stackoverflow.com/questions/902713/how-do-i-programmatically-click-a-link-with-javascript
- */
- function clickAnchorTag(inputObj) {
- if (debug>=1) console.log("in clickAnchorTag.\n inputObj is",inputObj)
- var event = document.createEvent('MouseEvent');
- if (debug>=2) console.log ("proceeding to set new event")
- event = new CustomEvent('click');
- if (debug>=2) console.log ("clicking...")
- inputObj.dispatchEvent(event);
- } // end of clickAnchorTag
- /*
- Exception: Error while performing task "pretty-print": Error: Unexpected character '`' (529:53)
- [4]</pp.raise@resource://devtools/shared/acorn/acorn.js:940:13
- [13]</pp.getTokenFromCode@resource://devtools/shared/acorn/acorn.js:2785:3
- [13]</pp.readToken@resource://devtools/shared/acorn/acorn.js:2490:10
- [13]</pp.nextToken@resource://devtools/shared/acorn/acorn.js:2482:66
- [13]</pp.next@resource://devtools/shared/acorn/acorn.js:2431:3
- [13]</pp.getToken@resource://devtools/shared/acorn/acorn.js:2435:3
- prettyFast@resource://devtools/shared/pretty-fast/pretty-fast.js:778:15
- @resource://devtools/server/actors/pretty-print-worker.js:41:24
- createHandler/<@resource://devtools/shared/worker/helper.js:85:24
- @resource://devtools/server/actors/pretty-print-worker.js:51:12
- createHandler/<@resource://devtools/shared/worker/helper.js:85:24
- */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement