Print multiple pages for different scroll positions via JavaScript - 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.

Related

jQuery hide Show More button when text is limited

I am trying to use a Show More/Show Less button for texts to expand and contract the long texts/paragraphs. The code seems to work fine in all aspect. The one and only flaw is that I can still see the show more button even if the text is limited (say only 1 line). How can I set a condition for the button to only appear if a certain number of lines or text has been displayed (say 3 lines or 50 words or any such possible limitation). I am not being able to figure out how to do it as I am not that good in jQuery. Hence, I am here seeking a solution.
Codepen: [ https://codepen.io/zoomkraft/pen/ZEELQXL ][1]
In the sample above, try eradicating the paragraph limiting it to 1 or 2 line only. The show more button still shows when its not needed as there is not much text for expansion. The should be hidden for such limited texts.
[1]: https://codepen.io/zoomkraft/pen/ZEELQXL
UPDATED
HTML comes within while loop.
<?php while($content = $contents->fetch()){ ?>
<div class="text-container">
<div class="content hideContent">
<p><?php echo $content['uc_desc']; ?></p>
</div>
<div class="show-more">
<span>Show more</span>
</div>
</div>
<?php } ?>
I got the solution to why its not working in while loop.
Removed this
var lengthofContent = $(".content").text();
console.log(lengthofContent.length);
if(lengthofContent.length < 200){
$(this).find(".show-more").hide();
}
and added this
$('.content').each(function(k,v){
if ($(v).text().length < 200) {
$(v).removeClass('hideContent');
$(v).next().hide();
}
});
Can you add another class to the show option. Like this
<div class="show-more show-option">
<span>Show more</span>
</div>
then add the following in the script tag directly.
$('.content').each(function(key,value){
var lengthofContent = $(value).text();
if(lengthofContent.length < 200)
$(value).siblings('show-option').hide();
});
The value of 200 can be configured best based on the number you want.

Partially run code as html and as text

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>

Paginate long text using php

I've a long text (More than 10,000 words) contains html tags stored in a string.
And want to wrap every 1000 words with <div class="chunk"></div> with considering auto close opened html tags and auto open closed html tags in the different chunks.
I found many solutions but they depend on the number of characters and don't consider auto open/close html tags.
Also the php function wordwrap neglects fixing html tags problem.
Simulation
<div id="long-text">
Dynamic long text more than 10,000 words (Text contains HTML (img, p, span, i, ...etc) tags)
</div>
Wrong result
<div id="long-text">
<div class="chunk">
<p>Chunk 1 : first approximately 1000 words with their html tags
<img src="image.jpg"> ## Unclosed <p> tag ##
</div>
<div class="chunk">
## The closed <p> tag of the previous chunk ##
</p><p>Chunk 2 : second approximately 1000 words with their html tags
<img src="image.jpg"> </p><p> ## unclosed <p> tag ##
</div>
<div class="chunk">
## Missing open <p> tag because it was cut in the previous chunk ##
Chunk 3 : third approximately 1000 words with their html tags</p>
</div>
</div>
Expected result
<div id="long-text">
<div class="chunk">
<p>Chunk 1 : first approximately 1000 words with their html tags
<img src="image.jpg"> </p>
</div>
<div class="chunk">
<p>Chunk 2 : second approximately 1000 words with their html tags
<img src="image.jpg"> </p>
</div>
<div class="chunk">
<p>Chunk 3 : third approximately 1000 words with their html tags</p>
</div>
</div>
And then i can paginate the result with javascript.
After searching i found the accepted answer here: Shortening text tweet-like without cutting links inside
cutting the text (from the start only) and auto close opened html tags.
I tried to modify the code to auto open closed tags if i cut from the middle of the text but unfortunately i failed to do the job.
I don't mind if there are another better solutions to paginate the long text according to the number of words using (php or javascript or both of them).
So the idea is to use JQuery to chunk the immediate children via cloning and splitting the internal text. It may need some more work for further nested HTML but it's a start:
function chunkText(length) {
var words = $(this).text().split(" ");
var res = [$(this)];
if (words.length > br) {
var overflow = $(this).clone();
var keepText = words.slice(0,length);
$(this).text(keepText.join(" "));
overflow.text(words.slice(length).join(" "));
res = res.concat(chunkText.call(overflow, length));
}
return res;
}
var br = 10; //Words to split on
$("#long-text > *").each( function () {
var chunks = chunkText.call(this,br);
$.each(chunks, function (i,v) {
$("#long-text")
.append($("<div>").addClass("chunk").append(v))
.append($("<img>").attr("src","image.jpg")));
});
});
Basic demo:
https://jsfiddle.net/o2d8zf4v/

Hiding text using Javascript / JQuery

I am developing a Chrome extension and trying to hide part of a page. I am fairly new to this stuff so apologies in advance if I am using the wrong terms or the question seems obvious!
The page format appears as below:
<div class="wrapper">
<span class="loud" style="text-decoration: none;">...</span>
<div class="leave-gap">...</div>
<quote>...</quote>
"*** I can't figure how to hide this ***"
<p></p>
<span id="id_12345" class="none">...</span>
<div class="block-footer">...</div>
<div class="leave-gap">...</div>
</div>
As the snippet suggests, I cannot figure out how to hide the bit highlighted with stars.
I have a function that takes as an input a variable that is the first element in class "wrapper":
function processComment(commentStart)
{
$element = commentStart;
while($element)
{
if(some condition)
{
$element.hide();
}
$element.next();
}
Because this text is sitting by itself outside any tags, it is not being picked up. I can't just hide the whole of the wrapper class because there are some bits inside it that I need to show.
Any suggestions gratefully received.
Thanks,
Richard
Set visibility propery or wrapper as collapse and set visibility of childrens to visible for overriding visibility property. That it will only hide the text node.
$('.wrapper').css('visibility', 'collapse').find('*').css('visibility', 'visible');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="wrapper">
<span class="loud" style="text-decoration: none;">...</span>
<div class="leave-gap">...</div>
<quote>...</quote>
"*** I can't figure how to hide this ***"
<p></p>
<span id="id_12345" class="none">...</span>
<div class="block-footer">...</div>
<div class="leave-gap">...</div>
</div>
Your best bet might be to place the content you want hidden into a span of its own.
If I well understand, your if (some condition) is regarding the text content by itself, so it's a regex stuff or something like.
So what you can do is, having located the involved text part, wrap it between <span> tags: then you can address this <span> element the usual way.
It may look like this (here assuming your meta-example meant $elements are successive words):
function processComment(comment) {
// (comment is supposed to be the HTML element to process)
var words = $(comment).html().split(' ');
for (var i in words) {
var word = words|i];
if(some condition on word) {
words[i] = '<span class="hidden-word">' + word + '</span>';
}
}
$(comment).html(words.join(' '));
}

Can I use document.createElement to create a series of DIVs on the fly?

I'm javascript to loop through a large number of items and using
document.getElementById("flap" + i).innerHTML = flap(i)
to write to a large number (50 at a time) of DIVs on my page. Rather than filling my HTML page with a large number of DIVs which in most cases won't be used I would like to use:
HTML DOM createElement() Method ...... to create the 50 DIVs on the fly and only as many as I need.
Here is my sample HTML DIV which I need to create on the fly (so, I need to start at "0" and create up to "49":
<div style="clear:both"> <br> </div>
<div id="flapBox3">
<p id='whenFlap3' class= "mscFlapTime">&nbsp &nbsp &#149 &nbsp posted &nbsp<b >7 minutes ago</b></p>
<b id='flapSum3' class= "mscFlapSumBold">Summary</b>
<p id='flap3'class= "mscFlapText" >Lorem ipsum .. </p>
</div>
I'm not sure how to do this and sure could use some help! Thanks!!!
You can, but adding elements to the DOM over and over a gain is really quite slow because it forces the browser to reflow/repaint the viewport every time. It's much more efficient to build up your HTML in memory and insert it all in one go at the end.
You can build it up as a string, then use innerHTML to insert it. Alternately you can create DOM nodes in a document fragment, then insert that into the DOM.
See: Add to DOM without jQuery on how to add and manipulate a dom node. This example does not use multiple nodes, but you'll get the idea.
here is a basic example using innerHTML:
http://jsfiddle.net/rn5GG/2/
var template = document.getElementById('template').innerHTML;
var htmlString = template;
for(var i=0;i<50;i++){
htmlString += template;
}
document.body.innerHTML = htmlString;
HTML:
<div id='template' style="clear:both"> <br>
<div id="flapBox3">
<p id='whenFlap3' class= "mscFlapTime">&nbsp &nbsp &#149 &nbsp posted &nbsp<b >7 minutes ago</b></p>
<b id='flapSum3' class= "mscFlapSumBold">Summary</b>
<p id='flap3'class= "mscFlapText" >Lorem ipsum .. </p>
</div>

Categories

Resources