UNPKG

dreemgl

Version:

DreemGL is an open-source multi-screen prototyping framework for mediated environments, with a visual editor and shader styling for webGL and DALi runtimes written in JavaScript. As a toolkit for gpu-accelerated multiscreen development, DreemGL includes

1,788 lines (1,389 loc) 80.5 kB
/* DreemGL is a collaboration between Teeming Society & Samsung Electronics, sponsored by Samsung and others. Copyright 2015-2016 Teeming Society. 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.*/ // Micro AMD module loader for browser and node.js and basic system homogenisation library // READ FIRST // The nodejs server is CLIENT-SIDE-MIDDELWARE for prototyping and development // not a web-facing backend. // dreemGL is not optimized to be a neat NPM module, fit into browserify or express or otherwise // be a grab-and-use JS thing that respects the constraints that this would require. // Also any nodejs code is NOT deploy-safe because of the automatic RPC system and should // NOT be put webfacing as is. // And since it keeps websocket connections to all clients for live reloading // and RPC, it will rapidly stop working if faced with any large number of connections. // The RPC is by default broadcast and does NOT have cross client security considerations // A packaging solution for deploying any dreemGL to a webpage is on the todo list // Notes on the design and edgeconditions of dreemGL: // dreemGL has a decent amount of global types and values. yes. globals. // dreemGL is a prototyping toolkit, and aimed at quickly being able to do certain things // having globals helps there and most have been carefully chosen to be global. // Also all the math things are global, in math.js to be more GLSL-like in JS. // The prototypes of Float32Array are modified to add the GLSL swizzle (.xyxy) apis. // The nodeJS Module loader is hooked to allow for loading the custom AMD extension // you see in dreemGL, which fuses classes with modules. And this may have some // compatibility repercussions for some modules that assume 'define' works a certain way // If you need to require nodejs modules using the require provided by define, // use require('module') dont use './' or '/' these are interpreted as define.js modules // The browser require will ignore require('module') so if you stick to a clean // classdef it can safely load up 'nodejs' classes to inspect their interfaces for RPC // All these choices are to support the design goals of dreemGL some of which are: // - be a low cognitive overhead prototyping toolkit // - symmetrical loading of the entire 'app' in both nodejs and browser for automatic // rpc interface handling // - low jank, all rendering in JS (the timeline is super important here) // - do not transcompile anything and run in browser and node with same files // - optimized live editability and reloading of all class hierarchy parts (thats the reason // the modulesystem + classes are fused) (function define_module(){ var config_define if(typeof window !== 'undefined') config_define = window.define else if(typeof self !== 'undefined') config_define = self.define else if(typeof global !== 'undefined') config_define = global.define // the main define function function define(factory, package){ if(package !== undefined){ // precompiled version define.factory[package] = factory return } define.last_factory = factory // store for the script tag // continue calling if(define.define) define.define(factory) } if(typeof window !== 'undefined') window.define = define, define.$environment = 'browser' else if(typeof self !== 'undefined') self.define = define, define.$environment = 'worker' else if (typeof global !== 'undefined'){ Object.defineProperty(global, "define", { value: define, writable: false }) define.$environment = 'nodejs' } else define.$environment = 'v8' // default config variables define.inner = define_module define.$root = '' define.$system = '$root/system' define.local_classes = {} define.local_require_stack = [] define.ignore_reload = {} define.partial_reload = true define.reload_id = 0 // turns on debug naming of classes (very useful) define.debug = true // copy configuration onto define if(typeof config_define == 'object') for(var key in config_define){ define[key] = config_define[key] } // storage structures define.module = {} define.factory = {} // File path handling utilities define.fileName = function(file){ file = file.replace(/\\/g,'/') var file = file.slice(define.filePath(file).length) if(file.charAt(0) == '/') return file.slice(1) return file } define.filePath = function(file){ if(!file) return '' file = file.replace(/\.\//g, '') var m = file.match(/([\s\S]*)\/[^\/]*$/) return m ? m[1] : '' } define.fileExt = function(file){ // parse from the last . to end var m = file.match(/\.([^.\/]+)($|\?)/) if(!m) return '' return m[1].toLowerCase() } define.fileBase = function(file){ var fn = define.fileName(file) var idx = fn.lastIndexOf('.') if(idx !== -1) return fn.slice(0, idx) return fn } define.cleanPath = function(path){ return path.replace(/^\/+/,'/').replace(/([^:])\/+/g,'$1/') } define.joinPath = function(base, relative, rx){ if(relative.charAt(0) != '.'){ // relative is already absolute if(relative.charAt(0) == '/' || relative.indexOf(':') != -1){ return relative } var path = base + '/' + relative return define.cleanPath(path) } base = base.split(rx || /\//) relative = relative.replace(/\.\.\//g,function(){ base.pop(); return ''}).replace(/\.\//g, '') return define.cleanPath(base.join('/') + '/' + relative) } // constrain the path to any $symbol/ directory define.safePath = function(name){ name = name.replace(/\\/g,'/') var id = name.indexOf('..') if(id !== -1){ var base = name.slice(0,id) var rel= name.slice(id) var path = define.joinPath(base, rel) if(path.indexOf('..') !== -1) return undefined if(path.indexOf('./') !== -1) return undefined if(path.charAt(0)!=='$') return undefined return path } if(name.charAt(0) !== '$') return undefined if(name.indexOf('./') !== -1) return undefined return name } define.expandVariables = function(str){ return define.cleanPath(str.replace(/(\$[a-zA-Z0-9]+[a-zA-Z0-9]*)/g, function(all, lut){ if(!(lut in define)){ throw new Error("Cannot find " + lut + " used in require of " + Object.keys(define)) } return define.expandVariables(define[lut]) })) } define.lookupFileType = function(type){ type = type.toLowerCase() if(type === 'json') return 'json' if(type === 'txt' || type === 'obj' || type === 'text' || type === 'md') return 'text' return 'arraybuffer' } define.processFileType = function(type, blob){ if(type === 'glf') return define.parseGLF(blob) return blob } /* define.global = function(object){ var glob = typeof process !== 'undefined'? global: typeof window !=='undefined'? window: self for(var key in object){ glob[key] = object[key] } } */ // returns a class dir you can use, has / appended already define.classPath = function(cls){ if(cls.prototype) cls = cls.prototype var mod = cls.constructor.module var fn = mod.filename.replace(/\\/g,'/') for(var key in define.paths){ var path = define.expandVariables(define['$'+key]) if(fn.indexOf(path) === 0){ // Return the class path as a symbol base var ext = fn.slice(path.length) return define.filePath('$'+key+(ext.charAt(0)!=='/'?'/':'')+ext) + '/' } } } define.deferPromise = function(){ var res, rej var prom = new define.Promise(function(ires, irej){ res = ires, rej = irej }) prom.resolve = res prom.reject = rej return prom } // require implementation define.localRequire = function(base_path, from_file){ function require(dep_path, ext){ // skip nodejs style includes var abs_path = define.joinPath(base_path, define.expandVariables(dep_path)) if(!ext && !define.fileExt(abs_path)) abs_path = abs_path + '.js' // lets look it up var module = define.module[abs_path] if(module) return module.exports // otherwise lets initialize the module var factory = define.factory[abs_path] if(!factory && dep_path.indexOf('$') === -1 && dep_path.charAt(0) !== '.'){ //console.log(abs_path) //console.log('skipping', dep_path) return null } // lets reverse our path module = {exports:{}, factory:factory, id:abs_path, filename:abs_path} define.module[abs_path] = module if(factory === null) return null // its not an AMD module, but accept that if(!factory) throw new Error("Cannot find factory for module (file not found): " + dep_path + " > " + abs_path) // call the factory if(typeof factory == 'function'){ var localreq = define.localRequire(define.filePath(abs_path), abs_path) localreq.module = module define.local_require_stack.push(localreq) try{ var ret = factory.call(module.exports, localreq, module.exports, module) } finally{ define.local_require_stack.pop() } if(ret !== undefined) module.exports = ret } else module.exports = factory // post process hook if(define.atModule) define.atModule(module) return module.exports } require.loaded = function(path, ext){ var dep_path = define.joinPath(base_path, define.expandVariables(path)) if(define.factory[dep_path]){ return true } } require.async = function(path, ext){ var dep_path = define.joinPath(base_path, define.expandVariables(path)) return new define.Promise(function(resolve, reject){ if(define.factory[dep_path]){ // if its already asynchronously loading.. var module = require(path, ext) return resolve(module) } define.loadAsync(dep_path, from_file, ext).then(function(){ var module = require(path, ext) resolve(module) }, reject) }) } require.reloadAsync = function(path){ return new define.Promise(function(resolve, reject){ define.reload_id++ // lets wipe the old module var old_module = define.module[path] var old_factory = define.factory[path] define.module[path] = define.factory[path] = undefined // lets require it async define.require.async(path).then(function(new_class){ // fetch all modules dependent on this class, and all dependent on those // and cause them to reinitialize function wipe_module(name){ //console.log("Reloading "+define.fileName(name)) for(var key in define.factory){ var fac = define.factory[key] if(!fac) continue var deps = fac.deps if(key !== name && define.module[key] && deps && deps.indexOf(name) !== -1){ // remove module define.module[key] = undefined // try to wipe all modules that depend our this one wipe_module(key) } } } wipe_module(path) resolve(define.module[path]) }).catch(function(error){ define.module[path] = old_module define.factory[path] = old_factory reject(error) }) }) } return require } define.require = define.localRequire('','root') define.findRequiresInFactory = function(factory, req){ var search = factory.toString() if(factory.body) search += '\n' + factory.body.toString() if(factory.depstring) search += '\n' + factory.depstring.toString() req = req || [] // bail out if we redefine require if(search.match(/function\s+require/) || search.match(/var\s+require/)){ return req } search.replace(/\/\*[\s\S]*?\*\//g,'').replace(/([^:]|^)\/\/[^\n]*/g,'$1').replace(/require\s*\(\s*["']([^"']+)["']\s*\)/g, function(m,path){ req.push(path) }) // fetch string baseclasses for nested classes and add them var baserx = new RegExp(/define\.class\s*\(\s*(?:this\s*,\s*['"][$_\w]+['"]\s*,\s*)?(?:['"]([^"']+)['"]\s*,\s*)function/g) while((result = baserx.exec(search)) !== null) { req.push(result[1]) } return req } define.buildClassArgs = function(fn){ // Ideally these regexps are better, not vastly slower but maybe proper specwise matching for stuff, its a bit rough now // This is otherwise known as a 'really bad idea'. However this makes the modules work easily, with a relatively small collision risk. var str = fn.toString() str = str.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/[^\n]*/g, '') var result; var output = [] var result = str.match(/function\s*[$_\w]*\s*\(([$_\w,\s]*)\)/) var map = result[1].split(/\s*,\s*/) for(var i = 0; i<map.length; i++) if(map[i] !== '') output.push(map[i].trim()) // now fetch all the fast classdeps var matchrx = new RegExp(/define\.class\s*\(\s*(?:this\s*,\s*['"][$_\w]+['"]\s*,\s*)?(?:(?:['"][[^"']+['"]|[$_\w]+)\s*,\s*)?function\s*[$_\w]*\s*\(([$_\w,\s]*)\)\s*\{/g) while((result = matchrx.exec(str)) !== null) { output.push('$$') var map = result[1].split(/\s*,\s*/) for(var i = 0; i<map.length; i++)if(map[i] !== '') output.push(map[i]) } return output } // Class implementation define.builtinClassArgs = { exports:1, module:2, require:3, constructor:1, baseclass:5, outer:6 } define.walkClassArgs = function(map, callback){ var path = './' for(var i = 0; i < map.length; i++){ var arg = map[i] var builtin = define.builtinClassArgs[arg] if(builtin){ callback(builtin, undefined, i) continue } if(arg === '$$'){ path = './' } else if(arg.charAt(0) === '$'){ if(arg.charAt(arg.length - 1) === '$'){ // $blabla$ path = '$' + arg.slice(1).replace(/\$/g, '/') } else{ if(arg.charAt(1) ==='$'){ callback(undefined, './' + arg.slice(2).replace(/\$/g,'/'), i) // local absolute path } else{ callback(undefined, '$' + arg.slice(1).replace(/\$/g,'/'), i) // absolute path } } } // relative path processor else if(arg.charAt(arg.length - 1) === '$'){ path += arg.replace(/\$/g,'/') } else{ // callback callback(undefined, path + arg.replace(/\$/g,'/'), i) } } } define.applyBody = function(body, Constructor, baseclass, require){ if(typeof body == 'object' && body){ for(var key in body) Constructor.prototype[key] = body[key] return } if(typeof body !== 'function') return // named arguments for the class body var classargs = body.classargs if(!classargs) classargs = body.classargs = define.buildClassArgs(body) // allright lets go figure out our body arguments. var args = [] this.walkClassArgs(classargs, function(builtin, path, i){ if(builtin){ if(builtin === 1) args[i] = Constructor else if(builtin === 2) args[i] = Constructor.module else if(builtin === 3){ if(!require) throw new Error('You cant get require on the class body as argument here') args[i] = require } else if(builtin === 4) args[i] = Constructor.prototype else if(builtin === 5) args[i] = baseclass? baseclass.prototype: undefined else if(builtin === 6) args[i] = body.outer } else{ args[i] = require(path) args[i].nested_module = Constructor.module } }) Object.defineProperty(Constructor, 'body', {value:body}) body.class_args = args return body.apply(Constructor.prototype, args) } define.EnvironmentStub = function(dep){ this.dep = dep } define.makeClass = function(baseclass, body, require, module, nested_module, outer_this, in_name){ function MyConstructor(){ // if called without new, just do a new var obj = this if(!(obj instanceof MyConstructor)){ var constructor = define.atConstructor? define.atConstructor(MyConstructor, arguments[0]): MyConstructor obj = Object.create(constructor.prototype) Object.defineProperty(obj, 'constructor', {value:constructor}) } var outer = MyConstructor.outer // pass on the classroot property if(outer !== undefined){ if(obj.outer === undefined) obj.outer = outer } if(obj._atConstructor) obj._atConstructor.apply(obj, arguments) if(obj.atConstructor){ obj.atConstructor.apply(obj, arguments) } return obj } if(define.debug){ var fnname if(in_name){ fnname = in_name } else if(body && (body.classname || body.name)){ fnname = (body.classname || body.name) } else if(module){ fnname = define.fileBase(module.filename).replace(/\.|\-|\s/g,'_')//.replace(/\.js/g,'').replace(/\./g,'_').replace(/\//g,'_') } else{ // lets make an fnname based on our callstack var origin = new Error().stack.split(/\n/)[3].match(/\/([a-zA-Z0-9\.]+)\:(\d+)\:\d+\)/) if(!origin || origin[1] === 'define.js'){ fnname = 'extend' if(baseclass && baseclass.prototype.constructor) fnname += '_' + baseclass.prototype.constructor.name } else fnname = origin[1].replace(/\.js/g,'').replace(/\.|\-|\s/g,'_').replace(/\//g,'_') + '_' + origin[2] } var code = 'return ' + MyConstructor.toString().replace(/MyConstructor/g, fnname) var Constructor = new Function(code)() } else{ var Constructor = MyConstructor } var final_at_extend = Array.isArray(body)? body: [] if(baseclass){ Constructor.prototype = Object.create(baseclass.prototype) Object.defineProperty(Constructor.prototype, 'constructor', {value:Constructor}) } Object.defineProperty(Constructor, 'extend', {value:function(body, outer_this, in_name){ //if(this.prototype.constructor === define.StubbedClass) return define.StubbedClass return define.makeClass(this, body, require, undefined, this.nested_module, outer_this, in_name) }}) Object.defineProperty(Constructor, 'overlay', {value:function(body){ return define.applyBody(body, this, baseclass) }}) Object.defineProperty(Constructor, 'mixin', {value:function(body){ var obj = body if(typeof body === 'function') obj = body.prototype var out = this.prototype for(var key in obj){ out[key] = obj[key] } }}) Object.defineProperty(Constructor, 'body', {value:body}) if(outer_this) Constructor.outer = outer_this if(Array.isArray(body)){ if(Constructor.prototype.atExtend) body.push(Constructor.prototype) } else{ if(module){ if(body && body.mixin) module.exports = Constructor.prototype else module.exports = Constructor Object.defineProperty(Constructor, 'module', {value:module}) define.applyBody(body, Constructor, baseclass, require) } else if(nested_module){ Object.defineProperty(Constructor, 'module', {value:nested_module}) define.applyBody(body, Constructor, baseclass) } else { define.applyBody(body, Constructor, baseclass) } if(Constructor.prototype.atExtend) Constructor.prototype.atExtend() // call atExtend on nested classes so outer class bodies can apply properties on nested classes if(final_at_extend.length){ for(var i = 0; i < final_at_extend.length; i++){ final_at_extend[i].atExtend() } } } return Constructor } define.mixin = function(body, body2){ if(typeof body2 === 'function') body = body2 body.mixin = true return define.class.apply(define, arguments) } define.packagedClass = function(packaged, args){ args[args.length - 1].packaged = packaged define.class.apply(define, args) } // define.class('base', function(){}) 2 // define.class(function(){}) 1 // define.class(this, 'prop', 'base', function(){}) 4 define.class = function(){ // lets make a class var base_class var body var is_inner if(arguments.length >= 3){ // inner class is_inner = true var outer_this = arguments[0] var classname = arguments[1] Object.defineProperty(outer_this, classname, { get:function(){ var cls = this['_' + classname] if(cls) cls.outer = this return cls }, set:function(value){ // lets kick the class off if(this.atInnerClassAssign) return this.atInnerClassAssign(classname, value) // default if(value === undefined || value === null || value === 0){ this['_' + classname] = undefined if(this.atInnerClassAssign) this.atInnerClassAssign(classname, undefined) return } if(typeof value === 'function' && Object.getPrototypeOf(value.prototype) !== Object.prototype){ this['_' + classname] = value return } // otherwise use it as an extend var cls = this['_' + classname] // ok so the problem here is, that if we are inherited this['_' + classname] = cls.extend(value, this) } }) if(arguments.length>3){ base_class = arguments[2] body = arguments[3] } else{ body = arguments[2] } if(typeof body === 'function'){ body.classname = classname body.outer = outer_this } } else if(arguments.length > 1){ // class with baseclass base_class = arguments[0] body = arguments[1] } else{ body = arguments[0] } function moduleFactory(require, exports, module){ var base if(typeof base_class === 'string') base = require(base_class) else if (base_class) base = base_class define.makeClass(base, body, require, module, undefined, outer_this) } // make an argmap body.classargs = define.buildClassArgs(body) // lets parse the named argument pattern for the body moduleFactory.body = body moduleFactory.deps = [] // put the baseclass on the deps if(base_class && typeof base_class === 'string'){ moduleFactory.baseclass = define.expandVariables(base_class) moduleFactory.deps.push(define.expandVariables(base_class)) moduleFactory.depstring = 'require("' + base_class + '")' } // add automatic requires if(body.classargs){ define.walkClassArgs(body.classargs, function(builtin, requirepath){ if(builtin) return if(!moduleFactory.depstring) moduleFactory.depstring = '' moduleFactory.deps.push(define.expandVariables(requirepath)) moduleFactory.depstring += 'require("' + requirepath + '")' if(!base_class) base_class = requirepath }) } // if we have a local_require_stack we are a define inside a class or module body // so then treat it as a local class if(define.local_require_stack.length){ var outer_require = define.local_require_stack[define.local_require_stack.length - 1] var outer_module = outer_require.module var module = {exports:{}, filename:outer_module.filename, factory:moduleFactory, outer:outer_module} moduleFactory(outer_require, module.exports, module) if(outer_this){ outer_this['_' + classname] = module.exports if(outer_this.atInnerClassAssign) outer_this.atInnerClassAssign(classname, module.exports) if(!outer_this.hasOwnProperty('_inner_classes')) outer_this._inner_classes = Object.create(outer_this._inner_classes || {}) outer_this._inner_classes[classname] = module.exports } return module.exports } //if(typeof arguments[arguments.length - 1] == 'string'){ // packaged // define(moduleFactory, arguments[arguments.length - 1]) //} //else{ // unpackaged define(moduleFactory, body.packaged) //} } // Implementation of a debug promise define.debugPromiseLib = function(exports){ // Use polyfill for setImmediate for performance gains var asap = Promise.immediateFn || (typeof setImmediate === 'function' && setImmediate) || function(fn) { setTimeout(fn, 1); } // Polyfill for Function.prototype.bind function bind(fn, thisArg) { return function() { fn.apply(thisArg, arguments) } } var isArray = Array.isArray || function(value) { return Object.prototype.toString.call(value) === "[object Array]" } function Promise(fn) { if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new') if (typeof fn !== 'function') throw new TypeError('not a function') this._state = null this._value = null this._deferreds = [] doResolve(fn, bind(resolve, this), bind(reject, this)) } function handle(deferred) { var me = this if (this._state === null) { this._deferreds.push(deferred) return } asap(function() { var cb = me._state ? deferred.onFulfilled : deferred.onRejected if (cb === null) { (me._state ? deferred.resolve : deferred.reject)(me._value) return } var ret; //try { ret = cb(me._value) //} //catch (e) { // deferred.reject(e) // return; //} deferred.resolve(ret) }) } function resolve(newValue) { //try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure if (newValue === this) throw new TypeError('A promise cannot be resolved with itself.') if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { var then = newValue.then if (typeof then === 'function') { doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this)) return; } } this._state = true this._value = newValue finale.call(this) //} catch (e) { reject.call(this, e); } } function reject(newValue) { this._state = false this._value = newValue finale.call(this) } function finale() { for (var i = 0, len = this._deferreds.length; i < len; i++) { handle.call(this, this._deferreds[i]) } this._deferreds = null } function Handler(onFulfilled, onRejected, resolve, reject){ this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null this.onRejected = typeof onRejected === 'function' ? onRejected : null this.resolve = resolve this.reject = reject } function doResolve(fn, onFulfilled, onRejected) { var done = false; try { fn(function (value) { if (done) return done = true onFulfilled(value) }, function (reason) { if (done) return done = true onRejected(reason) }) } catch (ex) { if (done) return done = true onRejected(ex) } } Promise.prototype['catch'] = function (onRejected) { return this.then(null, onRejected); } Promise.prototype.then = function(onFulfilled, onRejected) { var me = this; return new Promise(function(resolve, reject) { handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject)) }) } Promise.all = function () { var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments) return new Promise(function (resolve, reject) { if (args.length === 0) return resolve([]) var remaining = args.length function res(i, val) { //try { if (val && (typeof val === 'object' || typeof val === 'function')) { var then = val.then if (typeof then === 'function') { then.call(val, function (val) { res(i, val) }, reject) return } } args[i] = val if (--remaining === 0) { resolve(args) } //} catch (ex) { // reject(ex) //} } for (var i = 0; i < args.length; i++) { res(i, args[i]) } }) } Promise.resolve = function (value) { if (value && typeof value === 'object' && value.constructor === Promise) { return value } return new Promise(function (resolve) { resolve(value) }) } Promise.reject = function (value) { return new Promise(function (resolve, reject) { reject(value) }) } Promise.race = function (values) { return new Promise(function (resolve, reject) { for(var i = 0, len = values.length; i < len; i++) { values[i].then(resolve, reject) } }) } return Promise } // check if we are in debug mode define.Promise = define.debugPromiseLib() define.startLoader = function(){ } define.endLoader = function(){ } define.getReloadID = function(){ var num = define.reload_id var s = '' while(num%26){ s += String.fromCharCode(num%26 +97) num = Math.floor(num/26) } return s } // Packaged function define_packaged(){ define.require = define.localRequire('') } // Browser function define_browser(){ // browser implementation // if define was already defined use it as a config store // storage structures define.cputhreads = navigator.hardwareConcurrency || 2 define.download_queue = {} define.script_tags = {} // the require function passed into the factory is local var app_root = define.filePath(window.location.href) define.getModule = function(name){ var expanded = define.expandVariables(name) return define.module[expanded] } // loadAsync is the resource loader define.loadAsync = function(files, from_file, inext){ function loadResource(url, from_file, recurblock, module_deps){ var ext = inext === undefined ? define.fileExt(url): inext; var abs_url, fac_url if(url.indexOf('http:') === 0 || url.indexOf('https:') === 0){ // we are fetching a url.. fac_url = url abs_url = define.$root + '/proxy?' + encodeURIComponent(url) } else{ abs_url = define.expandVariables(url) if(!ext) ext = 'js', abs_url += '.' + ext fac_url = abs_url } if(define.reload_id) abs_url += '?' + define.getReloadID() if(module_deps && module_deps.indexOf(fac_url) === -1) module_deps.push(fac_url) if(define.factory[fac_url]) return new define.Promise(function(resolve){resolve()}) var prom = define.download_queue[abs_url] if(prom){ if(recurblock) return new define.Promise(function(resolve){resolve()}) return prom } if(ext === 'js'){ prom = loadScript(fac_url, abs_url, from_file) } else if(ext === 'jpg' || ext === 'jpeg' || ext === 'gif' || ext === 'png'){ prom = loadImage(fac_url, abs_url, from_file) } else prom = loadXHR(fac_url, abs_url, from_file, ext) define.download_queue[abs_url] = prom return prom } function loadImage(facurl, url, from_file){ return new define.Promise(function(resolve, reject){ var img = new Image() img.src = url img.onerror = function(){ var err = "Error loading " + url + " from " + from_file reject(err) } img.onload = function(){ define.factory[facurl] = img resolve(img) } }) } function loadXHR(facurl, url, from_file, type){ return new define.Promise(function(resolve, reject){ var req = new XMLHttpRequest() // todo, make type do other things req.responseType = define.lookupFileType(type) req.open("GET", url, true) req.onerror = function(){ var err = "Error loading " + url + " from " + from_file console.error(err) reject(err) } req.onreadystatechange = function(){ if(req.readyState == 4){ if(req.status != 200){ var err = "Error loading " + url + " from " + from_file console.error(err) return reject(err) } var blob = define.processFileType(type, req.response) define.factory[facurl] = blob // do a post process on the file type resolve(blob) } } req.send() }) } // insert by script tag function loadScript(facurl, url, from_file){ return new define.Promise(function(resolve, reject){ var script = document.createElement('script') var base_path = define.filePath(url) define.script_tags[location.origin + url] = script script.type = 'text/javascript' //define.script_tags[url] = script window.onerror = function(error, url, line){ var script = define.script_tags[url] if(script) script.onerror(error, url, line) } function onLoad(){ //for(var key in this)console.log(keys) //console.log("ONLOAD!", Object.keys(this)) if(this.rejected) return // pull out the last factor var factory = define.last_factory define.factory[facurl] = factory if(!factory) return reject("Factory is null for "+url+" from file "+from_file + " : " + facurl) var module_deps = factory.deps = [] define.last_factory = undefined // parse the function for other requires Promise.all(define.findRequiresInFactory(factory).map(function(path){ // ignore nodejs style module requires if(path.indexOf('$') === -1 && path.charAt(0) !== '.'){ return null } var dep_path = define.joinPath(base_path, define.expandVariables(path)) return loadResource(dep_path, url, true, module_deps) })).then(function(){ resolve(factory) reject = undefined }, function(err){ reject(err) }) } script.onerror = function(exception, path, line){ var error = "Error loading " + url + " from " + from_file console.error(error) this.rejected = true if(reject) reject({error:error, exception:exception, path:path, line:line}) else{ define.showException({error:error, exception:exception, path:path, line:line}) } } script.onload = onLoad script.onreadystatechange = function(){ console.log(script.readyState) if(script.readyState == 'loaded' || script.readyState == 'complete') onLoad() } define.in_body_exec = false script.src = url document.getElementsByTagName('head')[0].appendChild(script) }) } if(Array.isArray(files)){ return Promise.all(files.map(function(file){ return loadResource(file, from_file) })) } else return loadResource(files, from_file) } // make it available globally window.define = define define.hideException = function(){ if(define.exception_div){ define.exception_div.parentNode.removeChild(define.exception_div) define.exception_div = undefined } } define.showException = function(exc){ if(Object.keys(exc).length === 0) exc = {error:exc.stack, exception:exc.toString(), path:"", line:""} // lets append the div var div = define.exception_div = document.createElement('div') div.style.cssText ='position:absolute;left:10;top:10;padding:30px;background-color:white;border-radius:10px;border:2px dotted #ffc0c0;color:#202020;margin:20px;margin-left:20px;font-size:14pt;font-family:arial, helvetica;' div.innerHTML = "<b>DreemGL has encountered a problem!</b><br/>"+exc.error+"<br/><div>"+exc.exception+"<br/><br/><div style='color:black'><a href='view-source:"+exc.path+"#"+exc.line+"'>in "+exc.path+" on line "+exc.line+"</a></div>" document.body.appendChild(div) } // boot up using the MAIN property if(define.main){ define.loadAsync(define.main, 'main').then(function(){ if(define.atMain) setTimeout(function(){ define.atMain(define.require, define.main) },0) }, function(exc){ if(define.atException) define.atException(exc) else{ define.showException(exc) } }) } var backoff = 1 define.autoreloadConnect = function(){ if(this.reload_socket){ this.reload_socket.onclose = undefined this.reload_socket.onerror = undefined this.reload_socket.onmessage = undefined this.reload_socket.onopen = undefined this.reload_socket.close() this.reload_socket = undefined } this.reload_socket = new WebSocket((location.href.indexOf('https') === 0?'wss://':'ws://') + location.host) this.reload_socket.onopen = function(){ backoff = 1 } this.reload_socket.onerror = function(){ } this.reload_socket.onclose = function(){ if((backoff*=2) > 1000) backoff = 1000 setTimeout(function(){ define.autoreloadConnect() }, backoff) } this.reload_socket.onmessage = function(event){ var msg = JSON.parse(event.data) if (msg.type === 'filechange'){ if(define.ignore_reload && define.ignore_reload[msg.file]) return console.log("Ignoring file change on "+msg.file) var old_module = define.module[msg.file] define.hideException() if(define.partial_reload && old_module && typeof old_module.exports === 'function'){ define.require.reloadAsync(msg.file).then(function(){ if(define.atMain) define.atMain(define.require, define.main) }).catch(function(exception){ define.showException(exception) }) } else {//if (old_module){ //alert('filechange!' + msg.file) console.clear() location.href = location.href // reload on filechange } } else if (msg.type === 'close') { window.close() // close the window } else if (msg.type === 'delay') { // a delay refresh message console.log('Got delay refresh from server!'); setTimeout(function() { console.clear() location.href = location.href }, 1500) } } } if (define.$autoreloadConnect !== false) { define.autoreloadConnect() } } // NodeJS function define_nodejs(){ // nodeJS implementation module.exports = global.define = define //define.$root = define.filePath(process.mainModule.filename.replace(/\\/g,'/')) define.$root = __dirname.substring(0, __dirname.length - '/system/base'.length) var http = require("http") var url = require("url") var fs = require("fs") var path = require("path") var root = define.expandVariables(define.$root) define.makeCacheDir = function(name){ var cache_dir = path.join(root+'/cache') if(!fs.existsSync(cache_dir)) fs.mkdirSync(cache_dir) var cache_node = path.join(root+'/cache/'+name) if(!fs.existsSync(cache_node)) fs.mkdirSync(cache_node) return cache_node } var cache_path_root = define.makeCacheDir('node') define.mapToCacheDir = function(name){ return cache_path_root + url.parse(define.expandVariables(name)).path } define.getModule = function(name){ var expanded = define.expandVariables(name) if(expanded.indexOf('://')!==-1){ expanded = define.mapToCacheDir(expanded) } var module = define.module[expanded] return module } // fetch it async! define.httpGetCached = function(httpurl){ return new define.Promise(function(resolve, reject){ var myurl = url.parse(httpurl) // ok turn this url into a cachepath // lets make some dirs var path = define.filePath(myurl.path) var dirs = path.split('/') var total = cache_path_root + '/' for(var i = 0; i < dirs.length; i++){ total += dirs[i] if(!fs.existsSync(total)) fs.mkdirSync(total) total += '/' } var cache_path = cache_path_root + myurl.path // then we read our files ETag var headers = {'client-type':'nodejs'} fs.stat(cache_path, function(err, stat){ if(!err){ // build etag headers['if-none-match'] = stat.mtime.getTime() + '_' + stat.size } http.get({ host: myurl.hostname, port: myurl.port, path: myurl.path, headers:headers }, function(res){ //console.log(res) if(res.statusCode === 200){ } else if(res.statusCode === 304){ // cached return resolve({path:cache_path, type:res.headers['content-type']}) } else reject({path:myurl.path,code:res.statusCode}) if(res.headers['content-type'] === 'text/json' && define.fileExt(cache_path) === '') cache_path += '.json' // lets write it to disk var str = fs.createWriteStream(cache_path) res.pipe(str) str.on('finish', function(){ // lets set the exact timestamp on our file if(res.headers.mtime){ var time = res.headers.mtime / 1000 fs.utimes(cache_path, time, time) } resolve({path:cache_path, type:res.headers['content-type']}) }) }) }) }) } // hook compile to keep track of module objects var Module = require("module") var modules = [] var original_paths = [] var _compile = Module.prototype._compile Module.prototype._compile = function(content, filename){ modules.push(this) try { var ret = _compile.call(this, content, filename) } //catch(e){ throw e} //catch(e){ // console.log(e.linenumber) // } finally { modules.pop() } return ret } define.download_queue = {} define.define = function(factory) { if(factory instanceof Array) throw new Error("injects-style not supported") var module = modules[modules.length - 1] || require.main //console.log(original_paths) // store module and factory just like in the other envs define.module[module.filename] = module define.factory[module.filename] = factory function loadModuleAsync(modurl, includefrom){ modurl = modurl.replace(/\\/g , '/' ); var parsedmodurl = url.parse(modurl) var base_path = define.filePath(modurl) // block reentry if(define.download_queue[modurl]){ return new define.Promise(function(resolve, reject){ resolve( cache_path_root + url.parse(modurl).path ) }) //return define.download_queue[modurl]// } // we need to fetch the url, then look at its dependencies, fetch those return define.download_queue[modurl] = new define.Promise(function(resolve, reject){ // lets make sure we dont already have the module in our system define.httpGetCached(modurl).then(function(result){ // the root if(result.type === 'text/json' && define.fileExt(parsedmodurl.path) === ''){ var data = JSON.parse(fs.readFileSync(result.path).toString()) // alright we get a boot file // set our root properly var mathmodule = define.getModule('$system/base/math.js') // lets re-assign math define.paths = data.paths for(var key in data.paths){ define['$'+key] = '$root/'+key } define.paths.root = define.$root = 'http://'+parsedmodurl.hostname+':'+parsedmodurl.port+'/' var math2 = define.mapToCacheDir('$system/base/math.js') define.module[math2] = mathmodule // alright now, lets load up the root loadModuleAsync(define.expandVariables(data.boot), modurl).then(function(result){ // ok so, resolve(result) }) return } if(result.type.indexOf('javascript') !== -1){ // lets load up the module, without initializing it define.process_factory = true // open the fucker try{ //!TODO, make a neater way to fetch the module dependencies (dont require it twice) require(result.path) // and lets remove it again immediately delete Module._cache[result.path.indexOf("\\") !== -1?result.path.replace(/\//g, '\\'):result.path] } catch(e){ console.log(e.stack) } var factory = define.process_factory define.process_factory = false // alright we have a define.process_factory call we can now use. if(factory === true){ return resolve(result.path) } Promise.all(define.findRequiresInFactory(factory).map(function(path){ // ignore nodejs style module requires var dep_path if(path.indexOf('://') !== -1){ dep_path = path } else if(path.indexOf('$') === -1 && path.charAt(0) !== '.'){ return null } else dep_path = define.joinPath(base_path, define.expandVariables(path)) var ext = define.fileExt(dep_path) if(!ext) dep_path += '.js' return loadModuleAsync(dep_path, modurl) })).then(function(){ // lets finish up our factory resolve(result.path) }).catch(function(error){ console.log("CAUGHT ERROR ", error) }) return // lets initialize the module } return resolve(result.path) }).catch(function(err){ console.log("Error in "+modurl+" from "+includefrom,err,err.stack) }) }) } function noderequirewrapper(iname) { var name = iname if(arguments.length != 1) throw new Error("Unsupported require style") try{ name = define.expandVariables(name) } catch(e){ console.log("Cannot find "+e+" in module "+module.filename) throw e } if(name.indexOf('://') !== -1){ name = define.mapToCacheDir(name) } var full_name = name; try{ full_name = Module._resolveFilename(name, module) } catch(e){ // Don't generate an error becaues the image might be // remote, or a relative path was specified. } if (full_name instanceof Array) full_name = full_name[0] if(define.atRequire && ((full_name.charAt(0) == '/') || (full_name.indexOf('\\') >= 0)) ){ define.atRequire(full_name) } // we cant require non js files var ext = define.fileExt(full_name) if(ext !== '' && ext !== 'js'){ if(ext === 'jpg' || ext === 'jpeg' || ext === 'gif' || ext === 'png'){ // Construct a Texture.Image object given its path if(define.loadImage) return define.loadImage(full_name) return undefined } else{ // read it as an arraybuffer var buffer = fs.readFileSync(full_name) var ab = new ArrayBuffer(buffer.length) var view = new Uint8Array(ab) for (var i = 0; i < buffer.length; ++i) { view[i] = buffer[i] } return define.processFileType(ext, ab) //console.log(full_name) } return undefined } var old_stack = define.local_require_stack define.local_require_stack = [] try{ var ret = require(full_name) } //catch(e){ // console.log(e.stack) finally{ define.local_require_stack = old_stack } return ret } noderequirewrapper.clearCache = function(name){ Module._cache = {} } noderequirewrapper.module = module noderequirewrapper.loaded = function(path, ext){ var dep_path = define.joinPath(cache_path_root, define.expandVariables(path)) if(define.factory[dep_path]){ return true } } noderequirewrapper.async = function(modname){ // For dali (and probably nodejs) relative paths must be made // absolute to where the example is located. Retrieval // method is different if running from a remote server var remote = (define.$example.indexOf('://') !== -1); if (define.$platform == 'dali') { // Remote, relative if (remote && modname.indexOf('./') == 0) { modname = define.$example + modname.substring(2) return define.httpGetCached(modname); } // Remote, absolute if (remote && modname.indexOf('/') == 0) { var p = define.$example.indexOf('/', 8); modname = define.$example.substring(0, p) + modname; return define.httpGetCached(modname); } // Local, relative if (modname.indexOf('./') == 0) { modname = '$root/' + define.$example + modname.substring(2) modname = define.expandVariables(modname); return new define.Promise(function(resolve, reject) { return resolve(define.loadImage(modname)); }); } // Local, absolute if (modname.indexOf('/') == 0) { modname = '$root' + modname modname = define.expandVariables(modname); return new define.Promise(function(resolve, reject) { return resolve(define.loadImage(modname)); }); } if (remote && modname.indexOf('://') === -1) modname = define.$example + '/' + modname modname = define.expandVariables(modname) } if (define.$platform == 'dali' && modname.indexOf('./') == 0) modname = '$root' + '/' + define.$example + '/' + modname; if(typeof modname !== 'string') throw new Error("module name in require.async not a string") modname = define.expandVariables(modname) // Query if module is in local file system (DALI) var fs = require("fs") try { stats = fs.lstatSync(modname) var data = fs.readFileSync(modname) return new define.Promise(function(resolve, reject){ resolve(data) }) } catch(e) { } return new define.Promise(function(resolve, reject){ loadModuleAsync(modname, "root").then(function(path){ resolve(noderequirewrapper(path)) }).catch(function(e){ console.log("ERROR", e.stack) }) }) } module.factory = factory if (typeof factory !== "function") return module.exports = factory // we are being used for require.async if(define.process_factory){ define.process_factory = factory return } define.local_require_stack.push(noderequirewrapper) try{ var ret = factory.call(module.exports, noderequirewrapper, module.exports, module) } finally{ define.local_require_stack.pop() } if(ret !== undefined) module.exports = ret if(define.atModule) define.atModule(module) } global.define.require = require global.define.module = {} global.define.factory = {} // fetch a new require for the main module and return that define.define(function(require){ module.exports = require }) } // Worker function define_worker(){ self.define = define define.define = function(body){ } } // Struct implementation define.prim = { int8:function int8(value){ if(value && value.isArray) return value return parseInt(value) }, uint8:function uint8(value){ if(value && value.isArray) return value return parseInt(value) }, int16:function int16(value){ if(value && value.isArray) return value return parseInt(value) }, uint16:function uint16(value){ if(value && value.isArray) return value return parseInt(value) }, int32:function int32(value){ if(value && value.isArray) return value return parseInt(value) }, uint32:function uint32(value){ if(value && value.isArray) return value return parseInt(value) }, half:function half(value){ if(value && value.isArray) return value return parseFloat(value) }, float32:function float32(value){ if(value && value.isArray) return value return parseFloat(value) }, float64:fun