I have a set of widgets that are loaded dynamically depending on what the user requests. I have that aspect working (creating a script tag on the fly with the proper widget requested) as well as the browserification of the widgets into separate components.
My question is, is it possible to pass config information to the browserfied widget when it's loaded without using global variables?
For example:
var pageHead = document.getElementsByTagName("head")[0];
var script = document.createElement('script');
script.src = baseUrl +'/' + widget + '.js';
script.onload = function (event) {
// do something here maybe to pass information into the 'ad' widget
// for instance
};
pageHead.appendChild(script);
A good practice to pass configuration parameters to browsified widgets would be declare it in attributes.
<div prop1="abc" prop2="def" id="testwidget"></div>
and access them in javascript as required by using getAttribute() method...
document.getElementById("testwidget").getAttribute("prop1");
This will help you maintain configuration at the DOM level rather than keep a global variable.
Hope it helps!
Related
We're nearing the completion of working on an embedible JavaScript widget which has been built with AngularJS, to date everything it working fine, however we're now at the point where we need to allow the site owner to inject config settings into the JavaScript such as:
company_id
user
name
email
id
default overrides
In a previous iteration of our widget we used raw JavaScript, and were able to add settings like this:
Widget = window.Widget || [];
(function () {
var i, e;
i = document.createElement("script"),
i.type = 'text/javascript';
i.async = 1,
i.src = "https://path/to.js",
e = document.getElementsByTagName("script")[0],
e.parentNode.insertBefore(i, e);
})();
Widget.push(['set', {
'account_id': 'konami',
}]);
Inside our JavaScript, we simply had a window.Widget object which we overwrote with their settings and used wherever we needed.
However I'm at a loss as to how / where to receive this information and use it from inside our angular app, would appreciate any help on the best way to add in settings / configuration from when the JavaScript is loaded, and be able to use this in our services / controllers.
I ended up just adding the settings I wanted directly on an object on the window, then used that inside my angular project.
ala
_widget = window._widget || [];
_widget.company_id = '1234';
_widget.user = {name: 'Chris'};
Definitely not the pretty way I was hoping for, but it gets the job done.
How use getSelection?
It does not return the selected text:
function pageContextMenu (event) {
var window = require("sdk/window/utils").getMostRecentBrowserWindow();
var stringSelection = window.getSelection();
console.log(stringSelection.toString());
}
window.document.getElementById("contentAreaContextMenu").addEventListener("popupshowing", pageContextMenu);
You're mixing up content script code and backend/add-on code. Your main.js (backend/add-on) file has access to the SDK modules, your content scripts have access to the DOM (web page/document). If you want to use the DOM api (as you're doing with getSelection and getElementById) you must do so from the content script side. See this part of the guide to understand the distinction conceptually. Read these two tutorials to implement.
If you just want to access the selection from main.js and don't need any other DOM functions, then you'll have to do as #ZER0 suggested and use the sdk/selection module
I'm having a very frustrating problem with my javascript files. I have one global namespace that contains a page_data, utilities, modules etc namespace.
My directories look like this:
/utilities/single_utility.js
/modules/module.js etc tec
Anyways, I load the utilities before the modules, (which use the utilities) but keep getting problems.
My loader script looks like this (its wrapped in an SEAF):
for (var index in file_list) {
var url = file_list[index];
var new_script = document.createElement('script');
new_script.setAttribute("type", "text/javascript");
new_script.setAttribute("src", url);
element.appendChild(new_script);
}
Project is my global namespace, that holds all of these other namespaces. But when I try to reference a utility in one of my modules, with Project.utilities.session.exist() etc, it will sometimes throw an error that Project can't be found?
How can I fix this to make sure my files are loading properly or am I doing something else wrong?
Using async = false should protect your load order.
This is a quick snippet I use to load and maintain order.
var loadScript = function( url ) {
if (url) {
var doc = document,
t = doc.createElement("script"),
s = doc.getElementsByTagName("script")[0];
t.type = "text/javascript";
// Keep script order!
t.async = false;
t.src = url;
s.parentNode.insertBefore(t, s);
}
};
Some references backing this logic (and regarding browser support) from MDN and Microsoft
When you add the script tag to the page, the browser has to go out and download those scripts. There's no guarantee that they will download and execute in the same order that you created them.
You will need to add an event listener to new_script for when it loads, either onload or onreadystatechange, depending on the browser. But truly I'd recommend using a script loading library (such as RequireJS) that handles all the nasty details for you.
As an aside, why not just add all your script tags directly to your page? In that case they load sequentially.
It's better if you resolve this problem using a script loader.
As Matt said RequireJS is a good option if you also want to handle script dependencies.
For script loading only, you can take a look into LAB.js, is very small and straightforward:
for (var index in file_list) {
$LAB.script(file_list[index]);
}
If the scripts have dependencies and must be loaded in order, you can use .wait(), to do that keep the chain object returned by script. You can re-write the loop to keep the chain:
var chain;
for (var index in file_list) {
if (!chain) {
chain = $LAB.script(file_list[index]).wait();
} else {
chain.script(file_list[index]).wait();
}
}
Or just forget the file_list array and use $LAB directly:
$LAB.script('file1.js').wait()
.script('file2.js').wait()
/* ... */
So I'm trying to load a javascript remotely using jquery's $.getScript, but I'm puzzled on how I can pass data to the external script.
I've tried setting variables before the call but they aren't available in the script that gets loaded, and when I try to send/retrieve them using the query string, the remote script tries to read the querystring of the base file that it gets called from, not itself. Is there any other way to do this? Or is it possible to have a javascript file read its own querystring rather than the file it's called from (that's loaded in the browser)?
// editor ini
var editor_ini = { page: current_page, action: 'edit' };
var foo = 'bar';
// load the editor
$.getScript('assets/desktop/desklets/'+launcher.config.editor+'/execute.js', function(){});
In the execute.js file, the editor_ini and foo are both unavailable, I get the same result with:
// load the editor
$.getScript('assets/desktop/desklets/'+launcher.config.editor+'/execute.js', { page: current_page, action: 'edit', foo: 'bar' }, function(){});
because the remote script seems to be getting the query string from the original document rather than the one used when calling the file.
If it matters, I was trying to use the query object plugin for jquery for reading the query string.
global variable declared in inline javascript is accessible in external javascript page loaded using $.getScript().
I bet that your var foo='bar' is inside a function, so not visible in global scope. Try:
window.foo = 'bar'
Truly global variables will be accessible to your script. So, if they aren't, then it's probably because your variables that you think are global actually aren't. You can either move them to the top level scope or set them on the window object like Alexei suggested.
There are other ways to share data.
1) You can put an id on the <script> tag that loads the code and then have the code get the .src value from that tag and get the query string off the script's actual URL. I like this option, but I don't know if you can do it using jQuery.getScript() since I don't think it exposes that as an option.
2) You can have the loading script call a function that you provide and return an object with the desired data from that function.
3) Once the new script is loaded, you can call a setXXX() function in that script to set the state that it needs.
4) You can set information into a cookie that the other script can read.
5) You can encode data into a URL hash value that the other script can read.
I currently have one large external javascript file that is used on the page. I currently wrap the code in a self-invoking function because I have other sections that are loaded using ajax tabs, so I want to avoid naming clashes with those other external js files.
The code in the file is organized like below. I would like to split some of the code inside the plannerTab namespace into smaller files, yet still have it be part of that namespace.
How could I do this? Or, do you guys recommend a different approach? Thanks!
// Document Ready
$(function ()
{
// initializes table
plannerTab.plannerTable.init();
});
var plannerTab = (function ()
{
// All the code for the page is in here. I would like to extract sections
// from in here and put them into their own external files while still keeping
// the namespacing
}();
Update
How could I separate parts from within the plannerTab variable into smaller external js files, and still maintain that they are part of the plannerTab namespace? A small example below.
// Scope: plannerTab.config - Would like to store configuartion into a separate file
var config = {
selectors: {
tableId: '#plannerTable',
addTaskId: '#AddTask',
editTaskSelector: '#plannerTable .edit',
dateFilterSelector: '#plannerTable_TimeFilter li',
deleteTaskClass: '.delete',
searchFilter: '#plannerTable_filter',
selectedDateFilter: 'selected-dateFilter',
taskCellSelector: '#plannerTable .task-col',
taskClass: '.taskId'
},
urls: {
addTaskFormURL: '/Planner/Planner/LoadAddTaskForm',
editTaskFormURL: '/Planner/Planner/LoadEditTaskForm',
deleteTaskURL: '/Planner/Planner/DeleteTask',
getTasksByDateRangeURL: '/Planner/Planner/GetTasksByDateRange',
viewTaskURL: '/Planner/Planner/ViewTask'
}
};
Look at this example (from google)
<script type="text/javascript">
function importScript(url){
var tag = document.createElement("script");
tag.type="text/javascript";
tag.src = url;
document.body.appendChild(tag);
}
window.onload = function(){
// imports go here
importScript("foo.js"); // example
};
</script>
I'm assuming that plannerTab becomes an object return result of the self executing function. If you need to add properties or methods to that object dynamically, you can take a look at jQuery.extend() http://api.jquery.com/jQuery.extend/
You would need to modify the external JS to use the jQuery extend method to add onto existing properties and methods of plannerTab. As long as you keep plannerTab a global variable, you will continue adding to it as you import more external js files.
If you are using the module pattern to maintain private variables in plannerTab, be sure to test how those values behave once you use jQuery.extend().