var components = require('./component'); var schema = require('./schema'); var utils = require('../utils/'); var parseProperties = schema.parseProperties; var parseProperty = schema.parseProperty; var processSchema = schema.process; var isSingleProp = schema.isSingleProperty; var styleParser = utils.styleParser; var systems = module.exports.systems = {}; // Keep track of registered systems. /** * System class definition. * * Systems provide global scope and services to a group of instantiated components of the * same class. They can also help abstract logic away from components such that components * only have to worry about data. * * For example, a physics component that creates a physics world that oversees * all entities with a physics or rigid body component. * * TODO: Have the System prototype reuse the Component prototype. Most code is copied * and some pieces are missing from the Component facilities (e.g., attribute caching, * setAttribute behavior). * * @member {string} name - Name that system is registered under. * @member {Element} sceneEl - Handle to the scene element where system applies to. */ var System = module.exports.System = function (sceneEl) { var component = components && components.components[this.name]; // Set reference to scene. this.el = sceneEl; this.sceneEl = sceneEl; // Set reference to matching component (if exists). if (component) { component.Component.prototype.system = this; } // Process system configuration. this.buildData(); this.init(); this.update({}); }; System.prototype = { /** * Schema to configure system. */ schema: {}, /** * Init handler. Called during scene initialization and is only run once. * Systems can use this to set initial state. */ init: function () { /* no-op */ }, /** * Update handler. Called during scene attribute updates. * Systems can use this to dynamically update their state. */ update: function (oldData) { /* no-op */ }, /** * Build data and call update handler. * * @private */ updateProperties: function (rawData) { var oldData = this.data; if (!Object.keys(schema).length) { return; } this.buildData(rawData); this.update(oldData); }, /** * Parse data. */ buildData: function (rawData) { var schema = this.schema; if (!Object.keys(schema).length) { return; } rawData = rawData || window.HTMLElement.prototype.getAttribute.call(this.sceneEl, this.name); if (isSingleProp(schema)) { this.data = parseProperty(rawData, schema); } else { this.data = parseProperties(styleParser.parse(rawData) || {}, schema); } }, /** * Tick handler. * Called on each tick of the scene render loop. * Affected by play and pause. * * @param {number} time - Scene tick time. * @param {number} timeDelta - Difference in current render time and previous render time. */ tick: undefined, /** * Tock handler. * Called on each tock of the scene render loop. * Affected by play and pause. * * @param {number} time - Scene tick time. * @param {number} timeDelta - Difference in current render time and previous render time. */ tock: undefined, /** * Called to start any dynamic behavior (e.g., animation, AI, events, physics). */ play: function () { /* no-op */ }, /** * Called to stop any dynamic behavior (e.g., animation, AI, events, physics). */ pause: function () { /* no-op */ } }; /** * Registers a system to A-Frame. * * @param {string} name - Component name. * @param {object} definition - Component property and methods. * @returns {object} Component. */ module.exports.registerSystem = function (name, definition) { var i; var NewSystem; var proto = {}; var scenes = utils.findAllScenes(document); // Format definition object to prototype object. Object.keys(definition).forEach(function (key) { proto[key] = { value: definition[key], writable: true }; }); if (systems[name]) { throw new Error('The system `' + name + '` has been already registered. ' + 'Check that you are not loading two versions of the same system ' + 'or two different systems of the same name.'); } NewSystem = function (sceneEl) { System.call(this, sceneEl); }; NewSystem.prototype = Object.create(System.prototype, proto); NewSystem.prototype.name = name; NewSystem.prototype.constructor = NewSystem; NewSystem.prototype.schema = utils.extend(processSchema(NewSystem.prototype.schema)); systems[name] = NewSystem; // Initialize systems for existing scenes for (i = 0; i < scenes.length; i++) { scenes[i].initSystem(name); } };