Meteor JS: use external script - javascript

There are some services (like FB like or AddThis) that provide a snippet of code.
It looks like
<div class="service-name" data-something="x"></div>
<script type="text/javascript" src="http://example.com/service-name.js"></script>
OK, cool, so normally you paste it to your HTML and it works. Not with Meteor.
Here's what I see:
<script> inside of template / body is not loading -- I don't see it in Resources, something in Meteor is actually preventing browser from recognizing it as a JS file
it works from <head>
Now here are the problems and questions:
I don't want loading it from <head> -- because of the speed
Even if I load it from there -- we have QA and PROD environments. They must load this script from different domains (like service-domain-qa.example vs. example.com)
And surprisingly you cannot use template helpers / variables in the <head>.
With traditional frameworks it's not a question at all - you can include scripts anywhere and they just load; you can use logic / variables in any part of you server templates.
So, how should I do this in Meteor?
Let me repeat:
I need some external scripts (hosted on 3rd party domain) to be loaded into my app page
Saving this script into my project's folder is not an option
Script path depends on the environment (we already have the settings system), so the place of the template that renders it should be passed some data from the code
I know the way to achieve this with dynamic script loading from my code (with LAB.js or whatever) on Template.created, but this is so much an overkill...

<script> tags in body or templates aren't executed by Meteor, they are parsed and then handled by Meteor's templating system. You can't expect a script tag in either of those to just work as it would with a normal HTML page.
The solution is to use Template events (where you could manually append the script tag to the body or something) or load it dynamically like you said. It's not overkill, it's how Meteor works - remember, there is no traditional HTML page or body, there's just the Meteor API, and the Meteor API specifies that in order to load and execute external scripts, you must use the appropriate API methods.

My solution is use packages. See https://github.com/meteor/meteor/tree/master/packages/spiderable for more details.
Package.describe({
summary: "External script"
});
Package.on_use(function (api) {
api.use(['templating'], 'client');
api.add_files('external_script.html', 'client');
});
<head><script type="text/javascript" src=""//mc.yandex.ru/metrika/watch.js""></script></head>

If you are using IronRouter you can load external scipt using this package:
https://github.com/DerMambo/wait-on-lib
Router.map( function () {
this.route('codeEditor',{
waitOn: IRLibLoader.load('https://some-external.com/javascript.js')
});
});

Why not use jQuery's getscript?
http://api.jquery.com/jquery.getscript/
You can add a callback function

You could use something like yepnope to load the script asynchronously. I use this to load leaflet as and when I need. I'm starting to move over to loading more scripts via yepnope, so that my application renders the bare minimum on initial page load. I place the yepnope stuff inside Template.created.

Using iframe and the public directory was a hack I used to get script code embedded. In this it was for google adwords code and I did this my main html template:
<iframe src="/gads.html?v={{{unique}}}" seamless width="160" height="600"
scrolling="no" frameborder="0" marginheight="0" marginwidth="0"
style="margin:0;padding:0;border:none;width:160px;height:600px"></iframe>
and then in the public directory put an gads.html file with my google adwords code, like this:
<html>
<head>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div>
<script type="text/javascript"><!--
google_ad_client = "ca-pub-54*********";
google_ad_slot = "66******";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</body>
</html>
that worked to get the code on the page, although it's far from ideal (for one thing, I think it violates google's terms of service).

I'm using METEOR 1.0. I have put all external SCRIPT tags inside a DIV element right before the tag in the layout template. Meteor recognizes them without any issue and they are loaded by browser.

Related

Script type module doesn't work when injected into iframe

I'm trying to leverage modules to dynamically attach variables on iframe's Window.
The problem is, that when I'm doing simple iframe.contentDocument.write(html), with script of type='module', the script inside iframe won't run.
If i use script without type='module', the script runs (and for example logs something to console)
Why am I unable to inject module scripts into an iframe like this? Is this some layer of security I am not aware of?
Edit:
Here is piece of code i'm injecting, i checked the network tab, and browser fetches the dependency from public directory, console.log() doesn't run though
<script type="module">
import * as materialuicore from "/.vite/#material-ui_core.js"
console.log('materialuicore')
if (!window.__deps) window.__deps = {}
window.__deps["#material-ui/core"] = materialuicore
</script>

Safe/secure way to load plugins into web app

I have a web app that accepts JS plugins. That is, JavaScript code that someone else wrote that the user wants to load into my app.
Currently I am using eval() to evaluate their JS code into the runtime, but I know thats not secure. Is there a better method than eval() for doing this?
To be clear, the user is pointing me to a dry text file (a URL), and that JS in the file needs to come to life somehow.
There's only two ways I know of dynamically importing a JS script:
Use AJAX, get the JS code, then run eval() on it.
Dynamically add a <script> tag to the DOM
The purpose of the question is to figure out if one is more secure than the other or if there is a better way than the above 2 options.
Is one of these two more secure than the other?
No, they're equally bad (good) from a security perspective.
They differ in details that would lead to different approaches in making them more secure, but ultimately both do run code written by an untrusted third party in your environment, with all its privileges. It's basically a persisted XSS issue.
Is there a better way than the above 2 options?
Many. It depends mostly on what those plugins should do in your application, who writes them and who installs (enables) them. Neither you as the application provider nor the user wants arbitrary code run havoc on the user's data. If the plugins need to access the data, you need administrative measures to ensure that only trusted code will run, like plugin code audits. At least you will need to inform your users that they must trust the plugin authors before enabling the plugin, which puts the burden on them. Also you should ensure to have usable logs in case something went wrong.
If you really want to run arbitrary, untrusted code without giving it access to user data, you will want to consider sandboxing. There are various approaches that essentially do the execution in a virtual machine that the code cannot break out from.
For Chrome Extensions I would specifically use sandboxingEval which allows the loading of sandboxed files that can be accessed within an iframe that the extension hosts. The only message passing would be through normal iframe messaging.
For example, declare within the manifest.json which page to sandbox:
{
...
"sandbox": {
"pages": [
"plugin.html"
]
"content_security_policy":
"sandbox allow-scripts; script-src 'self' https://plugin.com/"
],
...
}
Make sure the external domain is white-listed so it could be embedded. In the CSP policy, allow-scripts is there if embedding <scripts> is needed.
Now within the sandboxed page plugin.html, do anything with the external script. In this case, the external plugin is downloaded, and passing messages back to the extension process through messaging.
<!doctype html>
<html>
<head>
<script src="https://plugin.com/mohamedmansour/plugin.js"></script>
</head>
<body>
<script>
// Whatever my plugin contract is, lets send something back to our extension
// that the plugin initialized.
Plugin.do.something.here(() => {
window.postMessage({
name: 'CustomInitEvent',
data: 'initializing'
}, *);
});
// Listen from your extension plugin.html page some events.
window.addEventListener('message', (event) => {
var command = event.data.command;
switch(command) {
case 'CustomCommandA':
event.source.postMessage({
command: 'CustomCommandHello',
data: 'pong command a'
}, event.origin);
break;
}
});
</script>
</body>
</html>
Now within the popup or anywhere, just embed the plugin.html. In this case, popup.html looks like this
<html>
<head>
<script src="plugin-manager.js"></script>
</head>
<body>
<iframe id="theFrame" src="plugin.html"></iframe>
</body>
</html>
Then your plugin-manager.js is responsible of controlling plugin.
const iframe = document.getElementById('theFrame');
window.addEventListener('message', function(event) {
switch(event.name) {
case 'CustomInitEvent':
console.log('Plugin Initialized');
break;
case 'CustomCommandHello':
console.log('Hey!');
break;
}
});
iframe.contentWindow.postMessage({
command: 'CustomCommandA'
});
iframe.contentWindow.postMessage({
command: 'CustomCommandB'
});
Something along those lines. If dynamic plugins is what is needed, just add query parameters to the iframe. Within plugin.html, just dynamically add the script element, and just call it this way:
<iframe id="theFrame" src="plugin.html?id=121212"></iframe>

Meteorjs load html script resource in template (not in body)

I am using the jsplumbtoolkit framework in order to load in several script html templates into my meteorjs application in order to create the appropriate divs/dialogues options necessary as a part of the api. After some troubleshooting I determined the issue seemed to be that Meteorjs was not loading my html script through the onRendered function that I supplied it with.
To give you a better idea of the problem
//Due to Meteorjs not able to load scripts directly in the template, I added the script load to my onRendered function in my template js
Template.mytemplate.onRendered(function(){
$(document).ready(function() {
var script = document.createElement("script");
script.type="text/x-jtk-templates";
script.src = "templates/workflowtemplate.html";
$("#rulesscripttemplate").replaceWith(script);
});
})
workflowtemplate.html is in the appropriate meteorjs directory /public/templates/workflowtemplate.html and I am assuming the directory is correct.
This is properly loaded when I check my client Mozilla developer kit as well
<script type="text/x-jtk-templates" src="templates/templaterulesworkflow.html"></script>
Is there a better way to confirm that this resource was infact loaded to the client through mozilla?
Figured it out. had to add the <script type="text/x-jtk-templates" src="templates/templaterulesworkflow.html"></script> to the tag of my application. That resolved the issue.

Certain .js pages not loading in time

I have certain pages on my site that loads a good amount of JavaScript files. In the code below, edit_onload() is in script1.js. Typically all the scripts load fine and edit_onload fires successfully. On occasion it seems like script1.js isn't loading in time because edit_onload() errors with object expected. If you refresh the page everything will load fine.
My question is, shouldn't the <script> tag below wait for all of the .js files to load and then execute edit_onload()?
<script LANGUAGE="javascript" DEFER="true" for="window" event="onload">
<xsl:comment>
<![CDATA[
edit_onload();
]]>
</xsl:comment>
</script>
<script language="javascript" src="/_scripts/script1.js" defer="true"></script>
<script language="javascript" src="/_scripts/script2.js" defer="true"></script>
<script language="javascript" src="/_scripts/script3.js" defer="true"></script>
I think, an implementation of processing of deferred scripts is browser specific. For example, they can handle inline and external scripts in different queues. In general the 'defer' attribute is just a recommendation provided by a site developer for a user agent, but the user agent will not necessarily abide by the recommendation.
Also you should try using 'defer="defer"'. Can you move the call to the script1.js itself? Also you can periodically check an existence of a specific element moved to the end of the content being loaded and run the method only after the element is discovered.
BTW, you could possibly gain more control over script loading if you use a dynamic script loading metaphor (the link is just one of examples).

Dynamically Trigger HTML5 Cache Manifest file?

I am using the new cache manifest functionality from HTML5 to cache my web app so it will work offline. The content is cached automatically when the page is loaded with the following html element:
<html lang="en" manifest="offline.manifest">
This works fine. However, I want to give my users the option of whether they want the content cached offline. So, here is my question:
Is there any way to trigger that an application be cached at runtime, using JavaScript, and not have it automatically done when the page is loaded.
For example, something like this (using jquery):
----------------index.html--------------
<head>
<meta charset="utf-8" />
<script src="http://code.jquery.com/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="Main.js"></script>
</head>
<body>
<button id="cacheButton">Cache Page</button>
</body>
</html>
---------Main.js---------
$(document).ready(
function()
{
$('#cacheButton').click(onCacheButtonClick);
}
)
function onCacheButtonClick(event)
{
console.log("Setting Offline Manifest");
$('#htmlRoot').attr("manifest","offline.manifest");
}
-------------offline.manifest-------------
CACHE MANIFEST
#version .85
#root
index.html
scripts/main.js
#jquery assets
http://code.jquery.com/jquery-1.4.4.min.js
Basically, when the button is clicked, I dynamically set the manifest attribute of the html element. This works (in the sense the element is set), but it does not cause the browser to then cache the page.
Any suggestions?
You dynamically trigger caching by adding an iframe that points to an empty page containing the actual cache manifest.
offline.html:
<!DOCTYPE html>
<html manifest="offline.appcache">
<head>
<title></title>
</head>
<body>
</body>
</html>
Make sure to add index.html to the cache manifest.
Then just add something like:
<iframe src="offline.html" width="0" height="0">
to document.body dynamically to trigger caching.
After many weeks spent with offline caching, the answer is no, you either cache or don't cache, setting the cache attribute on the client side has no effect.
You could consider offering an alternate url for the caching version, be aware that the page is also implicitly cached as a "master entry".
I am at a loss to understand why you would want to offline cache jquery though, since it is likely to be served with very long expiry anyway.
You may wish to consider offline storage as an alternative. Store the text of the scripts and inject them into the DOM on load. If not cached fetch using Ajax and inject the response, as creating a script tag with the src won't load the script.
Depending on your application, it may be possible to use a modified version of #schibum's approach by breaking down your app into "mini" apps, then caching the sub-sections in an iframe. Consider this example:
index.html
<html manifest="indexx.manifest">
<head>
<script src="jquery-2.1.4.min.js"></script>
<script src="index.js"></script>
<title>Index</title>
</head>
<body>
<ul>
<li>One
<li>Two
<li>Three
</ul>
<iframe id="if" />
</body>
</html>
index.manifest
CACHE MANIFEST
# 3
index.html
jquery-2.1.4.min.js
index.js
index.js
$( document).ready(function() {
var pages = ['1.html','2.html','3.html'];
var LoadNext = function() {
alert(pages[0]);
page = pages.shift();
alert(page)
if ( page !== undefined ) {
console.log("Attempting to load " + page);
$('#if').attr('src', page)
} else {
console.log("All done");
}
};
$('#if').load(function() {
console.log("Finished loading");
LoadNext()
});
LoadNext();
});
1.html
<html manifest="1.manifest">
<head>
<title>One</title>
</head>
<body>
<img src="1.jpg" width="50%">
</body>
</html>
1.manifest
CACHE MANIFEST
# 2
1.html
1.jpg
{2,3}.{html,manifest} follow 1.{html,manifest}'s form.
As a result, each sub-page (1.html, 2.html, 3.html) have their own manifest, and are thus cached independently. index.html has its own (minimal) manifest, so caching that unconditionally is not nearly as network-heavy as caching the entire app. Then the javascript is responsible for pre-loading every page in the iframe so that it gets cached.
Load index.html, then go offline and see how the sub-pages work. too.
An obvious drawback is that any assets shared between pages (e.g. jQuery) must be redundantly cached.
One thing you must remember. Do not cache the manifest file itself. So all you need to do is refresh the page with a different version of the manifest file according for your user selection. You can dynamically generate the manifest file itself, any change to that file will cause a cache refreshment. Best practice to trigger re-caching is to change the version of the manifest file, something like: # ver1 - 01/01/2018 to # ver2 - 02/02/2018 will do the trick.
So you cannot change it in client side but you can do it server side.

Categories