Jquery modify html with each - javascript

I have a problem with jQuery. I want to write a function that dynamically replaces some content in my HTML.
This is the function:
function renderPage(datas, html) {
$(html).find("*").each(function(){
if ($(this).attr("data-func")) {
var df = $(this).attr("data-func");
var content = null;
eval("content = " + df + "($(this),datas);");
console.log(content);
}
});
}
The problem is, that it has no effect! I see in the log that the content variable is right, but the output is not different.
My function in eval:
function fill(element,data) {
element.html("BASSZA MEG");
var events = data.events;
var i = 0;
var n = events.length;
for (;i<n; i++) {
obj = events[i];
var tr = $("<tr>");
var nev = $("<td>");
var link = $("<a href='programbelso.html#id="+obj["event_id"]+"&logo="+obj["event_logo"]+"'>");
link.html(obj["event_name"]);
nev.append(link);
tr.append(nev);
element.append(tr);
}
console.log("element: " + element);
return element;
}

Firstly, do NOT loop over every single element in the document, and then use an if() statement. You can drastically improve the performance by using the right selector.
The following sample should achieve what you're trying to do without needing to use the evil eval() function, too:
function renderPage(datas, html) {
$('[data-func]').each(function() {
var df = $(this).attr("data-func");
var the_func = window[$(this).attr("data-func")];
if(typeof the_func === 'function') {
var content = the_func($(this), datas);
console.log(content);
}
});
}

Related

Add a paragraph or table etc. at Cursor

I have a function for adding the contents of a separate google document at the cursor point within the active document, but I haven't been able to get it to work. I keep getting the "Element does not contain the specified child element" exception, and then the contents gets pasted to the bottom of the document rather than at the cursor point!
function AddTable() {
//here you need to get document id from url (Example, 1oWyVMa-8fzQ4leCrn2kIk70GT5O9pqsXsT88ZjYE_z8)
var FileTemplateFileId = "1MFG06knf__tcwHWdybaBk124Ia_Mb0gBE0Gk8e0URAM"; //Browser.inputBox("ID der Serienbriefvorlage (aus Dokumentenlink kopieren):");
var doc = DocumentApp.openById(FileTemplateFileId);
var DocName = doc.getName();
//Create copy of the template document and open it
var docCopy = DocumentApp.getActiveDocument();
var totalParagraphs = doc.getBody().getParagraphs(); // get the total number of paragraphs elements
Logger.log(totalParagraphs);
var cursor = docCopy.getCursor();
var totalElements = doc.getNumChildren();
var elements = [];
for (var j = 0; j < totalElements; ++j) {
var body = docCopy.getBody();
var element = doc.getChild(j).copy();
var type = element.getType();
if (type == DocumentApp.ElementType.PARAGRAPH) {
body.appendParagraph(element);
} else if (type == DocumentApp.ElementType.TABLE) {
body.appendTable(element);
} else if (type == DocumentApp.ElementType.LIST_ITEM) {
body.appendListItem(element);
}
// ...add other conditions (headers, footers...
}
Logger.log(element.editAsText().getText());
elements.push(element); // store paragraphs in an array
Logger.log(element.editAsText().getText());
for (var el = 0; el < elements.length; el++) {
var paragraph = elements[el].copy();
var doc = DocumentApp.getActiveDocument();
var bodys = doc.getBody();
var cursor = doc.getCursor();
var element = cursor.getElement();
var container = element.getParent();
try {
var childIndex = body.getChildIndex(container);
bodys.insertParagraph(childIndex, paragraph);
} catch (e) {
DocumentApp.getUi().alert("There was a problem: " + e.message);
}
}
}
You want to copy the objects (paragraphs, tables and lists) from the document of 1MFG06knf__tcwHWdybaBk124Ia_Mb0gBE0Gk8e0URAM to the active Document.
You want to copy the objects to the cursor position on the active Document.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Modification points:
In your script, appendParagraph, appendTable and appendListItem are used at the 1st for loop. I think that the reason that the copied objects are put to the last of the document is due to this.
var body = docCopy.getBody(); can be put to the out of the for loop.
In your case, I think that when the 1st for loop is modified, 2nd for loop is not required.
When above points are reflected to your script, it becomes as follows.
Modified script:
function AddTable() {
var FileTemplateFileId = "1MFG06knf__tcwHWdybaBk124Ia_Mb0gBE0Gk8e0URAM";
var doc = DocumentApp.openById(FileTemplateFileId);
var docCopy = DocumentApp.getActiveDocument();
var body = docCopy.getBody();
var cursor = docCopy.getCursor();
var cursorPos = docCopy.getBody().getChildIndex(cursor.getElement());
var totalElements = doc.getNumChildren();
for (var j = 0; j < totalElements; ++j) {
var element = doc.getChild(j).copy();
var type = element.getType();
if (type == DocumentApp.ElementType.PARAGRAPH) {
body.insertParagraph(cursorPos + j, element);
} else if (type == DocumentApp.ElementType.TABLE) {
body.insertTable(cursorPos + j, element);
} else if (type == DocumentApp.ElementType.LIST_ITEM) {
body.insertListItem(cursorPos + j, element);
}
}
}
It seems that DocName is not used in your script.
References:
insertParagraph()
insertTable()
insertListItem()
If I misunderstood your question and this was not the result you want, I apologize. At that time, can you provide the sample source Document? By this, I would like to confirm it.

Javascript replace function error

I have a problem with the javascript replace function and I don't succeed to resolve it.
This is my code : https://jsfiddle.net/r36k20sa/1/
var tags = ['zazie', 'johnny'];
tags.forEach(function(element) {
content = content.replace(
new RegExp("(?!<a.*?>.*?)(\\b" + element + "\\b)(?!.*?<\\/a>)", "igm"),
'$1'
);
});
In the tags array, if I reverse the array "johnny" then "zazie" all tags are well selected otherwise, some tags are missing. (The last in this example). What can be the trick?
What can be explained that ? It seems like the javascript replace function runs asynchronous?
Thanks for your help.
Are you seriously using regex to process HTML when you have a DOM parser at your fingertips?
var content = document.getElementById('content');
function findTextNodes(root,ret) {
// recursively descend into child nodes and return an array of text nodes
var children = root.childNodes, l = children.length, i;
ret = ret || [];
for( i=0; i<l; i++) {
if( children[i].nodeType == 1) { // ElementNode
// excluding A tags here, you might also want to exclude BUTTON tags
if( children[i].nodeName != "A") {
findTextNodes(children[i],ret);
}
}
if( children[i].nodeType == 3) { // TextNode
ret.push(children[i]);
}
}
return ret;
}
var textNodes = findTextNodes(content);
// now search those text node contents for matching tags.
var tags = ['zazie','johnny'], tagcount = tags.length, regexes, tag;
for( tag=0; tag<tagcount; tag++) {
regexes[tag] = new RegExp("\b"+tags[tag]+"\b","i");
}
var node, match, index, tagtext, newnode;
while(node = textNodes.shift()) {
for( tag=0; tag<tagcount; tag++) {
if( match = node.nodeValue.match(regexes[tag])) {
index = match.index;
textNodes.unshift(node.splitText(index + tags[tag].length));
tagtext = node.splitText(index);
newnode = document.createElement('a');
newnode.href = "";
newnode.className = "esk-seo-plu-link";
newnode.style.cssText = "background:red;color:white";
tagtext.parentNode.replaceChild(newnode,tagtext);
newnode.appendChild(tagtext);
}
}
}
// and done - no more action needed since it was in-place.
See it in action
Please replace . with \\.
var tags = ['zazie', 'johnny'];
tags.forEach(function(element) {
content = content.replace(
new RegExp("(?!<a.*?>\\.*?)(\\b" + element + "\\b)(?!\\.*?<\\/a>)", "igm"),
'$1'
);
});

Google Apps Script - Typewriter Effect

I'm trying to add a sort of 'typewriter effect' on my google apps script for google docs. I want to make it type out text, in this case a wikipedia article, as if a user was typing it, so add a delay. Unfortunately the function appendText(), even if you use Utilities.sleep, it still just types the entire article out as soon as the script finishes. What function would I use to accomplish something like this?
function onOpen(e) {
DocumentApp.getUi().createAddonMenu()
.addItem('Start', 'myFunction')
.addToUi();
}
function onInstall(e) {
onOpen(e);
}
function myFunction() {
var body = DocumentApp.getActiveDocument().getBody();
var text = body.editAsText();
var response = UrlFetchApp.fetch('https://en.wikipedia.org/w/api.php?&format=json&action=query&generator=random&grnnamespace=0&prop=title&grnlimit=1');
var json = JSON.parse(response);
for (key in json.query.pages) {
var title = json.query.pages[key].title;
}
var url = 'https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&explaintext=&titles=' + title
var response2 = UrlFetchApp.fetch(url);
var json2 = JSON.parse(response2);
for (key in json2.query.pages) {
var content = json2.query.pages[key].extract;
}
//content = content.replace(/==.*==/, '====')
var all = title + '\n' + content;
text.appendText("Start\n");
Utilities.sleep(1000);
text.appendText(content);
}
You need to flush the document. The DocumentApp API does not have a flush method (like SpreadsheetApp) but you can still flush it by using saveAndClose and then re-opening the document (for example with document=DocumentApp.openById("myid")
saveAndClose is automatically called when a script finishes, but not after every change you make as Google batches those changes for performance.
https://developers.google.com/apps-script/reference/document/document
I have tried #ZigMandel suggestion. It appears to work but the text is being typed from the left out.
function onOpen(e) {
DocumentApp.getUi().createAddonMenu()
.addItem('Start', 'myFunction')
.addToUi();
}
function onInstall(e) {
onOpen(e);
}
function myFunction() {
var body = DocumentApp.getActiveDocument().getBody();
var text = body.editAsText();
var response = UrlFetchApp.fetch('https://en.wikipedia.org/w/api.php?&format=json&action=query&generator=random&grnnamespace=0&prop=title&grnlimit=1');
var json = JSON.parse(response);
for (key in json.query.pages) {
var title = json.query.pages[key].title;
}
var url = 'https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&explaintext=&titles=' + title
var response2 = UrlFetchApp.fetch(url);
var json2 = JSON.parse(response2);
for (key in json2.query.pages) {
var content = json2.query.pages[key].extract;
}
//format(content);
//var par1 = body.insertParagraph(0, title);
//par1.setAlignment(DocumentApp.HorizontalAlignment.CENTER);
var str = "Sphinx of black quartz, judge my vow."
var split = str.split("");
for (var i = split.length - 1; i >= 0; i--) {
text.appendText(split[i]);
DocumentApp.getActiveDocument().saveAndClose();
body = DocumentApp.getActiveDocument().getBody();
text = body.editAsText();
}
}
function format(txt) {
txt = '\n' + txt;
txt = txt.replace(/\===(.+?)\===/g, "").replace(/\==
(.+?)\==/g,"").replace(/\n+/g, "\n").replace(/\n/g, "\n" + " ");
return txt;
}
To get the script to print out each character with a delay between, you can put your sleep method into a for loop.
for(var i = 0; i < content.length; i++) {
text.appendText(content[i]);
Utilities.sleep(200);
}
I think this gives you the effect you are looking for.

Want to put all words at the site with the color blue and uppercase

I'm building one site at JOOMLA and at this site i want to put all the word "Inovflow" on the site, at the color blue and upercase. Like this "INOVFLOW".
I put this code on the js folder of the site:
jQuery(document).fn.findText = function(params){
var phrases = params.query,
ignorance = params.ignorecase;
wrapper = $(this);
var source = wrapper.html();
selection_class_name = params.style;
source = source.replace(/[\n|\t]+/gi, '');
source = source.replace(/\s+/gi, ' ');
source = source.replace(/> /gi, '>');
source = source.replace(/(\w)</gi, function(m, w){return(w + " <");});
phrases.forEach(function(str){
var regexp = makeRegexp(str);
source = source.replace(regexp, function (m){
return (emulateSelection(m));
});
});
wrapper.html(source);
var res_array = wrapper.find("[search=xxxxx]")
return(res_array);
};
function makeRegexp(s){
var space = '( )?(<span[^>]*>)?(</span[^>]*>)?( )?';
var result = s.replace(/\s/gi, space);
result = new RegExp(space + result + space, "gi");
return(result);
}
function emulateSelection (htmlPiece){
htmlPiece = htmlPiece.replace(/(?!=>)[^><]+(?=<)/g, function(w){
return(wrapWords(w));}
);
htmlPiece = htmlPiece.replace(/^[^><]+/, function(w){
return(wrapWords(w));}
);
htmlPiece = htmlPiece.replace(/[^><]+$/, function(w){
return(wrapWords(w));}
);
htmlPiece = htmlPiece.replace(/^[^><]+$/, function(w){
return(wrapWords(w));}
);
return( htmlPiece );
}
function wrapWords(plainPiece){
console.log("plain: " + plainPiece);
var start = '<span search="xxxxx">',
stop = '</span>';
return(start + plainPiece + stop);
}
jQuery(document).each($('.container').findText({query: ['INOVFLOW']}), function (){
$(this).addClass("changeColorInovflow");
});
After this, the page seems to get on a Infinite loop and doesn't load.
if instead of jQuery(document) I use $. the JS returns a error and doesn't run.
Am I doing something wrong?
If findText is intended to be a jQuery plugin, you'll need to update the way the function is declared.
$.fn.findText = function(params) {
var phrases = params.query;
// removed unused 'ignorance' var
var wrapper = this; // this is already a jQuery object
var source = wrapper.html();
selection_class_name = params.style;
source = source.replace(/[\n|\t]+/gi, '');
source = source.replace(/\s+/gi, ' ');
source = source.replace(/> /gi, '>');
source = source.replace(/(\w)</gi, function(m, w){return(w + " <");});
phrases.forEach(function(str){
var regexp = makeRegexp(str);
source = source.replace(regexp, function (m){
return (emulateSelection(m));
});
});
wrapper.html(source);
var res_array = wrapper.find("[search=xxxxx]")
return this; // return 'this' to make it chainable
}
Here are the relevant docs:
https://learn.jquery.com/plugins/basic-plugin-creation/
Then, when you call findText, you can use a much simpler selector:
$('.container').each(function() {
$(this).findText({query: ['INOVFLOW']}).addClass("changeColorInovflow");
});
The original code wouldn't work because each() takes either a function or an array with a callback as parameters, not a selector.
.each(): http://api.jquery.com/each/
jQuery.each(): http://api.jquery.com/jquery.each/

Asynchronous Function in Iteration - javascript

I am trying not to replicate code and loop over a a function in d3 that is asynchronous. Here is some code
Since d3.text is asynchronous , I am not able to use the index u in a correct way to append objects to the DOM. How should I go about this? I need the loop to go to next iteration once d3.text finished
for(var u in urls) {
console.log(u);
var url = "interest_points/" + urls[u] + ".csv";
var data_gpBy_month = {};
var sortable_month = []
d3.text(url, function(text) {
// some code...
var data = d3.csv.parseRows(text).map(function(row) {
//some code...
});
//some code
});
}
Something like this (fiddle: http://jsfiddle.net/EYAYT/2/) ?
var urls = ["asd", "asdasd", "Asdasfa"];
var currentUrlIndex = 0;
var getUrl = function(){
if (currentUrlIndex >= urls.length){
return null;
} else {
return "interest_points/" + urls[currentUrlIndex++] + ".csv";
}
}
var execd3Text = function(){
var url = getUrl();
if (url){
d3.text(url, function(text) {
//some code;;
execd3Text();
});
}
}
execd3Text();
The loop should simply become this:
for(var u in urls) { loadParseAndRender(u); }
All your existing logic then moves into loadParseAndRender, but at this point u will never get overridden. I.e, in fancy terms, it gets captured in the closure.
function loadParseAndRender(u) {
// the rest of your code
}
What David W suggested is the same thing as abive, but without creating a named function for it, you'd do this:
for(var _u in urls) {
(function(u) { // this is an anonymous function
// the rest of you code
})(_u) // this function gets called as soon as it's declared
}
If I understood properly:
function doSomething(array) {
var u = array.shift();
console.log(u);
var url = "interest_points/" + urls[u] + ".csv";
var data_gpBy_month = {};
var sortable_month = []
d3.text(url, function(text) {
// some code...
var data = d3.csv.parseRows(text).map(function(row) {
//some code...
});
//some code
if (array.length > 0)
doSomething(array);
});
doSomething(Object.keys(urls));

Categories