dvargas92495 / SmartBlocks

Useful examples from developer community for Roam42 SmartBlocks
147 stars 7 forks source link

Bulk processing - Find & Replace (RegEx supporting)- Append & Prepend v. 0.7 #218

Open fbgallet opened 3 years ago

fbgallet commented 3 years ago

Read description and important instructions below. You can download the JSON file (in .zip file below) and import to your graph, or simply copy/paste code below. Bulk processing - Find & Replace - Append & Prepend v 0.7.zip

📋 Descritpion and instructions

Bulk processing on selected blocks, and only the expanded ones. For now:

How to run the commands: After selecting some blocks, open the workBench command box with Ctrl + ; (or Ctrl + $ on french Azerty keyboard), then enter somme letters of the command, like 'fre' or 'app', then Enter. That's it! The main commands are:

Regular Expressions are an advanced user feature, you must learn and experiment with their logic before using it (you can learn and test your formulas here) ! The most accessible feature is using the variable $RegEx for formating the replacement of matching strings: In Replace prompt, you can insert $RegEx in the replacing string, for ex., to add bold, enter: $RegEx There is 4 possible formating of the main variable:

Be careful, these operations are not trivial !

They change directly and potentially massively your data, in particular with the use of Regex! (undo is only for last bulk processing, and is lost if you refresh the tab)

Please inform me quickly of any issue that could affect other users!

📅 Version log

v 0.7 (6 August, 2021)

v 0.6 (22 april, 2021)

v 0.51 (22 april, 2021)

v 0.5 (18 april, 2021)

✅ Prerequisites or dependencies that are required for this Extension

It's not a SmartBlock but a Javascript using workBench commands from Roam42 (click here for informations and instruction to install it). Roam42 and workBench need to be activated in your graph. The code above must be pasted in a javascript block, below a {{[[roam/js]]}} block.

The loading of javascripts can be quite long at the opening of your graph, that's why there is a timer to wait for the opening of Roam42. It is currently set to 15000 (15 seconds) (last line of the code), you can increase it if the commands do not load properly.

💡 Additional Info

Bulk blocks format change (v 0.7): https://user-images.githubusercontent.com/74436347/128583971-c91ee268-9006-4cf0-b75f-007c4706aec2.mp4

Advanced features (v 0.5): https://user-images.githubusercontent.com/74436347/115165547-a3ee7700-a0ae-11eb-8fb5-4907b79b0335.mp4

Most common features: Video: https://www.youtube.com/watch?v=T6aYwSsiIoQ

https://user-images.githubusercontent.com/74436347/115165602-def0aa80-a0ae-11eb-8514-d2bf999a8d60.mp4

✂️ Code of the JavaScript to Copy/Paste


/*********************************************************** 
   BULK PROCESSING
    - Find & Replace (supporting RegEx & case insensitive)
    - Prepend or/and Append some string to selected blocks
    - Change block format (Heading, alignment, view)
    using Roam42 workBench Command

Version: 0.7
Published: August 6th, 2021
By: Fabrice Gallet
Twitter: @fbgallet
My other extensions: https://roamresearch.com/?server-port=3333#/app/Roam-En-Francais/page/eyPmdlKZ7

Support my work on: https://www.buymeacoffee.com/fbgallet

************************************************************/

{
  BulkFunctions = {};
    BulkFunctions.lastOperation = "";
    BulkFunctions.modifiedBlocksCopy = new Array();
    BulkFunctions.promptParameters = new Array();
    BulkFunctions.extractedArray = new Array();

  var loadingCounter = 0;
  const interval = setInterval( async ()=> {
    if (roam42.keyevents) {
      clearInterval(interval);
      setTimeout(async ()=>{

/******************************************************************************************
/*  Find and Replace (supporting regular expressions) (fre)
*****************************************************************************************/      
BulkFunctions.ReplaceOpened = async(uid, blockContent, find, replace, isOpened)=> {
   let replacedBlock = "";
   let isToReplace = false;
   let lastIndex = 0;
   let stringArray = [];
   let regexVarToInsert = false;

   if (find.test(blockContent)) {
     find.lastIndex=0;
     if (find.global) {
          var matchIterator = [...blockContent.matchAll(find)];
          if (replace.search(/\$regex/i) == -1 && replace.search(/\$1/) == -1 && replace.search(/\$2/) == -1) {
              replacedBlock = blockContent.replace(find, replace);
           } else {
               for (const m of matchIterator) {
                 if (m.index != 0) { stringArray.push(blockContent.substring(lastIndex, m.index)); } 
                 stringArray.push(BulkFunctions.RegexVarInsert(m, replace, blockContent));
                 lastIndex = m.index + m[0].length;
               }
               if (lastIndex < blockContent.length - 1) { stringArray.push(blockContent.substring(lastIndex)); }
               replacedBlock = stringArray.join('');
            }
       } else {
         const mFirst = blockContent.match(find);
            if (replace.search(/\$regex/i) == -1 && replace.search(/\$1/) == -1 && replace.search(/\$2/) == -1) {
              replacedBlock = blockContent.replace(find, replace);
           } else {
                if (mFirst.index != 0) { replacedBlock = blockContent.substring(0, mFirst.index); } 
                replacedBlock += BulkFunctions.RegexVarInsert(mFirst, replace, blockContent);
                lastIndex = mFirst.index + mFirst[0].length;
                if ( lastIndex < blockContent.length - 1) { replacedBlock += blockContent.substring(lastIndex); }
           }
      }
    BulkFunctions.modifiedBlocksCopy.push([uid, blockContent, isOpened]); 
    await roam42.common.updateBlock(uid, replacedBlock, isOpened); 
    }
}

BulkFunctions.RegexVarInsert = function(match, replace, blockContent) {
      let indexOfRegex = replace.search(/\$regex/i);
//      let replacedBlock = blockContent;
      let isWholeBlock = (blockContent.length == match[0].length);

      if ((isWholeBlock) && (indexOfRegex == 0 && replace.length == 6)) {
         return BulkFunctions.RegexFormat(replace, blockContent);
      } 
      else {
        let indexOfV1 = replace.search(/\$1/);
        let indexOfV2 = replace.search(/\$2/);
        let stringToInsert = replace;
        let stringArray= [];
        let replaceSplit = "";
        let regexWriting = "";

        if (indexOfRegex != -1) {
            regexWriting = replace.substring(indexOfRegex, indexOfRegex + 6);
            replaceSplit = replace.split(regexWriting);
            stringToInsert = BulkFunctions.RegexFormat(regexWriting, match[0]); 
            stringToInsert = replaceSplit[0] + stringToInsert + replaceSplit[1];
//           alert("String to insert: " + stringToInsert);
         }
         if (indexOfV1 != -1) {
              replaceSplit = stringToInsert.split('$1');
              stringToInsert = replaceSplit[0];            
              let i=1;
              while (i < replaceSplit.length) { stringToInsert += match[1] + replaceSplit[i++]; }
         }
         if (indexOfV2 != -1) {
            replaceSplit = stringToInsert.split('$2');
            stringToInsert = replaceSplit[0];
            let i=1;
            while (i < replaceSplit.length) { stringToInsert += match[2] + replaceSplit[i++]; }
          }
          return stringToInsert;
        }
}

BulkFunctions.RegexFormat = function(regexW, strMatch) {
    let strIns = "";
    switch(regexW) {
        case "$RegEx": strIns = strMatch;
          break;
        case "$REGEX": strIns = strMatch.toUpperCase();
          break;
        case "$regex": strIns = strMatch.toLowerCase();
          break;
        case "$Regex": strIns = strMatch.charAt(0).toUpperCase() + strMatch.slice(1);
          break;
        default: strIns = strMatch; 
          break;
      }
   return strIns;
}

BulkFunctions.AddFandRtowB = async function() {
  roam42.wB.commandAddRunFromMultiBlockSelection('Find & Replace (supporting RegEx) (fre)', async ()=>{
    iziToast.question({
        color: 'blue',
        layout: 2,
        drag: false,
        timeout: 100000,
        close: false,
        overlay: true,
        displayMode: 2,
        id: 'question',
        title: 'Character(s) to find & replace in the selected and expanded blocks:',
        message: '(Support regular expressions. In replace blox, variable $RegEx stands for matching strings)<br>(Click (?) for examples.)',
        position: 'center',
        inputs: [
          ['<label for="checkb">Case INsensitive ?  </label><input type="checkbox" id="checkb">', 'change', function (instance, toast, input, e) {
            console.info(input.checked);
         }, false],
            ['<input type="text" placeholder="Find...">', 'keyup', function (instance, toast, input, e) {
            console.info(input.value);
            }],
          ['<input type="text" placeholder="Replace by...blank=remove">', 'keydown', function (instance, toast, input, e) {
            console.info(input.value);
         }, true]
        ],
      buttons: [ 
            ['<button><b>Confirm</b></button>', function (instance, toast, button, e, inputs) {
                console.log("chec is: " + inputs[1].checked);
                let toFindStr = inputs[2].value;
                let replacingStr = inputs[3].value;
                if (toFindStr != null) {
                  if (replacingStr != null) {
                    BulkFunctions.lastOperation = "Find and Replace";
                    var regParts = toFindStr.match(/^\/(.*?)\/([gimsuy]*)$/);
                    if (regParts) { 
                      if (!(regParts[2].includes('i')) && (inputs[1].checked)) { regParts[2] += 'i'; }
                      var toFindregexp = new RegExp(regParts[1], regParts[2]); }
                    else {
                      let regPar2='g';
                      if (inputs[1].checked) {regPar2 += 'i';}
                      console.log(regPar2);
                      toFindStr = toFindStr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
                      var toFindregexp = new RegExp(toFindStr, regPar2); }

                    BulkFunctions.promptParameters = [toFindregexp, replacingStr];
                    while(BulkFunctions.modifiedBlocksCopy.length > 0) { 
                      BulkFunctions.modifiedBlocksCopy.pop(); }
                    BulkFunctions.SelectedNodesProcessing([toFindregexp, replacingStr], BulkFunctions.ReplaceOpened);
                  }
                }
                instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
            }, false], // true to focus
            ['<button>Cancel</button>', function (instance, toast, button, e) {
                instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
            }],
            ['<button>(?)</button>', function (instance, toast, button, e) {
              iziToast.show({
                    theme: 'dark',
                    closeOnClick: true,
                    title: 'Examples:',
                    message: "- /.*/ matches all block text,<br>"
                             + " - /blocks?/i matches first occurence of 'block(s)',<br>"
                             + " - /[A-Z]\\w+/g, matches all words beginning with a capital letter.<br>"
                             + "<br>"
                             + "In replace blox:<br>"
                             + "- [$regex](((pUmK-1wqt))) make each machting string as an alias.<br>"
                             + "- $RegEx leaves the machting string in its initial state.<br>"
                             + "- $REGEX capitalizes all letters.<br>"
                             + "- $regex set to lower case all letters.<br>"
                             + "- $Regex capitalize first letter.<br>"
                             + "- Capture groups $1 and $2 can also be used. Ex: **$1** n°$2",
                    position: 'center',
                    timeout: false,
                    // iconText: 'star',
                });
  //              instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
            }]
        ],
        onClosing: function(instance, toast, closedBy){
            // console.info('Closing | closedBy: ' + closedBy);
        },
        onClosed: function(instance, toast, closedBy){
            // console.info('Closed | closedBy: ' + closedBy);
        }
    });
  });
}

/******************************************************************************************
/*  Extract matching data to clipboard (experimental)
*****************************************************************************************/
BulkFunctions.ExtractToClipboard = async(uid, blockContent, find, replace, isOpened)=> {
   let extractedBlock = "";
   let stringArray = [];
   let regexVarToInsert = false;

//   alert(blockContent);

   if (find.test(blockContent)) {
     find.lastIndex=0;
     if (find.global) {
           let matchIterator = [...blockContent.matchAll(find)];
          if (replace.search(/\$regex/i) != -1 || replace.search(/\$1/) != -1 || replace.search(/\$2/) != -1) {
              regexVarToInsert = true;
           } else {
               for (const m of matchIterator) {
                 if (regexVarToInsert) {
                   extractedBlock=BulkFunctions.RegexVarInsert(m, replace, blockContent);
                   extractedBlock=extractedBlock.slice(2,extractedBlock.length-2);
                   stringArray.push(extractedBlock);
                 } else {
                   extractedBlock=m[0].slice(2,m[0].length-2);
                   stringArray.push(extractedBlock);
                 }
               }
               extractedBlock = stringArray.join('\n');
            }
       } 
    BulkFunctions.extractedArray.push(extractedBlock); 
    }
}

BulkFunctions.AddFandEtowB = async function() {
  roam42.wB.commandAddRunFromMultiBlockSelection('Find and Extract matching data to clipboard (fex)', async ()=>{
    let toFindStr = prompt("Character(s) to find in the selected and expanded bullets:\n\nWARNING: the searched string will not be case sensitive.");
    if (toFindStr != null) {
      let formatingStr = prompt("Format of'" + toFindStr + "' extracted data?\n");
      if (formatingStr != null) {
        BulkFunctions.lastOperation = "Find and Extract";
        let regParts = toFindStr.match(/^\/(.*?)\/([gimsuy]*)$/);
        let toFindregexp;

        if (regParts) { 
    //      alert(regParts[1]);
          toFindregexp = new RegExp(regParts[1], regParts[2]); }
        else {
          toFindStr = toFindStr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
          toFindStr = toFindStr + "(?:[^" + toFindStr + "]]*(?:\.)?)*" + toFindStr; 
          toFindregexp = new RegExp(toFindStr, 'g'); 
        }

        BulkFunctions.promptParameters = [toFindregexp, formatingStr];
        while(BulkFunctions.modifiedBlocksCopy.length > 0) { 
          BulkFunctions.modifiedBlocksCopy.pop(); }
        while(BulkFunctions.extractedArray.length > 0) { 
          BulkFunctions.extractedArray.pop(); }
        await BulkFunctions.SelectedNodesProcessing([toFindregexp, formatingStr], BulkFunctions.ExtractToClipboard);
      }  
    }
  });
}

/******************************************************************************************
/*  Append and/or Prepend
/******************************************************************************************/
 BulkFunctions.AppendAndPrepend = async(uid, blockContent, stringBefore, stringAfter, isOpened)=> {
            BulkFunctions.modifiedBlocksCopy.push([uid, blockContent, isOpened]);
            await roam42.common.updateBlock(uid, stringBefore + blockContent + stringAfter, isOpened);
      }

BulkFunctions.AddAPtowB = async function() {
    roam42.wB.commandAddRunFromMultiBlockSelection('Append or/and Prepend to seleted blocks', async ()=>{
      iziToast.question({
        color: 'blue',
        layout: 2,
        drag: false,
        timeout: false,
        close: false,
        overlay: true,
        displayMode: 1,
        id: 'question',
        title: 'Text to prepend or/and to append to each selected blocks:',
        message: '(Do not forget space if needed.)',
        position: 'center',
        inputs: [
        ['<input type="text" placeholder="to prepend">', 'keyup', function (instance, toast, input, e) {
            console.info(input.value);
        }, true],
        ['<input type="text" placeholder="to append">', 'keydown', function (instance, toast, input, e) {
            console.info(input.value);
        }],
        ],
        buttons: [
            ['<button><b>Confirm</b></button>', function (instance, toast, button, e, inputs) {
                let prefixe = inputs[0].value;
                let suffixe = inputs[1].value;
                BulkFunctions.lastOperation = "Append and/or Prepend";
                BulkFunctions.promptParameters = [prefixe, suffixe];
                while(BulkFunctions.modifiedBlocksCopy.length > 0) { 
                    BulkFunctions.modifiedBlocksCopy.pop(); }
                BulkFunctions.SelectedNodesProcessing([prefixe, suffixe], BulkFunctions.AppendAndPrepend);  

                instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
            }, false],
            ['<button>Cancel</button>', function (instance, toast, button, e) {
                instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
            }]
        ],
        onClosing: function(instance, toast, closedBy){
            // console.info('Closing | closedBy: ' + closedBy);
        },
        onClosed: function(instance, toast, closedBy){
            // console.info('Closed | closedBy: ' + closedBy);
        }
    });
  });
}

/******************************************************************************************
/*  Bulk change format of selected blocks
/******************************************************************************************/
 BulkFunctions.ChangeBlockFormat = async(uid, blockContent, headingLevel, alignment, view, isOpened)=> {
            let h;
            let hOld = headingLevel;
            let aOld = alignment;
            let vOld = view;
            let blockTree = await roam42.common.getBlockInfoByUID(uid, true);

            if (headingLevel != "noChange") {
                h = parseInt(headingLevel);  
                if (blockTree[0][0].heading != null) { hOld = blockTree[0][0].heading; }
                else {hOld = 0; }       
                if (blockTree[0][0].heading != h) {
                window.roamAlphaAPI.updateBlock({"block": 
                                                {"uid": uid,
                                                "heading": h}});
                }
            }
            if (alignment != "noChange") {
                if ((blockTree[0][0]["text-align"]) != null) { aOld = blockTree[0][0]["text-align"]; }
                else {aOld = "left"; } 
                console.log(aOld);
                window.roamAlphaAPI.updateBlock({"block": 
                                                {"uid": uid,
                                                "text-align": alignment}});
            }
            if (view != "noChange") {
                if (blockTree[0][0]["view-type"] != null) { vOld = blockTree[0][0]["view-type"]; }
                else {vOld = "bullet"; } 
                window.roamAlphaAPI.updateBlock({"block": 
                                                {"uid": uid,
                                                "children-view-type": view}});
            }
            BulkFunctions.modifiedBlocksCopy.push([uid, blockContent, isOpened, hOld, aOld, vOld]);
}

BulkFunctions.ChangeBlockFormatPrompt = async function() {
    iziToast.question({
        color: 'blue',
        layout: 1,
        drag: false, 
        timeout: false,
        close: false,
        overlay: true,
        displayMode: 1,
        id: 'question',
        progressBar: false,
        title: 'Format changes to apply to the selected blocks:',
        position: 'center',
        inputs: [
            ['<select><option value="noChange">Heading</option><option value="1">H1</option><option value="2">H2</option><option value="3">H3</option><option value="0">Normal</option></select>', 'change', function (instance, toast, select, e) {
            }, true],
            ['<select><option value="noChange">Alignment</option><option value="left">Left</option><option value="center">Center</option><option value="right">Right</option><option value="justify">Justify</option></select>', 'change', function (instance, toast, select, e) {
            }],
            ['<select><option value="noChange">View as...</option><option value="document">Document</option><option value="numbered">Numbered List</option><option value="bullet">Bulleted List</option></select>', 'change', function (instance, toast, select, e) {              
            }]
        ],
        buttons: [
            ['<button><b>Apply</b></button>', function (instance, toast, button, e, inputs) {
                let h = inputs[0].options[inputs[0].selectedIndex].value;
                let a = inputs[1].options[inputs[1].selectedIndex].value;
                let v = inputs[2].options[inputs[2].selectedIndex].value;
                console.log(v);
                BulkFunctions.lastOperation = "Bulk change format of selected blocks";
                BulkFunctions.promptParameters = [h,a,v];
                while(BulkFunctions.modifiedBlocksCopy.length > 0) { 
                  BulkFunctions.modifiedBlocksCopy.pop(); }
                BulkFunctions.SelectedNodesProcessing([h,a,v], BulkFunctions.ChangeBlockFormat);  

                instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');

            }, false], // true to focus
            ['<button>Cancel</button>', function (instance, toast, button, e) {
                instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
            }]
        ],
        onClosing: function(instance, toast, closedBy){
            // console.info('Closing | closedBy: ' + closedBy);
        },
        onClosed: function(instance, toast, closedBy){
            // console.info('Closed | closedBy: ' + closedBy);
        }
    });
}

BulkFunctions.AddChangeBlockFormat = async function() {
  roam42.wB.commandAddRunFromMultiBlockSelection('Bulk change format of selected blocks (bcf)', async ()=>{
    await BulkFunctions.ChangeBlockFormatPrompt();
  });
}

/******************************************************************************************      
/*  Undo last bulk operation
/******************************************************************************************/

BulkFunctions.UndoLastBulkOperation = async function() {
  let formatChange = false;
  if (BulkFunctions.modifiedBlocksCopy[0].length > 3) {
    formatChange = true;
  }  
  for (var index = 0; index < BulkFunctions.modifiedBlocksCopy.length; index++) {
        let uid = BulkFunctions.modifiedBlocksCopy[index][0];
        let blockContent = BulkFunctions.modifiedBlocksCopy[index][1];
        let blockState = BulkFunctions.modifiedBlocksCopy[index][2];
        let blockTree = await roam42.common.getBlockInfoByUID(uid, true);
        await roam42.common.updateBlock(uid, blockContent, blockState);
        if (formatChange) {
          if ((blockTree[0][0].heading != BulkFunctions.modifiedBlocksCopy[index][3]) && (BulkFunctions.modifiedBlocksCopy[index][3] != "noChange")) {
            window.roamAlphaAPI.updateBlock({"block": 
                                                  {"uid": uid,
                                                  "heading": BulkFunctions.modifiedBlocksCopy[index][3]}});
          }
          if (BulkFunctions.modifiedBlocksCopy[index][4] != "noChange") {
            window.roamAlphaAPI.updateBlock({"block": 
                                                  {"uid": uid,
                                                  "text-align": BulkFunctions.modifiedBlocksCopy[index][4]}});
          }
         if (BulkFunctions.modifiedBlocksCopy[index][5] != "noChange") {
            window.roamAlphaAPI.updateBlock({"block": 
                                                  {"uid": uid,
                                                  "children-view-type": BulkFunctions.modifiedBlocksCopy[index][5]}});
          }
          let hOld;
          if (blockTree[0][0].heading != null) { hOld = blockTree[0][0].heading; }
            else {hOld = 0; }
          BulkFunctions.modifiedBlocksCopy[index]=[blockTree[0][0].uid, blockTree[0][0].string, blockTree[0][0].open, 
                                                   hOld, blockTree[0][0]["text-align"], blockTree[0][0]["view-type"]];
        }
        else { BulkFunctions.modifiedBlocksCopy[index]=[blockTree[0][0].uid, blockTree[0][0].string, blockTree[0][0].open]; }
     }
    await BulkFunctions.UndoButton();
 }     

BulkFunctions.UndoButton = async function() {
    iziToast.warning({
        timeout: 8000, displayMode: 'replace', id: 'undo', zindex: 999, title: "Click to undo this \'" + BulkFunctions.lastOperation + 
            "\' operation. Do not use Ctrl + z.", position: 'bottomRight',  drag: false, close:true,
        buttons: [
                    ['<button>UNDO</button>', (instance, toast)=> {
                    BulkFunctions.lastOperation = "Undo";
                    BulkFunctions.UndoLastBulkOperation();
                    instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
            }],
        ],
     });
}

BulkFunctions.AddUndo = async function() {   
    roam42.wB.commandAddRunFromAnywhere('Undo last bulk operation', async ()=>{
     await BulkFunctions.UndoButton();
  });
} 

/******************************************************************************************      
/*  Redo last bulk operation
/******************************************************************************************/
BulkFunctions.AddRedo = async function() {
  roam42.wB.commandAddRunFromMultiBlockSelection('Redo last bulk operation', async ()=>{ 
    iziToast.warning({
        timeout: 20000, displayMode: 'replace', id: 'undo', zindex: 999, title: "Are you sure you want to do another time last bulk \'" + BulkFunctions.lastOperation + 
            "\' operation?", position: 'center', overlay: true, color: 'blue', drag: false, close:true,
        buttons: [
            ['<button>Yes</button>', (instance, toast)=> {
                  switch(BulkFunctions.lastOperation) {
                    case "": Alert("No bulk operation has been run.");
                         return;
                    case "Undo": 
                         BulkFunctions.UndoLastBulkOperation();
                         break;
                    case "Append and/or Prepend":
                         while(BulkFunctions.modifiedBlocksCopy.length > 0) { 
                             BulkFunctions.modifiedBlocksCopy.pop(); }
                         BulkFunctions.SelectedNodesProcessing(BulkFunctions.promptParameters, BulkFunctions.AppendAndPrepend);
                         break;
                    case "Find and Replace":
                         while(BulkFunctions.modifiedBlocksCopy.length > 0) { 
                             BulkFunctions.modifiedBlocksCopy.pop(); }
                         BulkFunctions.SelectedNodesProcessing(BulkFunctions.promptParameters, BulkFunctions.ReplaceOpened);
                         break;
                    case "Bulk change format of selected blocks":
                         while(BulkFunctions.modifiedBlocksCopy.length > 0) { 
                             BulkFunctions.modifiedBlocksCopy.pop(); }
                         BulkFunctions.SelectedNodesProcessing(BulkFunctions.promptParameters, BulkFunctions.ChangeBlockFormat);
                         break;
                    default: break;  
                  }
                  instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
            }, true],
          ['<button>No</button>', (instance, toast)=> {
            instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
          }
         ]]
   });    
  });
}

/******************************************************************************************      
/*  Copy extracted text
/******************************************************************************************/

BulkFunctions.AddPaste = async function() {   
    roam42.wB.commandAddRunFromAnywhere('Paste Extracted text (pex)', async ()=>{
        BulkFunctions.lastOperation = "Copy";
//        await BulkFunctions.UndoLastBulkOperation();
        let parentBlockUID = roam42.common.currentActiveBlockUID();
//        await roam42.common.sleep(150);
        for (var i = 0; i< BulkFunctions.extractedArray.length; i++) {
          await roam42.common.createBlock(parentBlockUID, -1, BulkFunctions.extractedArray[i]);
//          await roam42.common.createSiblingBlock(startingBlockUID, BulkFunctions.extractedArray[i], true);
        }
      });
} 

/******************************************************************************************      
/*  Recursive path through all open blocks, processing a generic function defined above
/******************************************************************************************/
BulkFunctions.SelectedNodesProcessing = async function(parameters, bulkFunction) {
    for (var index = 0; index < roam42.wB.triggeredState.selectedNodes.length; index++) {
          let uid = roam42.wB.triggeredState.selectedNodes[index].querySelector('.rm-block-text').id.slice(-9);
          let blockTree = await roam42.common.getBlockInfoByUID(uid, true);
          let currentBlockUid = blockTree[0][0].uid;
          let currentBlockContent = blockTree[0][0].string;
          await BulkFunctions.RecursiveTreeProcessing(currentBlockUid, parameters, bulkFunction);
       }
     await BulkFunctions.UndoButton();
}

BulkFunctions.RecursiveTreeProcessing = async (currentUid, parameters, bulkFunction)=> {            
      let subBlockTree = await roam42.common.getBlockInfoByUID(currentUid, true);
      let currentBlockContent = subBlockTree[0][0].string;
      let args = [currentUid, currentBlockContent];
      for (var i=0; i < parameters.length; i++) { args.push(parameters[i]);}
      if (subBlockTree[0][0].open) {
          args.push(true);
          await bulkFunction.apply(this, args);

          if (subBlockTree[0][0].children != null) {
              for (var subIndex = 0; subIndex < subBlockTree[0][0].children.length; subIndex++) {
                currentUid = subBlockTree[0][0].children[subIndex].uid;
                await BulkFunctions.RecursiveTreeProcessing(currentUid, parameters, bulkFunction);
              }
          }
      }
      else {
          args.push(false);
          await bulkFunction.apply(this, args);
      }
}     
        await BulkFunctions.AddFandRtowB();
        await BulkFunctions.AddAPtowB();
        await BulkFunctions.AddFandEtowB();
        await BulkFunctions.AddUndo();
        await BulkFunctions.AddRedo();
        await BulkFunctions.AddPaste();
        await BulkFunctions.AddChangeBlockFormat();
    }, 2000)
  }
  if(loadingCounter>20) { clearInterval(interval) } else {loadingCounter += 1}; 
}, 15000); 
}
lifsys commented 3 years ago

Wow, fantastic work!