jsdom does not fetch scripts on local file system - jsdom

This is how i construct it:
var fs = require("fs");
var jsdom = require("jsdom");
var htmlSource = fs.readFileSync("./test.html", "utf8");
var doc = jsdom.jsdom(htmlSource, {
features: {
FetchExternalResources : ['script'],
ProcessExternalResources : ['script'],
MutationEvents : '2.0'
},
parsingMode: "auto",
created: function (error, window) {
console.log(window.b); // always undefined
}
});
jsdom.jQueryify(doc.defaultView, 'https://code.jquery.com/jquery-2.1.3.min.js', function() {
console.log( doc.defaultView.b ); // undefined with local jquery in html
});
the html:
<!DOCTYPE HTML>
<html>
<head></head>
<body>
<script src="./js/lib/vendor/jquery.js"></script>
<!-- <script src="http://code.jquery.com/jquery.js"></script> -->
<script type="text/javascript">
var a = $("body"); // script crashes here
var b = "b";
</script>
</body>
</html>
As soon as i replace the jquery path in the html with a http source it works. The local path is perfectly relative to the working dir of the shell / actual node script. To be honest i don't even know why i need jQueryify, but without it the window never has jQuery and even with it, it still needs the http source inside the html document.

You're not telling jsdom where the base of your website lies. It has no idea how to resolve the (relative) path you give it (and tries to resolve from the default about:blank, which just doesn't work). This also the reason why it works with an absolute (http) URL, it doesn't need to know where to resolve from since it's absolute.
You'll need to provide the url option in your initialization to give it the base url (which should look like file:///path/to/your/file).
jQuerify just inserts a script tag with the path you give it - when you get the reference in the html working, you don't need it.

I found out. I'll mark Sebmasters answer as accepted because it solved one of two problems. The other cause was that I didn't properly wait for the load event, thus the code beyond the external scripts wasn't parsed yet.
What i needed to do was after the jsdom() call add a load listener to doc.defaultView.
The reason it worked when using jQuerify was simply because it created enough of a timeout for the embedded script to load.

I had the same issue when full relative path of the jquery library to the jQueryify function. and I solved this problem by providing the full path instead.
const jsdom = require('node-jsdom')
const jqueryPath = __dirname + '/node_modules/jquery/dist/jquery.js'
window = jsdom.jsdom().parentWindow
jsdom.jQueryify(window, jqueryPath, function() {
window.$('body').append('<div class="testing">Hello World, It works')
console.log(window.$('.testing').text())
})

Related

How should I include cdnjs libraries in custom javascript codes in Google tag manager?

I am new to Google analytics and Google Tag Manager and currently I am confused with using external JS libraries. Any help would be appreciated.
I need to use 'https://cdnjs.cloudflare.com/ajax/libs/fingerprintjs2/2.1.0/fingerprint2.min.js' in my custom javascript code in google tag manager. I have tried to include the script as a custom html or even custom template. The script is successfully added to the head and I see it in the inspection. But in my custom JS code , when I try to call and use library functions , it throws exception saying that the lib object is not defined.
I tried sequencing tags also , to make sure script loading tags are fired before my custom js code execution. But that did not fix the problem.
Any suggestion on how to include external JS libraries in your GTM custom JS code? I hope I explained the problem properly.
Thanks !
This example posted by jwest75674 should help. As you need to declare var = fingerprint in your script to allow Google Tag Manager to capture the data after the script has run
In short, you can copy the script below an add it as Custom HTML Tag in GTM (https://github.com/Valve/fingerprintjs2)
<script src="https://cdn.jsdelivr.net/npm/fingerprintjs2#2.1.0/dist/fingerprint2.min.js"></script>
<script type="text/javascript">
var fingerprint; // Variable to allow the Google Tag Manager to capture the data after this script has run.
if (window.requestIdleCallback) {
requestIdleCallback(function () {
Fingerprint2.get(function (components) {
console.log(components) // an array of components: {key: ..., value: ...}
})
})
} else {
setTimeout(function () {
Fingerprint2.get(function (components) {
console.log(components) // an array of components: {key: ..., value: ...}
})
}, 500)
}
</script>

Excluding bootstrap from specific routes in Meteor

I was hoping anyone could give some input on this,
I'm creating a meteor app in which I would like to use bootstrap to creating the admin environment, but have the visitor facing side using custom css. When I add the bootstrap package to my app using meteor it's available on every page, is there a way to restrict the loading of bootstrap to routes that are in '/admin' ?
When you add bootstrap package it's not possible. You can, however, add bootstrap csses to public directory and then load them in a header subtemplate that will only be rendered when you're in the dashboard.
EDIT
But then how would you go about creating seperate head templates?
Easy:
<head>
...
{{> adminHeader}}
...
</head>
<template name="adminHeader">
{{#if adminPage}}
... // Put links to bootstrap here
{{/if}}
</template>
Template.adminHeader.adminPage = function() {
return Session.get('adminPage');
}
Meteor.router.add({
'/admin': function() {
Session.set('adminPage', true);
...
}
});
DISCLAIMER: I am unsure of a 'meteor way' to do this, so here is how I would do it with plain JS.
jQuery
$("link[href='bootstrap.css']").remove();
JS - Credit to javascriptkit
function removejscssfile(filename, filetype){
var targetelement=(filetype=="js")? "script" : (filetype=="css")? "link" : "none" //determine element type to create nodelist from
var targetattr=(filetype=="js")? "src" : (filetype=="css")? "href" : "none" //determine corresponding attribute to test for
var allsuspects=document.getElementsByTagName(targetelement)
for (var i=allsuspects.length; i>=0; i--){ //search backwards within nodelist for matching elements to remove
if (allsuspects[i] && allsuspects[i].getAttribute(targetattr)!=null && allsuspects[i].getAttribute(targetattr).indexOf(filename)!=-1)
allsuspects[i].parentNode.removeChild(allsuspects[i]) //remove element by calling parentNode.removeChild()
}
}
removejscssfile("bootstrap.css", "css")
However, doing that would complete remove it from the page. I am not sure whether meteor would then try to readd it when a user goes to another page. If that does not automatically get readded, then you have an issue of bootstrap not being included when someone goes from the admin section to the main site, which would break the look of the site.
The way I would get around that would be to disable and enable the stylesheets:
Meteor.autorun(function(){
if(Session.get('nobootstrap')){
$("link[href='bootstrap.css']").disabled = true;
}else{
$("link[href='bootstrap.css']").disabled = false;
}
});
There my be other bootstrap resources which may need to be removed, take a look at what your page is loading.
To use jQuery in the same way but for the javascript files, remember to change link to script and href to src
From my tests, Meteor does not automatically re-add the files once they have been removed so you would need to find some way of re-adding them dynamically if you want the same user to be able to go back and forth between the main site and the admin site. Or simply if the http referrer to the main site is from the admin, force reload the page and then the bootstrap resources will load and everything will look pretty.
P.s. make sure you get the href correct for the jQuery version
If somebody is interested in including any js/css files, I've written a helper for it:
if (Meteor.isClient) {
// dynamic js / css include helper from public folder
Handlebars.registerHelper("INCLUDE_FILES", function(files) {
if (files != undefined) {
var array = files.split(',');
array.forEach(function(entity){
var regex = /(?:\.([^.]+))?$/;
var extension = regex.exec(entity)[1];
if(extension == "js"){
$('head').append('<script src="' + entity + '" data-dynamicJsCss type="text/javascript" ></script>');
} else if (extension == "css"){
$('head').append('<link href="' + entity + '" data-dynamicJsCss type="text/css" rel="stylesheet" />');
};
});
}
});
Router.onStop(function(){
$("[data-dynamicJsCss]").remove();
});
}
Then simply use:
{{INCLUDE_FILES '/css/html5reset.css, /js/test.js'}}
in any of your loaded templates :)

Rendering Javascript code in nested mustache templates with mustache.js handlebars.js ICanHaz.js

Is it possible to render Javascript in nested mustache.js templates as follows?
myApp.mustache:
{{#myapp}}
{{>userApp}}
{{/myapp}}
userApp.mustache:
{{#user}}
<script>
$(function () {
$("a[id='a_popover_{{username}}']").popover()
})
</script>
{{/user}}
The templates render correctly with pystache (Python's mustache library), but mustache.js, handlebars.js, ICanHaz.js, and ICanHandlebarz.js all complain something like #user was not closed properly.
Pretty sure the </script> bit is the problem, the browser sees that and parses it as end of the template script. Try escaping it like so: <\/script>
I was also stuck into similar issue, what ended up was creating a new script element, because even if the javascript code was rendered , is was not executed after appending into the body, it acts like string.
$('script:last').html('alert("ok !")'); // won't work
sample code for my case:
var render = Mustache.to_html(template, data_sources);
var sc = document.createElement('script');
sc.innerHTML = render;
var p_div = document.getElementById('template_wrap_div').parentNode;
p_div.innerHTML = "";
p_div.appendChild(sc);
Hope that gives some idea and help.

Get Image dimensions using Javascript during file upload

I have file upload UI element in which the user will upload images. Here I have to validate the height and width of the image in client side. Is it possible to find the size of the image having only the file path in JS?
Note: If No, is there any other way to find the dimensions in Client side?
You can do this on browsers that support the new File API from the W3C, using the readAsDataURL function on the FileReader interface and assigning the data URL to the src of an img (after which you can read the height and width of the image). Currently Firefox 3.6 supports the File API, and I think Chrome and Safari either already do or are about to.
So your logic during the transitional phase would be something like this:
Detect whether the browser supports the File API (which is easy: if (typeof window.FileReader === 'function')).
If it does, great, read the data locally and insert it in an image to find the dimensions.
If not, upload the file to the server (probably submitting the form from an iframe to avoid leaving the page), and then poll the server asking how big the image is (or just asking for the uploaded image, if you prefer).
Edit I've been meaning to work up an example of the File API for some time; here's one:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Show Image Dimensions Locally</title>
<style type='text/css'>
body {
font-family: sans-serif;
}
</style>
<script type='text/javascript'>
function loadImage() {
var input, file, fr, img;
if (typeof window.FileReader !== 'function') {
write("The file API isn't supported on this browser yet.");
return;
}
input = document.getElementById('imgfile');
if (!input) {
write("Um, couldn't find the imgfile element.");
}
else if (!input.files) {
write("This browser doesn't seem to support the `files` property of file inputs.");
}
else if (!input.files[0]) {
write("Please select a file before clicking 'Load'");
}
else {
file = input.files[0];
fr = new FileReader();
fr.onload = createImage;
fr.readAsDataURL(file);
}
function createImage() {
img = document.createElement('img');
img.onload = imageLoaded;
img.style.display = 'none'; // If you don't want it showing
img.src = fr.result;
document.body.appendChild(img);
}
function imageLoaded() {
write(img.width + "x" + img.height);
// This next bit removes the image, which is obviously optional -- perhaps you want
// to do something with it!
img.parentNode.removeChild(img);
img = undefined;
}
function write(msg) {
var p = document.createElement('p');
p.innerHTML = msg;
document.body.appendChild(p);
}
}
</script>
</head>
<body>
<form action='#' onsubmit="return false;">
<input type='file' id='imgfile'>
<input type='button' id='btnLoad' value='Load' onclick='loadImage();'>
</form>
</body>
</html>
Works great on Firefox 3.6. I avoided using any library there, so apologies for the attribute (DOM0) style event handlers and such.
The previous example is Okay, but it is far from perfect.
var reader = new FileReader();
reader.onload = function(e)
{
var image = new Image();
image.onload = function()
{
console.log(this.width, this.height);
};
image.src = e.target.result;
};
reader.readAsDataURL(this.files[0]);
If you use a flash based uploaded such as SWFUpload you can have all the info you want as well as multiple queued uploads.
I recommend SWFUpload and am in no way associated with them other than as a user.
You could also write a silverlight control to pick your file and upload it.
No, You can't, filename and file content are send to the server in http headerbody, javascript cannot manipulate those fields.
HTML5 is definitely the correct solution here.
You should always code for the future, not the past.
The best way to deal with HTML4 browsers is to either fall back on degraded functionality or use Flash (but only if the browser does not support the HTML5 file API)
Using the img.onload event will enable you to recover the dimensions of the file.
Its working for an app I'm working on.

asp.net eval script in ajax html response

I'm using update panel, my response have some javascript like bellow. After a success response, I need to eval it, load it (with external script)
ex: my html response
<script type="text/javascript" src="test.js"></script>
<script type="text/javascript">
alert('asd');
</script>
<div>test</div>
<div>blah blah blah</div>
I'm not sure whether this question is still important for you, however I will try to provide a reasonable answer below.
AJAX framework does not evaluate scripts which are returned via UpdatePanel calls. You have to re-attach external scripts to document, so that browser could request for them and evaluate all inline scripts. You can use a small module I have paste below.
var UpdatePanelEnhancer = function ()
{
var divSelector = '#liveArea';
function evaluateScripts()
{
$(divSelector).find('script').each(function ()
{
if (this.src)
{
$('body').append(this);
}
else
{
var content = $(this).html();
eval(content);
}
});
};
Sys.Application.add_load(evaluateScripts);
} ();
The weak part of it is that you have to provide a selector for the element where module should look for a scripts to evaluate ('liveArea' in example), although you can extend the module and provide some cinfiguration to it. Also, I would strongly recommend you to load external javascripts before. If you cannot do it for some reason you should additionally check whether script is already referenced or not to avoid necessary calls and potentially unexpected behaviors and errors .

Resources