'use strict'; const dotty = require("dotty"); const defaultValue = require("default-value"); const createObject = require("./create-object"); const createPropertyMapper = require("./property-mapper"); const requireProperties = require("./require-properties"); const max = require("./util/max"); const min = require("./util/min"); function getPosition(object) { return { x: defaultValue(object.x, 0), y: defaultValue(object.y, 0) } } module.exports = function createCompositeObject(options) { requireProperties(options, ["drawOrder", "objects"]); let mapProperties = createPropertyMapper(options.propertyMap); let initialPropertySet = mapProperties(options); let compositeObject = createObject(Object.assign({ onSet: function setCompositeProperties(properties) { let mappedProperties = mapProperties(properties); Object.keys(mappedProperties).forEach((objectName) => { if (objectName !== "_") { if (this.objects[objectName] != null) { this.objects[objectName].set(mappedProperties[objectName]); } else { throw new Error(`The object specified in the property map as '${objectName}' does not exist`); } } }); /* We return the `_` object here, which contains all the unmapped * properties - we assume that all unmapped properties are meant to * apply to the composite object rather than its child objects. The * original `.set` method (as defined in createObject) will then use * this result. */ return defaultValue(mappedProperties._, {}); }, onRender: function onRender(context) { this.drawOrder.forEach((objectName) => { let object = this.objects[objectName]; let position = getPosition(object); object.render(context, { x: object.x - this.offsetX, y: object.y - this.offsetY }); }); }, onRecalculateSize: function onRecalculateSize() { let objectMetrics = Object.keys(this.objects).map((objectName) => { let object = this.objects[objectName]; let position = getPosition(object); return { x1: position.x, y1: position.y, x2: position.x + object.renderWidth, y2: position.y + object.renderHeight } }); let x1 = min(objectMetrics.map(object => object.x1)); let y1 = min(objectMetrics.map(object => object.y1)); let x2 = max(objectMetrics.map(object => object.x2)); let y2 = max(objectMetrics.map(object => object.y2)); this.offsetX = x1; this.offsetY = y1; return { width: x2 - x1, height: y2 - y1 } } }, initialPropertySet._)); Object.keys(compositeObject.objects).forEach((objectName) => { compositeObject.objects[objectName].on("recalculatedSize", () => { compositeObject.recalculateSize(); }); compositeObject.objects[objectName].on("bustedCache", () => { compositeObject.bustCache(); }); }); compositeObject.set(options); return compositeObject; };