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
JavaScript
/* 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