Advertisement
rccharles

ASC HTML editor

Jan 20th, 2019
574
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.    multiple command key shortcuts 19Jan2019
  3.    
  4.    Function:
  5.       command + s    to save the post.
  6.       command + control + command + h    Toggles between showing the underlying html and back to normal.
  7.                                          Note: Apple filters the html.
  8.      
  9.       future commands
  10.         select picture
  11.         quote
  12.         strikeout
  13.  
  14.       Intercepts command + s.  "Clicks" on save/edit button. Prevent command+s bubbling.
  15.  
  16.  
  17.     interesting
  18.       https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
  19.       https://discussions.apple.com/thread/250079673?page=1
  20.  
  21.     from Greasemonkey hacks: tips & Tools for Remixing the web with Firefox
  22.     by Julien Couvreur
  23.     Code from book with his gracious permission
  24.  
  25.     Javascript: The Definitive Guide 6th Edition
  26.  
  27.     Avoids use of jquery.  jquery is not working for me at this time.
  28.  
  29. https://stackoverflow.com/questions/2878983/capture-key-press-without-placing-an-input-element-on-the-page
  30. window.addEventListener('keydown', function (e) {
  31.   if (e.ctrlKey && e.keyCode == 90) {
  32.     // Ctrl + z pressed
  33.   }
  34. });
  35.  
  36. What html characters do we need to escape?
  37.     &lt; (<)
  38.     &gt; (>)
  39.     &amp; (&)
  40.     &quot; double-quote (")
  41.     &apos; single quote (')
  42.   https://www.w3.org/International/questions/qa-escapes#use
  43.  
  44. Support for full regular expressions in include and exclude rules is also available.
  45. If the rule both starts and ends with a forward-slash (/) character,
  46. the contents inside those slashes are interpreted as a regular expression.
  47.  
  48. two types of url's we need to handle:
  49. https://discussions.apple.com/thread/250075737
  50. https://discussions.apple.com/create/question?communityId=2206020
  51. https://discussions.apple.com/create/userTip?communityId=2206020
  52. https://discussions.apple.com/docs/DOC-250000368
  53.  
  54. */
  55.  
  56. /* eslint                                        */
  57. /* https://eslint.org/docs/rules/no-multi-spaces */
  58. /* Most of these rule adjustment don't work in   */
  59. /*   this browser :-(.                            */
  60. /* eslint "ignoreEOLComments": "off", "ignoreComments": "off" */
  61. // /*eslint no-caller: "error"*/
  62. // /* eslint no-multi-spaces: ["error", { ignoreEOLComments: true }] */
  63.  
  64. // ==UserScript==
  65. // @name        multiple command key shortcuts 19Jan2019
  66. // @namespace   bubo-bubo/gmscripts
  67. // @description Implement save and html edit shortcut.
  68. // @include     /https://discussions\.apple\.com/+thread/+.*/
  69. // @include     /https://discussions\.apple\.com/create/question.*/
  70. // @include     /https://discussions\.apple\.com/create/userTip.*/
  71. // @include     /https://discussions\.apple\.com/docs/.*/
  72. // @version     19Jan2019
  73. // @grant       none
  74. // ==/UserScript==
  75.  
  76.  
  77. var debug = 2; // 0 -- no debug
  78.                // 1 -- normal debug
  79.                // 2 -- intense debug
  80.  
  81. // Keeps tracks of what stage we are in our HTML edit.
  82. //  0 is not editing
  83. //  1 is editing.  Thereby displaying actually html.
  84. var trackHTMLEdit = false;
  85.  
  86. // Is event listner load deployed?
  87. var deployListnerLoad = false;
  88.  
  89. let aDate = new Date();
  90. console.log ("====> starting command key shortcuts. " + aDate);
  91.  
  92. // Setup once page is loaded.
  93. // This program monitors incoming keypress.  When a known key is observed, this program takes the
  94. // assigned action.
  95.  
  96. /* Most likely will be interactive.
  97.     The script will run after the main page is loaded, but before other resources
  98.     (images, style sheets, etc.) have loaded.
  99.     https://wiki.greasespot.net/Metadata_Block#.40run-at  */
  100. if (debug) console.log("document.readyState is " + document.readyState);
  101.  
  102. //  ready to roll? More stuff for later?
  103. if (document.readyState=="complete") {
  104.   console.log("rolling along")
  105.   // rolling along
  106.   pageLoadComplete()
  107. }
  108. else {
  109.   if (debug>=2) console.log('adding listeners')
  110.   // wait out page loading.
  111.   window.addEventListener("load",
  112.                           pageLoadComplete,
  113.                           true);
  114.   deployListnerLoad = true;
  115. }
  116. // register event listeners
  117. // nothing like starting at the end ;-)
  118.  window.addEventListener('unload',
  119.         function(e)
  120.         {
  121.           if (debug) console.log('unloading.');
  122.           // may not be needed.  Wonder if that is bad?
  123.           if (deployListnerLoad) {
  124.             window.removeEventListener("load",
  125.                                        pageLoadComplete,
  126.                                        true);
  127.             deployListnerLoad = false;
  128.           }
  129.           window.removeEventListener("keypress",
  130.                                      processKey,
  131.                                      false);
  132.           if (debug) console.log('removing e.type is '+ e.type + " arg is " + arguments.callee);
  133.           // remove anonymous unload. [ Thus unload ]
  134.           window.removeEventListener(e.type,
  135.                                      arguments.callee,
  136.                                      true);
  137.         },
  138.         true);
  139.  
  140. // last set value is returned as return code.
  141. var done;
  142. done = 0;
  143.  
  144. // -----------------------------------------------------------------------------
  145. function pageLoadComplete() {
  146.   if (debug>=2) console.log("All page resources finished loading!")
  147.   // find how many reply buttons there are.  Not used for nothing.
  148.   var theNodes = document.querySelectorAll(
  149.     "button.button.button-black:not(.hidden):not(.button-checked)")
  150.   if (debug>=2) spew ("theNodes",theNodes)
  151.   if (debug)console.log("let's get started.")
  152.  
  153.   /* Let's get started. */
  154.   processPage()
  155.  
  156. } // end function pageLoadComplete
  157.  
  158. /* --------------------------------------------------------------------- */
  159. /* main line code */
  160. function processPage() {
  161.   if (debug) console.log ("--> processPage.  ");
  162.  
  163.   /* Who knows, we might have to wait a bit */
  164.   /* <div class="editor-section"> */
  165.   //var theNode = document.querySelector("div.editor-section")
  166.   var theNode = document.querySelector("div#main-content")
  167.   console.log("theNode is ",theNode)
  168.   theNode.addEventListener("keypress",
  169.                            processKey,
  170.                            false);
  171.  
  172.   if (debug) console.log ("done with processPage  ");
  173. } // end of processPage
  174.  
  175. // -----------------------------------------
  176. function processKey(event) {
  177.   if (debug) console.log("in processKey with event input event",event)
  178.   if (debug>=2) {
  179.     console.log("  event.altKey is " + event.altKey)
  180.     console.log("  event.ctrlKey is " + event.ctrlKey)
  181.     console.log("  event.metaKey is " + event.metaKey)
  182.     console.log("  event.shiftKey is " + event.shiftKey)
  183.     if ( event.charCode === 115){
  184.       console.log ("  s ")
  185.     }
  186.    if (event.metaKey){
  187.       console.log ("  event.metaKey ")
  188.    }
  189.   } // end if debug
  190.  
  191.   // Check modifier keys
  192.   if (event.altKey) {
  193.     // skip
  194.     if (debug>=2) console.log ("  skipping altKey! no actions assigned.")
  195.   }
  196.   else if (event.shiftKey) {
  197.     // skip
  198.     if (debug>=2) console.log ("  skipping shiftKey! no actions assigned.")
  199.   }
  200.   // meta key is required for save, html edit, quote and select picture
  201.   else if (event.metaKey) {
  202.     if (debug>=2) console.log ("  metaKey found!")
  203.     // control key is required for html edit, quote and select picture
  204.     if (event.ctrlKey) {
  205.       if (debug>=2) console.log ("  ctrlKey found!")
  206.       // could be command + control + h for html edit
  207.       if (event.charCode === 104) {
  208.         if (debug>=2) console.log ("  command + control + h!")
  209.         htmlEdit(event)
  210.       }
  211.       // could be command + control + p for pick picture
  212.       else if (event.charCode === 112) {
  213.         if (debug>=2) console.log ("  command + control + p!")
  214.       }
  215.     } // end of ctrlKey
  216.     else {
  217.       if (debug>=2) console.log ("  processing metaKey!")
  218.       // could be command + s for save post
  219.       if (event.charCode === 115 ){
  220.         if (debug>=2) console.log ("  found command + s!")
  221.         // <div class="editor-section hidden">
  222.         //   ... clipped ...
  223.         // <button class="button" data-action="submit-post">Post</button>
  224.         // same for repy and edit.
  225.         // div.editor-section
  226.         // let saveButton = document.querySelector('button.button[data-action="submit-post"]')
  227.         // noticed after reply, reminants are left hidden :-(. So, avoid hidden.
  228.         let saveButton = document.querySelector(
  229.                 'div.editor-section:not(.hidden) button.button[data-action="submit-post"], section.create-post div.create-post-buttons button.button.create-post-button')
  230.         if (debug>=2) console.log ("saveButton is ",saveButton)
  231.         if (saveButton) {
  232.           clickAnchorTag(saveButton)
  233.           //  we will take control of command+s, so don't pass up to other event handlers.
  234.           event.preventDefault()
  235.         }
  236.         else {
  237.           if (debug) console.log("We have no reply to save; bubble up.")
  238.         }
  239.       } // 115 or s
  240.     } // end of else not control key
  241.   } // metaKey
  242.   //GetDescriptionFor(e,"  ")
  243.   if (debug>=1) console.log("  done with processKey")
  244. } // end of processKey
  245.  
  246. /* --------------------------------------------------------------------- */
  247. function htmlEdit(event)
  248. {
  249.   /*
  250.       This html is for the reply and edit reply.  There is a slightly different format for
  251.       a new post and a user tip.
  252.       <div class="editor-section">
  253.         <div id="editor-placeholder" ... >
  254.           <div class="editor ql-container" ... >
  255.             <div class="ql-editor"
  256.               data-gramm="false"
  257.               data-placeholder="Tell us what’s on your mind."
  258.               contenteditable="true">
  259.   */
  260.   // find the editor section.
  261.   // Reply and Edit Reply catch first selector.
  262.   // Create new post and new user tip catch on second selector.
  263.   let saveTextNode = document.querySelector(
  264.     'div.editor-section:not(.hidden) div#editor-placeholder div.ql-editor, section.create-post div.editor.ql-container div.ql-editor')
  265.   if (debug>=2) console.log("saveTextNode is ",saveTextNode)
  266.   // is the user really doing an edit?
  267.   if (saveTextNode) {
  268.     // should we begin the HTML edit?
  269.     if (trackHTMLEdit===false) {
  270.       // request to edit.
  271.       let savedInnerHTML = saveTextNode.innerHTML
  272.       if (debug>=2) console.log("savedInnerHTML is ",savedInnerHTML)
  273.       // Convert display to HTML
  274.       // amazing.
  275.       // https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript
  276.       saveTextNode.innerText = savedInnerHTML
  277.       // toggle
  278.       trackHTMLEdit = true
  279.       //  we will take control of control + command + h, so don't pass up
  280.       //    to other event handlers.
  281.       event.preventDefault()
  282.     }
  283.     else {
  284.       // end HTML edit.  Convert to regular text.
  285.       if (debug>=2) console.log("displaying html. Convert back. ")
  286.       // already doing HTML edit.
  287.       let displayedText=saveTextNode.innerText
  288.       if (debug>=2) console.log("displayedText is ",displayedText)
  289.       saveTextNode.innerHTML = displayedText
  290.       // toggle
  291.       trackHTMLEdit = false
  292.       //  we will take control of control + command + h, so don't pass up
  293.       //    to other event handlers.
  294.       event.preventDefault()
  295.     }
  296.   } // end doing an edit found "right" node
  297.   else {
  298.     if (debug) console.log("reply edit note not found.")
  299.   }
  300. } // end of function htmlEdit
  301.  
  302. /* --------------------------------------------------------------------- */
  303. function GetDescriptionFor(e,inDent)
  304. {
  305. console.log (inDent + "in GetDescriptionFor")
  306. console.log (inDent + "e.keyCode is " + e.keyCode )
  307. console.log (inDent + "e.keyCode in hex is " +toHexString(e.keyCode,inDent+"  ") )
  308. var result, code;
  309. if ((e.charCode) && (e.keyCode==0))
  310. {
  311.  result = "charCode: " + e.charCode;
  312.  code = e.charCode;
  313. } else {
  314.  result = "keyCode: " + e.keyCode;
  315.  code = e.keyCode;
  316. }
  317.  
  318. if (code == 8) result += " BKSP"
  319. else if (code == 9) result += " TAB"
  320. else if (code == 46) result += " DEL"
  321. else if ((code >= 41) && (code <=126)) result += " '" + String.fromCharCode(code) + "'";
  322.  
  323. if (e.altKey) result += " alt";
  324. if (e.ctrlKey) result += " ctrl";
  325. if (e.metaKey) result += " meta";
  326. if (e.shiftKey) result += " shift";
  327. console.log (inDent + "result is " + result)
  328. return result;
  329. } // end of GetDescriptionFor
  330.  
  331. /* --------------------------------------------------------------------- */
  332. /* print out objects:
  333.   https://stackoverflow.com/questions/957537/how-can-i-display-a-javascript-object
  334.   https://code-maven.com/logging-javascript-objects
  335. */
  336. function spew (objName,inputObj) {
  337.   if (debug>=1) console.log ("starting spew")
  338.   if (debug>=2) {
  339.     console.log (objName + " is: ", inputObj)
  340.     console.log ("inputObj.length is " + inputObj.length)
  341.     console.log ("typeof inputObj is " + typeof inputObj)
  342.     //this will show object gandalf IN ALL BROWSERS!
  343.     console.log(JSON.stringify(inputObj));
  344.     //this will show object gandalf IN ALL BROWSERS! with beautiful indent
  345.     console.log(JSON.stringify(inputObj, null, 4));
  346.     console.log("keys",Object.keys(inputObj));
  347.     console.log("lenght is " + Object.keys(inputObj).length)
  348.     console.log(Object.values(inputObj));
  349.  
  350.     //There is also this new option if you're using ECMAScript 2016 or newer:
  351.     try {
  352.       Object.keys(inputObj).forEach(e => console.log(`key=${e}  value=${inputObj[e]}`));
  353.     }
  354.     catch (err) {
  355.       console.log (" You must need ECMAScript 2016 or newer \""+ err.message + "\"" )
  356.     } // end catch
  357.  
  358.   } // end of if debug printing code
  359.   if (debug>=1) console.log ("ending spew")
  360. } // end of function spew
  361.  
  362. // -----------------------------------------
  363. //  https://stackoverflow.com/questions/57803/how-to-convert-decimal-to-hex-in-javascript
  364. function toHexString(value,inDent) {
  365.   let hexString = value.toString(16);
  366.   if (debug>=2) console.log (inDent + "hexString is " + hexString)
  367.   if (hexString.length % 2) {
  368.     hexString = '0' + hexString;
  369.   }
  370. return hexString;
  371. } // end of toHexString
  372.  
  373. /* --------------------------------------------------------------------- */
  374. /* based on:
  375.      https://stackoverflow.com/questions/902713/how-do-i-programmatically-click-a-link-with-javascript
  376. */
  377. function clickAnchorTag(inputObj) {
  378.     if (debug>=1) console.log("in clickAnchorTag.\n inputObj is",inputObj)
  379.  
  380.     var event = document.createEvent('MouseEvent');
  381.     if (debug>=2) console.log ("proceeding to set new event")
  382.     event = new CustomEvent('click');
  383.     if (debug>=2) console.log ("clicking...")
  384.     inputObj.dispatchEvent(event);
  385. } // end of clickAnchorTag
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement