Partially run code as html and as text - javascript

Introduction
I'm currently creating a templatebuilder where users can build a template for an app. The user can drag and drop multiple blocks, such as text blocks and 'custom code' blocks. The template will be parsed within an app. Right now, a template could look like this:
<section>
<div class="row">
<div class="col-sm-12">
<section data-type="code">
<#code></#code>
</section>
</div>
</div>
<div class="row">
<div class="col-sm-12" data-type="container-content">
<section data-type="text">
<u>Lorem</u> ipsum
</section>
</div>
</div>
</section>
So, this template contains two elements (see the data-type attribute): one part is custom written code. Here, the user wrote custom code, including Apache Freemarker code. The second part is custom written text.
Situation
The code above will be used in two different ways.
Exactly this code will be used inside an app that's using the
template (so that's why they should be able to write Freemarker code,
because this will be parsed).
On my website, the user should be able
to edit this template. Because the code is stored in the database as
written above, there is a problem:
Problem
When I directly render the template in the web-interface, the text part will render correctly with the <u></u> tags, but the code part will be rendered as html as well which might cause weird behaviour (such as the freemarker notation </#list> being auto-converted to <!--#list-->).
But, if I render the full template as text only, the text part with the <u></u> tags will not be rendered as well.
Expected outcome
I want to read the template variable with JavaScript / jQuery and then parse each data-type with text as html, and with code as text.
How can I loop through the template and do this?

There is an alternative syntax that uses square brackets instead of angle brackets.
Check if it solves your tag identifying problem without messing any other feature.
https://freemarker.apache.org/docs/dgui_misc_alternativesyntax.html
EDIT 1
To show the source code inside the <#code> tags when the HTML is parsed, you could escape it in your database (escape html special chars like <, > and & to < > and &). So, when it is rendered, no html tags will be created in the code content and the document won't be messed up.
Then, you can render all the content of the database directly as HTML: text will keep markup and code will be text.
To do that modification, you can use regular expressions to find what is enclosed by <#code> tags and replace with the HTML-escaped equivalent. The exact way to do it depends on the language you will be using for the job, as there are some differences in RegExes and in the available escape funcions.
EDIT 2
If you are loading the content using AJAX, you have the chance of applying the replace in javascript, AFTER the content was obtained from the server, keeping your database as it is.

Recap of the problem
For parsing HTML in javascript, you generally use a DOMParser object (supported by IE10+).
Like you said, parsing fails inside the data-type="code" section, because it does not know how to handle the </#...> tags...
const templ = `<section><div class="row"><div class="col-sm-12"><section data-type="code"><#code></#code></section></div></div><div class="row"><div class="col-sm-12" data-type="container-content"><section data-type="text"><u>Lorem</u> ipsum</section></div></div></section>`;
const parser = new DOMParser();
const doc = parser.parseFromString(templ, "text/html");
console.log(
"Wrongly parsed </#code> tag:\n",
doc.querySelector("[data-type='code']").innerHTML
);
Finding a way around
Now, it might sound like a good idea to try and do a quick regex find-and-replace on the characters that need to be escaped, but I wouldn't recommend it...
As far as I know, there's no way to "break in" to the parsing process or pass a strategy for certain types of elements...
I'd say this leaves you with two options. Either:
Don't use the unparsable syntax inside the code section, as suggested by user Eduardo Poço in their answer
or, (my prefered direction), try to
Modify the template itself to stop parsing the contents of the code sections all together
Using a modified template
There's a tag for "script" like content in HTML! It's, unsurprisingly, the <script> tag. Let's inject it in our code section:
<section data-type="code">
<script type="text">
<#code></#code>
</script>
</section>
The DOMParser won't touch this tag, leaving it exactly as is:
const templ = '<section><div class="row"><div class="col-sm-12"><section data-type="code"><script type="text"><#code></#code></' + 'script></section></div></div><div class="row"><div class="col-sm-12" data-type="container-content"><section data-type="text"><u>Lorem</u> ipsum</section></div></div></section>';
const parser = new DOMParser();
const doc = parser.parseFromString(templ, "text/html");
console.log(
"Now, there's a <script> tag:\n",
doc.querySelector("[data-type='code']").innerHTML
);
Note that I had to join the template string from two parts to make sure stackoverflow's snippet doesn't break. Are they experiencing a similar issue? :-o
Now, all we have to do is use the general DOM methods, including innerText (not innerHTML) to get the script's inner content back in to the visible part of the DOM:
var templ = '<section><div class="row"><div class="col-sm-12"><section data-type="code"><script type="text"><#code></#code></' + 'script></section></div></div><div class="row"><div class="col-sm-12" data-type="container-content"><section data-type="text"><u>Lorem</u> ipsum</section></div></div></section>`;'
var parser = new DOMParser();
var doc = parser.parseFromString(templ, "text/html");
Array
.from(doc.querySelectorAll(
"[data-type='code'] > script")
)
.forEach(script => {
const codeTag = document.createElement("code");
codeTag.innerText = script.innerHTML;
script.replaceWith(codeTag);
});
document.getElementById("wrapper").appendChild(doc.body.firstChild);
code { background: #efefef; }
<div id="wrapper"></div>

You could use charset codes, so it does not execute before outputting.
HTML charset reference
They can edit it, since it appears normal and send it back to you or server. Make sure you include your charset reference in the head.
<meta charset="UTF-8"> // HTML5
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"> // HTML4
<!-- #CHARSET / No execute -->
<section>
<div class="row">
<div class="col-sm-12">
<section data-type="code">
<#code> <!-- Run this --> </#code>
</section>
</div>
</div>
<div class="row">
<div class="col-sm-12" data-type="container-content">
<section data-type="text">
<u> <!-- Don't run --> </u>
</section>
</div>
</div>
</section>

If i didn't misunderstand it, you can use <plaintext> tag for rendering block as text on the page.
<plaintext>
<#code></#code>
</plaintext>

What about something like this?
// https://stackoverflow.com/questions/7477/autosizing-textarea-using-prototype
function FitToContent(id, maxHeight)
{
var text = id && id.style ? id : document.getElementById(id);
if (!text)
return;
/* Accounts for rows being deleted, pixel value may need adjusting */
if (text.clientHeight == text.scrollHeight) {
text.style.height = "30px";
}
var adjustedHeight = text.clientHeight;
if (!maxHeight || maxHeight > adjustedHeight)
{
adjustedHeight = Math.max(text.scrollHeight, adjustedHeight);
if (maxHeight)
adjustedHeight = Math.min(maxHeight, adjustedHeight);
if (adjustedHeight > text.clientHeight)
text.style.height = adjustedHeight + "px";
}
}
$('textarea').each(function(i,v){
FitToContent($(v)[0], document.documentElement.clientHeight)
});
textarea {
background: transparent;
border: 0;
font-family: 'Times New Roman';
font-size: 1em;
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section>
<div class="row">
<div class="col-sm-12">
<h4>First code block</h4>
<section data-type="code">
<textarea class='code'><#code>
<h2>FreeMarker Spring MVC Hello World</h2>
<table class="datatable">
<tr>
<th>Make</th><th>Model</th>
</tr>
<#list model["carList"] as car>
<tr>
<td>${car.make}</td>
<td>${car.model}</td>
</tr>
</#list>
</table>
</#code></textarea>
</section>
</div>
</div>
<div class="row">
<div class="col-sm-12" data-type="container-content">
<h4>Regular HTML section</h4>
<section data-type="text">
<u>Lorem</u> ipsum
</section>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<h4>Second code block</h4>
<section data-type="code">
<textarea class='code'><#code>
<table class="datatable">
<tr>
<th>Name</th><th>Email</th>
</tr>
<#list model["personList"] as person>
<tr>
<td>${person.name}</td>
<td>${person.email}</td>
</tr>
</#list>
</table>
</#code></textarea>
</section>
</div>
</div>
</section>

Related

Indent created Elements in javascript

So I've used javascript to build up an HTML markup for a component (accordion)
const accordItem = document.createElement("div");
const accordTitle = document.createElement("p");
const accordContent = document.createElement("div");
const accordContentParagraph = document.createElement("p");
All the elements are appended to a container
container.append(accordItem);
accordItem.append(accordTitle, accordContent);
accordContent.append(accordContentParagraph);
In the code, I populate the divs and paragraphs with content. So, an accordion generator of a sort. So the imaginary user can see a preview of how it would look. And also download a text file.
But if I copy the code from developers' tools. Or write it in the file; I see this mess of a markup. Everything is in one line.
<div class="accordionItem"><p class="accordionTitle">This is some content</p><div class="accordionContent"><p>This is additional content! Something Something</p></div></div><div class="accordionItem"><p class="accordionTitle">This is some content, more</p><div class="accordionContent"><p>Moremore</p></div></div>
While not really a problem, I would like to learn why that is. (pretty new at this).
And is there an easy way to make it indented?
like so:
<div class="accordionItem">
<p class="accordionTitle">This is some content</p>
<div class="accordionContent">
<p>This is additional content! Something Something</p>
</div>
</div>
<div class="accordionItem">
<p class="accordionTitle">This is some content, more</p>
<div class="accordionContent">
<p>Moremore</p>
</div>
</div>
instead of using create element method. You can assign a variable with html string and just append the string with parent Element using insertAdjacentHTML method.
Example :
let accordionElem =
`<div id="accordionCont">
<span id="childElem1">Hi</span>
<span id="childElem2">Hello</span>
</div>`;
document.getElementById("parentCont").insertAdjacentHTML("afterbegin",accordionElem);
<!DOCTYPE html>
<html lang="en">
<head>
<title>InsertAdjacentHTML</title>
</head>
<body>
<div id="parentCont"></div>
</body>
</html>
MDN DOC

Print multiple pages for different scroll positions via JavaScript

I have an HTML page that is an output of some program.
The program is used a lot to produce evidence and it will be helpful to export this evidence to a PDF.
The HTML is split into two columns.
I need to print (to PDF) multiple pages such that each page shows the two columns where different parts of the columns are aligned.
I rely on the browser ability to print to PDF instead of to the printer.
I tried to use jsPDF but it does not support <pre> tags properly which are prevalent in my text.
The document is a given HTML with two <div>s side by side with a scrollbar.
I already have the code that successfully aligns the two columns in the required positions, and a button that prints it using window.print(). It looks something like this:
function align(loc) {
$('#1').scrollTop($("#mark0"+loc).offset().top);
$('#2').scrollTop($("#mark1"+loc).offset().top);
}
function print() {
window.print();
}
// All locations are known in advance
function printAll() {
for (i=1; i<=3; i++) {
align(i);
print();
}
}
<div id=0 style='left:0; overflow:scroll; height:100%; width:50%'>
A lot of text...
<span id='mark01' />
A lot of text...
<span id='mark02' />
A lot of text...
<span id='mark03' />
A lot of text...
</div>
<div id=1 style='right:0; overflow:scroll; height:100%; width:50%'>
<pre>
Some text.
<b>
A lot of text...
<span id='mark11' />
Some text.
</b>
Some text.
<b>
A lot of text...
</b>
<span id='mark12' />
A lot of text...
<span id='mark13' />
<b>
A lot of text...
</b>
</pre>
</div>
Currently, I have to align the columns using the script (user choose one of the alignments), then print to PDF (user clicks on the print button), then align to a different position, then print again, and so forth.
Is there a way to automatize this process?
I want to create a javascript script that will call align(), then print() multiple times and it will be printed in a single PDF.
The printAll() example obviously does not work.
Using JS, I'd construct a new section laid out for print, then move the content into it.
The goal would be to produce reasonable DOM, like this:
<div id="printzone">
<div class="page">
<div class="col"> col1 section1 </div>
<div class="col"> col2 section1 </div>
</div>
<div class="page">
<div class="col"> col1 section2 </div>
<div class="col"> col2 section2 </div>
</div>
....
</div>
The first part is straightforward, but it seems obvious that the original DOM is not well-suited to the second task. You'll probably have to work with the raw source to accomplish the second step.
Perhaps something like:
// grab source
var col1Source = $('#0').innerHTML;
// treat '<span id="mark##" />' as a delimiter -- BOOM!
var col1Sections = col1Source.split(/<span id='mark\d+'[^>]+>/);
// do same for col2
for(var i = 0,
iMax = Math.max(col1Sections.length, col2Sections.length);
i < iMax;
i++
) {
var newPage = $('<div class="page">');
newPage.append('<div class="col">' + col1Sections[i] + '</div>');
newPage.append('<div class="col">' + col2Sections[i] + '</div>');
$('#printzone').append(newPage);
}
Once you've finished constructing the print zone, open up your dev tools and (1) delete the original #0 and #1 DOM nodes, then (2) play with CSS until the print layout is satisfactory.

Flippant in RoR project

I am newbie in front-end web development.
I am using flippant.js for flipping the dialogue.
<div id="overlay-box" class="overlayBox">
<a class="closeBtn" onClick="closePop()">X</a>
<button id="flipCard">Flip</button>
<div class="content" ></div>
</div>
<script type="text/javascript">
var front = document.getElementById("overlay-box")
var back_content = "
<div class = 'overlayBox' style='top: 50.5px; left: 338px; display: block;'>
<h2>Hello</h2>
<a class = 'closeBtn' onClick = 'closePop()'>Y</a>
<button id = 'closeCard'>Back</button>
<div class = 'content'></div>
</div>"
var back
document.getElementById("flipCard").addEventListener('click',function(e){
back = flippant.flip(front, back_content,'card')
document.getElementById("closeCard").addEventListener('click',function(e){
back = back.close();
})
})
But the flippant plug-in asks for back_content rather than full HTML file.
I want to override that but facing a problem.
I tried to change the flippant.js file where it was taking content and converting that into HTML whereas the front document was not converted.
I am able to use it but then I will have to write my all HTML code in back_content variable which is ugly design.
I also tried to give HTML document directly by $(HTML_file).html but still no success.
The other problem is when I am giving all the HTML in the variable then it is not calling the js classes and all styling code also
Please suggest how to use flippant plug-in.
Thanks in advance
For flippant.js code refer to its github page https://github.com/mintchaos/flippant.js/blob/master/flippant.js

CKEditor: Keep source indentation

I'm using following config for CKEditor:
var wysiwyg = ck.replace(el[0], {
allowedContent: true,
protectedSource: [/\r|\n/g]
});
I'm loading HTML source into CKEditor as:
<div style='font-weight: bold;'>
<div>
<div> test </div>
</div>
</div>
On wysiwyg.getData() I receive:
<div style="font-weight: bold;">
<div>
<div>test</div>
</div>
</div>
How can I force CKEditor to keep my indentation as per source?
I tried to use different regex inside protectedSource to protect everything between HTML >...< like /(?:\>)([^<]*?)(?:\<)/g https://regex101.com/r/eV4dO0/1 but without luck.
I would like to keep the source formatting as it is. Is this possible?
No, it isn't. Content is passed through parsers, filters, writers and browser's DOM many times before it returns to you. You cannot expect to preserve every single tab or space character which is not significant in terms of the content. Please remember that CKEditor is not a code editor - it is a WYSIWYG editor.
Kindly,add those two lines for CKeditor to Config file
config.protectedSource = [/\r|\n/g];
config.allowedContent = true;

How does HTML tags work inside script tag?

For example, view-source at Joel Spolsky's public career profile
<script type="text/html" id="stackexchangeanswerswidget">
<h3>Top Answers</h3>
<div class="answers">
</div>
</script>
<script type="text/html" id="topanswer">
<div class="top-answer">
<div class="top-answer-stats">{{= shared.htmlEncode(Score) }}</div>
<span class="top-answer-title">{{= shared.htmlEncode(Title) }}</span>
<a class="add-answer">add</a>
<br class="clear" />
</div>
</script>
<script type="text/html" id="answer-view">
<div class="answer">
<div class="answer-stats {{= shared.htmlEncode(Site.toLowerCase().replace(/ /g, '')) }}">
<div class="score">
<strong>{{= shared.htmlEncode(Score) }}</strong>
<div class="votecount">votes</div>
</div>
<img class="answer-logo" src="{{= shared.htmlEncode(FaviconUrl) }}" />
</div>
<div class="answer-content">
<span class="q">Q:</span>
<div class="answer-top">
<a class="answer-title" href="{{= shared.htmlEncode(AnswerLink) }}">{{= shared.htmlEncode(Title) }}</a><br />
</div>
<span class="a">A:</span><div class="answer-body">{{= Body }}</div>
<div class="more-button" style="text-align:center; clear:both; display:none;"><a class="more">More</a></div>
</div>
</div>
</script>
How does HTML tags work inside script tag? and/or What kind of technology used for those html tags, and template alike codes {{= .... }} inside script tags?
You can put anything that you want in a <script> tag. If you specify a content type other than Javascript (or another scripting language that the browser understands), it will not be interpreted by the browser, and you can just access it as plain text. Since the contents of <script> tags are treated as CDATA, the contents are not parsed and you can store unquoted XML or HTML in the contents (as long as you don't ever put a </script> tag in the contents, since that will close your element).
This is used, for example, in SVG Web, a polyfill that allows you to use inline SVG in HTML and have that converted to native SVG in browsers that support it, or Flash in browsers that don't. It allows embedding of SVG in browsers that don't support it natively by wrapping it in a <script> tag, so the browser doesn't try and fail to parse it as HTML.
In the case of SO carreers, it looks like they storing templates for use with Backbone.js and Underscore.js in <script> tags, since that's a convenient place to stick templates in HTML. Here's a snippet of their code where they grab the template and provide it to the Underscore template engine:
TopAnswerView = Backbone.View.extend({
template: _.template($("#topanswer").html()),
events: {
"click .add-answer": "addAnswerToCV"
},
The key is in the script's type attribute. By setting it to type="text/html" it doesn't get run by the browser's JavaScript engine. Instead it is used for other things, such as templating. This example appears to be using these tags for templates.
Check out this MIX 2011 webcast that shows how something similar is used in Knockout.js:
http://channel9.msdn.com/events/mix/mix11/FRM08

Categories

Resources