From dd2a3fad7b94d4e2087956ba112ecd1f3404b411 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Tue, 22 May 2012 16:53:44 +0200 Subject: [PATCH] bootstrap cleanup, removing old code --- core/lively/bootstrap.js | 1533 +++++++++++++++++--------------------- 1 file changed, 703 insertions(+), 830 deletions(-) diff --git a/core/lively/bootstrap.js b/core/lively/bootstrap.js index 1d68f6d82f..84bfe47630 100644 --- a/core/lively/bootstrap.js +++ b/core/lively/bootstrap.js @@ -1,6 +1,8 @@ var isFirefox = window.navigator.userAgent.indexOf('Firefox') > -1; var isFireBug = isFirefox && window.console && window.console.firebug !== undefined; +function livelyConfigExists() { return typeof Config !== "undefined" } + (function setupConsole() { var platformConsole = window.console || @@ -63,985 +65,854 @@ var isFireBug = isFirefox && window.console && window.console.firebug !== undefi })(); -var JSLoader = { +var LoadingScreen = { - SVGNamespace: 'http:\/\/www.w3.org/2000/svg', - XLINKNamespace: 'http:\/\/www.w3.org/1999/xlink', - LIVELYNamespace: 'http:\/\/www.experimentalstuff.com/Lively', + id : 'loadingScreen', + consoleId : 'loadingConsole', + logoId: 'loadingLogo', + brokenWorldMsgId: 'loadingBrokenWorldMsg', - loadJs: function(url, onLoadCb, loadSync, okToUseCache, cacheQuery) { - if (this.scriptInDOM(url)) { - console.log('script ' + url + ' already loaded or loading'); - return null; - } - // it's called loadJs, not loadCSS !!! - var css = this.isCSS(url); + width: function() { return document.documentElement.clientWidth || 800 }, + height: function() { return document.documentElement.clientHeight || 800 }, - // adapt URL - var exactUrl = url; - if ((exactUrl.indexOf('!svn') <= 0) && !okToUseCache) { - exactUrl = this.makeUncached(exactUrl, cacheQuery); - } + buildBackground: function() { + var div1 = document.createElement('div') + div1.setAttribute('id', this.id); + div1.setAttribute('style', "position: fixed; left: 0px; top: 0px; background-color: rgba(100,100,100,0.7); overflow: auto"); + div1.style.width = this.width() + 'px' + div1.style.height = this.height() + 'px' - // create and configure script tag - var parentNode = this.findParentScriptNode(), - xmlNamespace = parentNode.namespaceURI, - el; + return div1 + }, - if (css) { - el = document.createElementNS(xmlNamespace, 'link'); - el.setAttributeNS(null, "rel", "stylesheet"); - el.setAttributeNS(null, "type", "text/css"); - } else { //assuming js - el = document.createElementNS(xmlNamespace, 'script'); - el.setAttributeNS(null, 'type', 'text/ecmascript'); - } + buildLoadingLogo: function() { + var logoAndText = document.createElement('div') + logoAndText.setAttribute('id', this.logoId); + logoAndText.setAttribute('style', "position: fixed; margin-left:auto; margin-right:auto; width: 80px; height: 108px; background-color: white;"); - parentNode.appendChild(el); - el.setAttributeNS(null, 'id', url); + var logo = document.createElement('img') + logo.setAttribute('style', "width: 80px; height: 80px;"); - return loadSync ? - this.loadSync(exactUrl, onLoadCb, el) : - this.loadAsync(exactUrl, onLoadCb, el); - }, + var text = document.createElement('div') + text.setAttribute('style', "text-align:center; font-family: sans-serif; font-size: large; color: gray") + text.textContent = 'Loading'; - loadSync: function(url, onLoadCb, script) { - if (this.isCSS(url)) { - console.log('skipping eval for css: ' + url ); - if (typeof onLoadCb === 'function') onLoadCb(); - return; - } - var source = this.getSync(url); - try { - eval(source); - } catch(e) { - console.error('Error when loading ' + url + ': ' + e + '\n' + e.stack); - } - if (typeof onLoadCb === 'function') onLoadCb(); - }, + logoAndText.style['top'] = (this.height() / 2 - 100) + 'px' + logoAndText.style['left'] = (this.width() / 2 - 40) + 'px' + logo.src = LivelyLoader.codeBase + 'media/loading.gif'; - loadAsync: function(url, onLoadCb, script) { - if (script.namespaceURI == this.SVGNamespace) { - script.setAttributeNS(this.XLINKNamespace, 'href', url); - } else if (this.isCSS(url)) { - script.setAttribute("href",url); - if (typeof onLoadCb === 'function') onLoadCb(); // huh? - } else { - script.setAttributeNS(null, 'src', url); - } + logoAndText.appendChild(logo); + logoAndText.appendChild(text); - if (onLoadCb) script.onload = onLoadCb; - script.setAttributeNS(null, 'async', true); + this.logo = logoAndText; + + return logoAndText; }, + buildBrokenWorldMessage: function() { + var el = document.createElement('div'), + text1 = document.createTextNode('An error occurred. If the world does not load you can visit '), + text2 = document.createTextNode(' for help.'), + link = document.createElement('a'), + repairURL = Config.rootPath + 'BrokenWorldRepairSite.xhtml?brokenWorldURL=' + document.location.href; - loadCombinedModules: function(combinedFileUrl, callback, hash) { - // If several modules are combined in one file they can be loaded with this method. - // The method will ensure that all included modules are loaded. If they - // have required modules that are not included in the combined file, those will - // be loaded as well. + el.setAttribute('id', this.brokenWorldMsgId); + el.setAttribute('style', "position: fixed; margin-left:auto; margin-right:auto; padding: 5px; background-color: white; font-family: Arial,times; color: red; font-size: large-x;"); - var originalLoader = this, - combinedLoader = { - expectToLoadModules: function(listOfRelativePaths) { - // urls like http://lively-kernel.org/repository/webwerkstatt/lively/Text.js - this.expectedModuleURLs = new Array(listOfRelativePaths.length); - var i; - for (i = 0; i < listOfRelativePaths.length; i++) - this.expectedModuleURLs[i] = LivelyLoader.codeBase + listOfRelativePaths[i]; + el.style['top'] = (this.height() / 2 - 70) + 'px' + el.style['left'] = (this.width() / 2 - 290) + 'px' - // modules like lively.Text - this.expectedModules = new Array(listOfRelativePaths.length); - for (i = 0; i < listOfRelativePaths.length; i++) { - var moduleName = listOfRelativePaths[i].replace(/\//g, '.'); - moduleName = moduleName.replace(/\.js$/g, ''); - this.expectedModules[i] = moduleName; - } + link.style.color = 'red'; + link.setAttribute('href', repairURL); + link.setAttribute('target', '_blank'); + link.textContent = 'the repair page'; - // create script tags that are found when tested if a file is already loaded - this.expectedModuleURLs.forEach(function(url) { - var script = document.createElement('script'); - script.setAttribute('id', url); - document.getElementsByTagName('head')[0].appendChild(script); - }); - }, + el.appendChild(text1); + el.appendChild(link); + el.appendChild(text2); - includedInCombinedFile: function(scriptUrl) { - return this.expectedModuleURLs && this.expectedModuleURLs.indexOf(scriptUrl) >= 0; - }, + return el; + }, - loadJs: function(url) { - console.log('load file that is not in combined modules: ' + url); - if (!this.includedInCombinedFile(url)) originalLoader.loadJs(url); - }, + ensureBrokenWorldMessage: function() { + this.removeElement(this.logo); + if (!document.getElementById(this.brokenWorldMsgId)) { + document.getElementById(this.id).appendChild(this.buildBrokenWorldMessage()); + } + }, - scriptInDOM: function(url) { - return originalLoader.scriptInDOM(url) || this.includedInCombinedFile(url); - } + buildConsole: function() { + var console = document.createElement('pre'), self = this; + console.setAttribute('id', this.consoleId); + console.setAttribute('style', "position: absolute; top: 0px; font-family: monospace; color: rgb(0,255,64); font-size: medium; padding-bottom: 20px;"); + this.console = console; + if (isFireBug) return console; - }, - callCallback = function() { - window.JSLoader = originalLoader; - // FIXME - // Filter out the modules already loaded - var realModules = combinedLoader.expectedModules.select(function(ea) { - // FIXME, better now throw error in Class.forName - return !ea.include('jquery') && Class.forName(ea) !== undefined; - }); - require(realModules).toRun(callback); - }; + function addLine(str, style) { + style = style || ''; + var line = document.createElement('div'); - if (this.scriptInDOM(combinedFileUrl)) { callCallback(); return; } + // html5 does not support cdata sections in the document. if + // the creation fails with a NOT_SUPPORTED_ERR, we simply + // create a text node. + var textElement; + try { + textElement = document.createCDATASection(str); + } catch (e) { + if (e.name === "NOT_SUPPORTED_ERR") { + textElement = document.createTextNode(str); + } else { + throw e; + } + } - // while loading the combined file we replace the loader - JSLoader = combinedLoader; + line.appendChild(textElement); + line.setAttribute('style', style); + console.appendChild(line); + if (console.parentNode && line.scrollIntoViewIfNeeded) + line.scrollIntoViewIfNeeded() + } - this.loadJs(combinedFileUrl, callCallback, undefined, undefined, hash); + this.consoleProxy = { + log: function(msg) { addLine(msg) }, + warn: function(msg) { addLine(msg, 'color: yellow;') }, + error: function(msg) { + if (!console.parentNode) self.toggleConsole(); + addLine(msg, 'color: red; font-size: large;'); + self.ensureBrokenWorldMessage(); + } + }; + + window.console.addConsumer(this.consoleProxy) + + return console; }, - loadAll: function(urls, cb) { - urls.reverse().reduce(function(loadPrevious, url) { - return function() { JSLoader.loadJs(url, loadPrevious); }; - }, function() { cb && cb(); })(); + removeConsole: function() { + var console = this.console; + this.console = null; + if (!console || isFireBug) return; + this.removeElement(console); + if (!this.consoleProxy) return + window.console.removeConsumer(this.consoleProxy) + this.consoleProxy = null; }, - resolveAndLoadAll: function(baseURL, urls, cb) { - for (var i = 0; i < urls.length; i++) { - urls[i] = baseURL + urls[i]; + toggleConsole: function() { + if (!this.console) this.buildConsole(); + if (this.console.parentNode) { + this.removeElement(this.console); + } else { + this.domElement.appendChild(this.console); } - return this.loadAll(urls, cb); }, - findParentScriptNode: function() { - var node = document.getElementsByTagName('head')[0]; - if (!node) throw new Error('Cannot find parent node for scripts'); - return node; + buildConsoleButton: function() { + var a = document.createElement('a'); + a.setAttribute('style', "position: fixed; right: 170px; top: 20px; width: 70px; text-align:center; font-family: monospace; border: 1px solid; border-color: rgb(100,100,100); color: rgb(100,100,100)"); + a.setAttribute('href', 'javascript:LoadingScreen.toggleConsole()'); + a.textContent = 'console'; + return a; }, - - getLinkAttribute: function(el) { - return el.getAttributeNS(this.XLINKNamespace, 'href') || el.getAttribute('src'); + buildCloseButton: function() { + var a = document.createElement('a'); + a.setAttribute('style', "position: fixed; right: 90px; top: 20px; width: 70px; text-align:center; font-family: monospace; border: 1px solid; border-color: rgb(100,100,100); color: rgb(100,100,100)"); + a.setAttribute('href', 'javascript:LoadingScreen.remove();'); + a.textContent = 'close'; + return a; }, - getScripts: function() { return document.getElementsByTagName('script'); }, - - scriptInDOM: function(url) { return this.scriptsThatLinkTo(url).length > 0; }, + build: function() { + var background = this.buildBackground(), + loadingLogo = this.buildLoadingLogo(), + consoleButton = this.buildConsoleButton(), + closeButton = this.buildCloseButton(), + console = this.buildConsole(); - scriptsThatLinkTo: function(url) { - var scriptsFound = [], - allScripts = this.getScripts(); - for (var i = 0; i < allScripts.length; i++) { - if (this.scriptElementLinksTo(allScripts[i], url)) { - scriptsFound.push(allScripts[i]); - } - } - return scriptsFound; - }, - - removeQueries: function(url) { return url.split('?')[0]; }, - - resolveURLString: function(urlString) { - // FIXME duplicated from URL class in lively. Network - // actually lively.Core should require lively.Network -- but lively.Network indirectly - // lively.Core ====>>> FIX that!!! - var result = urlString; - // resolve .. - do { - urlString = result; - result = urlString.replace(/\/[^\/]+\/\.\./, ''); - } while (result != urlString); - // foo//bar --> foo/bar - result = result.replace(/([^:])[\/]+/g, '$1/'); - // foo/./bar --> foo/bar - result = result.replace(/\/\.\//g, '/'); - return result; - }, - - scriptElementLinksTo: function(el, url) { - if (!el.getAttribute) return false; - // FIXME use namespace consistently - if (el.getAttribute('id') == url) return true; - var link = this.getLinkAttribute(el); - if (!link) return false; - if (url == link) return true; - var linkString = this.makeAbsolute(link), - urlString = this.makeAbsolute(url); - return linkString == urlString; - }, - - currentDir: function() { - return this.dirOfURL(document.location.href.toString()); - }, - - dirOfURL: function(url) { - return this.removeQueries(url).substring(0, url.lastIndexOf('/') + 1); - }, - - makeAbsolute: function(urlString) { - urlString = this.removeQueries(urlString); - if (!urlString.match(/^http/)) { - // make absolute - urlString = this.currentDir() + urlString; - } - return this.resolveURLString(urlString); - }, - - makeUncached: function(urlString, cacheQuery) { - cacheQuery = cacheQuery || new Date().getTime(); - return urlString + (urlString.indexOf('?') == -1 ? '?' : '&') + cacheQuery; - }, + background.appendChild(loadingLogo); + background.appendChild(consoleButton); + background.appendChild(closeButton); - removeAllScriptsThatLinkTo: function(url) { - var scripts = this.scriptsThatLinkTo(url); - for (var i = 0; i < scripts.length; i++) { - scripts[i].parentNode.removeChild(scripts[i]); - } + return background; }, - getSyncReq: function(url, forceUncached) { - if (typeof WebResource !== "undefined") { - var webR = new WebResource(url); - if (forceUncached) webR.forceUncached(); - var webRGet = webR.get(); - return { - status: webRGet.status.code(), - responseText: webRGet.content - }; - } - - if (typeof jsUri == "function") { - // a hack to prevent svg world loader from failing - // (jsuri is not inserted in combined modules) - var uri = new jsUri(url), - cBase = new jsUri(document.location.href); - if (uri.host() != cBase.host()){ // would violate the SOP of the browser - - var uriHost = uri.host(), - uriPort = uri.port(); - uri.setHost(cBase.host()); - uri.setPort(cBase.port()); - - var port = uriPort; - uri.setPath("/proxy/" + uriHost + ((uriPort)? (":" + uriPort):"") + uri.path()); - url = uri.toString(); - - // TODO take care of the equals sign that appears at the end of the url - // jsUri considers there to be one tupple of params for this url: - // http://www.lively-kernel.org/repository/webwerkstatt/lively/ide/SystemCodeBrowser.js?1305723746613 - // and puts and = sign at the end when it reconstructs the url - console.log("using proxy " + url ); - } + add: function() { + if (!this.domElement) { + this.domElement = this.build(); } - - var req = new XMLHttpRequest(); - if (forceUncached) url = this.makeUncached(url); - req.open('GET', url, false/*sync*/); - req.send(); - return req; - }, - - getSync: function(url, forceUncached) { - return this.getSyncReq(url, forceUncached).responseText; + document.body.appendChild(this.domElement); }, - getSyncStatus: function(url, forceUncached) { - return this.getSyncReq(url, forceUncached).status; + remove: function() { + this.removeConsole(); + this.removeElement(this.domElement); }, - isCSS: function(url){ - return url.match(/\.css$/) || url.match(/\.css\?/); + removeElement: function(el) { + if (el && el.parentNode) el.parentNode.removeChild(el); } }; -var LivelyLoader = { - // - // ------- generic load support ---------- - // - jqueryPath: 'lib/jquery-1_7_1.js', +function addBrowserPatches() { + // add support for commonly used JS methods in all browsers - codeBase: (function findCodeBase() { - // search for script that links to "bootstrap.js" and - // construct the codeBase path from its path - if (window.Config && Config.codeBase !== undefined) - return Config.codeBase; + var isIE = window.navigator && window.navigator.userAgent.indexOf("MSIE") > -1; + if (isIE) { + // enable IE9 mode + var meta = document.createElement('meta') + meta.setAttribute('http-equiv', "X-UA-Compatible") + meta.setAttribute('content', "IE=9") + document.getElementsByTagName('head')[0].appendChild(meta); + } - var bootstrapFileName = 'bootstrap.js', - scripts = JSLoader.getScripts(), - i = 0, node, urlFound; + (function patchJS() { + // ES 3.1 proposed static functions + // according to rationale_for_es3_1_static_object_methodsaug26.pdf on wiki.ecmascript.org + // implementation uses __defineGetter__/__proto__ logic - while (!urlFound && (node = scripts[i++])) { - var url = JSLoader.getLinkAttribute(node); - if (url && (url.indexOf(bootstrapFileName) >= 0)) urlFound = url; + if (!Object.hasOwnProperty('defineProperty') && !Object.prototype.hasOwnProperty('defineProperty')) { + Object.defineProperty = function(object, property, descriptor) { + if (typeof descriptor !== 'object') throw new TypeError(); + if (descriptor.value) { + object[String(property)] = descriptor.value; + } else { + if (descriptor.getter) + object.__defineGetter__(property, descriptor.getter); + if (descriptor.setter) + object.__defineSetter__(property, descriptor.setter); + } + return object; + }; } - if (!urlFound) { - console.warn('Cannot find codebase, have to guess...'); - return JSLoader.dirOfURL(window.location.href.toString()); + Object.defineProperties = function(object, descriptorSet) { + for (var name in descriptorSet) { + if (!descriptorSet.hasOwnProperty(name)) continue; + Object.defineProperty(object, name, descriptorSet[name]); + } + return object; } - var codeBase = JSLoader.makeAbsolute(JSLoader.dirOfURL(urlFound) + '../'); - console.log('Codebase is ' + codeBase); - - return codeBase; - })(), - - rootPath: (function findRootPath() { - - if (window.Config && Config.rootPath !== undefined) { - return Config.rootPath; + if (!Object.hasOwnProperty('getOwnPropertyDescriptor') && !Object.prototype.hasOwnProperty('getOwnPropertyDescriptor')) { + Object.defineProperties(Object, { + getOwnPropertyDescriptor: { + value: function(object, name) { + // FIXME? use $schema? + var descriptor = { enumerable: true, writable: true, flexible: true}; + var getter = object.__lookupGetter__(name); + var setter = object.__lookupSetter__(name); + if (getter || setter) { + descriptor.getter = getter; + descriptor.setter = setter; + } else { + descriptor.value = object[name]; + } + return descriptor; + } + } + }); } - if (window.Config && Config.standAlone) { - // copied from Config.getDocumentDirectory, - // Config not yet available... - var url = document.URL; - return url.substring(0, url.lastIndexOf('/') + 1); + if (!Object.hasOwnProperty('__lookupGetter__') && !Object.prototype.hasOwnProperty('__lookupGetter__')) { + Object.defineProperties(Object.prototype, { + '__lookupGetter__': { + enumerable: false, + value: function(prop) { + var propDef = Object.getOwnPropertyDescriptor(this, prop); + var protoPropDef = Object.getOwnPropertyDescriptor(this.constructor['prototype'], prop); + if (propDef) + return propDef.get; + else if (protoPropDef) + return protoPropDef.get; + else + return; + } + } + }); } - var bootstrapFileName = 'bootstrap.js', - scripts = JSLoader.getScripts(), - i = 0, node, urlFound; - - while (!urlFound && (node = scripts[i++])) { - var url = JSLoader.getLinkAttribute(node); - if (url && (url.indexOf(bootstrapFileName) >= 0)) urlFound = url; + if (!Object.hasOwnProperty('__lookupSetter__') && !Object.prototype.hasOwnProperty('__lookupSetter__')) { + Object.defineProperties(Object.prototype, { + '__lookupSetter__': { + enumerable: false, + value: function(prop) { + var propDef = Object.getOwnPropertyDescriptor(this, prop); + var protoPropDef = Object.getOwnPropertyDescriptor(this.constructor['prototype'], prop); + if (propDef) + return propDef.set; + else if (protoPropDef) + return protoPropDef.set; + else + return; + } + } + }); } - if (!urlFound) { - console.warn('Cannot find bootstrap.js, have to guess...'); - return JSLoader.dirOfURL(window.location.href.toString()); + if (!Object.hasOwnProperty('__defineGetter__') && !Object.prototype.hasOwnProperty('__defineGetter__')) { + Object.defineProperties(Object.prototype, { + '__defineGetter__': { + enumerable: false, + value: function(prop, func) { + if (!this.hasOwnProperty(prop)) this[prop] = undefined; + Object.defineProperty(this, prop, { get: func }); + } + } + }); } - var rootPath = JSLoader.makeAbsolute(urlFound.match(/(.*)core\/lively\/(.*)/)[1]); - console.log('Root path is ' + rootPath); - - return rootPath; - })(), - modulePaths: (function setModulePaths() { - if (!window.Config) { window.Config = {}; } - var defaultPaths = ['users/', 'projects/', 'documentation/', 'server/']; - if (Config.modulePaths === undefined) { - Config.modulePaths = defaultPaths; - } else { - Config.modulePaths = Config.modulePaths.concat(defaultPaths); + if (!Object.hasOwnProperty('__defineSetter__') && !Object.prototype.hasOwnProperty('__defineSetter__')) { + Object.defineProperties(Object.prototype, { + '__defineSetter__': { + enumerable: false, + value: function(prop, func) { + if (!this.hasOwnProperty(prop)) this[prop] = undefined; + Object.defineProperty(this, prop, { set: func }); + } + } + }); } - return Config.modulePaths; - })(), - - installWatcher: function(target, propName, haltWhenChanged) { - // observe slots, for debugging - var newPropName = '__' + propName; - target[newPropName] = target[propName]; - target.__defineSetter__(propName, function(v) { - target[newPropName] = v; - console.log(target.toString() + '.' + propName + ' changed: ' + v); - if (haltWhenChanged) debugger; - }); - target.__defineGetter__(propName, function() { return target[newPropName]; }); - console.log('Watcher for ' + target + '.' + propName + ' installed'); - }, - createConfigObject: function() { - // Should have addtional logic for the case when no no window object exist... - if (!window.Config) window.Config = {}; - window.Config.codeBase = this.codeBase; - window.Config.rootPath = this.rootPath; - window.Config.modulePaths = this.modulePaths; - }, + Object.defineProperties(Object, { + create: { + value: function(proto, descriptorSet) { //descriptor can be undefined + var object = {}; + object.__proto__ = proto; + Object.defineProperties(object, descriptorSet); + return object; + } + }, - bootstrap: function(thenDoFunc, isCanvas) { - this.createConfigObject(); + keys: { + value: function(object, optFast) { + if (typeof object !== 'object') throw new TypeError('not an object'); + var names = []; // check behavior wrt arrays + for (var name in object) { + if (object.hasOwnProperty(name)) + names.push(name); + } + if (!optFast) names.sort(); + return names; + } + }, - if (Config.standAlone) { thenDoFunc(); return; } + getOwnPropertyNames: { + value: function(object) { + // would be different from keys if we could access non-enumerable properties + return Object.keys(object); + } + }, - // FIXME somehow solve the canvas loading issue... - var url = document.URL, - codeBase = this.codeBase; - JSLoader.resolveAndLoadAll(codeBase, [ - this.jqueryPath, - 'lib/jsuri.js', - 'lively/Migration.js', - 'lively/JSON.js', - 'lively/miniprototype.js', - 'lively/defaultconfig.js', - 'lively/localconfig.js'], - function() { - var modules1 = [ - 'lively/Base.js', - 'lively/DOMAbstraction.js', - 'lively/OldBase.js', - 'lively/scene.js', - 'lively/Core.js' - ]; - - var modules2 = [ - 'lively/Data.js', - 'lively/Network.js', - 'lively/Text.js', - 'lively/Widgets.js', - 'lively/Storage.js', - 'lively/Tools.js', - 'lively/ide.js' - ]; - if (isCanvas) { - modules1.splice(1, 0, 'lively/EmuDom.js'); - modules1.push('lively/CanvasExptCoreFixes.js'); - modules2.push('lively/CanvasExpt.js'); + getPrototypeOf: { + value: function(object) { + if (typeof object !== 'object') throw new TypeError('type ' + (typeof object) + ' does not have a prototype'); + return object.__proto__; } + }, - JSLoader.resolveAndLoadAll(codeBase, - modules1, - function() { - if (isCanvas) Config.modulesOnWorldLoad = []; - JSLoader.resolveAndLoadAll(codeBase, modules2, thenDoFunc); - }); - }); - }, + seal: { + value: function(object) { + // prevent adding and removing properties + // in rhino only see use org.mozilla.javascript.tools.shell.Global.seal + // not implementable yet + return object; + } + }, - // - // ------- load world --------------- - // - loadMain: function(canvas, startupFunc) { - Config.loadUserConfigModule(); - require('lively.bindings', 'lively.Main').toRun(function() { - var loader = lively.Main.getLoader(canvas); - lively.bindings.connect(loader, 'finishLoading', LoadingScreen, 'remove'); - if (startupFunc) { - loader.startupFunc = startupFunc; - lively.bindings.connect(loader, 'finishLoading', loader, 'startupFunc'); + freeze: { + value: function(object) { + // like seal, but properties are read-only now + // not implementable yet + return object; + } } - loader.systemStart(canvas); - }); - }, - - startWorld: function(startupFunc) { - var canvas = this.findCanvas(document); - if (!canvas) return false; - var self = this; - LoadingScreen.add(); - this.bootstrap(function() { - self.loadMain(canvas, startupFunc); }); - return true; - }, - - findCanvas: function(doc) { - var canvas = doc.getElementById('canvas'); - if (canvas && canvas.tagName.toUpperCase() === 'SVG') return canvas; - canvas = doc.getElementById('canvas'); - if (canvas && canvas.tagName.toUpperCase() === 'DIV') return canvas; - return null; - }, + Object.defineProperties(Function.prototype, { + bind: { + value: function(self, var_args) { + var thisFunc = this; + if (arguments.length === 0) { + return function() { + return thisFunc.apply(self, arguments); + } + } + var leftArgs = Array.prototype.slice.call(arguments, 1); + return function(var_args) { + var args = leftArgs.concat(Array.prototype.slice.call(arguments, 0)); + return thisFunc.apply(self, args); + }; + } + }, - // - // ------- Canvas specific --------------- - // - startCanvasWorld: function(startupFunc) { - var canvas = this.findRealCanvas(document); - if (!canvas) return false; - var self = this; - LoadingScreen.add(); - this.bootstrap(function() { - self.loadMain(canvas, startupFunc); - }, true); - return true; - }, + // FIXME redefining, + bind: { + value: function bind() { + function cdr(iterable) { + var length = iterable.length, results = new Array(length - 1); + while (length--) results[length - 1] = iterable[length]; + return results; + } + // this is almost the prototype.js definition + if (arguments.length < 2 && arguments[0] === undefined) return this; + var __method = this, args = cdr(arguments), object = arguments[0], + wrappedFunc = function bound() { + return __method.apply(object, args.concat($A(arguments))); + } + wrappedFunc.isWrapper = true; + wrappedFunc.originalFunction = __method; + return wrappedFunc; + } + } + }); + })(); + + if (isIE) { + // support for func.name + Function.prototype.__defineGetter__('name', function() { + var source = String(this); + return source.split(/[\s\(]/g)[1]; + }) + } +} - findRealCanvas: function(doc) { - var canvas = doc.getElementById('lively.canvas'); - if (!canvas || canvas.tagName.toUpperCase() !== 'CANVAS') return null; - return canvas; - }, - startNewMorphicWorld: function(startupFunc) { - if (!window.Config || !Config.isNewMorphic) return false; - var self = this; +var JSLoader = { - LoadingScreen.add(); - this.bootstrapNewMorphicWorld(function() { - self.loadMain(document.body, startupFunc); - }); - return true; - }, + SVGNamespace: 'http:\/\/www.w3.org/2000/svg', + XLINKNamespace: 'http:\/\/www.w3.org/1999/xlink', + LIVELYNamespace: 'http:\/\/www.experimentalstuff.com/Lively', - bootstrapNewMorphicWorld: function(thenDoFunc) { - this.createConfigObject(); - var url = document.URL, - dontBootstrap = Config.standAlone || url.indexOf('dontBootstrap=true') >= 0; + loadJs: function(url, onLoadCb, loadSync, okToUseCache, cacheQuery) { + if (this.scriptInDOM(url)) { + console.log('script ' + url + ' already loaded or loading'); + return null; + } + // it's called loadJs, not loadCSS !!! + var css = this.isCSS(url); - if (dontBootstrap) { thenDoFunc(); return }; + // adapt URL + var exactUrl = url; + if ((exactUrl.indexOf('!svn') <= 0) && !okToUseCache) { + exactUrl = this.makeUncached(exactUrl, cacheQuery); + } - var codeBase = this.codeBase, - optimizedLoading = !url.match('quickLoad=false') - && !url.match('!svn') - && url.match('webwerkstatt') - && url.match('lively-kernel.org'); + // create and configure script tag + var parentNode = this.findParentScriptNode(), + xmlNamespace = parentNode.namespaceURI, + el; - if (optimizedLoading) { - console.log('optimized loading enabled'); - var hashUrl = codeBase + 'generated/combinedModulesHash.txt', - combinedModulesUrl = codeBase + 'generated/combinedModules.js', - hash = JSLoader.getSync(hashUrl, true/*uncached*/); - JSLoader.loadCombinedModules(combinedModulesUrl, thenDoFunc, hash); - return; + if (css) { + el = document.createElementNS(xmlNamespace, 'link'); + el.setAttributeNS(null, "rel", "stylesheet"); + el.setAttributeNS(null, "type", "text/css"); + } else { //assuming js + el = document.createElementNS(xmlNamespace, 'script'); + el.setAttributeNS(null, 'type', 'text/ecmascript'); } - var modules = [ - this.jqueryPath, - 'lib/jsuri.js', - 'lively/Migration.js', - 'lively/JSON.js', - 'lively/lang/Object.js', - 'lively/lang/Function.js', - 'lively/lang/String.js', - 'lively/lang/Array.js', - 'lively/lang/Number.js', - 'lively/defaultconfig.js', - 'lively/localconfig.js', - 'lively/Base.js', - 'lively/lang/Closure.js', // FIXME: require module instead - 'lively/lang/UUID.js', // FIXME: require module instead - 'lively/LocalStorage.js' // FIXME: require module instead - ]; - JSLoader.resolveAndLoadAll(codeBase, modules, thenDoFunc); + parentNode.appendChild(el); + el.setAttributeNS(null, 'id', url); + + return loadSync ? + this.loadSync(exactUrl, onLoadCb, el) : + this.loadAsync(exactUrl, onLoadCb, el); }, - startHeadless: function() { - if (!window.Config || !Config.headless) return false; - this.bootstrapHeadless(function() { console.log('Headless Lively loaded') }); - return true; + loadSync: function(url, onLoadCb, script) { + if (this.isCSS(url)) { + console.log('skipping eval for css: ' + url ); + if (typeof onLoadCb === 'function') onLoadCb(); + return; + } + var source = this.getSync(url); + try { + eval(source); + } catch(e) { + console.error('Error when loading ' + url + ': ' + e + '\n' + e.stack); + } + if (typeof onLoadCb === 'function') onLoadCb(); }, - bootstrapHeadless: function(thenDoFunc) { - this.createConfigObject(); + loadAsync: function(url, onLoadCb, script) { + if (script.namespaceURI == this.SVGNamespace) { + script.setAttributeNS(this.XLINKNamespace, 'href', url); + } else if (this.isCSS(url)) { + script.setAttribute("href",url); + if (typeof onLoadCb === 'function') onLoadCb(); // huh? + } else { + script.setAttributeNS(null, 'src', url); + } - if (Config.standAlone) { thenDoFunc(); return }; + if (onLoadCb) script.onload = onLoadCb; + script.setAttributeNS(null, 'async', true); + }, - var moduleNames = Config.onlyLoad || [], - codeBase = this.codeBase; - JSLoader.resolveAndLoadAll(codeBase, [ - this.jqueryPath, - 'lib/jsuri.js', - 'lively/Migration.js', - 'lively/JSON.js', - 'lively/miniprototype.js', - 'lively/defaultconfig.js', - 'lively/localconfig.js', - 'lively/Base.js'].concat(moduleNames), - thenDoFunc); - }, + loadCombinedModules: function(combinedFileUrl, callback, hash) { + // If several modules are combined in one file they can be loaded with this method. + // The method will ensure that all included modules are loaded. If they + // have required modules that are not included in the combined file, those will + // be loaded as well. - addPatches: function() { - var isIE = window.navigator && window.navigator.userAgent.indexOf("MSIE") > -1; - if (isIE) { - // enable IE9 mode - var meta = document.createElement('meta') - meta.setAttribute('http-equiv', "X-UA-Compatible") - meta.setAttribute('content', "IE=9") - document.getElementsByTagName('head')[0].appendChild(meta); - } + var originalLoader = this, + combinedLoader = { + expectToLoadModules: function(listOfRelativePaths) { + // urls like http://lively-kernel.org/repository/webwerkstatt/lively/Text.js + this.expectedModuleURLs = new Array(listOfRelativePaths.length); + var i, len = listOfRelativePaths.length; + for (i = 0; i < len; i++) { + this.expectedModuleURLs[i] = LivelyLoader.codeBase + listOfRelativePaths[i]; + } -(function() { -// ES 3.1 proposed static functions -// according to rationale_for_es3_1_static_object_methodsaug26.pdf on wiki.ecmascript.org -// implementation uses __defineGetter__/__proto__ logic + // modules like lively.Text + this.expectedModules = new Array(len); + for (i = 0; i < len; i++) { + var moduleName = listOfRelativePaths[i].replace(/\//g, '.'); + moduleName = moduleName.replace(/\.js$/g, ''); + this.expectedModules[i] = moduleName; + } -if (!Object.hasOwnProperty('defineProperty') && !Object.prototype.hasOwnProperty('defineProperty')) { - Object.defineProperty = function(object, property, descriptor) { - if (typeof descriptor !== 'object') throw new TypeError(); - if (descriptor.value) { - object[String(property)] = descriptor.value; - } else { - if (descriptor.getter) - object.__defineGetter__(property, descriptor.getter); - if (descriptor.setter) - object.__defineSetter__(property, descriptor.setter); - } - return object; - }; -} + // create script tags that are found when tested if a file is already loaded + this.expectedModuleURLs.forEach(function(url) { + var script = document.createElement('script'); + script.setAttribute('id', url); + document.getElementsByTagName('head')[0].appendChild(script); + }); + }, -Object.defineProperties = function(object, descriptorSet) { - for (var name in descriptorSet) { - if (!descriptorSet.hasOwnProperty(name)) continue; - Object.defineProperty(object, name, descriptorSet[name]); - } - return object; -} + includedInCombinedFile: function(scriptUrl) { + return this.expectedModuleURLs && this.expectedModuleURLs.indexOf(scriptUrl) >= 0; + }, -if (!Object.hasOwnProperty('getOwnPropertyDescriptor') && !Object.prototype.hasOwnProperty('getOwnPropertyDescriptor')) { - Object.defineProperties(Object, { - getOwnPropertyDescriptor: { - value: function(object, name) { - // FIXME? use $schema? - var descriptor = { enumerable: true, writable: true, flexible: true}; - var getter = object.__lookupGetter__(name); - var setter = object.__lookupSetter__(name); - if (getter || setter) { - descriptor.getter = getter; - descriptor.setter = setter; - } else { - descriptor.value = object[name]; + loadJs: function(url) { + console.log('load file that is not in combined modules: ' + url); + if (!this.includedInCombinedFile(url)) originalLoader.loadJs(url); + }, + + scriptInDOM: function(url) { + return originalLoader.scriptInDOM(url) || this.includedInCombinedFile(url); } - return descriptor; - } - } - }); -} -if (!Object.hasOwnProperty('__lookupGetter__') && !Object.prototype.hasOwnProperty('__lookupGetter__')) { - Object.defineProperties(Object.prototype, { - '__lookupGetter__': { - enumerable: false, - value: function(prop) { - var propDef = Object.getOwnPropertyDescriptor(this, prop); - var protoPropDef = Object.getOwnPropertyDescriptor(this.constructor['prototype'], prop); - if (propDef) - return propDef.get; - else if (protoPropDef) - return protoPropDef.get; - else - return; - } - } - }); -} + }, -if (!Object.hasOwnProperty('__lookupSetter__') && !Object.prototype.hasOwnProperty('__lookupSetter__')) { - Object.defineProperties(Object.prototype, { - '__lookupSetter__': { - enumerable: false, - value: function(prop) { - var propDef = Object.getOwnPropertyDescriptor(this, prop); - var protoPropDef = Object.getOwnPropertyDescriptor(this.constructor['prototype'], prop); - if (propDef) - return propDef.set; - else if (protoPropDef) - return protoPropDef.set; - else - return; - } - } - }); -} + callCallback = function() { + window.JSLoader = originalLoader; + // FIXME + // Filter out the modules already loaded + var realModules = combinedLoader.expectedModules.select(function(ea) { + // FIXME, better now throw error in Class.forName + return !ea.include('jquery') && Class.forName(ea) !== undefined; + }); + require(realModules).toRun(callback); -if (!Object.hasOwnProperty('__defineGetter__') && !Object.prototype.hasOwnProperty('__defineGetter__')) { - Object.defineProperties(Object.prototype, { - '__defineGetter__': { - enumerable: false, - value: function(prop, func) { - if (!this.hasOwnProperty(prop)) this[prop] = undefined; - Object.defineProperty(this, prop, { get: func }); - } - } - }); -} + }; -if (!Object.hasOwnProperty('__defineSetter__') && !Object.prototype.hasOwnProperty('__defineSetter__')) { - Object.defineProperties(Object.prototype, { - '__defineSetter__': { - enumerable: false, - value: function(prop, func) { - if (!this.hasOwnProperty(prop)) this[prop] = undefined; - Object.defineProperty(this, prop, { set: func }); - } - } - }); -} + if (this.scriptInDOM(combinedFileUrl)) { callCallback(); return; } -Object.defineProperties(Object, { - create: { - value: function(proto, descriptorSet) { //descriptor can be undefined - var object = {}; - object.__proto__ = proto; - Object.defineProperties(object, descriptorSet); - return object; - } + // while loading the combined file we replace the loader + JSLoader = combinedLoader; + + this.loadJs(combinedFileUrl, callCallback, undefined, undefined, hash); }, - keys: { - value: function(object, optFast) { - if (typeof object !== 'object') throw new TypeError('not an object'); - var names = []; // check behavior wrt arrays - for (var name in object) { - if (object.hasOwnProperty(name)) - names.push(name); - } - if (!optFast) names.sort(); - return names; - } + loadAll: function(urls, cb) { + urls.reverse().reduce(function(loadPrevious, url) { + return function() { JSLoader.loadJs(url, loadPrevious); }; + }, function() { cb && cb(); })(); }, - getOwnPropertyNames: { - value: function(object) { - // would be different from keys if we could access non-enumerable properties - return Object.keys(object); + resolveAndLoadAll: function(baseURL, urls, cb) { + for (var i = 0; i < urls.length; i++) { + urls[i] = baseURL + urls[i]; } + return this.loadAll(urls, cb); }, - getPrototypeOf: { - value: function(object) { - if (typeof object !== 'object') throw new TypeError('type ' + (typeof object) + ' does not have a prototype'); - return object.__proto__; - } + findParentScriptNode: function() { + var node = document.getElementsByTagName('head')[0]; + if (!node) throw new Error('Cannot find parent node for scripts'); + return node; }, - seal: { - value: function(object) { - // prevent adding and removing properties - // in rhino only see use org.mozilla.javascript.tools.shell.Global.seal - // not implementable yet - return object; - } + getLinkAttribute: function(el) { + return el.getAttributeNS(this.XLINKNamespace, 'href') || el.getAttribute('src'); }, - freeze: { - value: function(object) { - // like seal, but properties are read-only now - // not implementable yet - return object; - } - } -}); - -Object.defineProperties(Function.prototype, { - bind: { - value: function(self, var_args) { - var thisFunc = this; - if (arguments.length === 0) { - return function() { - return thisFunc.apply(self, arguments); - } + getScripts: function() { return document.getElementsByTagName('script'); }, + + scriptInDOM: function(url) { return this.scriptsThatLinkTo(url).length > 0; }, + + scriptsThatLinkTo: function(url) { + var scriptsFound = [], + allScripts = this.getScripts(); + for (var i = 0; i < allScripts.length; i++) { + if (this.scriptElementLinksTo(allScripts[i], url)) { + scriptsFound.push(allScripts[i]); } - var leftArgs = Array.prototype.slice.call(arguments, 1); - return function(var_args) { - var args = leftArgs.concat(Array.prototype.slice.call(arguments, 0)); - return thisFunc.apply(self, args); - }; } + return scriptsFound; }, - // FIXME redefining, - bind: { - value: function bind() { - function cdr(iterable) { - var length = iterable.length, results = new Array(length - 1); - while (length--) results[length - 1] = iterable[length]; - return results; - } - // this is almost the prototype.js definition - if (arguments.length < 2 && arguments[0] === undefined) return this; - var __method = this, args = cdr(arguments), object = arguments[0], - wrappedFunc = function bound() { - return __method.apply(object, args.concat($A(arguments))); - } - wrappedFunc.isWrapper = true; - wrappedFunc.originalFunction = __method; - return wrappedFunc; - } - } -}); -})(); + removeQueries: function(url) { return url.split('?')[0]; }, - if (isIE) { - // support for func.name - Function.prototype.__defineGetter__('name', function() { - var source = String(this); - return source.split(/[\s\(]/g)[1]; - }) - } - } -}; + resolveURLString: function(urlString) { + // FIXME duplicated from URL class in lively. Network + // actually lively.Core should require lively.Network -- but lively.Network indirectly + // lively.Core ====>>> FIX that!!! + var result = urlString; + // resolve .. + do { + urlString = result; + result = urlString.replace(/\/[^\/]+\/\.\./, ''); + } while (result != urlString); + // foo//bar --> foo/bar + result = result.replace(/([^:])[\/]+/g, '$1/'); + // foo/./bar --> foo/bar + result = result.replace(/\/\.\//g, '/'); + return result; + }, -LoadingScreen = { + scriptElementLinksTo: function(el, url) { + if (!el.getAttribute) return false; + // FIXME use namespace consistently + if (el.getAttribute('id') == url) return true; + var link = this.getLinkAttribute(el); + if (!link) return false; + if (url == link) return true; + var linkString = this.makeAbsolute(link), + urlString = this.makeAbsolute(url); + return linkString == urlString; + }, - id : 'loadingScreen', - consoleId : 'loadingConsole', - logoId: 'loadingLogo', - brokenWorldMsgId: 'loadingBrokenWorldMsg', + currentDir: function() { + return this.dirOfURL(document.location.href.toString()); + }, - width: function() { return document.documentElement.clientWidth || 800 }, - height: function() { return document.documentElement.clientHeight || 800 }, + dirOfURL: function(url) { + return this.removeQueries(url).substring(0, url.lastIndexOf('/') + 1); + }, - buildBackground: function() { - var div1 = document.createElement('div') - div1.setAttribute('id', this.id); - div1.setAttribute('style', "position: fixed; left: 0px; top: 0px; background-color: rgba(100,100,100,0.7); overflow: auto"); - div1.style.width = this.width() + 'px' - div1.style.height = this.height() + 'px' + makeAbsolute: function(urlString) { + urlString = this.removeQueries(urlString); + if (!urlString.match(/^http/)) { + // make absolute + urlString = this.currentDir() + urlString; + } + return this.resolveURLString(urlString); + }, - return div1 + makeUncached: function(urlString, cacheQuery) { + cacheQuery = cacheQuery || new Date().getTime(); + return urlString + (urlString.indexOf('?') == -1 ? '?' : '&') + cacheQuery; }, - buildLoadingLogo: function() { - var logoAndText = document.createElement('div') - logoAndText.setAttribute('id', this.logoId); - logoAndText.setAttribute('style', "position: fixed; margin-left:auto; margin-right:auto; width: 80px; height: 108px; background-color: white;"); + removeAllScriptsThatLinkTo: function(url) { + var scripts = this.scriptsThatLinkTo(url); + for (var i = 0; i < scripts.length; i++) { + scripts[i].parentNode.removeChild(scripts[i]); + } + }, - var logo = document.createElement('img') - logo.setAttribute('style', "width: 80px; height: 80px;"); + getSyncReq: function(url, forceUncached) { + if (typeof WebResource !== "undefined") { + var webR = new WebResource(url); + if (forceUncached) webR.forceUncached(); + var webRGet = webR.get(); + return { + status: webRGet.status.code(), + responseText: webRGet.content + }; + } - var text = document.createElement('div') - text.setAttribute('style', "text-align:center; font-family: sans-serif; font-size: large; color: gray") - text.textContent = 'Loading'; + var req = new XMLHttpRequest(); + if (forceUncached) url = this.makeUncached(url); + req.open('GET', url, false/*sync*/); + req.send(); + return req; + }, - logoAndText.style['top'] = (this.height() / 2 - 100) + 'px' - logoAndText.style['left'] = (this.width() / 2 - 40) + 'px' - logo.src = LivelyLoader.codeBase + 'media/loading.gif'; + getSync: function(url, forceUncached) { + return this.getSyncReq(url, forceUncached).responseText; + }, + + getSyncStatus: function(url, forceUncached) { + return this.getSyncReq(url, forceUncached).status; + }, + + isCSS: function(url){ + return url.match(/\.css$/) || url.match(/\.css\?/); + } +}; - logoAndText.appendChild(logo); - logoAndText.appendChild(text); +var LivelyLoader = { - this.logo = logoAndText; + // + // ------- generic load support ---------- + // + jqueryPath: 'lib/jquery-1_7_1.js', - return logoAndText; - }, + codeBase: (function findCodeBase() { + // search for script that links to "bootstrap.js" and + // construct the codeBase path from its path + if (window.Config && Config.codeBase !== undefined) + return Config.codeBase; - buildBrokenWorldMessage: function() { - var el = document.createElement('div'), - text1 = document.createTextNode('An error occurred. If the world does not load you can visit '), - text2 = document.createTextNode(' for help.'), - link = document.createElement('a'), - repairURL = Config.rootPath + 'BrokenWorldRepairSite.xhtml?brokenWorldURL=' + document.location.href; + var bootstrapFileName = 'bootstrap.js', + scripts = JSLoader.getScripts(), + i = 0, node, urlFound; - el.setAttribute('id', this.brokenWorldMsgId); - el.setAttribute('style', "position: fixed; margin-left:auto; margin-right:auto; padding: 5px; background-color: white; font-family: Arial,times; color: red; font-size: large-x;"); + while (!urlFound && (node = scripts[i++])) { + var url = JSLoader.getLinkAttribute(node); + if (url && (url.indexOf(bootstrapFileName) >= 0)) urlFound = url; + } - el.style['top'] = (this.height() / 2 - 70) + 'px' - el.style['left'] = (this.width() / 2 - 290) + 'px' + if (!urlFound) { + console.warn('Cannot find codebase, have to guess...'); + return JSLoader.dirOfURL(window.location.href.toString()); + } - link.style.color = 'red'; - link.setAttribute('href', repairURL); - link.setAttribute('target', '_blank'); - link.textContent = 'the repair page'; + var codeBase = JSLoader.makeAbsolute(JSLoader.dirOfURL(urlFound) + '../'); + console.log('Codebase is ' + codeBase); - el.appendChild(text1); - el.appendChild(link); - el.appendChild(text2); + return codeBase; + })(), - return el; - }, + rootPath: (function findRootPath() { - ensureBrokenWorldMessage: function() { - this.removeElement(this.logo); - if (!document.getElementById(this.brokenWorldMsgId)) { - document.getElementById(this.id).appendChild(this.buildBrokenWorldMessage()); + if (window.Config && Config.rootPath !== undefined) { + return Config.rootPath; } - }, - buildConsole: function() { - var console = document.createElement('pre'), self = this; - console.setAttribute('id', this.consoleId); - console.setAttribute('style', "position: absolute; top: 0px; font-family: monospace; color: rgb(0,255,64); font-size: medium; padding-bottom: 20px;"); - this.console = console; - if (isFireBug) return console; + if (window.Config && Config.standAlone) { + // copied from Config.getDocumentDirectory, + // Config not yet available... + var url = document.URL; + return url.substring(0, url.lastIndexOf('/') + 1); + } - function addLine(str, style) { - style = style || ''; - var line = document.createElement('div'); + var bootstrapFileName = 'bootstrap.js', + scripts = JSLoader.getScripts(), + i = 0, node, urlFound; - // html5 does not support cdata sections in the document. if - // the creation fails with a NOT_SUPPORTED_ERR, we simply - // create a text node. - var textElement; - try { - textElement = document.createCDATASection(str); - } catch (e) { - if (e.name === "NOT_SUPPORTED_ERR") { - textElement = document.createTextNode(str); - } else { - throw e; - } - } + while (!urlFound && (node = scripts[i++])) { + var url = JSLoader.getLinkAttribute(node); + if (url && (url.indexOf(bootstrapFileName) >= 0)) urlFound = url; + } - line.appendChild(textElement); - line.setAttribute('style', style); - console.appendChild(line); - if (console.parentNode && line.scrollIntoViewIfNeeded) - line.scrollIntoViewIfNeeded() + if (!urlFound) { + console.warn('Cannot find bootstrap.js, have to guess...'); + return JSLoader.dirOfURL(window.location.href.toString()); } + var rootPath = JSLoader.makeAbsolute(urlFound.match(/(.*)core\/lively\/(.*)/)[1]); + console.log('Root path is ' + rootPath); - this.consoleProxy = { - log: function(msg) { addLine(msg) }, - warn: function(msg) { addLine(msg, 'color: yellow;') }, - error: function(msg) { - if (!console.parentNode) self.toggleConsole(); - addLine(msg, 'color: red; font-size: large;'); - self.ensureBrokenWorldMessage(); - } - }; + return rootPath; + })(), - window.console.addConsumer(this.consoleProxy) + modulePaths: (function setModulePaths() { + if (!window.Config) { window.Config = {}; } + // FIXME this is webwerkstatt specific + var defaultPaths = ['users/', 'projects/', 'documentation/', 'server/']; + if (Config.modulePaths === undefined) { + Config.modulePaths = defaultPaths; + } else { + Config.modulePaths = Config.modulePaths.concat(defaultPaths); + } + return Config.modulePaths; + })(), - return console; + installWatcher: function(target, propName, haltWhenChanged) { + // observe slots, for debugging + var newPropName = '__' + propName; + target[newPropName] = target[propName]; + target.__defineSetter__(propName, function(v) { + target[newPropName] = v; + console.log(target.toString() + '.' + propName + ' changed: ' + v); + if (haltWhenChanged) debugger; + }); + target.__defineGetter__(propName, function() { return target[newPropName]; }); + console.log('Watcher for ' + target + '.' + propName + ' installed'); }, - removeConsole: function() { - var console = this.console; - this.console = null; - if (!console || isFireBug) return; - this.removeElement(console); - if (!this.consoleProxy) return - window.console.removeConsumer(this.consoleProxy) - this.consoleProxy = null; + createConfigObject: function() { + // Should have addtional logic for the case when no no window object exist... + if (!window.Config) window.Config = {}; + window.Config.codeBase = this.codeBase; + window.Config.rootPath = this.rootPath; + window.Config.modulePaths = this.modulePaths; }, - toggleConsole: function() { - if (!this.console) this.buildConsole(); - if (this.console.parentNode) { - this.removeElement(this.console); - } else { - this.domElement.appendChild(this.console); - } + // + // ------- load world --------------- + // + loadMain: function(canvas, startupFunc) { + lively.Config.loadUserConfigModule(); + require('lively.bindings', 'lively.Main').toRun(function() { + var loader = lively.Main.getLoader(canvas); + lively.bindings.connect(loader, 'finishLoading', LoadingScreen, 'remove'); + if (startupFunc) { + loader.startupFunc = startupFunc; + lively.bindings.connect(loader, 'finishLoading', loader, 'startupFunc'); + } + loader.systemStart(canvas); + }); }, - buildConsoleButton: function() { - var a = document.createElement('a'); - a.setAttribute('style', "position: fixed; right: 170px; top: 20px; width: 70px; text-align:center; font-family: monospace; border: 1px solid; border-color: rgb(100,100,100); color: rgb(100,100,100)"); - a.setAttribute('href', 'javascript:LoadingScreen.toggleConsole()'); - a.textContent = 'console'; - return a; - }, - buildCloseButton: function() { - var a = document.createElement('a'); - a.setAttribute('style', "position: fixed; right: 90px; top: 20px; width: 70px; text-align:center; font-family: monospace; border: 1px solid; border-color: rgb(100,100,100); color: rgb(100,100,100)"); - a.setAttribute('href', 'javascript:LoadingScreen.remove();'); - a.textContent = 'close'; - return a; + startNewMorphicWorld: function(startupFunc) { + if (!livelyConfigExists() || !Config.isNewMorphic) return false; + var self = this; + + LoadingScreen.add(); + this.bootstrap(function() { + self.loadMain(document.body, startupFunc); + }); + return true; }, - build: function() { - var background = this.buildBackground(), - loadingLogo = this.buildLoadingLogo(), - consoleButton = this.buildConsoleButton(), - closeButton = this.buildCloseButton(), - console = this.buildConsole(); + bootstrap: function(thenDoFunc) { + this.createConfigObject(); + var url = document.URL, + dontBootstrap = Config.standAlone || url.indexOf('dontBootstrap=true') >= 0; - background.appendChild(loadingLogo); - background.appendChild(consoleButton); - background.appendChild(closeButton); + if (dontBootstrap) { thenDoFunc(); return }; - return background; - }, + var codeBase = this.codeBase, + optimizedLoading = !url.match('quickLoad=false') + && !url.match('!svn') + && url.match('webwerkstatt') + && url.match('lively-kernel.org'); - add: function() { - if (!this.domElement) { - this.domElement = this.build(); + if (optimizedLoading) { + console.log('optimized loading enabled'); + var hashUrl = codeBase + 'generated/combinedModulesHash.txt', + combinedModulesUrl = codeBase + 'generated/combinedModules.js', + hash = JSLoader.getSync(hashUrl, true/*uncached*/); + JSLoader.loadCombinedModules(combinedModulesUrl, thenDoFunc, hash); + return; } - document.body.appendChild(this.domElement); - }, - remove: function() { - this.removeConsole(); - this.removeElement(this.domElement); - }, - - removeElement: function(el) { - if (el && el.parentNode) el.parentNode.removeChild(el); + var modules = [ + this.jqueryPath, + 'lively/Migration.js', + 'lively/JSON.js', + 'lively/lang/Object.js', + 'lively/lang/Function.js', + 'lively/lang/String.js', + 'lively/lang/Array.js', + 'lively/lang/Number.js', + 'lively/defaultconfig.js', + 'lively/localconfig.js', + 'lively/Base.js', + 'lively/lang/Closure.js', // FIXME: require module instead + 'lively/lang/UUID.js', // FIXME: require module instead + 'lively/LocalStorage.js' // FIXME: require module instead + ]; + JSLoader.resolveAndLoadAll(codeBase, modules, thenDoFunc); } + }; var EmbededLoader = { @@ -1128,7 +999,7 @@ var EmbededLoader = { isLivelyCanvas: function(el) { if (!el || !el.getAttribute) return false; var attr = this.getWorldAttributeFrom(el); - return attr != null && attr != ''; + return attr && attr != ''; }, findLivelyCanvasIn: function(element) { @@ -1148,20 +1019,24 @@ var EmbededLoader = { }; var LivelyMigrationSupport = { + // increase this value by hand if you make a change that effects object layout // LivelyMigrationSupport.migrationLevel migrationLevel: 4, documentMigrationLevel: 0, migrationLevelNodeId: 'LivelyMigrationLevel', moduleRenameDict: {}, + extractMigrationLevel: function(doc) { // LivelyMigrationSupport.extractMigrationLevel(document); var node = doc.getElementById(this.migrationLevelNodeId); return node ? Number(node.textContent) : 0; }, + setDocumentMigrationLevel: function(doc) { this.documentMigrationLevel = this.extractMigrationLevel(doc); }, + // module renaming fixModuleName: function(name) { if (/^Global\./.test(name)) name = name.substring(7/*Global.*/); @@ -1170,26 +1045,26 @@ var LivelyMigrationSupport = { } return name; }, + addModuleRename: function(oldName, newName, migrationLevel) { this.moduleRenameDict[oldName] = newName; } + }; -function startWorld(startupFunc) { - LivelyLoader.addPatches(); + +(function startWorld(startupFunc) { + addBrowserPatches(); window.addEventListener('DOMContentLoaded', function() { LivelyMigrationSupport.setDocumentMigrationLevel(document); if (EmbededLoader.embedLively() || - LivelyLoader.startCanvasWorld() || - LivelyLoader.startWorld() || - LivelyLoader.startNewMorphicWorld(startupFunc) || - LivelyLoader.startHeadless()) return; + LivelyLoader.startNewMorphicWorld(startupFunc)) return; console.warn('couldn\'t strt Lively'); }, true); window.addEventListener('beforeunload', function(evt) { - if (window.Config && window.Config.askBeforeQuit) { + if (livelyConfigExists() && window.Config.askBeforeQuit) { var msg = "Lively Kernel data may be lost if not saved."; evt.returnValue = msg; return msg; @@ -1197,6 +1072,4 @@ function startWorld(startupFunc) { return undefined; } }, true); -} - -startWorld(Config.onStartWorld); +})(livelyConfigExists() && Config.onStartWorld);