Javier Pacheco

Home | Blog | About Me |

Copy org blocks to clipboard

In this post I'll demostrate how to add a button to copy the content of a src block generated by ox-publish in emacs within org-mode, this script create a button in every instance of codeblock in the html file that ox-publish generate when create the web-page content.

copy.gif
Figure 1: How the buttons looks like.

The first issue.

When I started to looking in how to add a copy button to my org-block-src blocks, I was getting a little confused because of the lack of information about it (or maybe I was not ask in the proper way), but I found a webpage that help me to figure it out how to make this possible. But I found that there is a problem, it's javascript looks a <code> tag, and ox-publish (as far as I know) don't generate that tag in the html file, so I have been force to use my brain instead of only copy the scripts.

The Javascript code.

The code below can be improved? yes, but at this point for me it works and its ok, may be can be a little mess, but it do the job. This is the whole javascript code that I wrote:

function copyPreTextToClipboard(orgSrcContainer, classes) {
  let preTag;
  for (const classItem of classes) {
    preTag = orgSrcContainer.querySelector(`pre.${classItem}`);
    if (preTag) break;
  }

  if (preTag) {
    const textContent = preTag.textContent;

    // Get class name of the container
    const containerClass = orgSrcContainer.className;

    // Create a temporary textarea element to copy the text
    const tempTextarea = document.createElement('textarea');
    tempTextarea.value = textContent;

    // Append the textarea to the document
    document.body.appendChild(tempTextarea);

    // Select the text within the textarea
    tempTextarea.select();

    // Copy the selected text to the clipboard
    document.execCommand('copy');

    // Remove the temporary textarea
    document.body.removeChild(tempTextarea);

    console.log('Text copied to clipboard:', textContent);
    console.log('Class name of container:', containerClass);

    // Change button label to "Copied"
    const button = orgSrcContainer.querySelector('button');
    if (button) {
      button.textContent = 'Copied';
      button.disabled = true; // Disable the button after copying

      // Revert button to its initial state after 3 seconds
      setTimeout(() => {
        button.textContent = 'Copy Text';
        button.disabled = false; // Enable the button after timeout
      }, 3000);
    }
  } else {
    console.log('Pre tag with specified classes not found inside org-src-container');
  }
}

function createCopyButtonForDivs() {
  const orgSrcContainers = document.querySelectorAll('div.org-src-container');
  orgSrcContainers.forEach((container) => {
    const button = document.createElement('button');
    button.textContent = 'Copy Text';
    button.addEventListener('click', () => {
                                const classesToSearch = [
                                                'src.src-emacs-lisp',
                                                'src.src-python',
                                                'src.src-javascript',
                                                'src.src-c',
                                                'src.src-html',
                                                'src.src-css']; // Add more classes/languages as needed
                                copyPreTextToClipboard(container, classesToSearch);
    });

    container.appendChild(button);
  });
}

// Call the function to create copy buttons in each org-src-container div
createCopyButtonForDivs();

What the javascript script actually do:

The provided code is a JavaScript script that aims to add functionality to copy text from <pre> elements with specific classes when a corresponding button is clicked. Here's a breakdown of what each part of the code does:

copyPreTextToClipboard(orgSrcContainer, classes) This function receives two parameters:

orgSrcContainer: Represents the container element that holds <pre> elements. classes: An array of strings containing classes (like CSS classes) representing different languages in <pre> elements. It searches for a <pre> element with the specified classes inside the orgSrcContainer.

If found:

Grabs the text content of the <pre> element. Creates a temporary <textarea> element, sets its value to the extracted text, appends it to the document body, selects its content, and copies it to the clipboard using document.execCommand('copy'). Logs the copied text and the class name of the container to the console. Modifies the button inside the container to display "Copied" disables it temporarily, and reverts it to its initial state after 3 seconds. createCopyButtonForDivs() This function targets all <div> elements with the class org-src-container. For each of these divs, it creates a new button. The button is assigned the text "Copy Text" and an event listener that triggers the copyPreTextToClipboard() function when clicked. copyPreTextToClipboard() is called with the specific container (div.org-src-container) and an array of classes representing different programming languages. Overall Functionality.

This script aims to enhance the functionality of <div> containers by adding a "Copy Text" button to each container. When this button is clicked, it searches for <pre> elements with specific classes inside that container and copies their text content to the clipboard. Additionally, it temporarily disables and updates the button's label to indicate that the text has been copied.

How to run the script file?

To run this script automatically, when a org-src block appear in html I do this in the head.html file in my org project:

<script src="/cpbtn.js"></script>
<script>
  window.onload = function() {
      // Call the function to create copy buttons in each org-src-container div
      createCopyButtonForDivs();
  };
</script>

Test the buttons:

Python

import calendar
for i in range(100):
    print(i)

Emacs-lisp

(message "hello World")

C

#include <stdio>
int main(int argc, char *argv[])
{
        return 0;
}

Date: 2023-12-29 Fri 17:03

Emacs 29.3 (Org mode 9.6.15)