Adding dynamically third party javascript widgets to the web app - javascript

I am working on a React js project where I should dynamically add third party scripts (widgets) to the web app.
Widgets include any kind of third party platforms: Twitter, Instagram, Youplay, Youtube etc.
The current code that I have looks like this:
appendThirdPartyScript(externalScript) {
// Create an element outside the document to parse the string with
const head = document.createElement("head");
// Parse the string
head.innerHTML = externalScript;
// Copy those nodes to the real `head`, duplicating script elements so
// they get processed
let node = head.firstChild;
while (node) {
const next = node.nextSibling;
if (node.tagName === "SCRIPT") {
// Just appending this element wouldn't run it, we have to make a fresh copy
const newNode = document.createElement("script");
if (node.src) {
newNode.src = node.src;
}
while (node.firstChild) {
// Note we have to clone these nodes
newNode.appendChild(node.firstChild.cloneNode(true));
node.removeChild(node.firstChild);
}
node = newNode;
}
document.head.appendChild(node);
node = next;
}
}
So basically the scripts urls comes from the backend/api and that is a list of scripts smth like ['http://twitter-widget.js', 'http://instagram-widget.js']
So since I have an array of scripts as string I use a for loop to go thru each of element and call appendThirdPartyScript('somescript')
data.externalScripts.map(script => {
this.appendThirdPartyScript("" + script);
});
This solution worked for almost all cases until I came to this case:
['http://embed.tt.se/v10/tt-widget.js', 'new tt.TTWidget({WhereToAddWidget: 'tt-quiz-XMuxmuWHc',type: 'quiz',ID: 'xxx',clientID: 'xxx',});']
So basically the error I get is:
tt is not a function
I am assuming that the first script hasn't completed loading (in this case http://embed.tt.se/v10/tt-widget.js) and the next one is trying to call something that does not exists.
When I try to hard code http://embed.tt.se/v10/tt-widget.js within head tag in index.html directly than it works!
So this approach of dynamically adding third party widgets is not reliable. Anyone can let me know if my current code needs to be changed or any suggestion would be pretty much appreciated!

This seems to work but probably not best solution:
const delayTime = 500;
externalScriptArray.forEach((script, index) => {
setTimeout(() => {
this.appendNewTagElementToDom(script);
}, delayTime*index);
});

Related

How can I make jQuery replaceWith() "non-blocking" (async)?

I'm using the function replaceStr() to replace some strings in the body tag. If the html page is small, the replacing is not noticeable. But if the html page is bigger and more complex you notice it. The replacing is blocking the browser. My question, how is it possible to make the replacing non-blocking? The replacing is not critical to the page, so it can happen in the background, when the browser is not busy. I tried to use async and await, but I think the replaceWith() function can't handle Promises and that's why it's not working with async/await. But how can you do it then?
function replaceStr(myStrArr) {
const container = $('body :not(script)');
myStrArr.map((mystr) => {
container
.contents()
.filter((_, i) => {
return i.nodeType === 3 && i.nodeValue.match(mystr.reg);
})
.replaceWith(function () {
return this.nodeValue.replace(mystr.reg, mystr.newStr);
});
});
}
Thank you for your help.
Your current implementation has a few spots where it could be optimized before going the async route. For example you could get rid of the jQuery dependency. It doesn't help much in your case but adds overhead.
Then currently you're mapping over your replacements and for each over all candidate nodes, replacing the nodeValue each time. This possibly triggers a repaint every time.
Instead you could use a TreeWalker to quickly iterate over the relevant nodes, and only update the nodeValues once.
In my tests, the following runs roughly 16 times faster, than your current code. Maybe that's already enough?
function replaceStr_upd(replacements) {
// create a tree walker for all text nodes
const it = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
// but skip SCRIPTs
acceptNode: node => node.parentNode.nodeName === 'SCRIPT'
? NodeFilter.FILTER_REJECT
: NodeFilter.FILTER_ACCEPT
});
// a helper function
const applyReplacements = initial => replacements.reduce((text, r) => text.replace(r.reg, r.newStr), initial);
// iterate over all text node candidates
while (it.nextNode()) {
// but only update once per node:
it.currentNode.nodeValue = applyReplacements(it.currentNode.nodeValue);
}
}

Google App Script , find file's parent id if exist in a Sheets

I'm trying to find (in a Google Sheets list of file Id) file's parent ID in a loop, until it's present in another Google Sheets. All is ok for the first parent but it doesn't work when i'm trying to get 2nd or 3rd parent's generation higher. My fileParent is "undefined" , i don't know why cause row 3 my var file return the correct first parent ID.
`
//all var are initialized before
var files = DriveApp.getFileById(*My File ID*);
var myFiles = files.getParents()
var file = myFiles.next().getId();
for(var rowRef in tab_ref){
if(file != tab_ref[rowRef][9]){
while(tab_ref[rowRef][9] != file && files.hasNext()){
var fileParent = DriveApp.getFolderById(file);
files = fileParent.getParents();
file = files.next().getId();
}
if(tab_ref[rowRef][9] == id_file){
sheet_files.activate();
sheet_files.getActiveCell().offset(2,10).setValue(file);
}
}
}
I'm not sure this is the easiest way to find what your looking but I think it is a way to do it. I use these sorts of loops to build directory trees for access to all sorts of hierarchical data and I also incorporate JQuery jstree to create an easy to use UI for anybody that's use to navigating on a computer. So here's the code. If your not familiar with this kind of coding it may take you a while to figure it out. Have fun with it. I incorporated comments that refer to a parentsArray[]. The parentsArray[] could be used as kind of stack where you push and pop current folder names onto and off of the stack in order to keep track of your current path or parental ancestry of folders.
function traverseFolder(folderObj)
{
//parentsArray[parentsArray.length-1] is the immediate parent of the current folderObj.
//Put folderObj name, size info here
//Push folderobj name on to parentsArray
var subs = folderObj.getFolders();
var files = folderObj.getFiles();
if(files)
{
while(files.hasNext())
{
var fi = files.next();;
//Put file name,size,info here
//parentsArray[parentsArray.length-1] is the immediate parent of all of these files
//parentsArray[0]/parentsArray[1].../parentsArray[parentsArray.length-1] is the path to this file.
}
}
while (subs.hasNext())
{
traverseFolder(subs.next());
}
//Pop last folderobj off of the parentsArray
}

How to find if a javascript is already loaded or not?

I am developing a chrome extension to ajaxify every non ajax website like wikipedia. etc. It works fine but it fail to load the js files. So, I have written a script to load js files thats are required for the page by getting src attribute from tag. But the problem is when i am loading another page of that website every scripts are loaded again. So it is useless to make it with a purpose to reduce bandwidth.
So, I want to know whether there is any way to match the script array of the new with its previous page. and to identify which scripts are new and load only them.
var array1 = [];
for (var i = 0; i < jsn; i++) {
var jssrc = document.getElementsByTagName("script")[i].src;
array1.push(jssrc);
}
var array2 = [how to find out array script source of the new page]
Array.prototype.diff = function(array2) {
var ret = [];
for(i in this) {
if(array2.indexOf( this[i] ) > -1){
ret.push( this[i] );
}
}
return ret;
};
ajaxpagefetcher.load("ajax-script", "", true, array1.diff(array2);)
How to find script src array of the new page that is to be loaded..
one more question, with out reloading the page if i delete the body tag through remove() function, are the scripts that already loaded also removed?
Thanks
I am waiting with eager for your replay....
At the end of the JS file:
window.iAmLoadedOnThisWindow = true;
And in the loader part:
if(! window.iAmLoadedOnThisWindow) {
// append the script with some dom method
}
If the JS file is a library, test the library : if(jQuery), if(_), etc.

Cannot Execute Javascript XPath queries on created document

Problem
I'm creating a document with javascript and I'd like to execute XPath queries on this document.
I've tried this in safari/chrome
I've read up on createDocument / xpath searches and it really seems like this code should work
At this point it seems like it may be a webkit bug
My requirements:
I can use innerHTML() to setup the document
I can execute xpath searches w tagnames
The code:
If you copy/paste the following into the webkit inspector, you should be able to repro.
function search(query, root) {
var result = null;
result = document.evaluate(query, root, null, 7,null);
var nodes = [];
var node_count = result.snapshotLength;
for(var i = 0; i < node_count; i++) {
nodes.push(result.snapshotItem(i));
}
return nodes;
}
x = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', 'HTML');
body = x.createElement('body');
body.innerHTML = "<span class='mything'><a></a></span>";
xdoc = x.documentElement; //html tag
xdoc.appendChild(body);
console.log(search(".", xdoc)); // --> [<html>​…​</html>​]
console.log(search("/*", xdoc)); // --> [<html>​…​</html>​]
console.log(search("/html", xdoc)); // --> []
Best Guess
So I can definitely search using XPath, but I cannot search using tagnames. Is there something silly I'm missing about the namespace?
Have you tried:
console.log(search("//html", xdoc));
I'm not familiar with Safari specifically, but the problem might be that Safari is adding another node above HTML or something. If this was the case, the first two queries might be showing you that node plus it's children, which would make it look like they're working properly, while the third query would fail because there wouldn't be a root=>HTML node.
Just a thought.

How do I create a message queue?

I want to display little messages to provide feedback to the user while he
is providing input or just interacting with the UI.
I need it for my firefox addon, so I have to develop it in plain javascript
and not jQuery.
I want the message to appear, but only one message can be visible at the same
time, so I need some kind of queue to manage incomming messages. After a certain time
e.g. 3 sec the message should fade away or just disappear.
For now I am able to add messages to the DOM. Any suggestions how to implement the queue
and how to push the messages forward according to the time?
Thanks!
Perheps you need the concept of FIFO (First In First Out)
Take a look at this simple example in plan java script language:
function Queue() {
var data = [];
this.isEmpty = function() {
return (data.length == 0);
};
this.enqueue = function(obj) {
data.push(obj);
};
this.dequeue = function() {
return data.shift();
};
this.peek = function() {
return data[0];
};
this.clear = function() {
data = [];
};
}
You can use jQuery in a firefox plugin:
Include a script tag in the xul file that points to the jQuery file, e.g.:
<script type="text/javascript" src="chrome://extensionname/content/jquery.js" />
In each js function that uses jQuery, insert this line:
$jQuizzle = jQuery.noConflict();
In each jQuery call, if you are trying to manipulate the document in the current browser window, you must supply the context as "window.content.document", like this:
$jQuizzle(".myClass", window.content.document).show();
Then you can use this jQuery plugin:
http://benalman.com/projects/jquery-message-queuing-plugin/
It's not clear what sort of message you want to display. The nsIAlertsService can display messages but I'm not sure how well it queues them. If you want something simpler then perhaps you could just show a custom <tooltip> element.

Categories