392 lines
9.7 KiB
JavaScript
392 lines
9.7 KiB
JavaScript
|
|
require('mootools');
|
|
|
|
Vn = module.exports = {
|
|
Locale : require('./locale')
|
|
,Enum : require('./enum')
|
|
,Type : require('./type')
|
|
,Object : require('./object')
|
|
,Mutators : require('./mutators')
|
|
,Browser : require('./browser')
|
|
,Cookie : require('./cookie')
|
|
,Date : require('./date')
|
|
,Value : require('./value')
|
|
,Url : require('./url')
|
|
,LotIface : require('./lot-iface')
|
|
,Lot : require('./lot')
|
|
,LotQuery : require('./lot-query')
|
|
,Hash : require('./hash')
|
|
,ParamIface : require('./param-iface')
|
|
,Param : require('./param')
|
|
,Spec : require('./spec')
|
|
,Model : require('./model')
|
|
,ModelIface : require('./model-iface')
|
|
,ModelProxy : require('./model-proxy')
|
|
,IteratorIface : require('./iterator-iface')
|
|
,Iterator : require('./iterator')
|
|
,Form : require('./form')
|
|
,Node : require('./node')
|
|
,NodeBuilder : require('./node-builder')
|
|
,Component : require('./component')
|
|
,Builder : require('./builder')
|
|
,JsonException : require('./json-exception')
|
|
,JsonConnection : require('./json-connection')
|
|
|
|
,Config: {}
|
|
,includes: {}
|
|
,cssIncludes: {}
|
|
,currentDeps: []
|
|
,currentCallback: null
|
|
,head: document.getElementsByTagName('head')[0]
|
|
,isMobileCached: null
|
|
|
|
,getVersion: function() {
|
|
if (this._version === undefined) {
|
|
var re = /[; ]vnVersion=([^\\s;]*)/;
|
|
var sMatch = (' '+ document.cookie).match(re);
|
|
this._version = (sMatch) ? '?'+ unescape(sMatch[1]) : '';
|
|
}
|
|
|
|
return this._version;
|
|
}
|
|
|
|
,setVersion(version) {
|
|
document.cookie = `vnVersion=${version}; SameSite=Lax;`;
|
|
}
|
|
|
|
/**
|
|
* Includes a new CSS stylesheet in the current document, if the stylesheet
|
|
* is already included, does nothing.
|
|
*
|
|
* @param {string} fileName The stylesheet file name
|
|
*/
|
|
,includeCss: function(fileName) {
|
|
var cssData = this.cssIncludes[fileName];
|
|
|
|
if (!cssData) {
|
|
var link = document.createElement('link');
|
|
link.rel = 'stylesheet';
|
|
link.type = 'text/css';
|
|
link.href = fileName + this.getVersion();
|
|
this.head.appendChild(link);
|
|
|
|
this.cssIncludes[fileName] =
|
|
{
|
|
included: true
|
|
,link: link
|
|
};
|
|
} else if (!cssData.included) {
|
|
cssData.link.disabled = false;
|
|
cssData.included = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Excludes a CSS stylesheet from the current document.
|
|
*
|
|
* @param {string} fileName The stylesheet file name
|
|
*/
|
|
,excludeCss: function(fileName) {
|
|
var cssData = this.cssIncludes[fileName];
|
|
|
|
if (cssData && cssData.included) {
|
|
cssData.link.disabled = true;
|
|
cssData.included = false;
|
|
}
|
|
}
|
|
|
|
,_createIncludeData: function(path) {
|
|
var includeData = {
|
|
depCount: 0
|
|
,success: false
|
|
,loaded: false
|
|
,callbacks: []
|
|
,dependants: []
|
|
};
|
|
|
|
this.includes[path] = includeData;
|
|
return includeData;
|
|
}
|
|
|
|
,_handleCallback: function(includeData, callback) {
|
|
if (!callback)
|
|
return;
|
|
|
|
if (includeData.success)
|
|
callback(includeData.loaded);
|
|
else
|
|
includeData.callbacks.push(callback);
|
|
}
|
|
|
|
,_resolveDeps: function(includeData) {
|
|
includeData.success = true;
|
|
|
|
var callbacks = includeData.callbacks;
|
|
|
|
if (callbacks)
|
|
for (var i = 0; i < callbacks.length; i++)
|
|
callbacks[i](includeData.loaded);
|
|
|
|
var dependants = includeData.dependants;
|
|
|
|
if (dependants)
|
|
for (var i = 0; i < dependants.length; i++) {
|
|
var dependant = dependants[i];
|
|
dependant.depCount--;
|
|
|
|
if (dependant.depCount == 0)
|
|
this._resolveDeps(dependant);
|
|
}
|
|
|
|
delete includeData.callbacks;
|
|
delete includeData.dependants;
|
|
delete includeData.depCount;
|
|
}
|
|
|
|
/**
|
|
* Initializes the library and calls the passed function when all
|
|
* includes and its dependencies are resolved.
|
|
* Should be called on the last statically incuded script.
|
|
*
|
|
* @param {Function} callback The main function
|
|
*/
|
|
,main: function(callback) {
|
|
if (this.mainCalled) {
|
|
Vn.warning("Vn: main method should be called only once");
|
|
return;
|
|
}
|
|
|
|
this.mainCalled = true;
|
|
this.mainCallback = callback;
|
|
|
|
var basePath = location.protocol +'//'+ location.host;
|
|
basePath += location.port ? ':'+ location.port : '';
|
|
basePath += location.pathname;
|
|
|
|
var scripts = this.head.getElementsByTagName('script');
|
|
var includes = this.currentDeps;
|
|
|
|
for (var i = 0; i < scripts.length; i++) {
|
|
var path = scripts[i].src.substr(basePath.length);
|
|
path = path.substr(0, path.indexOf('.js')) +'.js';
|
|
|
|
var includeData = this.includes[path];
|
|
|
|
if (includeData === undefined) {
|
|
this.currentDeps = includes;
|
|
var includeData = this._createIncludeData(path);
|
|
this._onScriptLoad(includeData, true);
|
|
}
|
|
}
|
|
|
|
includeData.callbacks.push(this._onMainDepsLoad.bind(this));
|
|
window.addEventListener('load', this._onWindowLoad.bind(this));
|
|
}
|
|
|
|
,_onMainDepsLoad: function() {
|
|
this.mainDepsLoaded = true;
|
|
this._callMain();
|
|
}
|
|
|
|
,_onWindowLoad: function() {
|
|
this.windowReady = true;
|
|
this._callMain();
|
|
}
|
|
|
|
,_callMain: function() {
|
|
if (this.mainCallback && this.windowReady && this.mainDepsLoaded)
|
|
this.mainCallback();
|
|
}
|
|
|
|
/**
|
|
* Includes a set of javascript files and sets it as dependecies of the
|
|
* current script.
|
|
*
|
|
* @param {...} The list of files as function arguments
|
|
*/
|
|
,include: function() {
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
var includeData = this._realIncludeJs(arguments[i] +'.js');
|
|
|
|
if (!includeData.success)
|
|
this.currentDeps.push(includeData);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Downloads a set of resources and sets it as dependecies of the
|
|
* current script.
|
|
*
|
|
* @param {...} The list of files as function arguments
|
|
*/
|
|
,resource: function() {
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
var includeData = this._realLoadXml(arguments[i]);
|
|
|
|
if (!includeData.success)
|
|
this.currentDeps.push(includeData);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the function that will be called when current script dependencies
|
|
* are resolved.
|
|
*
|
|
* @param {Function} callback The callback function
|
|
*/
|
|
,define: function(callback) {
|
|
this.currentCallback = callback;
|
|
}
|
|
|
|
/**
|
|
* Includes an entire Javascript library including it's localized file.
|
|
*
|
|
* @param {string} libName The folder of the library
|
|
* @param {Array<string>} files Array with every library file name
|
|
*/
|
|
,includeLib: function(libName, files) {
|
|
Vn.Locale.loadScript('js/'+ libName +'.js');
|
|
|
|
for (var i = 0; i < files.length; i++)
|
|
this.include('js/'+ libName +'/'+ files[i]);
|
|
}
|
|
|
|
/**
|
|
* Includes a new Javascript in the current document, if the script
|
|
* is already included, does nothing and calls the callback.
|
|
*
|
|
* @param {string} fileName The script file name
|
|
* @param {Function} callback The function to call when script is
|
|
* downloaded and included
|
|
*/
|
|
,includeJs: function(fileName, callback, skipVersion) {
|
|
var includeData = this._realIncludeJs(fileName, skipVersion);
|
|
this._handleCallback(includeData, callback);
|
|
}
|
|
|
|
,_realIncludeJs: function(fileName, skipVersion) {
|
|
var includeData = this.includes[fileName];
|
|
|
|
if (includeData === undefined) {
|
|
includeData = this._createIncludeData(fileName);
|
|
|
|
var src = fileName;
|
|
|
|
if (!skipVersion)
|
|
src = src + this.getVersion();
|
|
|
|
var script = document.createElement('script');
|
|
script.type = 'text/javascript';
|
|
script.async = false;
|
|
script.src = src;
|
|
|
|
script.onload =
|
|
this._onScriptLoad.bind(this, includeData, true);
|
|
script.onerror =
|
|
this._onScriptLoad.bind(this, includeData, false);
|
|
script.onreadystatechange =
|
|
this._onScriptStateChange.bind(this, includeData, script);
|
|
|
|
this.head.appendChild(script);
|
|
}
|
|
|
|
return includeData;
|
|
}
|
|
|
|
,_onScriptStateChange: function(includeData, script) {
|
|
if (script.readyState == 'complete')
|
|
this._onScriptLoad(includeData, true);
|
|
}
|
|
|
|
,_onScriptLoad: function(includeData, loaded) {
|
|
includeData.loaded = loaded;
|
|
|
|
if (loaded) {
|
|
if (this.currentCallback)
|
|
includeData.callbacks.unshift(this.currentCallback);
|
|
|
|
var includes = this.currentDeps;
|
|
|
|
if (includes && includes.length > 0) {
|
|
includeData.depCount = includes.length;
|
|
|
|
for (var i = 0; i < includes.length; i++)
|
|
includes[i].dependants.push(includeData);
|
|
} else
|
|
this._resolveDeps(includeData);
|
|
} else
|
|
this._resolveDeps(includeData);
|
|
|
|
this.currentDeps = [];
|
|
this.currentCallback = null;
|
|
}
|
|
|
|
/**
|
|
* Request an XML file.
|
|
*
|
|
* @param {string} path The file path
|
|
* @param {Function} callback The function to call when file is downloaded
|
|
*/
|
|
,loadXml: function(path, callback) {
|
|
var includeData = this._realLoadXml(path);
|
|
this._handleCallback(includeData, callback);
|
|
}
|
|
|
|
,_realLoadXml: function(path) {
|
|
var includeData = this.includes[path];
|
|
|
|
if (includeData === undefined) {
|
|
includeData = this._createIncludeData(path);
|
|
|
|
var request = new XMLHttpRequest();
|
|
request.onreadystatechange =
|
|
this._onXmlReady.bind(this, includeData, request);
|
|
request.open('get', path + this.getVersion(), true);
|
|
request.send();
|
|
}
|
|
|
|
return includeData;
|
|
}
|
|
|
|
,_onXmlReady: function(includeData, request) {
|
|
if (request.readyState != 4)
|
|
return;
|
|
|
|
includeData.loaded = request.status == 200;
|
|
|
|
if (includeData.loaded)
|
|
includeData.xml = request.responseXML;
|
|
|
|
this._resolveDeps(includeData);
|
|
}
|
|
|
|
/**
|
|
* Gets the DOM object from an included XML file.
|
|
*
|
|
* @param {string} path The file path
|
|
* @return {Object} The DOM object
|
|
*/
|
|
,getXml: function(path) {
|
|
var includeData = this.includes[path];
|
|
|
|
if (!(includeData && includeData.success))
|
|
return null;
|
|
|
|
return includeData.xml;
|
|
}
|
|
|
|
/**
|
|
* Checks if user is using a mobile browser.
|
|
*
|
|
* return {boolean} %true if is mobile, %false otherwise.
|
|
*/
|
|
,isMobile: function() {
|
|
if (this.isMobileCached === null) {
|
|
var regExp = /(Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone)/i;
|
|
this.isMobileCached = navigator.userAgent.match(regExp);
|
|
}
|
|
|
|
return this.isMobileCached;
|
|
}
|
|
};
|