Support RunKit preamble (#696)

* feat(runkit-tag): support preamble

* chore(runkit-tag): remove unnecessary length check

* docs(editor guide text): add preamble to runkit snippet

* fix(spec/runkit-tag): add preamble code to spec

* fix(runkit-tag): prevent xss attacks

* fix(runkit-tag): prevent race condition
This commit is contained in:
Gabriel Lebec 2018-09-21 11:27:42 -04:00 committed by Ben Halpern
parent 160056a19d
commit 4d673e9601
3 changed files with 37 additions and 28 deletions

View file

@ -1,7 +1,7 @@
class RunkitTag < Liquid::Block
def initialize(tag_name, markup, tokens)
super
@markup = markup
@preamble = ActionView::Base.full_sanitizer.sanitize(markup, tags: [])
end
def render(context)
@ -9,7 +9,8 @@ class RunkitTag < Liquid::Block
parsed_content = content.xpath("//html/body").text
html = <<~HTML
<div class="runkit-element">
#{parsed_content}
<code style="display: none">#{@preamble}</code>
<code>#{parsed_content}</code>
</div>
HTML
html
@ -18,15 +19,15 @@ class RunkitTag < Liquid::Block
def self.special_script
<<~JAVASCRIPT
var targets = document.getElementsByClassName("runkit-element");
if (targets.length > 0) {
for (var i = 0; i < targets.length; i++) {
var content = targets[i].textContent;
targets[i].innerHTML = "";
var notebook = RunKit.createNotebook({
element: targets[i],
source: content,
});
}
for (var i = 0; i < targets.length; i++) {
var preamble = targets[i].children[0].textContent;
var content = targets[i].children[1].textContent;
targets[i].innerHTML = "";
var notebook = RunKit.createNotebook({
element: targets[i],
source: content,
preamble: preamble
});
}
JAVASCRIPT
end
@ -36,16 +37,17 @@ class RunkitTag < Liquid::Block
var checkRunkit = setInterval(function() {
if(typeof(RunKit) !== 'undefined') {
var targets = document.getElementsByClassName("runkit-element");
if (targets.length > 0) {
for (var i = 0; i < targets.length; i++) {
var content = targets[i].textContent;
if(/^(\<iframe src)/.test(content) === false) {
targets[i].innerHTML = "";
var notebook = RunKit.createNotebook({
element: targets[i],
source: content,
});
}
for (var i = 0; i < targets.length; i++) {
var wrapperContent = targets[i].textContent;
if(/^(\<iframe src)/.test(wrapperContent) === false) {
var preamble = targets[i].children[0].textContent;
var content = targets[i].children[1].textContent;
targets[i].innerHTML = "";
var notebook = RunKit.createNotebook({
element: targets[i],
source: content,
preamble: preamble
});
}
}
clearInterval(checkRunkit);

View file

@ -136,7 +136,7 @@
<h3><strong>RunKit Embed</strong></h3>
<p>Put executable code within a runkit liquid block, as follows:</p>
<pre>{% runkit %}<br>console.log("Place javascript here!"); <br>{% endrunkit %} <br></pre>
<pre>{% runkit<br>// hidden setup JavaScript code goes in this preamble area<br>const hiddenVar = 42<br>%}<br>// visible, reader-editable JavaScript code goes here<br>console.log(hiddenVar)<br>{% endrunkit %} <br></pre>
<h3><strong>repl.it Embed</strong></h3>
<p>All you need is the URL after the domain name:</p>

View file

@ -2,6 +2,12 @@ require "rails_helper"
RSpec.describe RunkitTag, type: :liquid_template do
describe "#render" do
let(:preamble) do
<<~CODE
const myVar = 9001
CODE
end
let(:content) do
<<~CODE
// GeoJSON!
@ -11,22 +17,23 @@ RSpec.describe RunkitTag, type: :liquid_template do
CODE
end
def generate_new_liquid(block)
def generate_new_liquid(preamble_str, block)
Liquid::Template.register_tag("runkit", described_class)
Liquid::Template.parse("{% runkit %}#{block}{% endrunkit %}")
Liquid::Template.parse("{% runkit #{preamble_str}%}#{block}{% endrunkit %}")
end
def generate_script(block)
def generate_script(preamble_str, block)
<<~HTML
<div class="runkit-element">
#{block}
<code style="display: none">#{preamble_str}</code>
<code>#{block}</code>
</div>
HTML
end
it "generates proper div with content" do
liquid = generate_new_liquid(content)
expect(liquid.render).to eq(generate_script(content))
liquid = generate_new_liquid(preamble, content)
expect(liquid.render).to eq(generate_script(preamble, content))
end
end
end