<commit_before>import { DocumentAssembler } from 'dommodule'
import { HTMLDocumentTypeAssembler } from './doctype'
import { Html } from './html'

const { HTMLDocument, document : { implementation } } = window

const DEFAULT_TITLE = ''

export class HTMLDocumentAssembler extends DocumentAssembler {
    /**
     * @param {HTMLElement} activeElement
     */
    set activeElement(activeElement) {
        if(this.node.contains(activeElement)) {
            activeElement.focus()
        }
    }

    /**
     * @returns {HTMLElement}
     */
    get activeElement() {
        return this.node.activeElement
    }

    /**
     * @param {HTMLBodyElement} body
     */
    set body(body) {
        this.node.body = body
    }

    /**
     * @returns {HTMLBodyElement}
     */
    get body() {
        return this.node.body
    }

    /**
     * @param {HTMLHeadElement} head
     */
    set head(head) {
        const element = this.head
        if(element) element.replaceWith(head)
        else this.documentElement.prepend(head)
    }

    /**
     * @returns {HTMLHeadElement}
     */
    get head() {
        return this.node.head
    }

    /**
     * @param {String} title
     */
    set title(title) {
        this.node.title = title
    }

    /**
     * @returns {String}
     */
    get title() {
        return this.node.title
    }

    /**
     * @param {*} [init]
     * @returns {HTMLDocument}
     */
    static create(init) {
        const { title = this.title } = init || this
        return implementation.createHTMLDocument(title)
    }

    /**
     * @returns {Function}
     */
    static get doctypeAssembler() {
        return HTMLDocumentTypeAssembler
    }

    /**
     * @returns {Function} Html
     */
    static get elementAssembler() {
        return Html
    }

    /**
     * @returns {window.HTMLDocument}
     */
    static get interface() {
        return HTMLDocument
    }

    /**
     * @returns {String}
     */
    static get title() {
        return DEFAULT_TITLE
    }
}
<commit_msg>Edit JSDoc<commit_after>import { DocumentAssembler } from 'dommodule'
import { HTMLDocumentTypeAssembler } from './doctype'
import { Html } from './html'

const { HTMLDocument, document : { implementation } } = window

const DEFAULT_TITLE = ''

/**
 * @see https://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/html.html#ID-26809268
 */
export class HTMLDocumentAssembler extends DocumentAssembler {
    /**
     * @param {HTMLElement} activeElement
     */
    set activeElement(activeElement) {
        if(this.node.contains(activeElement)) {
            activeElement.focus()
        }
    }

    /**
     * @returns {HTMLElement}
     */
    get activeElement() {
        return this.node.activeElement
    }

    /**
     * @param {HTMLBodyElement} body
     */
    set body(body) {
        this.node.body = body
    }

    /**
     * @returns {HTMLBodyElement}
     */
    get body() {
        return this.node.body
    }

    /**
     * @param {HTMLHeadElement} head
     */
    set head(head) {
        const element = this.head
        if(element) element.replaceWith(head)
        else this.documentElement.prepend(head)
    }

    /**
     * @returns {HTMLHeadElement}
     */
    get head() {
        return this.node.head
    }

    /**
     * @param {String} title
     */
    set title(title) {
        this.node.title = title
    }

    /**
     * @returns {String}
     */
    get title() {
        return this.node.title
    }

    /**
     * @param {*} [init]
     * @returns {HTMLDocument}
     * @override
     */
    static create(init) {
        const { title = this.title } = init || this
        return implementation.createHTMLDocument(title)
    }

    /**
     * @returns {Function}
     * @override
     */
    static get doctypeAssembler() {
        return HTMLDocumentTypeAssembler
    }

    /**
     * @returns {Function} Html
     * @override
     */
    static get elementAssembler() {
        return Html
    }

    /**
     * @returns {window.HTMLDocument}
     * @override
     */
    static get interface() {
        return HTMLDocument
    }

    /**
     * @returns {String}
     */
    static get title() {
        return DEFAULT_TITLE
    }
}
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs')
var path = require('path')

var mkdirp = require('mkdirp')

var DBEntity = require('./entities/db-entity')
var Entity = require('./entities/entity')

/*
** Entity Manager
*/

class Entities {
	constructor(app) {
		this.app = app
		this.DiffType = app.history.DiffType
		this.entities = {}
		this.relations = []

		this.app.history.register(this.DiffType.CREATE_ENTITY, (data, opts) => {
			return this.add(data.value, opts)
		})

		this.app.history.register(this.DiffType.RENAME_ENTITY, (data, opts) => {
			return this.rename(data.table, data.value, opts)
		})

		this.app.history.register(this.DiffType.DELETE_ENTITY, (data, opts) => {
			return this.remove(data.table, opts)
		})

		this.app.history.register(this.DiffType.ADD_FIELD, (data, opts) => {
			return this.get(data.table).addFieldAt(data.value, data.position, opts)
		})

		this.app.history.register(this.DiffType.CHANGE_FIELD, (data, opts) => {
			return this.get(data.table).updateField(data.name, data.value, opts)
		})

		this.app.history.register(this.DiffType.DELETE_FIELD, (data, opts) => {
			return this.get(data.table).removeField(data.value, opts)
		})

		this.app.history.register(this.DiffType.ADD_RELATION, (data, opts) => {
			return this.get(data.table).addRelation(data.value, opts)
		})

		this.app.history.register(this.DiffType.DELETE_RELATION, (data, opts) => {
			return this.get(data.table).removeRelation(data.value, opts)
		})

		this.app.history.register(this.DiffType.ADD_QUERY, (data, opts) => {
			return this.get(data.table).addQuery(data.id, data.values.type, data.values.params, data.values.opts, opts)
		})

		this.app.history.register(this.DiffType.DELETE_QUERY, (data, opts) => {
			return this.get(data.table).removeQuery(data.id, opts)
		})

		this.app.history.register(this.DiffType.ADD_QUERY_PARAM, (data, opts) => {
			return this.get(data.table).getQuery(data.id).addParam(data.value, opts)
		})

		this.app.history.register(this.DiffType.DELETE_QUERY_PARAM, (data, opts) => {
			return this.get(data.table).getQuery(data.id).delParam(data.value, opts)
		})

		this.app.history.register(this.DiffType.UPDATE_QUERY_VALUE, (data, opts) => {
			return this.get(data.table).getQuery(data.id).updateValue(data.value.name, data.value.value, opts)
		})
	}

	load() {
		return new Promise((accept, reject) => {
			this.entities = {}
			let files;
			try {
				files = fs.readdirSync(path.join(this.app.path, 'entities'))
			} catch (e) {
				files = []
				fs.mkdirSync(path.join(this.app.path, 'entities'))
				return accept(false)
			}

			let promises = []

			files.forEach((file) => {
				try {
					if (file.substr(file.length - 5, 5) == '.json') {
						let content = fs.readFileSync(path.join(this.app.path, 'entities', file))
						let entity = JSON.parse(content.toString())
						entity.name = file.substr(0, file.length - 5)
						promises.push(this.add(entity, {history: false, db: false, save: false, wait_relations:true}));
					}
				} catch (e) {
					e += ' in ' + file
					throw new Error(e)
				}
			})

			Promise.all(promises).then(() => {
				promises = []
				for (let name in this.entities)
					promises.push(this.entities[name].applyRelations())
				return Promise.all(promises)
			}).then(() => {
				accept()
			}).catch((err) => {
				reject(err)
			})
		})
	}

	start() {
		return this.detect_rename().then(() => {
			this._save_id_map()
		}).then(() => {
			return this.app.database.sync()
		})
	}

	//Add an Entity
	add(entityobj, options) {
		options = options || {}

		let entity, createPromise
		if (entityobj instanceof Entity) {
			entity = entityobj
			createPromise = Promise.resolve()
		} else {
			if (! entityobj.name)
				return Promise.reject(new Error('missing entity name'))
			entity = new DBEntity(this.app)
			if ( ! entityobj.isRelation && (! entityobj.fields || ! entityobj.fields.length)) {
				entityobj.fields = [
					{
						"name": "id",
						"type": "number",
						"required": true,
						"primary": true,
						"unique": true,
						"default": false,
						"autoIncrement": true,
						"read": true,
						"write": false
					}
				]
			}
			if (entityobj.overwritable && this.entities[entity.name] && this.apply)
				return Promise.resolve(entity)
			if (entityobj.isRelation && entityobj.relations)
				delete entityobj.relations
			createPromise = entity.create(entityobj, {wait_relations:options.wait_relations})
		}

		return new Promise((accept, reject) => {
			createPromise.then(() => {
				if (options.save != false)
					entity.save(options)

				if (options.apply != false) {
					this.entities[entity.name] = entity
					this.app.emit('entity:added', entity)
				}

				if (options.overwritable)
					return accept(entity)

				if (options.history != false) {
					this.app.history.push({
						type: this.DiffType.CREATE_ENTITY,
						table: entity.name,
						value: entity.toJson()
					},{
						type: this.DiffType.DELETE_ENTITY,
						table: entity.name
					})
				}

				if (options.db == false)
					return accept(entity)

				if (options.apply != false) {
					entity.initDefaultQuery()
					this.sync()
					entity.refreshQueries()
				}
				else {
					entity.loadModel()
				}

				entity.model.sync().then(function() {
					accept(entity)
				}, function(err) {
					reject(err)
				})
			}, function(err) {
				reject(err)
			})
		})
	}

	_save_id_map() {
		let name_map = {}
		for (let entity_name in this.entities) {
			let entity = this.entities[entity_name]
			name_map[entity.id] = entity_name
		}

		mkdirp(path.join(this.app.path, '.materia'), (err) => {
			if (err) {
				console.error(err)
				return
			}
			try {
				fs.writeFileSync(path.join(this.app.path, '.materia', 'ids.json'), JSON.stringify(name_map))
			} catch (e) {
				console.error(e)
				return
			}
		})
	}

	detect_rename() {
		let name_map
		try {
			let content = fs.readFileSync(path.join(this.app.path, '.materia', 'ids.json')).toString()
			name_map = JSON.parse(content)
		} catch (e) {
			if (e.code == 'ENOENT')
				return Promise.resolve()
			console.error(e)
			return Promise.reject(e)
		}

		let diffs = []
		for (let entity_name in this.entities) {
			let entity = this.entities[entity_name]
			if (name_map[entity.id] && name_map[entity.id] != entity_name) {
				diffs.push({
					redo: {
						type: this.DiffType.RENAME_ENTITY,
						table: name_map[entity.id],
						value: entity_name
					},
					undo: {
						type: this.DiffType.RENAME_ENTITY,
						table: entity_name,
						value: name_map[entity.id]
					}
				})
			}
		}

		return this.app.history.apply(diffs, {
			//save: false,
			full_rename: false
		})
	}

	remove(name, options) {
		options = options || {}
		if ( ! name) {
			return false
		}

		let p = Promise.resolve()
		for (let entity_name in this.entities) {
			for (let relation of this.entities[entity_name].relations) {
				if (relation.reference.entity == name) {
					p = p.then(() => {
						//console.log('delete relation => ', relation)
						return this.entities[entity_name].removeRelation(relation, options)
					})
				}
			}
		}

		return p.then(() => {
			let entity = this.entities[name]
			if (options.apply != false) {
				delete this.entities[name]
			}

			if (options.history != false) {
				this.app.history.push({
					type: this.DiffType.DELETE_ENTITY,
					table: entity.name
				},{
					type: this.DiffType.CREATE_ENTITY,
					table: entity.name,
					value: entity.toJson()
				})
			}

			if (options.save != false && entity && fs.existsSync(path.join(this.app.path, 'entities', name + '.json'))) {
				if (options && options.beforeSave) {
					options.beforeSave()
				}
				fs.unlinkSync(path.join(this.app.path, 'entities', name + '.json'))
				if (options && options.afterSave) {
					options.afterSave()
				}
			}

			if (options.db == false) {
				return
			}

			// TODO: remove constraint to avoid force:true ? more tests needed
			return this.app.database.sequelize.getQueryInterface().dropTable(name, {force:true})
		})
	}

	rename(name, new_name, options) {
		options = options || {}
		let entity = this.get(name)

		if ( ! entity && options.full_rename != false)
			return Promise.reject(new Error('Entity ' + name + ' does not exist.'))

		if ( entity && this.get(new_name))
			return Promise.reject(new Error('Entity ' + new_name + ' already exists.'))

		if (options.history != false) {
			this.app.history.push({
				type: this.DiffType.RENAME_ENTITY,
				table: name,
				value: new_name
			},{
				type: this.DiffType.RENAME_ENTITY,
				table: new_name,
				value: name
			})
		}

		if (options.apply != false) {
			for (let entity_name in this.entities) {
				let entity = this.entities[entity_name]
				let need_save = false
				for (let relation of entity.relations) {
					if (relation.reference && relation.reference.entity == name) {
						need_save = true
						relation.reference.entity = new_name
					}
				}
				if (need_save && options.save != false)
					entity.save(options)
			}

			/*if (entity.fields[0].name == 'id_' + name) {
				entity.fields[0].name = 'id_' + new_name
				// TODO: rename field (with options db:options.db save:false history:false)
			}*/

			if (entity) {
				entity.name = new_name
				this.entities[new_name] = entity
				delete this.entities[name]
			}
		}

		if (options.save != false) {
			if (fs.existsSync(path.join(this.app.path, 'entities', name + '.json'))) {
				if (options && options.beforeSave) {
					options.beforeSave()
				}
				fs.unlinkSync(path.join(this.app.path, 'entities', name + '.json'))
				if (options && options.afterSave) {
					options.beforeSave()
				}
			}
			this.save(options)
		}

		if (options.db == false)
			return Promise.resolve()

		return this.app.database.sequelize.getQueryInterface().renameTable(name, new_name)
	}

	findAll() {
		let ents = []
		for (let ent in this.entities)
			ents.push(this.entities[ent])
		return ents
	}

	get(name) { return this.entities[name] }

	getOrAdd(name, entityobj, options) {
		return new Promise((accept, reject) => {
			if (this.entities[name])
				return accept(this.entities[name])
			entityobj.name = name
			this.add(entityobj, options).then((entity) => {
				accept(entity)
			}, (err) => {
				reject(err)
			})
		})
	}

	save(opts) {
		for (let ent in this.entities) {
			this.entities[ent].save(opts)
		}
	}

	/*getCurrentDiff() {
		let diffs = []
		this.entities.forEach((entity) => {
			//entity.
		})
	}

	hasDiff() {
		for (let entity of this.entities) {

		}
	}*/

	sync() {
		for (let ent in this.entities) {
			this.entities[ent].loadModel()
		}

		//Need a second loop to executes relations when all models are created
		for (let ent in this.entities) {
			this.entities[ent].loadRelationsInModel()
		}
	}

	toJson() {
		let res = {entities: [], relations: []}
		for (let ent in this.entities) {
			res.entities.push(this.entities[ent].toJson())
		}

		for (let rel in this.relations) {
			res.relations.push(this.relations[rel].toJson())
		}

		return res
	}

	findAllRelations(opts) {
		opts = opts || {}
		let res = []
		for (let ent in this.entities) {
			let entity = this.entities[ent]
			for (let r of entity.relations) {
				if ( ! r.implicit || opts.implicit) {
					r.entity = ent
					res.push(r)
				}
			}
			if (entity.isRelation && opts.implicit) {
				for (let f of entity.getFields()) {
					let r = f.isRelation
					if (r) {
						r.entity = ent
						r.implicit = true
						r.field = f.name
						res.push(r)
					}
				}
			}
		}
		return res
	}
}

module.exports = Entities
<commit_msg>Fix entity rename (sequelize model was not reloaded, ids.son was not updated)<commit_after>'use strict';

var fs = require('fs')
var path = require('path')

var mkdirp = require('mkdirp')

var DBEntity = require('./entities/db-entity')
var Entity = require('./entities/entity')

/*
** Entity Manager
*/

class Entities {
	constructor(app) {
		this.app = app
		this.DiffType = app.history.DiffType
		this.entities = {}
		this.relations = []

		this.app.history.register(this.DiffType.CREATE_ENTITY, (data, opts) => {
			return this.add(data.value, opts)
		})

		this.app.history.register(this.DiffType.RENAME_ENTITY, (data, opts) => {
			return this.rename(data.table, data.value, opts)
		})

		this.app.history.register(this.DiffType.DELETE_ENTITY, (data, opts) => {
			return this.remove(data.table, opts)
		})

		this.app.history.register(this.DiffType.ADD_FIELD, (data, opts) => {
			return this.get(data.table).addFieldAt(data.value, data.position, opts)
		})

		this.app.history.register(this.DiffType.CHANGE_FIELD, (data, opts) => {
			return this.get(data.table).updateField(data.name, data.value, opts)
		})

		this.app.history.register(this.DiffType.DELETE_FIELD, (data, opts) => {
			return this.get(data.table).removeField(data.value, opts)
		})

		this.app.history.register(this.DiffType.ADD_RELATION, (data, opts) => {
			return this.get(data.table).addRelation(data.value, opts)
		})

		this.app.history.register(this.DiffType.DELETE_RELATION, (data, opts) => {
			return this.get(data.table).removeRelation(data.value, opts)
		})

		this.app.history.register(this.DiffType.ADD_QUERY, (data, opts) => {
			return this.get(data.table).addQuery(data.id, data.values.type, data.values.params, data.values.opts, opts)
		})

		this.app.history.register(this.DiffType.DELETE_QUERY, (data, opts) => {
			return this.get(data.table).removeQuery(data.id, opts)
		})

		this.app.history.register(this.DiffType.ADD_QUERY_PARAM, (data, opts) => {
			return this.get(data.table).getQuery(data.id).addParam(data.value, opts)
		})

		this.app.history.register(this.DiffType.DELETE_QUERY_PARAM, (data, opts) => {
			return this.get(data.table).getQuery(data.id).delParam(data.value, opts)
		})

		this.app.history.register(this.DiffType.UPDATE_QUERY_VALUE, (data, opts) => {
			return this.get(data.table).getQuery(data.id).updateValue(data.value.name, data.value.value, opts)
		})
	}

	load() {
		return new Promise((accept, reject) => {
			this.entities = {}
			let files;
			try {
				files = fs.readdirSync(path.join(this.app.path, 'entities'))
			} catch (e) {
				files = []
				fs.mkdirSync(path.join(this.app.path, 'entities'))
				return accept(false)
			}

			let promises = []

			files.forEach((file) => {
				try {
					if (file.substr(file.length - 5, 5) == '.json') {
						let content = fs.readFileSync(path.join(this.app.path, 'entities', file))
						let entity = JSON.parse(content.toString())
						entity.name = file.substr(0, file.length - 5)
						promises.push(this.add(entity, {history: false, db: false, save: false, wait_relations:true}));
					}
				} catch (e) {
					e += ' in ' + file
					throw new Error(e)
				}
			})

			Promise.all(promises).then(() => {
				promises = []
				for (let name in this.entities)
					promises.push(this.entities[name].applyRelations())
				return Promise.all(promises)
			}).then(() => {
				accept()
			}).catch((err) => {
				reject(err)
			})
		})
	}

	start() {
		return this.detect_rename().then(() => {
			this._save_id_map()
		}).then(() => {
			return this.app.database.sync()
		})
	}

	//Add an Entity
	add(entityobj, options) {
		options = options || {}

		let entity, createPromise
		if (entityobj instanceof Entity) {
			entity = entityobj
			createPromise = Promise.resolve()
		} else {
			if (! entityobj.name)
				return Promise.reject(new Error('missing entity name'))
			entity = new DBEntity(this.app)
			if ( ! entityobj.isRelation && (! entityobj.fields || ! entityobj.fields.length)) {
				entityobj.fields = [
					{
						"name": "id",
						"type": "number",
						"required": true,
						"primary": true,
						"unique": true,
						"default": false,
						"autoIncrement": true,
						"read": true,
						"write": false
					}
				]
			}
			if (entityobj.overwritable && this.entities[entity.name] && this.apply)
				return Promise.resolve(entity)
			if (entityobj.isRelation && entityobj.relations)
				delete entityobj.relations
			createPromise = entity.create(entityobj, {wait_relations:options.wait_relations})
		}

		return new Promise((accept, reject) => {
			createPromise.then(() => {
				if (options.save != false)
					entity.save(options)

				if (options.apply != false) {
					this.entities[entity.name] = entity
					this.app.emit('entity:added', entity)
				}

				if (options.overwritable)
					return accept(entity)

				if (options.history != false) {
					this.app.history.push({
						type: this.DiffType.CREATE_ENTITY,
						table: entity.name,
						value: entity.toJson()
					},{
						type: this.DiffType.DELETE_ENTITY,
						table: entity.name
					})
				}

				if (options.db == false)
					return accept(entity)

				if (options.apply != false) {
					entity.initDefaultQuery()
					this.sync()
					entity.refreshQueries()
				}
				else {
					entity.loadModel()
				}

				entity.model.sync().then(function() {
					accept(entity)
				}, function(err) {
					reject(err)
				})
			}, function(err) {
				reject(err)
			})
		})
	}

	_save_id_map() {
		let name_map = {}
		for (let entity_name in this.entities) {
			let entity = this.entities[entity_name]
			name_map[entity.id] = entity_name
		}

		mkdirp(path.join(this.app.path, '.materia'), (err) => {
			if (err) {
				console.error(err)
				return
			}
			try {
				fs.writeFileSync(path.join(this.app.path, '.materia', 'ids.json'), JSON.stringify(name_map))
			} catch (e) {
				console.error(e)
				return
			}
		})
	}

	detect_rename() {
		let name_map
		try {
			let content = fs.readFileSync(path.join(this.app.path, '.materia', 'ids.json')).toString()
			name_map = JSON.parse(content)
		} catch (e) {
			if (e.code == 'ENOENT')
				return Promise.resolve()
			console.error(e)
			return Promise.reject(e)
		}

		let diffs = []
		for (let entity_name in this.entities) {
			let entity = this.entities[entity_name]
			if (name_map[entity.id] && name_map[entity.id] != entity_name) {
				diffs.push({
					redo: {
						type: this.DiffType.RENAME_ENTITY,
						table: name_map[entity.id],
						value: entity_name
					},
					undo: {
						type: this.DiffType.RENAME_ENTITY,
						table: entity_name,
						value: name_map[entity.id]
					}
				})
			}
		}

		return this.app.history.apply(diffs, {
			//save: false,
			full_rename: false
		})
	}

	remove(name, options) {
		options = options || {}
		if ( ! name) {
			return false
		}

		let p = Promise.resolve()
		for (let entity_name in this.entities) {
			for (let relation of this.entities[entity_name].relations) {
				if (relation.reference.entity == name) {
					p = p.then(() => {
						//console.log('delete relation => ', relation)
						return this.entities[entity_name].removeRelation(relation, options)
					})
				}
			}
		}

		return p.then(() => {
			let entity = this.entities[name]
			if (options.apply != false) {
				delete this.entities[name]
			}

			if (options.history != false) {
				this.app.history.push({
					type: this.DiffType.DELETE_ENTITY,
					table: entity.name
				},{
					type: this.DiffType.CREATE_ENTITY,
					table: entity.name,
					value: entity.toJson()
				})
			}

			if (options.save != false && entity && fs.existsSync(path.join(this.app.path, 'entities', name + '.json'))) {
				if (options && options.beforeSave) {
					options.beforeSave()
				}
				fs.unlinkSync(path.join(this.app.path, 'entities', name + '.json'))
				if (options && options.afterSave) {
					options.afterSave()
				}
			}

			if (options.db == false) {
				return
			}

			// TODO: remove constraint to avoid force:true ? more tests needed
			return this.app.database.sequelize.getQueryInterface().dropTable(name, {force:true})
		})
	}

	rename(name, new_name, options) {
		options = options || {}
		let entity = this.get(name)

		if ( ! entity && options.full_rename != false)
			return Promise.reject(new Error('Entity ' + name + ' does not exist.'))

		if ( entity && this.get(new_name))
			return Promise.reject(new Error('Entity ' + new_name + ' already exists.'))

		if (options.history != false) {
			this.app.history.push({
				type: this.DiffType.RENAME_ENTITY,
				table: name,
				value: new_name
			},{
				type: this.DiffType.RENAME_ENTITY,
				table: new_name,
				value: name
			})
		}

		if (options.apply != false) {
			for (let entity_name in this.entities) {
				let entity = this.entities[entity_name]
				let need_save = false
				for (let relation of entity.relations) {
					if (relation.reference && relation.reference.entity == name) {
						need_save = true
						relation.reference.entity = new_name
					}
				}
				if (need_save && options.save != false)
					entity.save(options)
			}

			/*if (entity.fields[0].name == 'id_' + name) {
				entity.fields[0].name = 'id_' + new_name
				// TODO: rename field (with options db:options.db save:false history:false)
			}*/

			if (entity) {
				entity.name = new_name
				this.entities[new_name] = entity
				delete this.entities[name]
			}
		}

		if (options.save != false) {
			if (fs.existsSync(path.join(this.app.path, 'entities', name + '.json'))) {
				if (options && options.beforeSave) {
					options.beforeSave()
				}
				fs.unlinkSync(path.join(this.app.path, 'entities', name + '.json'))
				this._save_id_map()
				if (options && options.afterSave) {
					options.beforeSave()
				}
			}
			this.save(options)
		}

		if (options.db == false)
			return Promise.resolve()

		return this.app.database.sequelize.getQueryInterface().renameTable(name, new_name).then(() => {
			entity.loadModel()
			entity.loadRelationsInModel()
		})
	}

	findAll() {
		let ents = []
		for (let ent in this.entities)
			ents.push(this.entities[ent])
		return ents
	}

	get(name) { return this.entities[name] }

	getOrAdd(name, entityobj, options) {
		return new Promise((accept, reject) => {
			if (this.entities[name])
				return accept(this.entities[name])
			entityobj.name = name
			this.add(entityobj, options).then((entity) => {
				accept(entity)
			}, (err) => {
				reject(err)
			})
		})
	}

	save(opts) {
		for (let ent in this.entities) {
			this.entities[ent].save(opts)
		}
	}

	/*getCurrentDiff() {
		let diffs = []
		this.entities.forEach((entity) => {
			//entity.
		})
	}

	hasDiff() {
		for (let entity of this.entities) {

		}
	}*/

	sync() {
		for (let ent in this.entities) {
			this.entities[ent].loadModel()
		}

		//Need a second loop to executes relations when all models are created
		for (let ent in this.entities) {
			this.entities[ent].loadRelationsInModel()
		}
	}

	toJson() {
		let res = {entities: [], relations: []}
		for (let ent in this.entities) {
			res.entities.push(this.entities[ent].toJson())
		}

		for (let rel in this.relations) {
			res.relations.push(this.relations[rel].toJson())
		}

		return res
	}

	findAllRelations(opts) {
		opts = opts || {}
		let res = []
		for (let ent in this.entities) {
			let entity = this.entities[ent]
			for (let r of entity.relations) {
				if ( ! r.implicit || opts.implicit) {
					r.entity = ent
					res.push(r)
				}
			}
			if (entity.isRelation && opts.implicit) {
				for (let f of entity.getFields()) {
					let r = f.isRelation
					if (r) {
						r.entity = ent
						r.implicit = true
						r.field = f.name
						res.push(r)
					}
				}
			}
		}
		return res
	}
}

module.exports = Entities
<|endoftext|>


<commit_before>(function(){

    xtag.register('widget-Filter', {
        extends: 'div',
        lifecycle: {
            created: function() {
                var spawnCondition, filterTitle, currentFilter,
                    controlWrap = document.createElement('form'),
                    controlRows = [],
                    encodedFilter = document.createElement('input'),
                    applyFilter = document.createElement('input'),
                    filterNameLabel = document.createElement('label'),
                    filterName = document.createElement('input'),
                    saveFilter = document.createElement('button'),
                    chooseFilterLabel = document.createElement('label'),
                    chooseFilter = document.createElement('select'),
                    loadFilter = document.createElement('button'),
                    deleteFilter = document.createElement('button'),
                    loadTarget = document.createElement('input'),
                    deleteTarget = document.createElement('input'),
                    saveLoadFilter = document.createElement('button');

                this.filterConditions = [];
                this.nRows = [];
                this.detectors = ['DANTE', 'DESCANT', 'GRIFFIN', 'PACES', 'SCEPTAR', 'ZDS'];
                this.detectorCodes = ['DA', 'DS', 'GR', 'PA', 'SE', 'ZD'],
                this.filterOptions = ['Singles', 'Coincidences', 'Prescale'];
                this.filterCodes = ['S', 'C', 'P'];

                filterTitle = document.createElement('h1');
                filterTitle.innerHTML = 'Filter Control';
                this.appendChild(filterTitle);
                currentFilter = document.createElement('h3');
                currentFilter.setAttribute('id', 'currentFilter');
                this.appendChild(currentFilter);

                this.conditionWrap = document.createElement('div');
                this.conditionWrap.setAttribute('id', 'conditionWrap');
                this.appendChild(this.conditionWrap);

                this.spawnFilterCondition();

                spawnCondition = document.createElement('button');
                spawnCondition.innerHTML = 'OR new condition';
                spawnCondition.setAttribute('class', 'stdin');
                spawnCondition.onclick = this.spawnFilterCondition.bind(this);
                this.appendChild(spawnCondition);

                controlWrap.setAttribute('class', 'filterControl summary');
                controlWrap.setAttribute('id', 'filterDefinitionForm');
                controlWrap.setAttribute('method', 'POST');
                controlWrap.setAttribute('action', 'registerFilter');
                this.appendChild(controlWrap);

                controlRows[0] = document.createElement('span')
                controlWrap.appendChild(controlRows[0]);

                encodedFilter.setAttribute('type', 'text');
                encodedFilter.setAttribute('id', 'encodedFilter');
                encodedFilter.setAttribute('style', 'display:none');
                encodedFilter.setAttribute('name', 'filterString');
                controlRows[0].appendChild(encodedFilter);

                applyFilter.setAttribute('type', 'checkbox');
                applyFilter.setAttribute('id', 'applyFilter');
                applyFilter.setAttribute('style', 'display:none');
                applyFilter.setAttribute('name', 'applyFilter');
                controlRows[0].appendChild(applyFilter);

                filterNameLabel.innerHTML = 'Filter Name:';
                filterName.setAttribute('class', 'stdin');
                filterName.setAttribute('type', 'text');
                filterName.setAttribute('id', 'filterName');
                filterName.setAttribute('name', 'filterName');
                filterName.setAttribute('pattern',"(?=.*[a-zA-Z])(?=.*[\d\W])[\S]*$");
                filterName.setAttribute('required', true);
                controlRows[0].appendChild(filterNameLabel);
                controlRows[0].appendChild(filterName);

                saveFilter.setAttribute('class', 'stdin');
                saveFilter.innerHTML = 'Save Filter Definition';
                saveFilter.onclick = this.registerNewFilter.bind(this);
                controlRows[0].appendChild(saveFilter);

                saveLoadFilter.setAttribute('class', 'stdin');
                saveLoadFilter.innerHTML = 'Save & Apply Filter Definition';
                saveLoadFilter.onclick = function(){
                    this.registerNewFilter();
                    document.getElementById('applyFilter').checked = true;
                }.bind(this);
                controlRows[0].appendChild(saveLoadFilter);

                controlRows[1] = document.createElement('span');
                controlWrap.appendChild(controlRows[1]);

                chooseFilterLabel.innerHTML = 'Load / Delete Filter:'
                controlRows[1].appendChild(chooseFilterLabel);
                chooseFilter.setAttribute('class', 'stdin');
                chooseFilter.setAttribute('id', 'filterList');
                controlRows[1].appendChild(chooseFilter);

                loadTarget.setAttribute('type', 'text');
                loadTarget.setAttribute('name', 'loadTarget');
                loadTarget.setAttribute('id', 'loadTarget');
                loadTarget.setAttribute('value', 'null');
                loadTarget.setAttribute('style', 'display:none');
                controlRows[1].appendChild(loadTarget);
                loadFilter.setAttribute('class', 'stdin');
                loadFilter.innerHTML = 'Load'
                loadFilter.onclick = function(){
                    document.getElementById('loadTarget').value = selected('filterList')
                }
                controlRows[1].appendChild(loadFilter);

                deleteTarget.setAttribute('type', 'text');
                deleteTarget.setAttribute('name', 'deleteTarget');
                deleteTarget.setAttribute('id', 'deleteTarget');
                deleteTarget.setAttribute('value', 'null');
                deleteTarget.setAttribute('style', 'display:none');
                controlRows[1].appendChild(deleteTarget);
                deleteFilter.setAttribute('class', 'stdin');
                deleteFilter.innerHTML = 'Delete'
                deleteFilter.onclick = function(){
                    document.getElementById('deleteTarget').value = selected('filterList')
                }
                controlRows[1].appendChild(deleteFilter);

                XHR('http://'+this.MIDAS+'/?cmd=jcopy&odb=/Filter&encoding=json-nokeys', this.registerFilterODB.bind(this));
            },
            inserted: function() {},
            removed: function() {},
            attributeChanged: function() {}
        },
        events: {

        },
        accessors: {
            'MIDAS':{
                attribute: {} //this just needs to be declared
            }
        },
        methods: {
            'spawnFilterCondition' : function(){
                var newCondition = document.createElement('div'),
                    leftTab = document.createElement('div'),
                    filterContent = document.createElement('div'),
                    conditionTable = document.createElement('table'),
                    addNewRow = document.createElement('button'),
                    deleteCondition = document.createElement('button'),
                    index = this.filterConditions.length;

                newCondition.setAttribute('id', 'filterCondition' + index);
                newCondition.setAttribute('class', 'filterCondition');
                this.conditionWrap.appendChild(newCondition);

                leftTab.setAttribute('id', 'leftTab' + index);
                leftTab.setAttribute('class', 'leftTab');
                leftTab.innerHTML = 'OR';
                newCondition.appendChild(leftTab);

                filterContent.setAttribute('id', 'filterContent' + index);
                filterContent.setAttribute('class', 'filterContent');
                newCondition.appendChild(filterContent)

                conditionTable.setAttribute('id', 'filterTable'+index);
                conditionTable.setAttribute('class', 'filterTable');
                filterContent.appendChild(conditionTable);

                addNewRow.setAttribute('id', 'addNewRow' + index);
                addNewRow.setAttribute('class', 'stdin lightButton');
                addNewRow.innerHTML = 'AND new condition';
                addNewRow.onclick = this.addNewRow.bind(this, index);
                filterContent.appendChild(addNewRow);

                deleteCondition.setAttribute('id', 'deleteCondition' + index);
                deleteCondition.setAttribute('class', 'stdin lightButton');
                deleteCondition.innerHTML = 'Delete This Block';
                deleteCondition.onclick = this.deleteOrBlock.bind(this, index);
                filterContent.appendChild(deleteCondition);

                this.nRows[index] = 0;
                addNewRow.onclick();

                this.filterConditions.push(newCondition);
            },

            'addNewRow' : function(index){
                var row, cell,
                    detectorSelect, detectorOption,
                    filterSelect, filterOption,
                    optionConfigWrap, optionConfigLabel, optionConfigInput,
                    deleteRow,
                    i,
                    rowIndex = this.nRows[index]

                row = document.createElement('tr');
                row.setAttribute('id', 'filterRow' + index + rowIndex);
                document.getElementById('filterTable' + index).appendChild(row);

                cell = document.createElement('td');
                cell.innerHTML = 'AND';
                row.appendChild(cell);

                cell = document.createElement('td');
                row.appendChild(cell);
                detectorSelect = document.createElement('select');
                detectorSelect.setAttribute('id', 'detectorSelect'+index+rowIndex);
                detectorSelect.setAttribute('class', 'stdin');
                for(i=0; i<this.detectors.length; i++){
                    detectorOption = document.createElement('option');
                    detectorOption.value = this.detectorCodes[i];
                    detectorOption.innerHTML = this.detectors[i];
                    detectorSelect.appendChild(detectorOption);
                }
                cell.appendChild(detectorSelect);

                cell = document.createElement('td');
                row.appendChild(cell);
                filterSelect = document.createElement('select');
                filterSelect.setAttribute('id', 'filterType'+index+rowIndex)
                filterSelect.setAttribute('class', 'stdin');
                filterSelect.onchange = function(idCode){
                    var filterType = selected('filterType'+idCode);

                    this.manageInputCell(filterType, idCode);

                }.bind(this, ''+index+rowIndex);
                for(i=0; i<this.filterOptions.length; i++){
                    filterOption = document.createElement('option');
                    filterOption.value = this.filterCodes[i];
                    filterOption.innerHTML = this.filterOptions[i];
                    filterSelect.appendChild(filterOption);
                }
                cell.appendChild(filterSelect);

                cell = document.createElement('td');
                row.appendChild(cell);
                optionConfigWrap = document.createElement('div');
                optionConfigWrap.setAttribute('id', 'optionConfigWrap'+index+rowIndex);
                optionConfigWrap.setAttribute('class', 'optionConfigWrap hiddenConfig');
                cell.appendChild(optionConfigWrap);
                optionConfigLabel = document.createElement('label');
                optionConfigLabel.setAttribute('id', 'optionConfigLabel'+index+rowIndex);
                optionConfigWrap.appendChild(optionConfigLabel);
                optionConfigInput = document.createElement('input');
                optionConfigInput.setAttribute('id', 'optionConfigInput'+index+rowIndex);
                optionConfigInput.setAttribute('class', 'stdin');
                optionConfigInput.setAttribute('type', 'number');
                optionConfigInput.setAttribute('step', 1);
                optionConfigInput.setAttribute('min', 0);
                optionConfigWrap.appendChild(optionConfigInput);

                cell = document.createElement('td');
                row.appendChild(cell);
                deleteRow = document.createElement('button');
                deleteRow.innerHTML = 'Remove';
                deleteRow.setAttribute('class', 'stdin');
                deleteRow.onclick = this.deleteRow.bind(this, index, ''+index+rowIndex);
                cell.appendChild(deleteRow);

                this.nRows[index]++;

            },

            'manageInputCell' : function(filterType, idCode){

                if(filterType == 'S'){
                    document.getElementById('optionConfigWrap'+idCode).setAttribute('class', 'optionConfigWrap hiddenConfig');
                } else if(filterType == 'C'){
                    document.getElementById('optionConfigWrap'+idCode).setAttribute('class', 'optionConfigWrap');
                    document.getElementById('optionConfigLabel'+idCode).innerHTML = 'Multiplicity: ';
                    document.getElementById('optionConfigInput'+idCode).setAttribute('value', 2);
                } else if(filterType == 'P'){
                    document.getElementById('optionConfigWrap'+idCode).setAttribute('class', 'optionConfigWrap');
                    document.getElementById('optionConfigLabel'+idCode).innerHTML = 'Factor: ';
                    document.getElementById('optionConfigInput'+idCode).setAttribute('value', 10);
                }
            },

            'deleteOrBlock' : function(index){
                var block = document.getElementById('filterCondition' + index);
                this.conditionWrap.removeChild(block);
            },

            'deleteRow' : function(index, idCode){
                var row = document.getElementById('filterRow'+idCode);
                document.getElementById('filterTable'+index).removeChild(row);

            },

            'registerNewFilter' : function(){
                var filterConditions = this.querySelectorAll('div.filterCondition'),
                    tableRows,
                    selects,
                    detector, filter, scale, condition,
                    encoded = [],
                    i,j;

                for(i=0; i<filterConditions.length; i++){
                    tableRows = filterConditions[i].querySelectorAll('tr');
                    if(!tableRows || tableRows.length == 0) continue;
                    encoded[i] = [];

                    for(j=0; j<tableRows.length; j++){
                        selects = tableRows[j].querySelectorAll('select');

                        detector = selected(selects[0].id);
                        filter = selected(selects[1].id);
                        scale = parseInt(tableRows[j].querySelectorAll('input')[0].value, 10);
                        if(!scale) scale = 1;

                        condition = detector + '-' + filter + '-' + scale;

                        encoded[i][j] = condition;

                    }
                }

                document.getElementById('encodedFilter').value = JSON.stringify(encoded);
            },

            'registerFilterODB' : function(responseText){
                var data = JSON.parse(responseText),
                    currentName = data.Current,
                    currentFilter = data.Filters[currentName],
                    filterSelect = document.getElementById('filterList'),
                    filterOptions, key;

                this.filterRecord = data;

                this.loadFilter(currentFilter);
                document.getElementById('filterName').value = currentName;
                document.getElementById('currentFilter').innerHTML = 'Current Active Filter: ' + currentName;

                for(key in data.Filters){
                    filterOptions = document.createElement('option');
                    filterOptions.innerHTML = key;
                    filterOptions.value = key;
                    filterSelect.appendChild(filterOptions);

                }

                filterSelect.value = currentName;
            },

            'loadFilter' : function(currentFilter){

            }

        }
    });

})();<commit_msg>messing around with regex<commit_after>(function(){

    xtag.register('widget-Filter', {
        extends: 'div',
        lifecycle: {
            created: function() {
                var spawnCondition, filterTitle, currentFilter,
                    controlWrap = document.createElement('form'),
                    controlRows = [],
                    encodedFilter = document.createElement('input'),
                    applyFilter = document.createElement('input'),
                    filterNameLabel = document.createElement('label'),
                    filterName = document.createElement('input'),
                    saveFilter = document.createElement('button'),
                    chooseFilterLabel = document.createElement('label'),
                    chooseFilter = document.createElement('select'),
                    loadFilter = document.createElement('button'),
                    deleteFilter = document.createElement('button'),
                    loadTarget = document.createElement('input'),
                    deleteTarget = document.createElement('input'),
                    saveLoadFilter = document.createElement('button');

                this.filterConditions = [];
                this.nRows = [];
                this.detectors = ['DANTE', 'DESCANT', 'GRIFFIN', 'PACES', 'SCEPTAR', 'ZDS'];
                this.detectorCodes = ['DA', 'DS', 'GR', 'PA', 'SE', 'ZD'],
                this.filterOptions = ['Singles', 'Coincidences', 'Prescale'];
                this.filterCodes = ['S', 'C', 'P'];

                filterTitle = document.createElement('h1');
                filterTitle.innerHTML = 'Filter Control';
                this.appendChild(filterTitle);
                currentFilter = document.createElement('h3');
                currentFilter.setAttribute('id', 'currentFilter');
                this.appendChild(currentFilter);

                this.conditionWrap = document.createElement('div');
                this.conditionWrap.setAttribute('id', 'conditionWrap');
                this.appendChild(this.conditionWrap);

                this.spawnFilterCondition();

                spawnCondition = document.createElement('button');
                spawnCondition.innerHTML = 'OR new condition';
                spawnCondition.setAttribute('class', 'stdin');
                spawnCondition.onclick = this.spawnFilterCondition.bind(this);
                this.appendChild(spawnCondition);

                controlWrap.setAttribute('class', 'filterControl summary');
                controlWrap.setAttribute('id', 'filterDefinitionForm');
                controlWrap.setAttribute('method', 'POST');
                controlWrap.setAttribute('action', 'registerFilter');
                this.appendChild(controlWrap);

                controlRows[0] = document.createElement('span')
                controlWrap.appendChild(controlRows[0]);

                encodedFilter.setAttribute('type', 'text');
                encodedFilter.setAttribute('id', 'encodedFilter');
                encodedFilter.setAttribute('style', 'display:none');
                encodedFilter.setAttribute('name', 'filterString');
                controlRows[0].appendChild(encodedFilter);

                applyFilter.setAttribute('type', 'checkbox');
                applyFilter.setAttribute('id', 'applyFilter');
                applyFilter.setAttribute('style', 'display:none');
                applyFilter.setAttribute('name', 'applyFilter');
                controlRows[0].appendChild(applyFilter);

                filterNameLabel.innerHTML = 'Filter Name:';
                filterName.setAttribute('class', 'stdin');
                filterName.setAttribute('type', 'text');
                filterName.setAttribute('id', 'filterName');
                filterName.setAttribute('name', 'filterName');
                filterName.setAttribute('pattern',"/\S/g");
                filterName.setAttribute('required', true);
                controlRows[0].appendChild(filterNameLabel);
                controlRows[0].appendChild(filterName);

                saveFilter.setAttribute('class', 'stdin');
                saveFilter.innerHTML = 'Save Filter Definition';
                saveFilter.onclick = this.registerNewFilter.bind(this);
                controlRows[0].appendChild(saveFilter);

                saveLoadFilter.setAttribute('class', 'stdin');
                saveLoadFilter.innerHTML = 'Save & Apply Filter Definition';
                saveLoadFilter.onclick = function(){
                    this.registerNewFilter();
                    document.getElementById('applyFilter').checked = true;
                }.bind(this);
                controlRows[0].appendChild(saveLoadFilter);

                controlRows[1] = document.createElement('span');
                controlWrap.appendChild(controlRows[1]);

                chooseFilterLabel.innerHTML = 'Load / Delete Filter:'
                controlRows[1].appendChild(chooseFilterLabel);
                chooseFilter.setAttribute('class', 'stdin');
                chooseFilter.setAttribute('id', 'filterList');
                controlRows[1].appendChild(chooseFilter);

                loadTarget.setAttribute('type', 'text');
                loadTarget.setAttribute('name', 'loadTarget');
                loadTarget.setAttribute('id', 'loadTarget');
                loadTarget.setAttribute('value', 'null');
                loadTarget.setAttribute('style', 'display:none');
                controlRows[1].appendChild(loadTarget);
                loadFilter.setAttribute('class', 'stdin');
                loadFilter.innerHTML = 'Load'
                loadFilter.onclick = function(){
                    document.getElementById('loadTarget').value = selected('filterList')
                }
                controlRows[1].appendChild(loadFilter);

                deleteTarget.setAttribute('type', 'text');
                deleteTarget.setAttribute('name', 'deleteTarget');
                deleteTarget.setAttribute('id', 'deleteTarget');
                deleteTarget.setAttribute('value', 'null');
                deleteTarget.setAttribute('style', 'display:none');
                controlRows[1].appendChild(deleteTarget);
                deleteFilter.setAttribute('class', 'stdin');
                deleteFilter.innerHTML = 'Delete'
                deleteFilter.onclick = function(){
                    document.getElementById('deleteTarget').value = selected('filterList')
                }
                controlRows[1].appendChild(deleteFilter);

                XHR('http://'+this.MIDAS+'/?cmd=jcopy&odb=/Filter&encoding=json-nokeys', this.registerFilterODB.bind(this));
            },
            inserted: function() {},
            removed: function() {},
            attributeChanged: function() {}
        },
        events: {

        },
        accessors: {
            'MIDAS':{
                attribute: {} //this just needs to be declared
            }
        },
        methods: {
            'spawnFilterCondition' : function(){
                var newCondition = document.createElement('div'),
                    leftTab = document.createElement('div'),
                    filterContent = document.createElement('div'),
                    conditionTable = document.createElement('table'),
                    addNewRow = document.createElement('button'),
                    deleteCondition = document.createElement('button'),
                    index = this.filterConditions.length;

                newCondition.setAttribute('id', 'filterCondition' + index);
                newCondition.setAttribute('class', 'filterCondition');
                this.conditionWrap.appendChild(newCondition);

                leftTab.setAttribute('id', 'leftTab' + index);
                leftTab.setAttribute('class', 'leftTab');
                leftTab.innerHTML = 'OR';
                newCondition.appendChild(leftTab);

                filterContent.setAttribute('id', 'filterContent' + index);
                filterContent.setAttribute('class', 'filterContent');
                newCondition.appendChild(filterContent)

                conditionTable.setAttribute('id', 'filterTable'+index);
                conditionTable.setAttribute('class', 'filterTable');
                filterContent.appendChild(conditionTable);

                addNewRow.setAttribute('id', 'addNewRow' + index);
                addNewRow.setAttribute('class', 'stdin lightButton');
                addNewRow.innerHTML = 'AND new condition';
                addNewRow.onclick = this.addNewRow.bind(this, index);
                filterContent.appendChild(addNewRow);

                deleteCondition.setAttribute('id', 'deleteCondition' + index);
                deleteCondition.setAttribute('class', 'stdin lightButton');
                deleteCondition.innerHTML = 'Delete This Block';
                deleteCondition.onclick = this.deleteOrBlock.bind(this, index);
                filterContent.appendChild(deleteCondition);

                this.nRows[index] = 0;
                addNewRow.onclick();

                this.filterConditions.push(newCondition);
            },

            'addNewRow' : function(index){
                var row, cell,
                    detectorSelect, detectorOption,
                    filterSelect, filterOption,
                    optionConfigWrap, optionConfigLabel, optionConfigInput,
                    deleteRow,
                    i,
                    rowIndex = this.nRows[index]

                row = document.createElement('tr');
                row.setAttribute('id', 'filterRow' + index + rowIndex);
                document.getElementById('filterTable' + index).appendChild(row);

                cell = document.createElement('td');
                cell.innerHTML = 'AND';
                row.appendChild(cell);

                cell = document.createElement('td');
                row.appendChild(cell);
                detectorSelect = document.createElement('select');
                detectorSelect.setAttribute('id', 'detectorSelect'+index+rowIndex);
                detectorSelect.setAttribute('class', 'stdin');
                for(i=0; i<this.detectors.length; i++){
                    detectorOption = document.createElement('option');
                    detectorOption.value = this.detectorCodes[i];
                    detectorOption.innerHTML = this.detectors[i];
                    detectorSelect.appendChild(detectorOption);
                }
                cell.appendChild(detectorSelect);

                cell = document.createElement('td');
                row.appendChild(cell);
                filterSelect = document.createElement('select');
                filterSelect.setAttribute('id', 'filterType'+index+rowIndex)
                filterSelect.setAttribute('class', 'stdin');
                filterSelect.onchange = function(idCode){
                    var filterType = selected('filterType'+idCode);

                    this.manageInputCell(filterType, idCode);

                }.bind(this, ''+index+rowIndex);
                for(i=0; i<this.filterOptions.length; i++){
                    filterOption = document.createElement('option');
                    filterOption.value = this.filterCodes[i];
                    filterOption.innerHTML = this.filterOptions[i];
                    filterSelect.appendChild(filterOption);
                }
                cell.appendChild(filterSelect);

                cell = document.createElement('td');
                row.appendChild(cell);
                optionConfigWrap = document.createElement('div');
                optionConfigWrap.setAttribute('id', 'optionConfigWrap'+index+rowIndex);
                optionConfigWrap.setAttribute('class', 'optionConfigWrap hiddenConfig');
                cell.appendChild(optionConfigWrap);
                optionConfigLabel = document.createElement('label');
                optionConfigLabel.setAttribute('id', 'optionConfigLabel'+index+rowIndex);
                optionConfigWrap.appendChild(optionConfigLabel);
                optionConfigInput = document.createElement('input');
                optionConfigInput.setAttribute('id', 'optionConfigInput'+index+rowIndex);
                optionConfigInput.setAttribute('class', 'stdin');
                optionConfigInput.setAttribute('type', 'number');
                optionConfigInput.setAttribute('step', 1);
                optionConfigInput.setAttribute('min', 0);
                optionConfigWrap.appendChild(optionConfigInput);

                cell = document.createElement('td');
                row.appendChild(cell);
                deleteRow = document.createElement('button');
                deleteRow.innerHTML = 'Remove';
                deleteRow.setAttribute('class', 'stdin');
                deleteRow.onclick = this.deleteRow.bind(this, index, ''+index+rowIndex);
                cell.appendChild(deleteRow);

                this.nRows[index]++;

            },

            'manageInputCell' : function(filterType, idCode){

                if(filterType == 'S'){
                    document.getElementById('optionConfigWrap'+idCode).setAttribute('class', 'optionConfigWrap hiddenConfig');
                } else if(filterType == 'C'){
                    document.getElementById('optionConfigWrap'+idCode).setAttribute('class', 'optionConfigWrap');
                    document.getElementById('optionConfigLabel'+idCode).innerHTML = 'Multiplicity: ';
                    document.getElementById('optionConfigInput'+idCode).setAttribute('value', 2);
                } else if(filterType == 'P'){
                    document.getElementById('optionConfigWrap'+idCode).setAttribute('class', 'optionConfigWrap');
                    document.getElementById('optionConfigLabel'+idCode).innerHTML = 'Factor: ';
                    document.getElementById('optionConfigInput'+idCode).setAttribute('value', 10);
                }
            },

            'deleteOrBlock' : function(index){
                var block = document.getElementById('filterCondition' + index);
                this.conditionWrap.removeChild(block);
            },

            'deleteRow' : function(index, idCode){
                var row = document.getElementById('filterRow'+idCode);
                document.getElementById('filterTable'+index).removeChild(row);

            },

            'registerNewFilter' : function(){
                var filterConditions = this.querySelectorAll('div.filterCondition'),
                    tableRows,
                    selects,
                    detector, filter, scale, condition,
                    encoded = [],
                    i,j;

                for(i=0; i<filterConditions.length; i++){
                    tableRows = filterConditions[i].querySelectorAll('tr');
                    if(!tableRows || tableRows.length == 0) continue;
                    encoded[i] = [];

                    for(j=0; j<tableRows.length; j++){
                        selects = tableRows[j].querySelectorAll('select');

                        detector = selected(selects[0].id);
                        filter = selected(selects[1].id);
                        scale = parseInt(tableRows[j].querySelectorAll('input')[0].value, 10);
                        if(!scale) scale = 1;

                        condition = detector + '-' + filter + '-' + scale;

                        encoded[i][j] = condition;

                    }
                }

                document.getElementById('encodedFilter').value = JSON.stringify(encoded);
            },

            'registerFilterODB' : function(responseText){
                var data = JSON.parse(responseText),
                    currentName = data.Current,
                    currentFilter = data.Filters[currentName],
                    filterSelect = document.getElementById('filterList'),
                    filterOptions, key;

                this.filterRecord = data;

                this.loadFilter(currentFilter);
                document.getElementById('filterName').value = currentName;
                document.getElementById('currentFilter').innerHTML = 'Current Active Filter: ' + currentName;

                for(key in data.Filters){
                    filterOptions = document.createElement('option');
                    filterOptions.innerHTML = key;
                    filterOptions.value = key;
                    filterSelect.appendChild(filterOptions);

                }

                filterSelect.value = currentName;
            },

            'loadFilter' : function(currentFilter){

            }

        }
    });

})();<|endoftext|>


<commit_before>/**
 * Based on http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs
 * @author mrdoob / http://mrdoob.com/
 */

THREE.CSS3DObject = function ( element ) {

	THREE.Object3D.call( this );

	this.element = element;
	this.element.style.position = "absolute";
	this.element.style.WebkitTransformStyle = 'preserve-3d';
	this.element.style.MozTransformStyle = 'preserve-3d';
	this.element.style.oTransformStyle = 'preserve-3d';
	this.element.style.transformStyle = 'preserve-3d';

};

THREE.CSS3DObject.prototype = Object.create( THREE.Object3D.prototype );

THREE.CSS3DSprite = function ( element ) {

	THREE.CSS3DObject.call( this, element );

};

THREE.CSS3DSprite.prototype = Object.create( THREE.CSS3DObject.prototype );

//

THREE.CSS3DRenderer = function () {

	console.log( 'THREE.CSS3DRenderer', THREE.REVISION );

	var _width, _height;
	var _widthHalf, _heightHalf;
	var _projector = new THREE.Projector();

	var _tmpMatrix = new THREE.Matrix4();

	this.domElement = document.createElement( 'div' );

	this.domElement.style.overflow = 'hidden';

	this.domElement.style.WebkitTransformStyle = 'preserve-3d';
	this.domElement.style.WebkitPerspectiveOrigin = '50% 50%';

	this.domElement.style.MozTransformStyle = 'preserve-3d';
	this.domElement.style.MozPerspectiveOrigin = '50% 50%';

	this.domElement.style.oTransformStyle = 'preserve-3d';
	this.domElement.style.oPerspectiveOrigin = '50% 50%';

	this.domElement.style.transformStyle = 'preserve-3d';
	this.domElement.style.perspectiveOrigin = '50% 50%';

	// TODO: Shouldn't it be possible to remove cameraElement?

	this.cameraElement = document.createElement( 'div' );

	this.cameraElement.style.WebkitTransformStyle = 'preserve-3d';
	this.cameraElement.style.MozTransformStyle = 'preserve-3d';
	this.cameraElement.style.oTransformStyle = 'preserve-3d';
	this.cameraElement.style.transformStyle = 'preserve-3d';

	this.domElement.appendChild( this.cameraElement );

	this.setSize = function ( width, height ) {

		_width = width;
		_height = height;

		_widthHalf = _width / 2;
		_heightHalf = _height / 2;

		this.domElement.style.width = width + 'px';
		this.domElement.style.height = height + 'px';

		this.cameraElement.style.width = width + 'px';
		this.cameraElement.style.height = height + 'px';

	};

	var epsilon = function ( value ) {

		return Math.abs( value ) < 0.000001 ? 0 : value;

        };

	var getCameraCSSMatrix = function ( matrix ) {

		var elements = matrix.elements;

		return 'matrix3d(' +
			epsilon( elements[ 0 ] ) + ',' +
			epsilon( - elements[ 1 ] ) + ',' +
			epsilon( elements[ 2 ] ) + ',' +
			epsilon( elements[ 3 ] ) + ',' +
			epsilon( elements[ 4 ] ) + ',' +
			epsilon( - elements[ 5 ] ) + ',' +
			epsilon( elements[ 6 ] ) + ',' +
			epsilon( elements[ 7 ] ) + ',' +
			epsilon( elements[ 8 ] ) + ',' +
			epsilon( - elements[ 9 ] ) + ',' +
			epsilon( elements[ 10 ] ) + ',' +
			epsilon( elements[ 11 ] ) + ',' +
			epsilon( elements[ 12 ] ) + ',' +
			epsilon( - elements[ 13 ] ) + ',' +
			epsilon( elements[ 14 ] ) + ',' +
			epsilon( elements[ 15 ] ) +
		')';

	}

	var getObjectCSSMatrix = function ( matrix ) {

		var elements = matrix.elements;

		return 'translate3d(-50%,-50%,0) matrix3d(' +
			epsilon( elements[ 0 ] ) + ',' +
			epsilon( elements[ 1 ] ) + ',' +
			epsilon( elements[ 2 ] ) + ',' +
			epsilon( elements[ 3 ] ) + ',' +
			epsilon( - elements[ 4 ] ) + ',' +
			epsilon( - elements[ 5 ] ) + ',' +
			epsilon( - elements[ 6 ] ) + ',' +
			epsilon( - elements[ 7 ] ) + ',' +
			epsilon( elements[ 8 ] ) + ',' +
			epsilon( elements[ 9 ] ) + ',' +
			epsilon( elements[ 10 ] ) + ',' +
			epsilon( elements[ 11 ] ) + ',' +
			epsilon( elements[ 12 ] ) + ',' +
			epsilon( elements[ 13 ] ) + ',' +
			epsilon( elements[ 14 ] ) + ',' +
			epsilon( elements[ 15 ] ) +
		')';

	}

	this.render = function ( scene, camera ) {

		var fov = 0.5 / Math.tan( camera.fov * Math.PI / 360 ) * _height;

		this.domElement.style.WebkitPerspective = fov + "px";
		this.domElement.style.MozPerspective = fov + "px";
		this.domElement.style.oPerspective = fov + "px";
		this.domElement.style.perspective = fov + "px";

		var objects = _projector.projectScene( scene, camera, false ).objects;

		var style = "translate3d(0,0," + fov + "px)" + getCameraCSSMatrix( camera.matrixWorldInverse ) + " translate3d(" + _widthHalf + "px," + _heightHalf + "px, 0)";

		this.cameraElement.style.WebkitTransform = style;
		this.cameraElement.style.MozTransform = style;
		this.cameraElement.style.oTransform = style;
		this.cameraElement.style.transform = style;

		for ( var i = 0, il = objects.length; i < il; i ++ ) {

			var object = objects[ i ].object;

			if ( object instanceof THREE.CSS3DObject ) {

				var element = object.element;

				if ( object instanceof THREE.CSS3DSprite ) {

					// http://swiftcoder.wordpress.com/2008/11/25/constructing-a-billboard-matrix/

					_tmpMatrix.copy( camera.matrixWorldInverse );
					_tmpMatrix.transpose();
					_tmpMatrix.extractPosition( object.matrixWorld );

					_tmpMatrix.elements[ 3 ] = 0;
					_tmpMatrix.elements[ 7 ] = 0;
					_tmpMatrix.elements[ 11 ] = 0;
					_tmpMatrix.elements[ 15 ] = 1;

					style = getObjectCSSMatrix( _tmpMatrix );

				} else {

					style = getObjectCSSMatrix( object.matrixWorld );

				}

				/*
				element.style.WebkitBackfaceVisibility = 'hidden';
				element.style.MozBackfaceVisibility = 'hidden';
				element.style.oBackfaceVisibility = 'hidden';
				element.style.backfaceVisibility = 'hidden';
				*/

				element.style.WebkitTransform = style;
				element.style.MozTransform = style;
				element.style.oTransform = style;
				element.style.transform = style;

				if ( element.parentNode !== this.cameraElement ) {

					this.cameraElement.appendChild( element );

				}

			}

		}

	};

};
<commit_msg>Make CSS3DRenderer respect CSS3DSprite scale<commit_after>/**
 * Based on http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs
 * @author mrdoob / http://mrdoob.com/
 */

THREE.CSS3DObject = function ( element ) {

	THREE.Object3D.call( this );

	this.element = element;
	this.element.style.position = "absolute";
	this.element.style.WebkitTransformStyle = 'preserve-3d';
	this.element.style.MozTransformStyle = 'preserve-3d';
	this.element.style.oTransformStyle = 'preserve-3d';
	this.element.style.transformStyle = 'preserve-3d';

};

THREE.CSS3DObject.prototype = Object.create( THREE.Object3D.prototype );

THREE.CSS3DSprite = function ( element ) {

	THREE.CSS3DObject.call( this, element );

};

THREE.CSS3DSprite.prototype = Object.create( THREE.CSS3DObject.prototype );

//

THREE.CSS3DRenderer = function () {

	console.log( 'THREE.CSS3DRenderer', THREE.REVISION );

	var _width, _height;
	var _widthHalf, _heightHalf;
	var _projector = new THREE.Projector();

	var _tmpMatrix = new THREE.Matrix4();

	this.domElement = document.createElement( 'div' );

	this.domElement.style.overflow = 'hidden';

	this.domElement.style.WebkitTransformStyle = 'preserve-3d';
	this.domElement.style.WebkitPerspectiveOrigin = '50% 50%';

	this.domElement.style.MozTransformStyle = 'preserve-3d';
	this.domElement.style.MozPerspectiveOrigin = '50% 50%';

	this.domElement.style.oTransformStyle = 'preserve-3d';
	this.domElement.style.oPerspectiveOrigin = '50% 50%';

	this.domElement.style.transformStyle = 'preserve-3d';
	this.domElement.style.perspectiveOrigin = '50% 50%';

	// TODO: Shouldn't it be possible to remove cameraElement?

	this.cameraElement = document.createElement( 'div' );

	this.cameraElement.style.WebkitTransformStyle = 'preserve-3d';
	this.cameraElement.style.MozTransformStyle = 'preserve-3d';
	this.cameraElement.style.oTransformStyle = 'preserve-3d';
	this.cameraElement.style.transformStyle = 'preserve-3d';

	this.domElement.appendChild( this.cameraElement );

	this.setSize = function ( width, height ) {

		_width = width;
		_height = height;

		_widthHalf = _width / 2;
		_heightHalf = _height / 2;

		this.domElement.style.width = width + 'px';
		this.domElement.style.height = height + 'px';

		this.cameraElement.style.width = width + 'px';
		this.cameraElement.style.height = height + 'px';

	};

	var epsilon = function ( value ) {

		return Math.abs( value ) < 0.000001 ? 0 : value;

        };

	var getCameraCSSMatrix = function ( matrix ) {

		var elements = matrix.elements;

		return 'matrix3d(' +
			epsilon( elements[ 0 ] ) + ',' +
			epsilon( - elements[ 1 ] ) + ',' +
			epsilon( elements[ 2 ] ) + ',' +
			epsilon( elements[ 3 ] ) + ',' +
			epsilon( elements[ 4 ] ) + ',' +
			epsilon( - elements[ 5 ] ) + ',' +
			epsilon( elements[ 6 ] ) + ',' +
			epsilon( elements[ 7 ] ) + ',' +
			epsilon( elements[ 8 ] ) + ',' +
			epsilon( - elements[ 9 ] ) + ',' +
			epsilon( elements[ 10 ] ) + ',' +
			epsilon( elements[ 11 ] ) + ',' +
			epsilon( elements[ 12 ] ) + ',' +
			epsilon( - elements[ 13 ] ) + ',' +
			epsilon( elements[ 14 ] ) + ',' +
			epsilon( elements[ 15 ] ) +
		')';

	}

	var getObjectCSSMatrix = function ( matrix ) {

		var elements = matrix.elements;

		return 'translate3d(-50%,-50%,0) matrix3d(' +
			epsilon( elements[ 0 ] ) + ',' +
			epsilon( elements[ 1 ] ) + ',' +
			epsilon( elements[ 2 ] ) + ',' +
			epsilon( elements[ 3 ] ) + ',' +
			epsilon( - elements[ 4 ] ) + ',' +
			epsilon( - elements[ 5 ] ) + ',' +
			epsilon( - elements[ 6 ] ) + ',' +
			epsilon( - elements[ 7 ] ) + ',' +
			epsilon( elements[ 8 ] ) + ',' +
			epsilon( elements[ 9 ] ) + ',' +
			epsilon( elements[ 10 ] ) + ',' +
			epsilon( elements[ 11 ] ) + ',' +
			epsilon( elements[ 12 ] ) + ',' +
			epsilon( elements[ 13 ] ) + ',' +
			epsilon( elements[ 14 ] ) + ',' +
			epsilon( elements[ 15 ] ) +
		')';

	}

	this.render = function ( scene, camera ) {

		var fov = 0.5 / Math.tan( camera.fov * Math.PI / 360 ) * _height;

		this.domElement.style.WebkitPerspective = fov + "px";
		this.domElement.style.MozPerspective = fov + "px";
		this.domElement.style.oPerspective = fov + "px";
		this.domElement.style.perspective = fov + "px";

		var objects = _projector.projectScene( scene, camera, false ).objects;

		var style = "translate3d(0,0," + fov + "px)" + getCameraCSSMatrix( camera.matrixWorldInverse ) + " translate3d(" + _widthHalf + "px," + _heightHalf + "px, 0)";

		this.cameraElement.style.WebkitTransform = style;
		this.cameraElement.style.MozTransform = style;
		this.cameraElement.style.oTransform = style;
		this.cameraElement.style.transform = style;

		for ( var i = 0, il = objects.length; i < il; i ++ ) {

			var object = objects[ i ].object;

			if ( object instanceof THREE.CSS3DObject ) {

				var element = object.element;

				if ( object instanceof THREE.CSS3DSprite ) {

					// http://swiftcoder.wordpress.com/2008/11/25/constructing-a-billboard-matrix/

					_tmpMatrix.copy( camera.matrixWorldInverse );
					_tmpMatrix.transpose();
					_tmpMatrix.extractPosition( object.matrixWorld );
					_tmpMatrix.scale( object.scale );

					_tmpMatrix.elements[ 3 ] = 0;
					_tmpMatrix.elements[ 7 ] = 0;
					_tmpMatrix.elements[ 11 ] = 0;
					_tmpMatrix.elements[ 15 ] = 1;

					style = getObjectCSSMatrix( _tmpMatrix );

				} else {

					style = getObjectCSSMatrix( object.matrixWorld );

				}

				/*
				element.style.WebkitBackfaceVisibility = 'hidden';
				element.style.MozBackfaceVisibility = 'hidden';
				element.style.oBackfaceVisibility = 'hidden';
				element.style.backfaceVisibility = 'hidden';
				*/

				element.style.WebkitTransform = style;
				element.style.MozTransform = style;
				element.style.oTransform = style;
				element.style.transform = style;

				if ( element.parentNode !== this.cameraElement ) {

					this.cameraElement.appendChild( element );

				}

			}

		}

	};

};
<|endoftext|>


<commit_before>import { zipSync, strToU8 } from '../libs/fflate.module.min.js';

class USDZExporter {

	async parse( scene ) {

		let output = buildHeader();

		const materials = {};
		const textures = {};

		scene.traverse( ( object ) => {

			if ( object.isMesh ) {

				const geometry = object.geometry;
				const material = object.material;

				materials[ material.uuid ] = material;

				if ( material.map !== null ) textures[ material.map.uuid ] = material.map;
				if ( material.normalMap !== null ) textures[ material.normalMap.uuid ] = material.normalMap;
				if ( material.aoMap !== null ) textures[ material.aoMap.uuid ] = material.aoMap;
				if ( material.roughnessMap !== null ) textures[ material.roughnessMap.uuid ] = material.roughnessMap;
				if ( material.metalnessMap !== null ) textures[ material.metalnessMap.uuid ] = material.metalnessMap;
				if ( material.emissiveMap !== null ) textures[ material.emissiveMap.uuid ] = material.emissiveMap;

				output += buildXform( object, buildMesh( geometry, material ) );

			}

		} );

		output += buildMaterials( materials );
		output += buildTextures( textures );

		const files = {};

		for ( const uuid in textures ) {

			const texture = textures[ uuid ];
			files[ 'Texture_' + texture.id + '.jpg' ] = await imgToU8( texture.image );

		}

		return zipSync( { 'model.usda': strToU8( output ), 'textures': files }, { level: 0 } );

	}

}

async function imgToU8( image ) {

	if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
		( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
		( typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas ) ||
		( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {

		const canvas = document.createElement( 'canvas' );
		canvas.width = Math.min( 1024, image.width );
		canvas.height = Math.min( 1024, image.height );

		const context = canvas.getContext( '2d' );
		context.drawImage( image, 0, 0, canvas.width, canvas.height );

		const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/jpeg', 1 ) );
		return new Uint8Array( await blob.arrayBuffer() );

	}

}

//

function buildHeader() {

	return `#usda 1.0
(
    doc = "Three.js"
    metersPerUnit = 1
    upAxis = "Y"
)

`;

}

// Xform

function buildXform( object, define ) {

	const name = 'Object_' + object.id;
	const transform = buildMatrix( object.matrixWorld );

	return `def Xform "${ name }"
{
    matrix4d xformOp:transform = ${ transform }
    uniform token[] xformOpOrder = ["xformOp:transform"]

    ${ define }
}

`;

}

function buildMatrix( matrix ) {

	const array = matrix.elements;

	return `( ${ buildMatrixRow( array, 0 ) }, ${ buildMatrixRow( array, 4 ) }, ${ buildMatrixRow( array, 8 ) }, ${ buildMatrixRow( array, 12 ) } )`;

}

function buildMatrixRow( array, offset ) {

	return `(${ array[ offset + 0 ] }, ${ array[ offset + 1 ] }, ${ array[ offset + 2 ] }, ${ array[ offset + 3 ] })`;

}

// Mesh

function buildMesh( geometry, material ) {

	const name = 'Geometry_' + geometry.id;
	const attributes = geometry.attributes;
	const count = attributes.position.count;

	return `def Mesh "${ name }"
    {
        int[] faceVertexCounts = [${ buildMeshVertexCount( geometry ) }]
        int[] faceVertexIndices = [${ buildMeshVertexIndices( geometry ) }]
        rel material:binding = </Materials/Material_${ material.id }>
        normal3f[] normals = [${ buildVector3Array( attributes.normal, count )}] (
            interpolation = "vertex"
        )
        point3f[] points = [${ buildVector3Array( attributes.position, count )}]
        float2[] primvars:st = [${ buildVector2Array( attributes.uv, count )}] (
            elementSize = 1
            interpolation = "vertex"
        )
        int[] primvars:st:indices
        uniform token subdivisionScheme = "none"
    }
`;

}

function buildMeshVertexCount( geometry ) {

	const count = geometry.index !== null ? geometry.index.array.length : geometry.attributes.position.count;

	return Array( count / 3 ).fill( 3 ).join( ', ' );

}

function buildMeshVertexIndices( geometry ) {

	if ( geometry.index !== null ) {

		return geometry.index.array.join( ', ' );

	}

	const array = [];
	const length = geometry.attributes.position.count;

	for ( let i = 0; i < length; i ++ ) {

		array.push( i );

	}

	return array.join( ', ' );

}

function buildVector3Array( attribute, count ) {

	if ( attribute === undefined ) {

		console.warn( 'USDZExporter: Normals missing.' );
		return Array( count ).fill( '(0, 0, 0)' ).join( ', ' );

	}

	const array = [];
	const data = attribute.array;

	for ( let i = 0; i < data.length; i += 3 ) {

		array.push( `(${ data[ i + 0 ] }, ${ data[ i + 1 ] }, ${ data[ i + 2 ] })` );

	}

	return array.join( ', ' );

}

function buildVector2Array( attribute, count ) {

	if ( attribute === undefined ) {

		console.warn( 'USDZExporter: UVs missing.' );
		return Array( count ).fill( '(0, 0)' ).join( ', ' );

	}

	const array = [];
	const data = attribute.array;

	for ( let i = 0; i < data.length; i += 2 ) {

		array.push( `(${ data[ i + 0 ] }, ${ 1 - data[ i + 1 ] })` );

	}

	return array.join( ', ' );

}

// Materials

function buildMaterials( materials ) {

	const array = [];

	for ( const uuid in materials ) {

		const material = materials[ uuid ];

		array.push( buildMaterial( material ) );

	}

	return `def "Materials"
{
${ array.join( '' ) }
}

`;

}

function buildMaterial( material ) {

	// https://graphics.pixar.com/usd/docs/UsdPreviewSurface-Proposal.html

	const pad = '            ';
	const parameters = [];

	if ( material.map !== null ) {

		parameters.push( `${ pad }color3f inputs:diffuseColor.connect = </Textures/Texture_${ material.map.id }.outputs:rgb>` );

	} else {

		parameters.push( `${ pad }color3f inputs:diffuseColor = ${ buildColor( material.color ) }` );

	}

	if ( material.emissiveMap !== null ) {

		parameters.push( `${ pad }color3f inputs:emissiveColor.connect = </Textures/Texture_${ material.emissiveMap.id }.outputs:rgb>` );

	} else {

		parameters.push( `${ pad }color3f inputs:emissiveColor = ${ buildColor( material.emissive ) }` );

	}

	if ( material.normalMap !== null ) {

		parameters.push( `${ pad }normal3f inputs:normal.connect = </Textures/Texture_${ material.normalMap.id }.outputs:rgb>` );

	}

	if ( material.aoMap !== null ) {

		parameters.push( `${ pad }float inputs:occlusion.connect = </Textures/Texture_${ material.aoMap.id }.outputs:r>` );

	}

	if ( material.roughnessMap !== null ) {

		parameters.push( `${ pad }float inputs:roughness.connect = </Textures/Texture_${ material.roughnessMap.id }.outputs:g>` );

	} else {

		parameters.push( `${ pad }float inputs:roughness = ${ material.roughness }` );

	}

	if ( material.metalnessMap !== null ) {

		parameters.push( `${ pad }float inputs:metallic.connect = </Textures/Texture_${ material.metalnessMap.id }.outputs:b>` );

	} else {

		parameters.push( `${ pad }float inputs:metallic = ${ material.metalness }` );

	}

	return `
    def Material "Material_${ material.id }"
    {
        token outputs:surface.connect = </Materials/Material_${ material.id }/PreviewSurface.outputs:surface>

        def Shader "PreviewSurface"
        {
            uniform token info:id = "UsdPreviewSurface"
${ parameters.join( '\n' ) }
            int inputs:useSpecularWorkflow = 0
            token outputs:surface
        }
    }
`;

}

function buildTextures( textures ) {

	const array = [];

	for ( const uuid in textures ) {

		const texture = textures[ uuid ];

		array.push( buildTexture( texture ) );

	}

	return `def "Textures"
{
${ array.join( '' ) }
}

`;

}

function buildTexture( texture ) {

	return `
    def Shader "Texture_${ texture.id }"
    {
        uniform token info:id = "UsdUVTexture"
        asset inputs:file = @textures/Texture_${ texture.id }.jpg@
        token inputs:isSRGB = "auto"
        token inputs:wrapS = "repeat"
        token inputs:wrapT = "repeat"
        float outputs:r
        float outputs:g
        float outputs:b
        float3 outputs:rgb
    }
`;

}

function buildColor( color ) {

	return `(${ color.r }, ${ color.g }, ${ color.b })`;

}

export { USDZExporter };
<commit_msg>USDZExporter: Clean up<commit_after>import { zipSync, strToU8 } from '../libs/fflate.module.min.js';

class USDZExporter {

	async parse( scene ) {

		let output = buildHeader();

		const materials = {};
		const textures = {};

		scene.traverse( ( object ) => {

			if ( object.isMesh ) {

				const geometry = object.geometry;
				const material = object.material;

				materials[ material.uuid ] = material;

				if ( material.map !== null ) textures[ material.map.uuid ] = material.map;
				if ( material.normalMap !== null ) textures[ material.normalMap.uuid ] = material.normalMap;
				if ( material.aoMap !== null ) textures[ material.aoMap.uuid ] = material.aoMap;
				if ( material.roughnessMap !== null ) textures[ material.roughnessMap.uuid ] = material.roughnessMap;
				if ( material.metalnessMap !== null ) textures[ material.metalnessMap.uuid ] = material.metalnessMap;
				if ( material.emissiveMap !== null ) textures[ material.emissiveMap.uuid ] = material.emissiveMap;

				output += buildXform( object, buildMesh( geometry, material ) );

			}

		} );

		output += buildMaterials( materials );
		output += buildTextures( textures );

		const files = {};

		for ( const uuid in textures ) {

			const texture = textures[ uuid ];
			files[ 'Texture_' + texture.id + '.jpg' ] = await imgToU8( texture.image );

		}

		return zipSync( { 'model.usda': strToU8( output ), 'textures': files }, { level: 0 } );

	}

}

async function imgToU8( image ) {

	if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
		( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
		( typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas ) ||
		( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {

		const canvas = document.createElement( 'canvas' );
		canvas.width = Math.min( 1024, image.width );
		canvas.height = Math.min( 1024, image.height );

		const context = canvas.getContext( '2d' );
		context.drawImage( image, 0, 0, canvas.width, canvas.height );

		const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/jpeg', 1 ) );
		return new Uint8Array( await blob.arrayBuffer() );

	}

}

//

function buildHeader() {

	return `#usda 1.0
(
    customLayerData = {
        string creator = "Three.js USDZExporter"
    }
    metersPerUnit = 1
    upAxis = "Y"
)

`;

}

// Xform

function buildXform( object, define ) {

	const name = 'Object_' + object.id;
	const transform = buildMatrix( object.matrixWorld );

	return `def Xform "${ name }"
{
    matrix4d xformOp:transform = ${ transform }
    uniform token[] xformOpOrder = ["xformOp:transform"]

    ${ define }
}

`;

}

function buildMatrix( matrix ) {

	const array = matrix.elements;

	return `( ${ buildMatrixRow( array, 0 ) }, ${ buildMatrixRow( array, 4 ) }, ${ buildMatrixRow( array, 8 ) }, ${ buildMatrixRow( array, 12 ) } )`;

}

function buildMatrixRow( array, offset ) {

	return `(${ array[ offset + 0 ] }, ${ array[ offset + 1 ] }, ${ array[ offset + 2 ] }, ${ array[ offset + 3 ] })`;

}

// Mesh

function buildMesh( geometry, material ) {

	const name = 'Geometry_' + geometry.id;
	const attributes = geometry.attributes;
	const count = attributes.position.count;

	return `def Mesh "${ name }"
    {
        int[] faceVertexCounts = [${ buildMeshVertexCount( geometry ) }]
        int[] faceVertexIndices = [${ buildMeshVertexIndices( geometry ) }]
        rel material:binding = </Materials/Material_${ material.id }>
        normal3f[] normals = [${ buildVector3Array( attributes.normal, count )}] (
            interpolation = "vertex"
        )
        point3f[] points = [${ buildVector3Array( attributes.position, count )}]
        float2[] primvars:st = [${ buildVector2Array( attributes.uv, count )}] (
            elementSize = 1
            interpolation = "vertex"
        )
        int[] primvars:st:indices
        uniform token subdivisionScheme = "none"
    }
`;

}

function buildMeshVertexCount( geometry ) {

	const count = geometry.index !== null ? geometry.index.array.length : geometry.attributes.position.count;

	return Array( count / 3 ).fill( 3 ).join( ', ' );

}

function buildMeshVertexIndices( geometry ) {

	if ( geometry.index !== null ) {

		return geometry.index.array.join( ', ' );

	}

	const array = [];
	const length = geometry.attributes.position.count;

	for ( let i = 0; i < length; i ++ ) {

		array.push( i );

	}

	return array.join( ', ' );

}

function buildVector3Array( attribute, count ) {

	if ( attribute === undefined ) {

		console.warn( 'USDZExporter: Normals missing.' );
		return Array( count ).fill( '(0, 0, 0)' ).join( ', ' );

	}

	const array = [];
	const data = attribute.array;

	for ( let i = 0; i < data.length; i += 3 ) {

		array.push( `(${ data[ i + 0 ] }, ${ data[ i + 1 ] }, ${ data[ i + 2 ] })` );

	}

	return array.join( ', ' );

}

function buildVector2Array( attribute, count ) {

	if ( attribute === undefined ) {

		console.warn( 'USDZExporter: UVs missing.' );
		return Array( count ).fill( '(0, 0)' ).join( ', ' );

	}

	const array = [];
	const data = attribute.array;

	for ( let i = 0; i < data.length; i += 2 ) {

		array.push( `(${ data[ i + 0 ] }, ${ 1 - data[ i + 1 ] })` );

	}

	return array.join( ', ' );

}

// Materials

function buildMaterials( materials ) {

	const array = [];

	for ( const uuid in materials ) {

		const material = materials[ uuid ];

		array.push( buildMaterial( material ) );

	}

	return `def "Materials"
{
${ array.join( '' ) }
}

`;

}

function buildMaterial( material ) {

	// https://graphics.pixar.com/usd/docs/UsdPreviewSurface-Proposal.html

	const pad = '            ';
	const parameters = [];

	if ( material.map !== null ) {

		parameters.push( `${ pad }color3f inputs:diffuseColor.connect = </Textures/Texture_${ material.map.id }.outputs:rgb>` );

	} else {

		parameters.push( `${ pad }color3f inputs:diffuseColor = ${ buildColor( material.color ) }` );

	}

	if ( material.emissiveMap !== null ) {

		parameters.push( `${ pad }color3f inputs:emissiveColor.connect = </Textures/Texture_${ material.emissiveMap.id }.outputs:rgb>` );

	} else {

		parameters.push( `${ pad }color3f inputs:emissiveColor = ${ buildColor( material.emissive ) }` );

	}

	if ( material.normalMap !== null ) {

		parameters.push( `${ pad }normal3f inputs:normal.connect = </Textures/Texture_${ material.normalMap.id }.outputs:rgb>` );

	}

	if ( material.aoMap !== null ) {

		parameters.push( `${ pad }float inputs:occlusion.connect = </Textures/Texture_${ material.aoMap.id }.outputs:r>` );

	}

	if ( material.roughnessMap !== null ) {

		parameters.push( `${ pad }float inputs:roughness.connect = </Textures/Texture_${ material.roughnessMap.id }.outputs:g>` );

	} else {

		parameters.push( `${ pad }float inputs:roughness = ${ material.roughness }` );

	}

	if ( material.metalnessMap !== null ) {

		parameters.push( `${ pad }float inputs:metallic.connect = </Textures/Texture_${ material.metalnessMap.id }.outputs:b>` );

	} else {

		parameters.push( `${ pad }float inputs:metallic = ${ material.metalness }` );

	}

	return `
    def Material "Material_${ material.id }"
    {
        token outputs:surface.connect = </Materials/Material_${ material.id }/PreviewSurface.outputs:surface>

        def Shader "PreviewSurface"
        {
            uniform token info:id = "UsdPreviewSurface"
${ parameters.join( '\n' ) }
            int inputs:useSpecularWorkflow = 0
            token outputs:surface
        }
    }
`;

}

function buildTextures( textures ) {

	const array = [];

	for ( const uuid in textures ) {

		const texture = textures[ uuid ];

		array.push( buildTexture( texture ) );

	}

	return `def "Textures"
{
${ array.join( '' ) }
}

`;

}

function buildTexture( texture ) {

	return `
    def Shader "Texture_${ texture.id }"
    {
        uniform token info:id = "UsdUVTexture"
        asset inputs:file = @textures/Texture_${ texture.id }.jpg@
        token inputs:wrapS = "repeat"
        token inputs:wrapT = "repeat"
        float outputs:r
        float outputs:g
        float outputs:b
        float3 outputs:rgb
    }
`;

}

function buildColor( color ) {

	return `(${ color.r }, ${ color.g }, ${ color.b })`;

}

export { USDZExporter };
<|endoftext|>


<commit_before>/*jslint nomen: true, todo: true */
"use strict";

var _ = require('lodash'),
  util = require("util"),
  utils = require("./utils"),
  flooconn = require("./flooconn"),
  FlooUrl = require("./floourl").FlooUrl,
  FlooHandler = require("./floohandler").FlooHandler,
  flooview = require("./floobitsview"),
  listener = require("./listener"),
  recentworkspaceview = require("./recentworkspaceview");

var Floobits = function () {
  atom.commands.add("atom-workspace", {
    "floobits:JoinWorkspace": this.join_workspace.bind(this),
    "floobits:LeaveWorkspace": this.leave_workspace.bind(this),
    "floobits:JoinRecentWorkspace": this.join_recent_workspace.bind(this),
  });
};

Floobits.prototype.join_recent_workspace = function () {

  var view = new recentworkspaceview.RecentWorkspaceView();
  // new asdf();
};

Floobits.prototype.join_workspace = function (url) {
  var self = this,
    floorc = utils.load_floorc(),
    root = atom.project.getRootDirectory(),
    floo,
    floourl,
    parsed_url;

  listener.start();
  if (root) {
    floo = utils.load_floo(root.path);
    url = url || floo.url;
  }
  parsed_url = utils.parse_url(url);
  floourl = new FlooUrl(parsed_url.owner, parsed_url.workspace, parsed_url.host, parsed_url.port);
  self.handler = new FlooHandler(floourl, floorc, root.path);
  self.handler.start();
  self.FloobitsView = new flooview.FloobitsView(util.format("You joined: %s", floourl.toString()));
  atom.workspaceView.append(self.FloobitsView);
  setTimeout(function () {
    self.FloobitsView.empty();
  }, 3000);
};

Floobits.prototype.leave_workspace = function () {
  console.log("leaving");
  if (this.handler){
    this.handler.stop();
    this.handler = null;
  }
  listener.stop();
};

module.exports = {
  floobits: null,
  activate: function (state) {
    var self = this,
      pathToFloobits;

    this.floobits = new Floobits();

    function shouldJoinWorkspace (pathToFloobits) {
      // TODO: check if we are currently connected
      var dir = atom.project.getRootDirectory();
      console.error("shouldJoinWorkspace", pathToFloobits, dir && dir.path);
      return dir && pathToFloobits && dir.path === pathToFloobits;
    }

    function joinWorkspace () {
      var url = atom.config.get('floobits.atoms-api-sucks-url');
      atom.config.set('floobits.atoms-api-sucks-path', null);
      console.log("found pathToFloobits, will floobits");
      self.floobits.join_workspace(url);
    }

    atom.config.observe('floobits.atoms-api-sucks-path', function (value) {
      if (shouldJoinWorkspace(value)) {
        joinWorkspace();
      }
    });

    pathToFloobits = atom.config.get('floobits.atoms-api-sucks-path');
    if (shouldJoinWorkspace(pathToFloobits)) {
      joinWorkspace();
    }
  },
  deactivate: function () {
    this.floobits.leave_workspace();
  },
  serialize: function () {
  }
};
<commit_msg>Detach the DOM element, don't just leave an empty one sitting around.<commit_after>/*jslint nomen: true, todo: true */
"use strict";

var _ = require('lodash'),
  util = require("util"),
  utils = require("./utils"),
  flooconn = require("./flooconn"),
  FlooUrl = require("./floourl").FlooUrl,
  FlooHandler = require("./floohandler").FlooHandler,
  flooview = require("./floobitsview"),
  listener = require("./listener"),
  recentworkspaceview = require("./recentworkspaceview");

var Floobits = function () {
  atom.commands.add("atom-workspace", {
    "floobits:JoinWorkspace": this.join_workspace.bind(this),
    "floobits:LeaveWorkspace": this.leave_workspace.bind(this),
    "floobits:JoinRecentWorkspace": this.join_recent_workspace.bind(this),
  });
};

Floobits.prototype.join_recent_workspace = function () {

  var view = new recentworkspaceview.RecentWorkspaceView();
  // new asdf();
};

Floobits.prototype.join_workspace = function (url) {
  var self = this,
    floorc = utils.load_floorc(),
    root = atom.project.getRootDirectory(),
    floo,
    floourl,
    parsed_url;

  listener.start();
  if (root) {
    floo = utils.load_floo(root.path);
    url = url || floo.url;
  }
  parsed_url = utils.parse_url(url);
  floourl = new FlooUrl(parsed_url.owner, parsed_url.workspace, parsed_url.host, parsed_url.port);
  self.handler = new FlooHandler(floourl, floorc, root.path);
  self.handler.start();
  self.FloobitsView = new flooview.FloobitsView(util.format("You joined: %s", floourl.toString()));
  atom.workspaceView.append(self.FloobitsView);
  setTimeout(function () {
    self.FloobitsView.detach();
  }, 3000);
};

Floobits.prototype.leave_workspace = function () {
  console.log("leaving");
  if (this.handler){
    this.handler.stop();
    this.handler = null;
  }
  listener.stop();
};

module.exports = {
  floobits: null,
  activate: function (state) {
    var self = this,
      pathToFloobits;

    this.floobits = new Floobits();

    function shouldJoinWorkspace (pathToFloobits) {
      // TODO: check if we are currently connected
      var dir = atom.project.getRootDirectory();
      console.error("shouldJoinWorkspace", pathToFloobits, dir && dir.path);
      return dir && pathToFloobits && dir.path === pathToFloobits;
    }

    function joinWorkspace () {
      var url = atom.config.get('floobits.atoms-api-sucks-url');
      atom.config.set('floobits.atoms-api-sucks-path', null);
      console.log("found pathToFloobits, will floobits");
      self.floobits.join_workspace(url);
    }

    atom.config.observe('floobits.atoms-api-sucks-path', function (value) {
      if (shouldJoinWorkspace(value)) {
        joinWorkspace();
      }
    });

    pathToFloobits = atom.config.get('floobits.atoms-api-sucks-path');
    if (shouldJoinWorkspace(pathToFloobits)) {
      joinWorkspace();
    }
  },
  deactivate: function () {
    this.floobits.leave_workspace();
  },
  serialize: function () {
  }
};
<|endoftext|>