As many of you will be aware, Google offers an API whereby you can load particular modules/libraries by calling a simple function:
<script type="text/javascript" src="//www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("jquery", "1.7.1");
google.load("jqueryui", "1.8.2");
</script>
I am currently in the process of developing my own library of code of which I would like to distribute amongst my websites easily and efficiently and I cannot think of a better method than the one above.
However, I am not too sure what the best way of writing the code for this is. Obviously my library will be an object, so will start with something like this (I think, correct me if I am wrong):
function company(){
this.load = function(modules){
// Modules is an array of modules to load
// Load the separate modules here
// from external files in their minified format
}
}
var company = new company;
company.load(['forms']);
Is the above method the right way to go? How do I then load in the modules from separate files?
You could go with the CommonJS Module spec. Then you can load them with RequireJS.
After searching around and gaining experience from others, I figured the following was the best method. Others may disagree, feel free to comment
/**
* Sine Macula Javascript API
* The Sine Macula API contains all base functions for use throughout
* all websites
* #name class.sinemacula.js
* #author Ben Carey
* #version 1.0
* #date 25/10/2012
* #copyright (c) 2012 Sine Macula Limited (sinemaculammviii.com)
*/
function SineMacula(){
// Only proceed if jQuery has been loaded
if(typeof jQuery=='undefined'){
// jQuery has not been loaded
console.log('jQuery has not been loaded');
}else{
/**
* Sine Macula Load
* Load the Sine Macula Libraries and Plugins
* into the current document
*
* The options:
* - package: the package of libraries to load
* - packageURL: a remote source to load the package details from
* - libraries: any additional libraries to load
*
* #param object options The options for the Sine Macula load
*/
this.load = function(options){
var url,query,script;
// Set the defaults for the loader
var options = $.extend({
package: 'none', // Do not load any packages by default
packageURL: false, // Do not retrieve the package details from a URL by default
libraries: [] // Do not load any libraries by default
},options);
// Build the query based on the parameters supplied
if(options.packageURL){
// Build the query to allow for a remote
// package definition
query = '?packageURL='+encodeURIComponent(options.packageURL);
}else if(options.package=='none'){
query = '?libraries='+encodeURIComponent(options.libraries.join());
}else{
query = encodeURIComponent(options.package)+'/?libraries='+encodeURIComponent(options.libraries.join());
}
// Complete the url by appending the query
url = '//libraries.sinemaculammviii.com/'+query;
// Append the script tag to the end of the document
script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
$('head')[0].appendChild(script);
}
}
};
Related
I'm working on a website, and the relevant portion of my file structure is:
site
classes
class1
js
Now, the js contains scripts that use other scripts within the same folder. The only way I can get the scripts to load properly on the php pages is to either copy the whole js folder into the relevant subfolder, or move the page to the site folder. Neither of these options is good.
I know that the issue is with the file pathing, so how do I get the includes in the js scripts to path relative to their location, and not the location of the php page?
An example of this:
I have a page in class1 called class1home.php.
It calls a js script called script.js.
script.js contains a function with include(script2.js), which is in the js folder.
Because of the pathing, the include is looking for site/classes/class1/script2.js.
I want it to go to site/js/script2.js.
Because there are multiple folders and scripts using script2.js, I can't just change the filepath within the include to be relative to that specific page.
Within script.js:
/**
* #function Include
* #description Includes an external scripts to the page
* #param {string} scriptUrl
*/
function include(scriptUrl) {
document.write('<script src="' + scriptUrl + '"></script>');
}
One place it is used (within script.js):
/**
* #module ToTop
* #description Enables ToTop Plugin
*/
(function ($) {
var o = $('html');
if (o.hasClass('desktop')) {
include('js/jquery.ui.totop.min.js');
$(document).ready(function () {
$().UItoTop({
easingType: 'easeOutQuart',
containerClass: 'ui-to-top fa fa-angle-up'
});
});
}
})($);
I normally solve this type of problem by having the page identify where it is with a call to Server. It looks like this:
$callingPageURL =$_SERVER['SCRIPT_FILENAME'];
Then, I parse that string to determine the file and the folder, by using the explode function. It looks like this:
$callingPageURLHolder = explode("/", $callingPageURL);
This loads up an array of values into callingPageURLHolder. From there, I use common data structure methods, like array pops, to get to the part of the URL that I think will be relevant to the program. It looks like this:
$callingPageFile = array_pop($callingPageURLHolder);
$callingPageFolder = array_pop($callingPageURLHolder);
Once you can parse out the array that comes back from Server, you could simply load up variables you need to concatenate into a URL that you will call in your include.
This same type of technique can be used to make small changes in a template page based on where it was included from, by adding in some flow control that tests these kinds of extracted values.
For example,
switch ($callingPageFile){
case "index.php":
// some response
break;
}
Using logic like that, I might build chains of cases in which I respond to anticipated URL parts. I use this type of code for when I might want to slightly customize a PHP page. Using these techniques, and some planning, you might be able to respond to the idea that you intend to transplant your code to a variety of places.
The disqus comments are loading here: http://www.oddprints.com/help
but not here: https://www.oddprints.com/help any ideas?
All resources appear to be secure (protocol relative urls) so I don't think it's that.
It because disqus was treating the two urls as different and therefore loading different threads. If you want both http and https urls to have the same comments thread on it, you need to supply a canonical url in the disqus config. This is how I did it:
<div id="disqus_thread"></div>
<script>
/**
* https://disqus.com/admin/universalcode/#configuration-variables
*/
var disqus_config = function () {
this.page.url = "http://www.oddprints.com/help";
//this.page.identifier = "oddprints"; // add a different id here if you want a fresh thread....
};
(function() {
var d = document, s = d.createElement('script');
s.src = '//oddprints.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the comments powered by Disqus.</noscript>
Note that in the config I passed the http url as the canonical url and I've not set the page.identifier. I've done this so that it continues to serve up the comments I already had from back when it was just http and using an older version of the disqus snippet.
Disqus comment are loaded but Disqus consider these two page as different.
Did you follow that : https://help.disqus.com/customer/portal/articles/542119-can-disqus-be-loaded-via-https- ?
I need to find the requested URL in Javascript (not the current URL) to identify which of two displayed sites is loaded.
Scenario: I'm currently building a module for the Icinga Web 2 which has the option the show two frames (two separate .phtml files) side by side. When two of these frames are open, the URL shown in the browser looks as follows:
http://host/icingaweb/mymodule/siteA#!/icingaweb/mymodule/siteB
You can reload the single frames separately by clicking a button on it. In the browser log (and apache log) you can see, that for each load just the specific page is requested:
// reloading siteA
GET http://host/icingaweb/mymodule/siteA
// reloading siteB
GET http://host/icingaweb/mymodule/siteB
I need exactly this single request in Javascript (at least the requested site).
Unfortunately the following functions do not work:
var pathname = window.location.pathname;
// returns (no matter which site is reloaded): "/icingaweb/mymodule/siteA"
var url = window.location.href;
// returns (no matter which site is reloaded): "http://host/icingaweb/mymodule/siteA#!/icingaweb/mymodule/siteB"
The Icinga Web 2 parses all javascript content to a single file which is afterwards globally accessible (icinga.min.js). Custom javascript has to be put in the module.js.
module.js
(function(Icinga) {
var mymodule = function(module) {
/**
* The Icinga.Module instance (public/js/Icinga/module.js)
*/
this.module = module;
this.initialize();
this.module.icinga.logger.info('mymodule module loaded');
};
mymodule.prototype = {
initialize: function()
{
/**
* Tell Icinga about our event handlers, these are then
* automatically detached once Icinga is unloading
*/
this.module.on('rendered', this.showURLs);
},
showURLs: function(event)
{
var pathname = window.location.pathname;
// returns (no matter which site is reloaded): "/icingaweb/module/siteA"
var url = window.location.href;
// returns (no matter which site is reloaded): "http://host/icingaweb/module/siteA#!/icingaweb/module/siteB"
}
};
/**
* Register this module so that Icinga knows about it
*
* It's important that the identifier used is the same
* as the name of the module itself (case sensitive)
*/
Icinga.availableModules.mymodule = mymodule;
}(Icinga));
For those who are interessted in the solution, JoNe an Icinga Web 2 developer answered my question at monitoring-portal.org
You can use the following to get the URL of the currently loaded frame/pages:
$(event.target).closest('.container').data('icingaUrl')
Many thanks to JoNe.
I need to read data from a GPSMap 62 device using the device control Javascript library. Problem is, unlike older devices, this device stores its waypoints in separate .GPX files every day. The javascript library expects all tracks and waypoints to be in the current.gpx file, but the 62 stores them in e.g. Waypoints_06-MAY-14.gpx and so on each day.
Short of requiring users to manually upload the appropriate file, has anyone gotten the DeviceControl library to actually support the newer devices with separate GPX files?
As an added bonus, the Garmin Device Control library is deprecated, so no updates are forthcoming.
Some code
startReadFromGps: function(deviceNumber) {
this.plugin.StartReadFromGps( deviceNumber ); //invokes the external plugin
},
I've checked out plugin in version 2.3-RC1 (I do not know, which version do you use).
Indeed there is startReadFromGps method:
/** Initiates the read from the gps device conneted. Use finishReadFromGps and getGpsProgressXml to
* determine when the plugin is done with this operation. Also, use getGpsXml to extract the
* actual data from the device. <br/>
* <br/>
* Minimum plugin version 2.0.0.4
*
* #param deviceNumber {Number} assigned by the plugin, see getDevicesXml for
* assignment of that number.
* #see #finishReadFromGps
* #see #cancelReadFromGps
* #see #getDevicesXml
*/
startReadFromGps: function(deviceNumber) {
this.plugin.StartReadFromGps( deviceNumber );
},
So it uses getGpsXml. I assume that it uses specified filename that is read and method returns file's content. My first thought is to change the filename - it is possible with:
/** This the filename that wil contain the gps xml once the transfer is complete. Use with
* setWriteGpsXml to set what the file contents will be. Also, use startWriteToGps to
* actually make the write happen.
*
* #private
* #param filename {String} the actual filename that will end up on the device. Should only be the
* name and not the extension. The plugin will append the extension portion to the file name--typically .gpx.
* #see #setWriteGpsXml, #startWriteToGps, #startWriteFitnessData
*/
_setWriteFilename: function(filename) {
this.plugin.FileName = filename;
},
But _setWriteFilename is private method. However called by
startWriteToGps: function(gpsXml, filename, deviceNumber)
and
startWriteFitnessData: function(tcdXml, deviceNumber, filename, dataTypeName)
Since now I will check if calling those methods with your specified filename will override filename value permanently and further calling of startReadFromGps will use new filename.
I cannot test it, I didn't use this library but you can give a shot.
I have heard and read a few articles about deferring JavaScript loading and am very interested. It seems to be very promising for web apps that may be useful on Mobile platforms where the amount of JavaScript that can be loaded and executed is limited.
Unfortunately, most of the articles talk about this at an extremely high level. How would one approach this?
EDIT
Normally, all JavaScript is loaded on page load, however, there may be functions that are not necessary until a certain action occurs, at which time, the JavaScript should be loaded. This helps ease the burden of the browser on page load.
Specifically, I have a page that very heavily uses JavaScript. When I load the page on my phone, it won't load properly. As I debugged the page, I eliminated some of the JS functions. Once enough was eliminated, the page suddenly worked.
I want to be able to load the JS as needed. And possibly even eliminate the functions simply used for start up.
The basics are simple - breaking up your JavaScript code into logically separate components and loading only what you need. Depending on what you are building you can use:
Loaders:
Modernizr.load (or yepnope.js by itself)
LABjs
Many, many, many other deferred loading libraries.
Dependency managers (which are also loaders):
Require.js
dojo.require
JavaScript MVC's steal.js
Several other dependency management libraries.
These tools make use of a wide variety of techniques to defer the loading of scripts, the execution of scripts, manage dependencies, etc. What you need depends on what you are building.
You may also want to read through this discussion to learn something more about the pros and cons of using such techniques.
Response to edit:
There isn't really a good way to unload JavaScript that you have already loaded - the closest approximation you can get is to keep all of your loading code namespaced inside your application's namespace and then "clean up" by setting that namespace, and all references to it to null.
I have used a simple script published on line with some modification done by me.
Assume that your COMPRESSED Javascript file is in the cache directory in your webserver and you want to defer the loading of this compressed js file.
Your compressed js file:
80aaad2a95e397a9f6f64ac79c4b452f.js
This is the code html code:
<script type="text/javascript" src="/resources/js/defer.js?cache=80aaad2a95e397a9f6f64ac79c4b452f.js"></script>
This is the defer.js file content:
(function() {
/*
* http://gtmetrix.com/
* In order to load a page, the browser must parse the contents of all <script> tags,
* which adds additional time to the page load. By minimizing the amount of JavaScript needed to render the page,
* and deferring parsing of unneeded JavaScript until it needs to be executed,
* you can reduce the initial load time of your page.
*/
// http://feather.elektrum.org/book/src.html
// Get the script tag from the html
var scripts = document.getElementsByTagName('script');
var myScript = scripts[ scripts.length - 1 ];
// Get the querystring
var queryString = myScript.src.replace(/^[^\?]+\??/,'');
// Parse the parameters
var params = parseQuery( queryString );
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = '/cache/' + params.cache; // Add the name of the js file
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
function parseQuery ( query ) {
var Params = new Object ();
if ( ! query ) return Params; // return empty object
var Pairs = query.split(/[;&]/);
for ( var i = 0; i < Pairs.length; i++ ) {
var KeyVal = Pairs[i].split('=');
if ( ! KeyVal || KeyVal.length != 2 ) continue;
var key = unescape( KeyVal[0] );
var val = unescape( KeyVal[1] );
val = val.replace(/\+/g, ' ');
Params[key] = val;
}
return Params;
}
})();
I would like to say thanks to http://feather.elektrum.org/book/src.html that helped me to understand how to get the parameters from the script tag.
bye
Deferring loading til when?
The reason typically why JS is loaded last, is so that the entire DOM has been loaded first.
An easy way is to just use
<body onload="doSomething();">
So you could easily have doSomething() function to load all your JS.
You can also add a function to window.onload, like
window.onload = function(){ };
Also, if you are using JS librarys, such as jQuery and Dojo, they each have their own onReady and addOnLoad methods in order to run some JS only after the document has already loaded.
Here's a useful article on the script element's defer and async attributes. Specifying these attributes will get the browser to defer loading in different ways. You can also load in an external script using JavaScript after page load.
It should also be noted that the position of your script elements within your HTML document will determine load and execution order if neither defer nor async have been specified.