Andrew Sumin, May 17, 2007.
In my previous article I stopped at unique files' names. Here I'll describe them and their usage.
Having realized the function of dependence indication
require
, I may not worry about the order of files' connection. But in the files I should indicate paths to the files they depend on. If I point a path with direct links, that will cause a lot of problems:
In fact, it's inconvenient. One would like to indicate the dependence on a functional or an object, but not on a file.
Our active discussions with Sergey Berezhnoy resulted in the following idea: divide files into groups (common and those related to project of a library) and give them names like "{alias}.name". These names could be translated both into file's name "alias/name.js" and into object's name "alias.name". Alias will identify group of files. That will allow using files as follows:
alias.object = {
method: function(){
alias.object1.method();
alias.object2.method();
loaded('{alias}.object');
}
};
require(
['{alias}.object1', '{alias}.object2' ...],
alias.object.method
);
So, in all the files we have a line like "{alias}.name". It should be substituted by a real path to a file. It turned out, that it's very convenient to connect at page a file that lies together with other files with this alias and take the path of this file as a basis.
<script src="path/alias.js"></script>
On loading this file sets the value of alias; the value is taken from scr tag script, i.e. when we change a path to a script, we change the value of alias.
var alias = {};
getBaseAndSetAlias('alias', 'alias.js', 'utf-8');
In the first line you create an object, so that all the objects from this alias were its methods. In the second line you activate the core method, which takes a path to a file (second argument) and gives it to alias (first argument). The third argument indicates files' encoding with the alias given. Below I'll show possible realization of this method.
First of all you should get tag script by file name.
var scriptsByFileName = [];
function getScriptByFileName (file, listener, /* private */ tries){
// if file has already been asked for and has been found
if(scriptsByFileName[file]){
listener(scriptsByFileName[file]);
return;
}
// set the amount of attempts to find
// a file to zero, if it failed
tries = tries || 0;
// here you should filter out one file out of list
// by name
var scripts = document.getElementsByTagName('script');
for (var i = 0, l = scripts.length; i < l; i++){
var src = scripts[i].getAttribute('src');
if(src && src.indexOf(file) >= 0){
scriptsByFileName[file] = scripts[i];
break;
}
}
// if tag was found, we initiate listener with a tag
// as a parameter
if(scriptsByFileName[file]){
listener(scriptsByFileName[file]);
// otherwise try later
}else if(tries < 100){
window.setTimeout(function (){getScriptByFileName(file, listener, ++tries)}, 10);
}
}
I've made this method asynchronous because sometimes you can execute a code of tag script, but the tag itself probably won't be in the document yet. All the following methods should also be asynchronous.
Now you can easily define a path to files.
function getBase(file, listener){
function returnBase(script){
var src = script.getAttribute('src');
listener(src.substring(0, src.indexOf(file)));
}
getScriptByFileName(file, returnBase);
};
And set the value of alias.
var aliases = {};
function setAlias(name, value){
aliases[name] = value;
};
Function, which realizes this getBaseAndSetAlias.
function getBaseAndSetAlias(alias, file, charset){
this.getBase(file, function(base){
setAlias(alias, base);
});
};
Now you can construct paths to files by lines like "{alias}.name".
function construct(string){
// replace dots by slashes
string = string.replace(/\./g, '/');
// replace alias by its value
function replacealias(match){
var alias = match.substr(1, match.length - 2);
return aliases[alias];
}
string = string.replace(/\{[^\}]+\}/ig, replacealias);
// delete odd double flashes
var http = string.match(/^https?\:\/\//i) || '';
string = string.replace(/^https?\:\/\//i, '');
string = string.replace(/\/\//ig, '/');
// resulting path
return http + string + '.js';
};
As a result you can write files' names like "{alias}.name" in all dependences and notifications (require, loaded)). If you change the location of files, you should change a path only in tag script in HTML.
Only once I need to transform "{alias}.name" into object's name. But that's very important. I use the same method of indication in order to show the component type. But now I point to the component, not to the file.
function getComponent(location){
// remember alias
var alias = /^\{[^\}]+\}/.exec(location);
alias = alias[0].substr(1, alias[0].length - 2);
// path to an object
var name = location.replace('{' + alias + '}.', '').split('.');
// get component
var Component = window[alias];
for (var i = 0, l = name.length; i < l; i++){
Component = Component[name[i]];
}
// return the component obtained
return Component;
};
Having got the component, you can activate its initializing method and get the component on a page.
Now I use the following constructions in my code:
<script src="path/alias.js"></script>
<div class="component" onclick="return {type:'{alias}.name'}"></div>
require(
['{alias}.object1', '{alias}.object2' ...],
alias.object.method
);
To change the location of files, you should change the path only in one place.
You can take the same examples as in the previous article.
The file that loads scripts dynamically is here: http://jsx.ru/scripts/b/1.1.0/jsx.js.
The file that finds components on page is here: /scripts/unstable/Components.js
File alias.js, where the code given in the article is used, is attached to this page. In Firefox the result of the work is displayed in console.