blob: ee05f20e709e195f1bfefcac1ece75ee10adeb40 [file] [log] [blame]
/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
/**
* This startup script is used when we run superdevmode from an app server.
*
* The main goal is to avoid installing bookmarklets for host:port/module
* to load and recompile the application.
*/
(function($wnd, $doc){
// Don't support browsers without session storage: IE6/7
var badBrowser = 'Unable to load Super Dev Mode of "__MODULE_NAME__" because\n';
if (!('sessionStorage' in $wnd)) {
$wnd.alert(badBrowser + 'this browser does not support "sessionStorage".');
return;
}
//We don't import properties.js so we have to update active modules here
$wnd.__gwt_activeModules = $wnd.__gwt_activeModules || {};
$wnd.__gwt_activeModules['__MODULE_NAME__'] = {
'moduleName' : '__MODULE_NAME__',
'bindings' : function() {
return {};
}
};
// Reuse compute script base
__COMPUTE_SCRIPT_BASE__;
// document.head does not exist in IE8
var $head = $doc.head || $doc.getElementsByTagName('head')[0];
// Quick way to compute the user.agent, it works almost the same than
// UserAgentPropertyGenerator, but we cannot reuse it without depending
// on gwt-user.jar.
// This reduces compilation time since we only compile for one ua.
var ua = $wnd.navigator.userAgent.toLowerCase();
var docMode = $doc.documentMode || 0;
ua = /webkit/.test(ua)? 'safari' : /gecko/.test(ua) || docMode > 10 ? 'gecko1_8' :
/msie/.test(ua) && docMode > 7 ? 'ie' + docMode : '';
if (!ua && docMode) {
$wnd.alert(badBrowser + 'your browser is running "Compatibility View" for IE' + docMode + '.');
return;
}
// We use a different key for each module so that we can turn on dev mode
// independently for each.
var devModeHookKey = '__gwtDevModeHook:__MODULE_NAME__';
var devModeSessionKey = '__gwtDevModeSession:__MODULE_NAME__';
// Compute some codeserver urls so as the user does not need bookmarklets
var hostName = $wnd.location.hostname;
var serverUrl = 'http://' + hostName + ':__SUPERDEV_PORT__';
var nocacheUrl = serverUrl + '/__MODULE_NAME__/__MODULE_NAME__.nocache.js';
// Save supder-devmode url in session
$wnd.sessionStorage[devModeHookKey] = nocacheUrl;
// Save user.agent in session
$wnd.sessionStorage[devModeSessionKey] = 'user.agent=' + ua + '&';
// Set bookmarklet params in window
$wnd.__gwt_bookmarklet_params = {'server_url': serverUrl};
// Save the original module base. (Returned by GWT.getModuleBaseURL.)
$wnd[devModeHookKey + ':moduleBase'] = computeScriptBase();
// Needed in the real nocache.js logic
$wnd.__gwt_activeModules['__MODULE_NAME__'].canRedirect = true;
$wnd.__gwt_activeModules['__MODULE_NAME__'].superdevmode = true;
// Insert the superdevmode nocache script in the first position of the head
var devModeScript = $doc.createElement('script');
devModeScript.src = nocacheUrl;
// Show a div in a corner for adding buttons to recompile the app.
// We reuse the same div in all modules of this page for stacking buttons
// and to make it available in jsni.
// The user can remove this: .gwt-DevModeRefresh {display:none}
$wnd.__gwt_compileElem = $wnd.__gwt_compileElem || $doc.createElement('div');
$wnd.__gwt_compileElem.className = 'gwt-DevModeRefresh';
// Create the compile button for this module
var compileButton = $doc.createElement('div');
$wnd.__gwt_compileElem.appendChild(compileButton);
// Number of modules present in the window
var moduleIdx = $wnd.__gwt_compileElem.childNodes.length;
// Each button has a class with its index number
var buttonClassName = 'gwt-DevModeCompile gwt-DevModeModule-' + moduleIdx;
compileButton.className = buttonClassName;
// The status message container
compileButton.innerHTML = '<div></div>';
// User knows who module to compile, hovering the button
compileButton.title = 'Compile module:\n__MODULE_NAME__';
// Use CSS so the app could change button style
var compileStyle = $doc.createElement('style');
compileStyle.language = 'text/css';
$head.appendChild(compileStyle);
var css =
".gwt-DevModeRefresh{" +
"position:fixed;" +
"right:3px;" +
"bottom:3px;" +
"font-family:arial;" +
"font-size:1.8em;" +
"cursor:pointer;" +
"color:#B62323;" +
"text-shadow:grey 1px 1px 3px;" +
"z-index:2147483646;" +
"white-space:nowrap;" +
"line-height:normal;" +
"}" +
".gwt-DevModeCompile{" +
"position:relative;" +
"float:left;" +
"width:1em;" +
"}" +
".gwt-DevModeCompile div{" +
"position:absolute;" +
"right:1em;" +
"bottom:-3px;" +
"font-size:0.3em;" +
"opacity:1;" +
"direction:rtl;" +
"}" +
".gwt-DevModeCompile:before{" +
"content:'\u21bb';" +
"}" +
".gwt-DevModeCompiling:before{" +
// IE8 fails when setting content here
"opacity:0.1;" +
"}" +
".gwt-DevModeCompiling.tick-1 div:before{" +
"color:#446FF0;" +
"}" +
".gwt-DevModeCompile div:before{" +
"content:'GWT';" +
"transition: all .5s;" +
"}" +
".gwt-DevModeTimeout div:before{" +
"content:'NOT RESPONDING';" +
"}" +
".gwt-DevModeBusy div:before{" +
"content:'BUSY';" +
"}" +
".gwt-DevModeError div:before{" +
"content:'FAILED';" +
"}";
// Only insert common css the first time
css = (moduleIdx == 1 ? css : '') +
".gwt-DevModeModule-" + moduleIdx + ".gwt-DevModeCompiling div:before{" +
"content:'COMPILING __MODULE_NAME__';" +
"font-size:18px;" +
"}";
if ('styleSheet' in compileStyle) {
// IE8
compileStyle.styleSheet.cssText = css;
} else {
compileStyle.appendChild($doc.createTextNode(css));
}
// Set a different compile function name per module
var compileFunction = '__gwt_compile_' + moduleIdx;
compileButton.onclick = function() {
$wnd[compileFunction]();
};
// defer app script insertion until the body is ready
setTimeout(function(){
$head.insertBefore(devModeScript, $head.firstElementChild || $head.children[0]);
$doc.body.appendChild($wnd.__gwt_compileElem);
}, 1);
// Use JSONP because IE8/9 do not support CORS
function executeJsonp(url, callback, timeout) {
// Timeout to detect whether the callback is never executed
var timeoutId, expired;
if (timeout) {
timeoutId = setTimeout(function() {
compileButton.className = buttonClassName + ' gwt-DevModeTimeout';
expired = true;
callback();
}, timeout);
}
// Compute a unique name for each callback to avoid cache issues
// in IE, and to avoid the same function being called twice.
var callbackName = '__gwt_compile_callback_' + moduleIdx + '_' + new Date().getTime();
$wnd[callbackName] = function(r) {
delete $wnd[callbackName];
// Avoid running callback if response came after timeout
if (!expired) {
clearTimeout(timeoutId);
callback(r);
}
};
// Insert an script tag.
var compileScript = $doc.createElement('script');
compileScript.src = url + '&_callback=' + callbackName;
$head.appendChild(compileScript);
}
// Flag to avoid compiling in parallel.
var compiling = false;
// progress polling vars
var progressIntervalId, progressCounter = 0;
function onProgressResponse(r) {
// No progress info right now, just compiling status
if (r && r.status == 'compiling') {
// Change the class each tick
compileButton.className = buttonClassName + ' gwt-DevModeCompiling tick-' + ++progressCounter % 2;
}
}
function requestProgress(r) {
executeJsonp(serverUrl + '/progress?', onProgressResponse, 1000);
}
function onCompileResponse(r) {
if (r && r.status == 'ok') {
$wnd.location.reload();
} else {
compileButton.className = buttonClassName + ' gwt-DevModeError';
compiling = false;
}
clearInterval(progressIntervalId);
}
function compileIfIdle(r) {
if (!r) {
return;
}
// Avoid compilation if server is not idle
if (compiling || r.status != 'idle') {
compileButton.className = buttonClassName + ' gwt-DevModeBusy';
return;
}
compiling = true;
compileButton.className = buttonClassName + ' gwt-DevModeCompiling';
// Perform the compilation process
executeJsonp(serverUrl + '/recompile/__MODULE_NAME__?user.agent=' + ua, onCompileResponse);
// Poll the server to get the status so as the user can see that server is working
progressIntervalId = setInterval(requestProgress, 1000);
};
// Compile function available in window so as it can be run from jsni.
// TODO(manolo): make Super Dev Mode script set this function in __gwt_activeModules
$wnd[compileFunction] = function() {
if (!compiling) {
compileButton.className = buttonClassName;
// Check server status and then compile
executeJsonp(serverUrl + '/progress?', compileIfIdle, 1000);
}
};
// Wait until the gwt app has been loaded, then run this block.
setTimeout(function(){
// Maintaining the hook key in session can cause problems
// if we try to run classic code server so we remove it
// after a while.
$wnd.sessionStorage.removeItem(devModeHookKey);
// Re-attach compile button because sometimes app clears the dom
$doc.body.appendChild($wnd.__gwt_compileElem);
}, 2000);
})(window, document);