6

To avoid the XY problem, let me say that what I actually want is to embed Javascript code into <script> tags in the HTML files produced via Org mode export, in such a way that (1) the Javascript code is included in the Org file (not a separate Javascript file), and (2) it is very easy to edit the Javascript in a Javascript specific major mode. The question title is only one way to achieve that.

Currently to embed Javascript in the resulting exported HTML I use:

#+BEGIN_EXPORT html
<script type="text/javascript">
 // javascript code goes here
</script>
#+END_EXPORT

That works fine, of course, but I'd like to edit the Javascript code in a Javascript major mode. Using C-' on an HTML block sensibly opens a buffer in HTML mode. So I've been using temp buffers I manually open and put in Javascript mode.

To embed code in an Org file that's conveniently editable in the correct major mode, you can use source blocks. Is there a way to get that Javascript code to export into a <script> tag? Or some other convenient way to get what I want?


EDIT: Updated the #+BEGIN_HTML to #+BEGIN_EXPORT html for the shiny new Org 9.0, per @mutbuerger's suggestion.

  • But do you get js from #+BEGIN_EXPORT html is shown in the resulting HTML file just like text ? Because I do :( What I get is <div class="export"> <p> &lt;script type="text/javascript"&gt; &lt;/script&gt; </p> </div> – Dima Fomin Oct 17 '17 at 9:40
  • 1
    What version of Org do you have, @DimaFomin? That syntax is only used in version 9 and higher. The result you got sounds like version 8. You can find out the version by running M-x org-version. – Omar Oct 17 '17 at 14:18
  • Аrhhh! Thank you! You are right! Mine org-vesion is 8.2.10 :( This strange for me that it's not that fresh cause I've copied my Emacs just a week ago, it's itself 25.3.1. – Dima Fomin Oct 17 '17 at 14:25
9
+100

It's worth mentioning that Org 9.0 is out and the #+BEGIN_HTML syntax you used is replaced with #+BEGIN_EXPORT language for export blocks.

There are different ways to achieve what you want, I'm using Org-babel's noweb references here. The syntax <<inline-js>> inserts the literal javascript block before the elisp code block is evaluated:

#+name: inline-js
#+begin_src javascript :exports none
console.log('Test');
#+end_src

#+begin_src elisp :noweb yes :exports results :results html
(concat
 "<script type=\"text/javascript\">\n"
 "<<inline-js>>\n"
 "</script>")
#+end_src

#+results:
#+BEGIN_EXPORT html
<script type="text/javascript">
console.log('Test');
</script>
#+END_EXPORT

Lorem ipsum dolor sit amet, consectetuer adipiscing elit.

This is the exported document:

<!DOCTYPE html>
<html lang="en">
<head>

<!-- ... -->

</head>
<body>
<div id="content">
<script type="text/javascript">
console.log('Test');
</script>

<p>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. </p>
</div>
</body>
</html>

EDIT: The following may be more appropriate if you reuse the elisp block a lot:

#+name: inline-js
#+begin_src elisp :exports none :results html :var blk=""
(concat
 "<script type=\"text/javascript\">\n"
 (cadr (org-babel-lob--src-info blk))
 "\n"
 "</script>")
#+end_src

#+name: foo
#+begin_src javascript :exports none
console.log('foo');
#+end_src

#+call: inline-js("foo")

#+name: bar
#+begin_src javascript :exports none
console.log('bar');
#+end_src

#+call: inline-js("bar")
  • Good! Can I reuse the Emacs Lisp source block? I mean, name it and have it take the name of the Javascript source block as a parameter? (I'm not familiar with noweb.) – Omar Nov 3 '16 at 21:07
  • It's a little annoying that I'll have all the JavaScript code appear twice in the Org file, but I guess I can keep the html export block folded most of the time and not see it. – Omar Nov 3 '16 at 21:17
  • You don't have to duplicate the js code (i.e. evaluate the elisp blocks in the buffer), the export engine expands the result anyway. – mutbuerger Nov 3 '16 at 22:34
  • 1
    Oh, that's right: I don't know why I thought I had to evaluate the blocks before callign export. Thanks a lot. For this new version, I think I'll just name the source block, put it in my library of Babel and then just use #+call: inline-js("foo") :exports results :results html in the Org files. – Omar Nov 3 '16 at 22:51
  • 1
    I'm pretty happy with my minor modification of your code. I'll wait a couple of days to not discourage further innovation from others but if no vastly superior approach arises, I'll award you the bounty. Thanks a lot for your help. – Omar Nov 4 '16 at 18:51
5

I think I just found my ideal approach: add a new language to babel! It's much easier than I thought. Here's how you add inline-js, the language that you edit in some JavaScript major mode and "evaluate" by wrapping in a <script> tag!

(add-to-list 'org-src-lang-modes '("inline-js" . javascript)) ;; js2 if you're fancy
(defvar org-babel-default-header-args:inline-js
  '((:results . "html")
    (:exports . "results")))
(defun org-babel-execute:inline-js (body _params)
  (format "<script type=\"text/javascript\">\n%s\n</script>" body))

Now you can write blocks like this:

#+BEGIN_SRC inline-js
  console.log("Hello World!");
#+END_SRC

That block exports like this:

#+RESULTS:
#+BEGIN_EXPORT html
<script type="text/javascript">
console.log("Hello World!");
</script>
#+END_EXPORT
  • 2
    Thanks for suggesting to look at ob-js, @mutbuerger, it made me find out how to add languages to Babel and when I saw how easy it was, I decided to do what's in this answer. (By the way, one could probably add a :results script option to ob-js that outputs a script instead of evaluating.) – Omar Nov 6 '16 at 18:24
  • 1
    This is really clever. – amitp Nov 6 '16 at 18:32
  • 1
    Oh, you can award a bounty to one answer and accept another! In that case, I'll follow your suggestion and accept this one, @mutbuerger. (It is, after all, what I'll use in my files.) – Omar Nov 7 '16 at 5:20
4

This answer is really a comment on @mutbuerger's answer. I'm posting it here because comments can't and shouldn't contain blocks of code. I figured out how to avoid the need for inventing names for the JavaScript blocks:

#+name: inline-js
#+begin_src emacs-lisp :exports none :results html
  (format
   "<script type =\"text/javascript\">\n%s\n</script>\n"
   (save-excursion
     (org-babel-next-src-block)
     (cadr (org-babel-get-src-block-info))))
#+end_src

#+call: inline-js()
#+begin_src javascript :exports none
console.log('foo');
#+end_src

#+call: inline-js()
#+begin_src javascript :exports none
console.log('bar');
#+end_src

I initially tried to use org-babel-previous-src-block and to keep the #+call: inline-js()'s after the JavaScript blocks as in mutbuerger's answer, but that doesn't work because by the time the Org exporter get's to the #+call line it has already removed the JavaScript block from the buffer (because of the :exports none)! Ah, the joys of stateful programming.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.