vaadin-framework/WebContent/VAADIN/vaadinBootstrap.js
Leif Åstrand bd923f394c Detach previous UI with the same window.name (#10338, #12255)
Change-Id: I15234985f1591d6af383c6e014679762619d5000
2013-07-31 14:24:40 +00:00

325 行
8.7 KiB
JavaScript

(function() {
var apps = {};
var themesLoaded = {};
var widgetsets = {};
var log;
if (typeof console === "undefined" || !window.location.search.match(/[&?]debug(&|$)/)) {
//If no console.log present, just use a no-op
log = function() {};
} else if (typeof console.log === "function") {
//If it's a function, use it with apply
log = function() {
console.log.apply(console, arguments);
};
} else {
//In IE, its a native function for which apply is not defined, but it works without a proper 'this' reference
log = console.log;
}
var loadTheme = function(url) {
if(!themesLoaded[url]) {
log("loadTheme", url);
var stylesheet = document.createElement('link');
stylesheet.setAttribute('rel', 'stylesheet');
stylesheet.setAttribute('type', 'text/css');
stylesheet.setAttribute('href', url + "/styles.css");
document.getElementsByTagName('head')[0].appendChild(stylesheet);
themesLoaded[url] = true;
}
};
var isWidgetsetLoaded = function(widgetset) {
var className = widgetset.replace(/\./g, "_");
return (typeof window[className]) != "undefined";
};
var loadWidgetset = function(url, widgetset) {
if (widgetsets[widgetset]) {
return;
}
log("load widgetset", url, widgetset);
setTimeout(function() {
if (!isWidgetsetLoaded(widgetset)) {
alert("Failed to load the widgetset: " + url);
}
}, 15000);
var scriptTag = document.createElement('script');
scriptTag.setAttribute('type', 'text/javascript');
scriptTag.setAttribute('src', url);
document.getElementsByTagName('head')[0].appendChild(scriptTag);
widgetsets[widgetset] = {
pendingApps: []
};
};
var isInitializedInDom = function(appId) {
var appDiv = document.getElementById(appId);
if (!appDiv) {
return false;
}
for ( var i = 0; i < appDiv.childElementCount; i++) {
var className = appDiv.childNodes[i].className;
// If the app div contains a child with the class
// "v-app-loading" we have only received the HTML
// but not yet started the widget set
// (UIConnector removes the v-app-loading div).
if (className && className.contains("v-app-loading")) {
return false;
}
}
return true;
};
window.vaadin = window.vaadin || {
initApplication: function(appId, config) {
var testbenchId = appId.replace(/-\d+$/, '');
if (apps[appId]) {
if (window.vaadin && window.vaadin.clients && window.vaadin.clients[testbenchId] && window.vaadin.clients[testbenchId].initializing) {
throw "Application " + appId + " is already being initialized";
}
if (isInitializedInDom(appId)) {
throw "Application " + appId + " already initialized";
}
}
log("init application", appId, config);
window.vaadin.clients[testbenchId] = {
isActive: function() {
return true;
},
initializing: true
};
var getConfig = function(name) {
var value = config[name];
return value;
};
var fetchRootConfig = function() {
log('Fetching root config');
var url = getConfig('browserDetailsUrl');
if (!url) {
// No special url defined, use the same URL that loaded this page (without the fragment)
url = window.location.href.replace(/#.*/,'');
}
url += ((/\?/).test(url) ? "&" : "?") + "v-browserDetails=1";
var rootId = getConfig("v-rootId");
if (rootId !== undefined) {
url += "&v-rootId=" + rootId;
}
// Tell the UI what theme it is configured to use
var theme = getConfig('theme');
if (theme !== undefined) {
url += '&theme=' + encodeURIComponent(theme);
}
url += "&v-appId=" + appId;
var extraParams = getConfig('extraParams')
if (extraParams !== undefined) {
url += extraParams;
}
url += '&' + vaadin.getBrowserDetailsParameters(appId);
// Timestamp to avoid caching
url += '&v-' + (new Date()).getTime();
var r;
try {
r = new XMLHttpRequest();
} catch (e) {
r = new ActiveXObject("MSXML2.XMLHTTP.3.0");
}
r.open('POST', url, true);
r.onreadystatechange = function (aEvt) {
if (r.readyState == 4) {
var text = r.responseText;
if (r.status == 200){
log("Got root config response", text);
var updatedConfig = JSON.parse(text);
// Copy new properties to the config object
for (var property in updatedConfig) {
if (updatedConfig.hasOwnProperty(property)) {
config[property] = updatedConfig[property];
}
}
// Try bootstrapping again, this time without fetching missing info
bootstrapApp(false);
} else {
log('Error', r.statusText, text);
//Let TB waitForVaadin work again
delete window.vaadin.clients[testbenchId];
// Show the error in the app's div
var appDiv = document.getElementById(appId);
appDiv.innerHTML = text;
appDiv.style['overflow'] = 'auto';
}
}
};
r.send(null);
log('sending request to ', url);
};
//Export public data
var app = {
'getConfig': getConfig
};
apps[appId] = app;
if (!window.name) {
window.name = appId + '-' + Math.random();
}
var bootstrapApp = function(mayDefer) {
var vaadinDir = getConfig('vaadinDir');
var themeUri = vaadinDir + 'themes/' + getConfig('theme');
loadTheme(themeUri);
var widgetset = getConfig('widgetset');
var widgetsetUrl = getConfig('widgetsetUrl');
if (!widgetsetUrl) {
widgetsetUrl = vaadinDir + 'widgetsets/' + widgetset + "/" + widgetset + ".nocache.js?" + new Date().getTime();
}
loadWidgetset(widgetsetUrl, widgetset);
if (getConfig('uidl') === undefined) {
if (mayDefer) {
fetchRootConfig();
} else {
throw "May not defer bootstrap any more";
}
} else {
if (widgetsets[widgetset].callback) {
log("Starting from bootstrap", appId);
widgetsets[widgetset].callback(appId);
} else {
log("Setting pending startup", appId);
widgetsets[widgetset].pendingApps.push(appId);
}
}
};
bootstrapApp(true);
if (getConfig("debug")) {
// TODO debug state is now global for the entire page, but should somehow only be set for the current application
window.vaadin.debug = true;
}
return app;
},
clients: {},
getApp: function(appId) {
var app = apps[appId];
return app;
},
loadTheme: loadTheme,
registerWidgetset: function(widgetset, callback) {
log("Widgetset registered", widgetset);
widgetsets[widgetset].callback = callback;
for(var i = 0; i < widgetsets[widgetset].pendingApps.length; i++) {
var appId = widgetsets[widgetset].pendingApps[i];
log("Starting from register widgetset", appId);
callback(appId);
}
widgetsets[widgetset].pendingApps = null;
},
getBrowserDetailsParameters: function(parentElementId) {
// Screen height and width
var url = 'v-sh=' + window.screen.height;
url += '&v-sw=' + window.screen.width;
// Window height and width
var cw = 0;
var ch = 0;
if(typeof(window.innerWidth) == 'number') {
// Modern browsers
cw = window.innerWidth;
ch = window.innerHeight;
} else {
// IE 8
cw = document.documentElement.clientWidth;
ch = document.documentElement.clientHeight;
}
url += '&v-cw=' + cw + '&v-ch=' + ch;
var d = new Date();
url += '&v-curdate=' + d.getTime();
var tzo1 = d.getTimezoneOffset(); // current offset
var dstDiff = 0;
var rtzo = tzo1;
for (var m=12;m>0;m--) {
d.setUTCMonth(m);
var tzo2 = d.getTimezoneOffset();
if (tzo1 != tzo2) {
dstDiff = (tzo1 > tzo2 ? tzo1-tzo2 : tzo2-tzo1); // offset w/o DST
rtzo = (tzo1 > tzo2 ? tzo1 : tzo2); // offset w/o DST
break;
}
}
// Time zone offset
url += '&v-tzo=' + tzo1;
// DST difference
url += '&v-dstd=' + dstDiff;
// Raw time zone offset
url += '&v-rtzo=' + rtzo;
// DST in effect?
url += '&v-dston=' + (tzo1 != rtzo);
var pe = document.getElementById(parentElementId);
if (pe) {
url += '&v-vw=' + pe.offsetWidth;
url += '&v-vh=' + pe.offsetHeight;
}
// Location
url += '&v-loc=' + encodeURIComponent(location.href);
// Window name
if (window.name) {
url += '&v-wn=' + encodeURIComponent(window.name);
}
// Detect touch device support
var supportsTouch = false;
try {
document.createEvent("TouchEvent");
supportsTouch = true;
} catch (e) {
// Chrome and IE10 touch detection
supportsTouch = 'ontouchstart' in window
|| navigator.msMaxTouchPoints;
}
if (supportsTouch) {
url += "&v-td=1";
}
return url;
}
};
log('Vaadin bootstrap loaded');
})();