Jump to content
  • Asynchronous Loading of JavaScript in Spotfire Text Areas - How to load JavaScript from External Resources


    ==== This article is in DRAFT - please do not rely on it! ====

    It is often useful to be able to manage JavaScript scripts that are used in Spotfire Text Areas outside of a Spotfire file. Such JavaScript scripts could be hosted on an external website, or hosted within your organization and version controlled in some way. Spotfire has the ability to load scripts into a Text Area by way of asynchronously loading them once the Spotfire page is ready. You can do this by adapting these boiler-plate scripts provided below.

    Note that it's also possible to load CSS in the same way!

    One word of warning: this method will bypass any security that exists in Spotfire for preventing rogue scripts from executing. It also bypasses HTML sanitation - in that you can include JavaScript that uses HTML elements that are normally disallowed by HTML sanitation. This is a potential security risk, so it is my recommendation to only load JavaScript from trusted resources (preferably hosted on-premises). You also need to make sure that such scripts are only authored by trusted script authors and that they understand the full implications of the potential security risks.

    The example scripts render an accordion control in the text area. First of all, we need some boilerplate code to load the JavaScript that will be used. In this case, it is to load a library for producing the accordion. I have hosted it locally on my machine for testing purposes:

    /*
    The MIT License (MIT)
    
    Copyright (c) 2014 Filament Group
    
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    
    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
    */
    
    /*! loadJS: load a JS file asynchronously. [c]2014 @scottjehl, Filament Group, Inc. (Based on http://goo.gl/REQGQ by Paul Irish). Licensed MIT */
    (function( w ){
    	var loadJS = function( src, cb ){
    		"use strict";
    		var ref = w.document.getElementsByTagName( "script" )[ 0 ];
    		var script = w.document.createElement( "script" );
    		script.src = src;
    		script.async = true;
    		ref.parentNode.insertBefore( script, ref );
    		if (cb && typeof(cb) === "function") {
    			script.onload = cb;
    		}
    		return script;
    	};
    	// commonjs
    	if( typeof module !== "undefined" ){
    		module.exports = loadJS;
    	}
    	else {
    		w.loadJS = loadJS;
    	}
    }( typeof global !== "undefined" ? global : this ));
    
    
    
    /*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
    (function(w){
    	"use strict";
    	/* exported loadCSS */
    	var loadCSS = function( href, before, media ){
    		// Arguments explained:
    		// `href` [REQUIRED] is the URL for your CSS file.
    		// `before` [OPTIONAL] is the element the script should use as a reference for injecting our stylesheet <link> before
    			// By default, loadCSS attempts to inject the link after the last stylesheet or script in the DOM. However, you might desire a more specific location in your document.
    		// `media` [OPTIONAL] is the media type or query of the stylesheet. By default it will be 'all'
    		var doc = w.document;
    		var ss = doc.createElement( "link" );
    		var ref;
    		if( before ){
    			ref = before;
    		}
    		else {
    			var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes;
    			ref = refs[ refs.length - 1];
    		}
    
    		var sheets = doc.styleSheets;
    		ss.rel = "stylesheet";
    		ss.href = href;
    		// temporarily set media to something inapplicable to ensure it'll fetch without blocking render
    		ss.media = "only x";
    
    		// wait until body is defined before injecting link. This ensures a non-blocking load in IE11.
    		function ready( cb ){
    			if( doc.body ){
    				return cb();
    			}
    			setTimeout(function(){
    				ready( cb );
    			});
    		}
    		// Inject link
    			// Note: the ternary preserves the existing behavior of "before" argument, but we could choose to change the argument to "after" in a later release and standardize on ref.nextSibling for all refs
    			// Note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/
    		ready( function(){
    			ref.parentNode.insertBefore( ss, ( before ? ref : ref.nextSibling ) );
    		});
    		// A method (exposed on return object for external use) that mimics onload by polling document.styleSheets until it includes the new sheet.
    		var onloadcssdefined = function( cb ){
    			var resolvedHref = ss.href;
    			var i = sheets.length;
    			while( i-- ){
    				if( sheets[ i ].href === resolvedHref ){
    					return cb();
    				}
    			}
    			setTimeout(function() {
    				onloadcssdefined( cb );
    			});
    		};
    
    		function loadCB(){
    			if( ss.addEventListener ){
    				ss.removeEventListener( "load", loadCB );
    			}
    			ss.media = media || "all";
    		}
    
    		// once loaded, set link's media back to `all` so that the stylesheet applies once it loads
    		if( ss.addEventListener ){
    			ss.addEventListener( "load", loadCB);
    		}
    		ss.onloadcssdefined = onloadcssdefined;
    		onloadcssdefined( loadCB );
    		return ss;
    	};
    	// commonjs
    	if( typeof exports !== "undefined" ){
    		exports.loadCSS = loadCSS;
    	}
    	else {
    		w.loadCSS = loadCSS;
    	}
    }( typeof global !== "undefined" ? global : this ));
    
    
    // Asynchronously load the JavaScript for the Accordion control
    window.setTimeout(() => {
      loadJS('http://localhost:82/script.js', () => {  
    	console.log("loadedJS");
    	doit();
      });
    }, 750);
    
    // Load rangeslider.min.css
    window.setTimeout(() => {
      loadCSS('http://localhost:82/style.css');
    }, 1500);
     

     

    Note that the loading of JavaScript and CSS is done after a short timeout - this gives the page time to load before any extra JavaScript or CSS is loaded. Also, note the doit(); call. This calls the JavaScript hook in the script.js (not shown here) that initializes the accordion control itself.

    Then you can include the accordion in the HTML source of the text area with another script like this:

    $('#accordion').accordion({ 
        collapsible: true, 
        autoHeight: false, 
        defaultactive: true, 
    	animate: 100
    });
     

    Note that in this case a JQuery selector has been used.

    License:  TIBCO BSD-Style License


    User Feedback

    Recommended Comments

    There are no comments to display.


×
×
  • Create New...