Lets say I have the following markup:
<html>
<head>
<title>Page Title</title>
</head>
<body>
<h1>Some title</h1>
<p>First paragraph</p>
<p>Second paragraph</p>
</body>
<html>
I need to mark some parts of the text, namely "irst paragraph secon"
It would look something like this:
<html>
<head>
<title>Page Title</title>
</head>
<body>
<h1>Some title</h1>
<p>F
<mark>
irst paragraph</p><p>Secon
</mark>
d paragraph</p>
</body>
<html>
But the problem is be the html markup would be broken. The more complex the markup, the more problems this approach would have.
Question:
Looking for ideas on how can I take the first HTML example and apply a function to return a html structure where "irst paragraph second" is specifically marked somehow.
What I currently have is:
the parent container of the string "First paragraph"
the text "irst paragraph second"
the offset of the text "irst" in "First paragraph"
If you want to highlight text in a document then this plug-in will be helpful for you.
https://github.com/julmot/jquery.mark
Example fiddle: https://jsfiddle.net/julmot/vpav6tL1/
Usage is as simple as:
$(".context").mark("keyword");
In principle you have to:
split the documents into words
identify the first word by parent element
skip the offset
mark matching words
Making changes at word level will prevent you from breaking the markup.
I added a working example bellow. However I am not sure that it will work with all browsers.
Some of the functions like mergeWords are not used in the example but I included them because they can prove useful.
var splittedToWords = false;
function ignore(el) {
return (el.nodeType == 8) ||
(el.tagName == "BLOCKQUOTE") ||
(el.tagName == "SCRIPT") ||
(el.tagName == "DIV") ||
(!el.hasChildNodes() && el.textContent.match(/\S+/) == null);
}
function splitToWords(el) {
if (el.hasChildNodes()){
var count = el.childNodes.length;
for (var i = count - 1; i >= 0; i--) {
var node = el.childNodes[i];
if (!ignore(node))
splitToWords(node);
}
}
else { //text node
var words = el.textContent.match(/(\S+\s*)/g) || [];
var count = words.length;
var parentNode = el.parentNode;
for (var i = 0; i < count; i++) {
var wordNode = document.createElement("span");
wordNode.className = "word";
wordNode.innerText = words[i];
wordNode.setAttribute["word-index"] = i;
parentNode.insertBefore(wordNode, el);
}
parentNode.removeChild(el);
}
splittedToWords = true;
}
function unwrap(element) {
var next = element.nextSibling;
var parent = element.parentNode;
parent.removeChild(element);
var current;
var frag = document.createDocumentFragment();
do {
current = element.nextSibling;
frag.insertBefore(element, null);
} while ((element = current));
parent.insertBefore(frag, next);
}
function mergeWords(el) {
var words = document.getElementsByClassName("word");
count = words.length;
if (count > 0)
for (var i = 0; i < count; i++)
uwrap(words[i]);
}
function markWord(el, pos, len) {
var text = el.innerText;
var pre = text.substr(0, pos);
var mark = '<mark>' + text.substr(pos, len) + '</mark>';
var post = text.substring(pos + len, text.length);
el.innerHTML = pre + mark + post;
}
function mark(element, offset, text) {
if (!splittedToWords) {
var body = document.body;
splitToWords(body);
}
var words = document.getElementsByClassName("word");
var wordsCount = words.length;
var first = null;
for (var i = 0; i < wordsCount; i++ ) {
if (words[i].parentElement == element) {
first = i;
break;
}
}
done = false;
var i = first;
var pos = 0;
do {
var word = words[i];
var wordLength = word.innerText.length;
if (offset > pos + wordLength) {
i++;
pos += wordLength;
continue;
}
else {
done = true;
}
} while (!done);
var tWords = text.match(/(\S+\s*)/g) || [];
var tWordsCount = tWords.length;
if (tWordsCount == 0)
return;
for (var ti = 0; ti < tWordsCount; ti++) {
var wordEl = words[i++];
var word = wordEl.innerText;
var tWord = tWords[ti].trim();
var pos = word.indexOf(tWord);
if (pos == -1)
continue; //or maybe return.
markWord(wordEl, pos, tWord.length);
}
}
var e = document.getElementById("e");
//do the magic
mark(e, 1, 'irst paragraph Second');
<h1>Some title</h1>
<p id="e">First paragraph</p>
<p>Second paragraph</p>
Related
I'm trying to create a PDF using jsPDF and HTML2Canvas.
I have multiple DIVs to insert into the PDF.
If I try to put all DIVs into a container and render once then it only puts the first page height into the PDF.
Can't figure out how to render multiple divs and stick them in the same PDF so that it keeps going page by page.
JAVASCRIPT
function genPDF() {
html2canvas(document.getElementById("container"), {
onrendered: function (canvas) {
var img = canvas.toDataURL();
var doc = new jsPDF();
doc.addImage(img, 'PNG');
doc.addPage();
doc.save('test.pdf');
}
});
}
HTML
<div id="container">
<div class="divEl" id="div1">Hi <img src="img1.JPG"> </div>
<div class="divEl" id="div2">Why <img src="img2.PNG"> </div>
</div>
<button onClick="genPDF()"> Click Me </button>
Add each of your images separately.
You need to wait for all the html2canvas renderings are done and added to pdf and then save your final pdf.
One way to achieve this by using JQuery and array of promises, actual code would look like this:
function genPDF() {
var deferreds = [];
var doc = new jsPDF();
for (let i = 0; i < numberOfInnerDivs; i++) {
var deferred = $.Deferred();
deferreds.push(deferred.promise());
generateCanvas(i, doc, deferred);
}
$.when.apply($, deferreds).then(function () { // executes after adding all images
doc.save('test.pdf');
});
}
function generateCanvas(i, doc, deferred){
html2canvas(document.getElementById("div" + i), {
onrendered: function (canvas) {
var img = canvas.toDataURL();
doc.addImage(img, 'PNG');
doc.addPage();
deferred.resolve();
}
});
}
For me it works like that:
$('#cmd2').click(function () {
var len = 4; //$x(".//body/div/div").length
var pdf = new jsPDF('p', 'mm','a4');
var position = 0;
Hide
for (let i = 1;i <= len; i++){
html2canvas(document.querySelector('#pg'+i),
{dpi: 300, // Set to 300 DPI
scale: 1 // Adjusts your resolution
}).then(canvas => {
pdf.addImage(canvas.toDataURL("images/png", 1), 'PNG', 0,position, 210, 295);
if (i == len){
pdf.save('sample-file.pdf');
}else{
pdf.addPage();
}
});
}
});
You could try this, using the follwing javascript references in the HTML. Html2Canvas and jsPDF are quite picky with versions you use.
https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.js"
https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.0.272/jspdf.debug.js"
var forPDF = document.querySelectorAll(".js-panel-pdf");
var len = forPDF.length;
var thisPDF = new jsPDF('p', 'mm', [240, 210]); //210mm wide and 297mm high
for (var i = 0; i < forPDF.length; i++) {
html2canvas(forPDF[i], {
onrendered: function(canvas) {
thisPDF.addImage(canvas.toDataURL("images/png", 1), 'PNG', 0, 0, 210, 295);
if (parseInt(i + 1) === len) {
thisPDF.save('sample-file.pdf');
} else {
thisPDF.addPage();
}
}
});
}
Stuart Smith - This is not working it does not allow to download the pdf
here is java script code
function generatePDF(){
var imgData;
var forPDF = document.querySelectorAll(".summary");
var len `enter code here`= forPDF.length;
var doc =new jsPDF('p','pt','a4');
for (var i = 0; i < forPDF.length; i++){
html2canvas(forPDF[i],{
useCORS:true,
onrendered:function(canvas){
imgData = canvas.toDataURL('image/jpg',1.0);
var doc =new jsPDF('p','pt','a4');
doc.addImage(imgData,'jpg',10,10,500,480);
if (parseInt(i + 1) === len) {
doc.save('sample-file.pdf');
} else {
doc.addPage();
}
}
});
}
}
<svg xmlns="http://www.w3.org/2000/svg">
<title>On end test</title>
<circle r="50" cx="100" cy="100" style="fill: #F00">
<set attributeName="fill" attributeType="CSS" onbegin='alert("onbegin")'
onend='alert("onend")' to="#00F" begin="1s" dur="5s" />
</circle>
</svg>
you can try this code here
it looks onbegin event not work on the IOS device, dose anyone here has a clue?
#Rebert, you are right, but I found a another way to solve this problem in browsers which was not support begin/end event, here is the code:
window.XY = window.XY ? window.XY : {};
XY.svg = XY.svg ? XY.svg : {};
XY.svg.runtime_id = 0;
XY.svg.found_support_event = false;
XY.svg.SVG_Event_Compatibility = function(svg_object){
if(XY.svg.found_support_event) return;
if(XY.svg.runtime_id != 0) clearInterval(XY.svg.runtime_id);
var _animate_list = [];
var _animate_has_event_list = [];
_animate_list = svg_object.getElementsByTagName('animate');
if(_animate_list.length == 0){
return;
}else{
if(typeof _animate_list[0].onbegin !== 'undefined'){
XY.svg.found_support_event = true;
return;
}
}
var _length = _animate_list.length;
for(var i=0; i<_length; i++){
var _cur = _animate_list[i];
var _cur_obj = { target:_cur};
var _has_event = false;
var _begin_hold = _cur.getAttributeNode('onbegin');
var _end_hold = _cur.getAttributeNode('onend');
if(_begin_hold){
_cur_obj.begin = _begin_hold.value;
_cur_obj.duration = _cur.getSimpleDuration();
_has_event = true;
}
if(_end_hold){
_cur_obj.end = _end_hold.value;
_cur_obj.duration = _cur.getSimpleDuration();
_has_event = true;
}
if(_has_event){
//console.log(_cur_obj);
_animate_has_event_list.push(_cur_obj);
}
}
//console.log('start' ,_animate_has_event_list);
function Run(){
if(_animate_has_event_list.length == 0){
clearInterval(XY.svg.runtime_id);
}
var _length = _animate_has_event_list.length;
//console.log("==========================");
for(var i=0; i<_animate_has_event_list.length; i++){
var _cur_obj = _animate_has_event_list[i];
if(_cur_obj.begin == null && _cur_obj.end == null){
//console.log('remove' ,_animate_has_event_list.splice(i,1));
continue;
}
var _start_time = _cur_obj.target.getStartTime();
var _current_time = _cur_obj.target.getCurrentTime();
//console.log(_start_time, _current_time, _cur_obj.duration ,isNaN(_start_time) ,_start_time < Infinity
// ,!isNaN(_start_time) && _start_time < Infinity && _start_time > _current_time
// ,!isNaN(_start_time) && _start_time < Infinity && (_current_time - _start_time > _cur_obj.duration));
if(_cur_obj.begin){
if(!isNaN(_start_time) && _start_time < Infinity && _start_time < _current_time){
var _begin = eval(_cur_obj.begin);
if(typeof _begin === 'function'){
_begin.apply(_cur_obj.target);
}
_cur_obj.begin = null;
}
}
if(_cur_obj.end){
if(!isNaN(_start_time) && _start_time < Infinity && (_current_time - _start_time > _cur_obj.duration)){
var _end = eval(_cur_obj.end);
if(typeof _end === 'function'){
_end.apply(_cur_obj.target);
}
_cur_obj.end = null;
}
}
}
//console.log('runtime', _animate_has_event_list);
}
XY.svg.runtime_id = setInterval(Run, 100);
}
This is not implemented in webkit currently. You'd have write a patch for bug 81995 or pay someone else to do it for you.
The focus moves to the next input field before the event is fired. Can anyone help me find the bug, or figure out how to find it myself?
The goal is to catch the keyup event, verify that it is tab or shift+tab, and then tab as though it were tabbing through a table. When the focus gets to the last input that is visible, the three rows (see fiddle for visual) should move together to reveal hidden inputs. Once to the end of the inputs in that row, the three rows will slide back down to the beginning again, kind of like a carriage return on a typewriter, or tabbing into a different row in a table.
Right now, the tab event is moving just the row that holds the focus, and it is moving it before my script even starts to run. I just need to know why this is happening so that I can research how to resolve it.
Any help you can offer is appreciated. Please let me know if you need more information.
P.S. Using jquery 1.9.1
Link to Fiddle
jQuery(document).ready(function ($) {
// bind listeners to time input fields
//$('.timeBlock').blur(validateHrs);
$('.timeBlock').keyup(function () {
var caller = $(this);
var obj = new LayoutObj(caller);
if (event.keyCode === 9) {
if (event.shiftKey) {
obj.dir = 'prev';
}
obj.navDates();
}
});
// bind listeners to prev/next buttons
$('.previous, .next').on('click', function () {
var str = $(this).attr('class');
var caller = $(this);
var obj = new LayoutObj(caller);
obj.src = 'pg';
if (str === 'previous') {
obj.dir = 'prev';
}
obj.navDates();
});
});
function LayoutObj(input) {
var today = new Date();
var thisMonth = today.getMonth();
var thisDate = today.getDate();
var dateStr = '';
var fullDates = $('.dateNum');
var splitDates = new Array();
this.currIndex = 0; //currIndex defaults to 0
this.todayIndex;
fullDates.each(function (index) {
splitDates[index] = $(this).text().split('/');
});
//traverse the list of dates in the pay period, compare values and stop when/if you find today
for (var i = 0; i < splitDates.length; i++) {
if (thisMonth === (parseInt(splitDates[i][0], 10) - 1) && thisDate === parseInt(splitDates[i][1], 10)) {
thisMonth += 1;
thisMonth += '';
thisDate += '';
if (thisMonth.length < 2) {
dateStr = "0" + thisMonth + "/";
}
else {
dateStr = thisMonth + "/";
}
if (thisDate.length < 2) {
dateStr += "0" + thisDate;
}
else {
dateStr += thisDate;
}
fullDates[i].parentNode.setAttribute('class', 'date today');
this.todayIndex = i;
break;
}
}
//grab all of the lists & the inputs
this.window = $('div.timeViewList');
this.allLists = $('.timeViewList ul');
this.inputs = $('.timeBlock');
//if input`isn't null, set currIndex to match index of caller
if (input !== null) {
this.currIndex = this.inputs.index(input);
}
//else if today is in the pay period, set currIndex to todayIndex
else if (this.todayIndex !== undefined) {
this.currIndex = this.todayIndex;
}
//(else default = 0)
//grab the offsets for the cell, parent, and lists.
this.winOffset = this.window.offset().left;
this.cellOffset = this.inputs.eq(this.currIndex).offset().left;
this.listOffset = this.inputs.offset().left;
//grab the width of a cell, the parent, and lists
this.cellWidth = this.inputs.outerWidth();
this.listWidth = this.inputs.last().offset().left + this.cellWidth - this.inputs.eq(0).offset().left;
this.winWidth = this.window.outerWidth();
//calculate the maximum (left) offset between the lists and the parents
this.offsetMax = (this.listWidth - this.winWidth);
//set default scroll direction as fwd, and default nav as tab
this.dir = 'next';
this.src = 'tab';
//grab the offsets for the cell, parent, and lists.
this.cellOffset = this.inputs.eq(this.currIndex).offset().left;
this.listOffset = this.inputs.eq(0).offset().left;
this.winOffset = this.allLists.parent().offset().left;
//calculate the maximum (left) offset between the lists and the parents
this.offsetMax = (this.listWidth - this.winWidth);
}
LayoutObj.prototype.focusDate = function () {
this.inputs.eq(this.currIndex).focus();
};
LayoutObj.prototype.slideLists = function (num) {
this.listOffset += num;
this.allLists.offset({ left: this.listOffset });
};
LayoutObj.prototype.navDates = function () {
if (!this.inWindow()) {
var slide = 0;
switch (this.src) {
case 'pg':
slide = this.winWidth - this.cellWidth;
break;
case 'tab':
slide = this.cellWidth + 1;
break;
default:
break;
}
if (this.dir === 'next') {
slide = -slide;
}
this.slideLists(slide);
}
this.focusDate();
};
LayoutObj.prototype.inWindow = function () {
//detects if cell intended for focus is visible in the parent div
if ((this.cellOffset > this.winOffset) && ((this.cellOffset + this.cellWidth) < (this.winOffset + this.winWidth))) {
return true;
}
else {
return false;
}
}
All it needed was 'keydown()' instead of 'keyup().'
I'm trying to learn javascript, and spent tonight writing a getElementByID() function using Breadth-First Search. In short: I'm lost.
Fiddle: http://jsfiddle.net/timdown/a2Fm6/
Code:
var nodes = [];
function getElementById(node, id) {
alert(nodes.length);
if (node.childNodes[i].id == id) {
return node.childNodes[i];
} else if (node.childNodes[i].length > 0) {
for (var i = 0, len = node.childNodes.length; i < len; ++i) {
nodes.push(node.childNodes[i]);
}
}
if (nodes.length > 0) {
getElementById(nodes[0], id);
}
}
var el = getElementById(document.body, 'id');
Any help?
You're missing a for loop in the top half of your code. Where is i defined?
Here's how I'd write it:
function getElementById(node, id) {
//An array of all the nodes at the same depth
var nodes = [node];
//While the array is not empty
while(nodes.length) {
var newNodes = [];
for(var i = 0; i < nodes.length; i++) {
var children = nodes[i].childNodes;
for(var j = 0; j < children.length; j++) {
var child = children[j];
if(child.id == id) {
return child
}
newNodes.push(child);
}
}
//Replace nodes with an array of the nodes the next level down
nodes = newNodes
}
}
I would like to allow the user of my XULRunner based app to be able to do copy/paste via a context menu.
Keyboard shortcuts Ctrl-C and Ctrl-V are already working fine
Here it is without flash. The getInputSelection function is from here: Is there an Internet Explorer approved substitute for selectionStart and selectionEnd?.
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window id="mywin" title="my app"
width="800" height="600" persist="screenX screenY width height sizemode"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<popupset>
<menupopup id="clipmenu">
<menuitem label="Copy" oncommand="copy()"/>
<menuitem label="Paste" oncommand="paste();"/>
</menupopup>
</popupset>
<browser
type="content-primary"
src="http://127.0.0.1/"
flex="1"
disablehistory="true"
id="browserId"
context="clipmenu"
/>
<script>
<![CDATA[
function copy()
{
var tabBrowser = document.getElementById("browserId");
var selectedTagName = tabBrowser.contentWindow.document.activeElement.tagName;
var windowObj;
if(selectedTagName == "IFRAME")
{
windowObj = tabBrowser.contentWindow.frames[tabBrowser.contentWindow.document.activeElement.name];
}
else
{
windowObj = document.getElementById("browserId").contentWindow;
}
var selectedText = windowObj.getSelection();
if(!selectedText || selectedText == "")
{
var focused = windowObj.document.activeElement;
if(focused && focused.value)
{
selectedText = getSelectionFromInput(focused);
}
}
//alert(selectedText + "---");
const clipHelper = Components.classes["#mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
clipHelper.copyString(selectedText);
}
function getSelectionFromInput(focused)
{
var focusedValue = focused.value;
var sel = getInputSelection(focused);
var selectedText = "";
if(focusedValue.length == (sel.end))
{
selectedText = focusedValue.substring(sel.start);
}
else
{
selectedText = focusedValue.substring(sel.start, (sel.end));
}
return selectedText;
}
function paste()
{
var clip = Components.classes["#mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard);
var trans = Components.classes["#mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
trans.addDataFlavor("text/unicode");
clip.getData(trans, clip.kGlobalClipboard);
var str = new Object();
var len = new Object();
trans.getTransferData("text/unicode",str,len);
str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
str = str.data.substring(0, len.value / 2);
var focused = document.commandDispatcher.focusedElement;
var focusedValue = focused.value;
var sel = getInputSelection(focused);
focused.value = focusedValue.substring(0,sel.start) + str + focusedValue.substring(sel.end);
}
function getInputSelection(el) {
var start = 0, end = 0, normalizedValue, range,
textInputRange, len, endRange;
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
start = el.selectionStart;
end = el.selectionEnd;
} else {
range = document.selection.createRange();
if (range && range.parentElement() == el) {
len = el.value.length;
normalizedValue = el.value.replace(/\r\n/g, "\n");
// Create a working TextRange that lives only in the input
textInputRange = el.createTextRange();
textInputRange.moveToBookmark(range.getBookmark());
// Check if the start and end of the selection are at the very end
// of the input, since moveStart/moveEnd doesn't return what we want
// in those cases
endRange = el.createTextRange();
endRange.collapse(false);
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
start = end = len;
} else {
start = -textInputRange.moveStart("character", -len);
start += normalizedValue.slice(0, start).split("\n").length - 1;
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
end = len;
} else {
end = -textInputRange.moveEnd("character", -len);
end += normalizedValue.slice(0, end).split("\n").length - 1;
}
}
}
}
return {
start: start,
end: end
};
}
]]>
</script>
</window>
Updated to support iframes.
Create menu using the code below.
<popupset>
<menupopup id="clipmenu">
<menuitem label="Copy" oncommand="copy();"/>
<menuseparator/>
<menuitem label="paste" oncommand="paste();"/>
</menupopup>
</popupset>
<browser type="content" src="chrome://myapp/content/theme1/index.html" flex="1" context="clipmenu"/>
Connect it to whatever you want, here am making it the context menu of the browser element by giving the id of menu in the context attribute of the browser element.
Then for each menu you can give the command to execute in oncommand event. We have given the function copy and paste.
Then write the code to copy text from whatever element you want or if you want to copy the selected data only.
See the below link for copying command.
http://www.deluxeblogtips.com/2010/06/javascript-copy-to-clipboard.html
One more example for "Copy" context menu in XULRunner under SWT Browser (code was implemented for Eclipse v3.5.1, XULRunner v1.8.1.3):
Browser browser = new Browser(parent, style | SWT.MOZILLA);
Menu menu = new Menu(browser);
MenuItem item = new MenuItem(menu, SWT.NONE);
item.setText("Copy");
item.addSelectionListener(new SelectionListener() {
#Override
public void widgetSelected(SelectionEvent e) {
nsIWebBrowser webBrowser = (nsIWebBrowser) browser.getWebBrowser();
nsIInterfaceRequestor req = (nsIInterfaceRequestor) webBrowser.queryInterface(nsIInterfaceRequestor.NS_IINTERFACEREQUESTOR_IID);
nsIDocShell docShell = (nsIDocShell) req.getInterface(nsIDocShell.NS_IDOCSHELL_IID);
nsIClipboardCommands cmds = (nsIClipboardCommands) docShell.queryInterface(nsIClipboardCommands.NS_ICLIPBOARDCOMMANDS_IID);
cmds.copySelection();
}
#Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
browser.setMenu(menu);