I have to use a local analytics.js that I serve from my server. I just want to use the local version if necessary - so is there a solution for checking if the call for analytics.js has failed?
I thought about checking it with a global window.onerror, but I don't think a failed call for an external file causes an error. I've tried checking if ga() is available, but it is even if analytics.js isn't loaded.
Any ideas? If you are wondering, not all users of this site has internet access, that's why I'm serving a local version. There is more things happening in this case, like adding a sendHitTask to redirect the answer from analytics.js to the local server.
EDIT
A solution where you check if the user has Internet access would also be OK. But I have not found any solution for this either that works on all modern browsers.
There's a function to track if the library has loaded. From the docs:
ga(function(tracker) {
var defaultPage = tracker.get('page');
});
The passed in function is executed when the library is loaded, so you could set a variable to keep track of whether or not it has loaded. You'd have to put it on some sort of timer to decide when you want to consider it failed:
var loaded = false;
ga(function() {
loaded = true;
});
// after one second do something if the library hasn't loaded
setTimeout(function(){
if (!loaded){
//do something
}
},1000);
instead of waiting for a callback, you can easily get it with
if(window.ga && ga.loaded) {
// yeps... it is loaded!
}
you can easily see this in the Firefox documentation
same trick can be applied if you want to see if the tracker is blocked (by any plugin for example)
if(window.ga && ga.q) {
// yeps... blocked! >:o
}
A particularly elegant solution would be to use RequireJS and leverage its support for fallback paths. I do this on my site to load a stub version of analytics.js if loading GA fails because the visitor uses a privacy tool blocking the request:
http://veithen.github.io/2015/02/14/requirejs-google-analytics.html
Your use case is similar, except that you want to fallback to a complete local copy. You also probably don't want to change all calls to GA as described in that article. If that's the case then you could use a hybrid approach where you only use RequireJS to load analytics.js (Google's version or the local copy), without changing any other code.
Setting this up would involve the following steps:
Add RequireJS to your site and configure it as follows:
require.config({
paths: {
"ga": [
"//www.google-analytics.com/analytics",
"local-copy-of-analytics"
]
}
});
Use the alternative version of the tracking code, but replace <script async src='//www.google-analytics.com/analytics.js'></script> with the following JavaScript code:
require(["ga"]);
Related
I am using phantomjs to retrieve CSS information from a page without execute its javascript. For example here is the code snippet.
page.settings.javascriptEnabled = false;
page.open('file:///home/sample.html', function(status) {
if (status !== 'success') {
console.log('Unable to access network');
} else {
page.includeJs("file:///home/sample.js", function() {
var class = page.evaluate(function() {
return document.querySelector('body').className;
});
console.log(class);
});
}
}
If I disabled the javascript, the evaluate function always return null. But when I tried to enable the javascript, the evaluate function will return some value. Is there any idea to disable the javascript in the page, but my included javascript have to work ?
No
page.evaluate() executes JavaScript on the page. If you disable JavaScript in PhantomJS, then you effectively can't use page.evaluate() anymore. And with it goes every way of accessing DOM elements. page.includeJs() will also not work, because it the script cannot be executed on the page.
You can still access page.content which provides access to the current page source (computed source). You may try to use some DOM library to parse the source into a DOM object1 or if the task is simple, you may try to use Regular Expressions.
1 Note that PhantomJS and node.js have different execution environments, so most node.js modules that deal with the DOM won't work
As suggested by Artjom, there is no way to disable execution of the target website JavaScript without disabling PhantomJS ability to execute JavaScript on the page. However, there is a simple way to ensure that no scripts are executed by the target website (which achieves the same result, at the end).
Create a HTTP proxy that intercepts all requests.
Detect responses with Content-Type: text/html.
Remove all <script> tags from the document.
You can configure phantomjs to use proxy using --proxy configuration.
Use http-proxy to create a proxy server.
Use cheerio to remove, comment out, or otherwise invalidate the <script> tags.
I'm using the MEAN stack (mongo, express, angular and node). I'm deploying relatively frequently to production...every couple of days. My concern is that I'm changing the client side code and the API at times and I would rather not have to ensure backwards compatibility of the API with previous versions of the client code.
In such a scenario, what is the most effective way of ensuring that all clients reload when I push to production? I have seen that Evernote for example has a pop-up that says something along the lines of please reload your browser for the latest version of Evernote. I would like to do something similiar...do I need to go down the path of socket.io or sock.js or am I missing something simple and there is a simpler way to achieve this?
Update:
AppCache was deprecated summer 2015 so the below is no longer the best solution. The new recommendation is to use Service Workers instead. However, Service Workers are currently still experimental with sketchy (read: probably no) support in IE and Safari.
Alternatively, many build tools now seamlessly incorporate cache-busting and file "versioning" techniques to address OPs question. WebPack is arguably the current leader in this space.
This might be a good use case for using HTML5's AppCache
You'd probably want to automate some of these steps into your deployment scripts, but here is some code you might find useful to get you started.
First, create your appcache manifest file. This will also allow you to cache resources in the client's browser until you explicitly modify the appcache manifest file's date.
/app.appcache:
CACHE MANIFEST
#v20150327.114142
CACHE:
/appcache.js
/an/image.jpg
/a/javascript/file.js
http://some.resource.com/a/css/file.css
NETWORK:
*
/
In app.appcache, the comment on line #v20150327.114142 is how we indicate to the browser that the manifest has changed and resources should be reloaded. It can be anything, really, as long as the file will look different to the browser from the previous version. During deployment of new code in your application, this line should be modified. Could also use a build ID instead.
Second, on any pages you want to use the appcache, modify the header tag as such:
<html manifest="/app.appcache"></html>
Finally, you'll need to add some Javascript to check the appcache for any changes, and if there are, do something about it. Here's an Angular module. For this answer, here's a vanilla example:
appcache.js:
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
// Browser downloaded a new app cache.
// Swap it in and reload the page to get the latest hotness.
window.applicationCache.swapCache();
if (confirm('A new version of the application is available. Would you like to load it?')) {
window.location.reload();
}
}
else {
// Manifest didn't changed. Don't do anything.
}
}, false);
Alternatively, if AppCache won't work for your situation, a more ghetto solution would be to create a simple API endpoint that returns the current build ID or last deployment date-time. Your Angular application occasionally hits this endpoint and compares the result to it's internal version, and if different, reloads itself.
Or, you may consider a live-reload script (example), but, while very helpful in development, I'm not sure how good of an idea it is to use live/in-place-reloading of assets in production.
I will tell you my problem first then I will recommend a tentative solution. I wanted to force my user to log out and then log in when a production build is been deployed. At any point in time, there will be two versions of software deployed on production. A version which software which FE knows and a version which Backend knows. Most of the time they would be the same. At any point in time if they go out of sync then we need to reload the client to let the client know that a new production build has been pushed.
I am assuming 99.99% of the time the backend would have the knowledge of the latest version of the deployed software on production.
following are the two approaches which I would love to recommend:-
The backend API should always return the latest version of the software in the response header. On the frontend, we should have a common piece of code that would check if the versions returned by the API and that present on the FE are the same. if not then reload.
Whenever a user logs in. the BE should encode the latest software version in the JWT. And the FE should keep sending this as a bearer token along with every API request. The BE should also write a common interceptor for every API request. which would compare the software version in the JWT received from the API request and the
Maybe you can add hash to your client code file name. eg app-abcd23.js.
So the browser will reload the file instead of get it from cache. or you can just add the hash to url.eg app.js?hash=abcd23 but some browser may still use the cached version.
i know rails has assets-pipline to handle it, but i am not familiar with MEAN stack. there should be some package in npm for that purpose.
And i dont think it is really necessary to use socket.io if you want to notify the user their client code is out of date. you can define your version in both html meta tag and js file,if mismatch, show a popup and tell the user to refresh.
Try to limit your js/files to expire within smaller periodic time, ie: 1 days.
But in case you want something that pop-out and tell your user to reload (ctrl+f5) their browser, then simply make a script that popup that news if you just changed some of your files, mark the ip/session who have just reload/told to reload, so they will not be annoyed with multiple popup.
I was facing the same problem recently. I fixed this by appending my app's build number with my js/css files. All my script and style tags were included by a script in a common include files so it was trivial to add a 'build number' at the end of the js/css file path like this
/foo/bar/main.js?123
This 123 is a number that I keep track of in my same header file. I increment it whenever I want the client to force download all the js files of the app. This gives me control over when new versions are downloaded but still allows the browser to leverage cache for every request after the first one. That is until I push another update by increment the build number.
This also means I can have a cache expiry header of however long I want.
Set a unique key to local storage during the build process
I am using react static and loading up my own data file, in there i set the ID each time my content changes
Then the frontend client reads the key with from local storage
(if the key does not exist it must be the first visit of the browser)
if the key from local storage does not match it means the content has changed
fire line below to force reload
window.replace(window.location.href + '?' + key)
in my case i had to run this same line again a second latter
like
setTimeout( (window.replace(window.location.href + '?' + key))=> {} , 1000)
full code below:
const reloadIfFilesChanged = (cnt: number = 0, manifest: IManifest) => {
try {
// will fail if window does not exist
if (cnt > 10) {
return;
}
const id = localStorage.getItem('id');
if (!id) {
localStorage.setItem('id', manifest.id);
} else {
if (id !== manifest.id) {
// manifest has changed fire reload
// and set new id
localStorage.setItem('id', manifest.id);
location.replace(window.location.href + '?' + manifest.id);
setTimeout(() => {
location.replace(window.location.href + '?' + manifest.id + '1');
}, 1000);
}
}
} catch (e) {
// tslint:disable-next-line:no-parameter-reassignment
cnt++;
setTimeout(() => reloadIfFilesChanged(cnt, manifest), 1000);
}
};
I'm trying to create a bootstrapped addon that just sets the new tab url at install to a new value and resets it to the old one when it gets uninstalled.
Here is my bootstrap.js. I think the install function throws an exception because require is not defined, but I'm not sure if the debugger executes the code I write in Scratchpad in the right scope.
I read somewhere that the api is the same for bootstrapped extensions as the with the add-on sdk, so the require should be fine. If this is not the case, could you please direct me to a page that describes the code I can use in the bootstrap.js, I didn't find anything :(
function startup(data, reason){
}
function shutdown(data, reason){
}
function install(data, reason){
var prev_new_tab_url = require("sdk/preferences/service").get("browser.newtab.url");
var data = require("sdk/self").data;
var url = data.url("startpage.html");
require("sdk/preferences/service").set("browser.newtab.url", url);
var ss = require("sdk/simple-storage");
ss.storage.prev_new_tab_url = prev_new_tab_url;
}
function uninstall(data, reason){
var ss = require("sdk/simple-storage");
var prev_new_tab_url = ss.storage.prev_new_tab_url;
require("sdk/preferences/service").set("browser.newtab.url", prev_new_tab_url);
}
from: https://forums.mozilla.org/viewtopic.php?f=7&t=22621&sid=4ea13ebd794f85600d6dcbcf6cc590a7
in bootstrap you dont have access to sdk stuff like that. im not sure how to access that stuff.
but i made exactly what you are looking for with localization :D took like 10min :D
https://github.com/NoitForks/l10n/tree/setpref-install-uninstall
note: the quirk that localization files are not available during the uninstall procedure. so i had to move this to shutodwn proc while testing for aReason of ADDON_DISABLE. it makes sense that files are not available in uninstall
you asked:
How do you know the Services.prefs.getCharPref method?
i responded:
I first imported the Services.jsm module then i looked on MDN for what all it had:
https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Services.jsm?redirectlocale=en-US&redirectslug=JavaScript_code_modules%2FServices.jsm
then i saw prefs then it linked to nsIPrefBranch and that documented all of it. nsIPrefBranch2 is deprecated so I knew it wasn't that.
MDN is your friend :)
Plain bootstrapped add-ons do not automatically get access to the SDK, i.e. there is no require.
Either use non-SDK stuff exclusively, like nsIPrefBranch, Services.jsm, etc.
Or write an SDK add-on in the first place
Or hook up the SDK loader for your add-on yourself. Only instance I know other than SDK add-ons themselves (heh) that does such a thing is Scriptish.
I work on a javascript library that customers include on their site to embed a UI widget. I want a way to test dev versions of the library live on the customer's site without requiring them to make any changes to their code. This would make it easy to debug issues and test new versions.
To do this I need to change the script include to point to my dev server, and then override the load() method that's called in the page to add an extra parameter to tell it what server to point to when making remote calls.
It looks like I can add JS to the page using a chrome extension, but I don't see any way to modify the page before it's loaded. Is there something I'm missing, or are chrome extensions not allowed to do this kind of thing?
I've done a fair amount of Chrome extension development, and I don't think there's any way to edit a page source before it's rendered by the browser. The two closest options are:
Content scripts allow you to toss in extra JavaScript and CSS files. You might be able to use these scripts to rewrite existing script tags in the page, but I'm not sure it would work out, since any script tags visible to your script through the DOM are already loaded or are being loaded.
WebRequest allows you to hijack HTTP requests, so you could have an extension reroute a request for library.js to library_dev.js.
Assuming your site is www.mysite.com and you keep your scripts in the /js directory:
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
if( details.url == "http://www.mysite.com/js/library.js" )
return {redirectUrl: "http://www.mysite.com/js/library_dev.js" };
},
{urls: ["*://www.mysite.com/*.js"]},
["blocking"]);
The HTML source will look the same, but the document pulled in by <script src="library.js"></script> will now be a different file. This should achieve what you want.
Here's a way to modify content before it is loaded on the page using the WebRequest API. This requires the content to be loaded into a string variable before the onBeforeRequest listener returns. This example is for javascript, but it should work equally well for other types of content.
chrome.webRequest.onBeforeRequest.addListener(
function (details) {
var javascriptCode = loadSynchronously(details.url);
// modify javascriptCode here
return { redirectUrl: "data:text/javascript,"
+ encodeURIComponent(javascriptCode) };
},
{ urls: ["*://*.example.com/*.js"] },
["blocking"]);
loadSynchronously() can be implemented with a regular XMLHttpRequest. Synchronous loading will block the event loop and is deprecated in XMLHttpRequest, but it is unfortunately hard to avoid with this solution.
You might be interested in the hooks available in the Opera browser. Opera used to have* very powerful hooks, available both to User JavaScript files (single-file things, very easy to write and deploy) and Extensions. Some of these are:
BeforeExternalScript:
This event is fired when a script element with a src attribute is encountered. You may examine the element, including its src attribute, change it, add more specific event listeners to it, or cancel its loading altogether.
One nice trick is to cancel its loading, load the external script in an AJAX call, perform text replacement on it, and then re-inject it into the webpage as a script tag, or using eval.
window.opera.defineMagicVariable:
This method can be used by User JavaScripts to override global variables defined by regular scripts. Any reference to the global name being overridden will call the provided getter and setter functions.
window.opera.defineMagicFunction:
This method can be used by User JavaScripts to override global functions defined by regular scripts. Any invocation of the global name being overridden will call the provided implementation.
*: Opera recently switched over to the Webkit engine, and it seems they have removed some of these hooks. You can still find Opera 12 for download on their website, though.
I had an idea, but I didn't try it, but it worked in theory.
Run content_script that was executed before the document was loaded, and register a ServiceWorker to replace page's requested file content in real time. (ServiceWorker can intercept all requests in the page, including those initiated directly through the dom)
Chrome extension (manifest v3) allow us to add rules for declarativeNetRequest:
chrome.declarativeNetRequest.updateDynamicRules({
addRules: [
{
"id": 1002,
"priority": 1,
"action": {
"type": "redirect",
"redirect": {
"url": "https://example.com/script.js"
}
},
"condition": {
"urlFilter": 'https://www.replaceme.com/js/some_script_to_replace.js',
"resourceTypes": [
'csp_report',
'font',
'image',
'main_frame',
'media',
'object',
'other',
'ping',
'script',
'stylesheet',
'sub_frame',
'webbundle',
'websocket',
'webtransport',
'xmlhttprequest'
]
}
},
],
removeRuleIds: [1002]
});
and debug it by adding listener:
chrome.declarativeNetRequest.onRuleMatchedDebug.addListener(
c => console.log('onRuleMatchedDebug', c)
)
It's not a Chrome extension, but Fiddler can change the script to point to your development server (see this answer for setup instructions from the author of Fiddler). Also, with Fiddler you can setup a search and replace to add that extra parameter that you need.
I have been reading up on HTML 5 worker threads but all the samples i have seen seem to require the javascript be in its own file.
so im basicly wondering if its posible to start a worker work directly towards a function.
The end goal here being something along the lines of:
function AllJavascriptIsLoaded()
{
if(gWorkersSupported)
{
var Worker = new Worker(MyFunc)
Worker.Start();
}
else
{
// Horrible user experience incomming.
MyFunc();
}
}
function MyFunc()
{
// Complex and time consuming tasks
}
To my knowledge, this is not allowed for security reasons. I'd assume that a child object, or any JS script in the same file, would potentially have access to the parent DOM window, which Web Workers are not allowed to access.
So, we're stuck with posting messages to other files unless someone finds a nicer way to do it ;)
You can use something called inline-worker.
Basically you create a script resource via dataURI or BlobURL for the worker script. Given that the content of the script can be generated, you can use Function.toString() to build the content of the worker.
Example use BlobURL: http://www.html5rocks.com/en/tutorials/workers/basics/
Example use both technique: https://github.com/jussi-kalliokoski/sink.js/blob/master/src/core/inline-worker.js
Jeffrey is right about the security restriction of WebWorker. The code running in the worker cannot access the DOM, so it should only be used for calculation heavy tasks. If you try to access the DOM inside worker's code it would raise an error.
vkThread plugin helps you to implement exactly what you requested.
take a look at http://www.eslinstructor.net/vkthread/
there are examples for different kind of functions: regular function, function with context, with dependencies, anonymous, lambda.