2CijWhliBOSA3g4Y8kQJ

NAI Card Reader Script (Finaller)

Aug 19th, 2021 (edited)
3,374
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name        Card Data
  3. // @namespace   Violentmonkey Scripts
  4. // @match       *://rentry.co/*
  5. // @match       *://rentry.org/*
  6. // @match       *://*catbox.moe/*
  7. // @grant       none
  8. // @version     1.2
  9. // @author      me :)
  10. // @description 8/17/2021, 5:50:10 PM
  11. // @require https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js
  12. // @grant GM_xmlhttpRequest
  13. // ==/UserScript==
  14.  
  15. // Card Data Shower: This script shows a box of the json data for an NAI card
  16. // Hover over the card and press control to make the box appear
  17. // Hover over the box and press shift to make the box go away
  18. // Double click on the box to copy just the json data
  19.  
  20.  
  21. // things you might want to change
  22. const triggerKey = 17; // 17 is control
  23. const closeKey   = 16; // 16 is shift
  24. const boxWidth  = "800px";
  25. const boxHeight = "600px";
  26. const boxMargin = "-300px 0 0 -400px";
  27. const fontSize  = "0.7em";
  28.  
  29. // other constants
  30. const pngSignature = [137, 80, 78, 71, 13, 10, 26, 10];
  31. const chunkSignature = [116, 69, 88, 116];
  32. const chunkStart = 33; // beginning of the NAI data chunk
  33. const typeStart = chunkStart + 4;
  34. const dataStart = chunkStart + 8;
  35.  
  36.  
  37. jQuery.noConflict();
  38. jQuery( document ).ready(function( $ ) {
  39.   var source; // identifying images by src
  40.  
  41.   // styles
  42.   $("head").append(`
  43.     <style>
  44.     #follow {
  45.       position: fixed;
  46.       z-index: 100;
  47.       top: 50%;
  48.       left: 50%;
  49.       margin: ${boxMargin};
  50.       width: ${boxWidth};  
  51.       height: ${boxHeight};
  52.       background-color: white;
  53.       overflow: auto;
  54.       border-style: solid;
  55.       border-width: 3px;  
  56.     }
  57.  
  58.     #jsonData {
  59.       font-size: ${fontSize};
  60.       color: black;
  61.     }
  62.  
  63.     #copyIndicator {
  64.       opacity: 0;
  65.       color: red;
  66.     }
  67.  
  68.     .cardHeader {
  69.       color: black;
  70.     }
  71.  
  72.     .cardEntry {
  73.       color: black;
  74.     }
  75.  
  76.     .keyList {
  77.       color: green;
  78.     }
  79.     </style>
  80.   `);
  81.  
  82.  
  83.   // wait until new images appear
  84.   waitForKeyElements ("img[cardEvent!='set']", function() {
  85.    
  86.     $("img[cardEvent!='set']").on("mouseover",function() {
  87.       source = this.src;
  88.      
  89.       if (source[0] == '/') {
  90.         source = "https:" + source;
  91.       }  
  92.      
  93.       $(document).on("keydown",function(e) {
  94.         if (e.keyCode == triggerKey) {          
  95.           getImageData(source);
  96.         }
  97.       });  
  98.     }).on("mouseleave",function() {
  99.       $(document).off("keydown");
  100.     });
  101.  
  102.     $("img").attr("cardEvent","set"); // stops repeated event assignment(?)
  103.   });
  104.  
  105.   // Gets the data from the card and writes
  106.   function getImageData(image) {
  107.     return GM_xmlhttpRequest({
  108.       method: "GET",
  109.       url: image,
  110.       responseType: "arraybuffer",
  111.       onloadend: function(responseObject) {
  112.         const byteArray = new Uint8Array(responseObject.response);
  113.         if (arraysEqual(byteArray.slice(0,8), pngSignature) && arraysEqual(byteArray.slice(typeStart,typeStart+4), chunkSignature)) {
  114.           $("#follow").remove();
  115.          
  116.           let dataLength = getLength(byteArray.slice(chunkStart,chunkStart + 4));
  117.           jsonData = atob(String.fromCharCode(...byteArray.slice(dataStart + 8, dataStart+dataLength)));
  118.           objData = JSON.parse(jsonData);
  119.          
  120.           // create the infobox and fill it up
  121.           $("body").prepend("<div id='follow'></div>");
  122.          
  123.           $("#follow").prepend(`<h3 class="cardHeader">Entries</h3>`);
  124.          
  125.           for (let i = 0; i < objData.entries.length; i++) {
  126.             $("#follow").append(`<p class="cardEntry">${objData.entries[i].text}</p>`);
  127.           }
  128.          
  129.           $("#follow").append(`<h3 class="cardHeader">JSON Data</h3>`);
  130.          
  131.           $("#follow").append(`<p id='jsonData'> ${jsonData} <p>`);
  132.          
  133.           $("#follow").append(`<p id='copyIndicator'>json copied</p>`);
  134.          
  135.          
  136.          
  137.           followEvents();
  138.         }
  139.       }
  140.     });  
  141.   }
  142.  
  143.   // Reads the 4 bytes as given in decimal and returns proper value for data length
  144.   function getLength(lengthBytes) {
  145.     var total = 0;
  146.     for (let i = 0; i < lengthBytes.length; i++) {
  147.       total += lengthBytes[i] * (256**(lengthBytes.length - i - 1));
  148.     }
  149.     return total;
  150.   }
  151.  
  152.   // sets the event handlers for the infobox
  153.   function followEvents() {
  154.     $("#follow").on("mouseover", function() {
  155.      
  156.       $(document).on("keydown",function(e) {
  157.         if (e.keyCode == closeKey) {          
  158.           $("#follow").remove();
  159.         }
  160.       });
  161.    
  162.     }).on("mouseleave", function() {
  163.       $(document).off("keydown");
  164.    
  165.     }).on("dblclick", function() {
  166.       navigator.clipboard.writeText($("#jsonData").text());
  167.       $("#copyIndicator").css("opacity", 1);
  168.       $("#copyIndicator").fadeTo("slow", 0);
  169.     });
  170.   }
  171.  
  172.   // checks if two (ordered) arrays are equal
  173.   function arraysEqual(a, b) {
  174.     if (a === b) return true;
  175.     if (a == null || b == null) return false;
  176.     if (a.length !== b.length) return false;
  177.    
  178.     for (var i = 0; i < a.length; ++i) {
  179.       if (a[i] !== b[i]) return false;
  180.     }
  181.     return true;
  182.   }
  183.    
  184.   // Gotten from https://gist.github.com/BrockA/2625891 ; for handling ajax stuff
  185.   function waitForKeyElements (
  186.     selectorTxt,    /* Required: The jQuery selector string that
  187.                         specifies the desired element(s).
  188.                     */
  189.     actionFunction, /* Required: The code to run when elements are
  190.                         found. It is passed a jNode to the matched
  191.                         element.
  192.                     */
  193.     bWaitOnce,      /* Optional: If false, will continue to scan for
  194.                         new elements even after the first match is
  195.                         found.
  196.                     */
  197.     iframeSelector  /* Optional: If set, identifies the iframe to
  198.                         search.
  199.                     */
  200.   ) {
  201.     var targetNodes, btargetsFound;
  202.  
  203.     if (typeof iframeSelector == "undefined")
  204.         targetNodes     = $(selectorTxt);
  205.     else
  206.         targetNodes     = $(iframeSelector).contents ()
  207.                                            .find (selectorTxt);
  208.  
  209.     if (targetNodes  &&  targetNodes.length > 0) {
  210.         btargetsFound   = true;
  211.         /*--- Found target node(s).  Go through each and act if they
  212.             are new.
  213.         */
  214.         targetNodes.each ( function () {
  215.             var jThis        = $(this);
  216.             var alreadyFound = jThis.data ('alreadyFound')  ||  false;
  217.  
  218.             if (!alreadyFound) {
  219.                 //--- Call the payload function.
  220.                 var cancelFound     = actionFunction (jThis);
  221.                 if (cancelFound)
  222.                     btargetsFound   = false;
  223.                 else
  224.                     jThis.data ('alreadyFound', true);
  225.             }
  226.         } );
  227.     }
  228.     else {
  229.         btargetsFound   = false;
  230.     }
  231.  
  232.     //--- Get the timer-control variable for this selector.
  233.     var controlObj      = waitForKeyElements.controlObj  ||  {};
  234.     var controlKey      = selectorTxt.replace (/[^\w]/g, "_");
  235.     var timeControl     = controlObj [controlKey];
  236.  
  237.     //--- Now set or clear the timer as appropriate.
  238.     if (btargetsFound  &&  bWaitOnce  &&  timeControl) {
  239.         //--- The only condition where we need to clear the timer.
  240.         clearInterval (timeControl);
  241.         delete controlObj [controlKey]
  242.     }
  243.     else {
  244.         //--- Set a timer, if needed.
  245.         if ( ! timeControl) {
  246.             timeControl = setInterval ( function () {
  247.                     waitForKeyElements (    selectorTxt,
  248.                                             actionFunction,
  249.                                             bWaitOnce,
  250.                                             iframeSelector
  251.                                         );
  252.                 },
  253.                 300
  254.             );
  255.             controlObj [controlKey] = timeControl;
  256.         }
  257.     }
  258.     waitForKeyElements.controlObj   = controlObj;
  259. }
  260.  
  261. });
Add Comment
Please, Sign In to add comment