You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

187 lines
4.4 KiB
CoffeeScript

uuid = require "uuid"
EventEmitter = require("events").EventEmitter
splitFilter = require "../lib/split-filter"
sceneNameIncrement = 1
objectNameIncrement = 1
class AuthoredObject extends EventEmitter
constructor: (@scene, options = {}) ->
@uuid = uuid.v4()
@_props = {}
@propMeta = {}
@registerInternalProperty "visible", true
@registerInternalProperty "editorVisible", true
@registerInternalProperty "editorFaded", false
if not options.name?
name = "Object #{objectNameIncrement++}"
else
name = options.name
@registerInternalProperty "name", name
@registerInternalProperty "type"
@registerProperty "x", type: "numeric"
@registerProperty "y", type: "numeric"
if options.type?
@type = options.type
registerInternalProperty: (propName, defaultValue) ->
@registerProperty(propName, null)
if defaultValue?
this[propName] = defaultValue
registerProperty: (propName, propMeta) ->
Object.defineProperty this, propName,
enumerable: true
get: => @_props[propName]
set: (value) =>
@_props[propName] = value
@emit "changed:#{propName}", @_props[propName], this
@emit "changed", propName, @_props[propName], this
# Explicitly allow specifying `null` for propMeta to avoid propMeta; this is used for internal properties.
if propMeta == undefined
@propMeta[propName] = {}
else if propMeta != null
@propMeta[propName] = propMeta
class AuthoredScene extends EventEmitter
constructor: (@stage, options = {}) ->
@uuid = uuid.v4()
@objects = {}
@orderedObjects = []
@activeObject = null
@_ignoredProperties = ["orderedObjects"]
if not options.name?
@name = "Scene #{sceneNameIncrement++}"
else
@name = options.name
_startAddObject: (object) ->
@objects[object.uuid] = object
@orderedObjects.push object
object.scene = this
_endAddObject: (object) ->
@emit "object:added", object
setName: (name) ->
@name = name
createObject: (options = {}) ->
newObject = new AuthoredObject(this, options)
@_startAddObject(newObject)
if options.autoActivate ? true
process.nextTick =>
@setActiveObject newObject.uuid
@emit "object:created", newObject
@_endAddObject(newObject)
addObject: (object) ->
@_startAddObject(object)
@_endAddObject(object)
removeObject: (uuid) ->
objectIndex = @orderedObjects.indexOf(@objects[uuid])
delete @objects[uuid]
[@orderedObjects, removedObjects] = splitFilter @orderedObjects, (object) ->
object.uuid != uuid
removedObjects.forEach (object) =>
@emit "object:removed", object
if objectIndex == 0
newActiveIndex = 0
else
newActiveIndex = objectIndex - 1
@setActiveObject(@orderedObjects[newActiveIndex]?.uuid)
getActiveObject: ->
return @objects[@activeObject]
setActiveObject: (uuid) ->
@activeObject = uuid
@emit "object:switched", @objects[uuid]
getObjectNameIncrement: ->
return objectNameIncrement++
module.exports = class AuthoredStage extends EventEmitter
constructor: (options = {}) ->
@plugins = []
@scenes = {}
@orderedScenes = []
@activeScene = null
process.nextTick =>
# Initial scene
@createScene()
use: (plugin) ->
pluginMeta = plugin.meta
# Verify that all dependencies exist...
if Array.isArray pluginMeta.dependencies
pluginMeta.dependencies.forEach (dep) =>
if not @plugins[dep]?
# FIXME: DependencyError
throw new Error "Unmet dependency: `#{pluginMeta.name}` requires `#{dep}`"
else if typeof pluginMeta.dependencies == "function"
pluginMeta.dependencies(this)
pluginAPI = plugin(this)
# In case of misbehaving plugins...
pluginAPI ?= {}
@plugins[pluginMeta.name] = pluginAPI
createScene: (options = {}) ->
newScene = new AuthoredScene(this, options)
@scenes[newScene.uuid] = newScene
@orderedScenes.push newScene
if options.autoActivate ? true
process.nextTick =>
@setActiveScene newScene.uuid
@emit "scene:added", newScene
removeScene: (uuid) ->
sceneIndex = @orderedScenes.indexOf(@scenes[uuid])
delete @scenes[uuid]
[@orderedScenes, removedScenes] = splitFilter @orderedScenes, (scene) ->
scene.uuid != uuid
removedScenes.forEach (scene) =>
@emit "scene:removed", scene
if sceneIndex == 0
newActiveIndex = 0
else
newActiveIndex = sceneIndex - 1
@setActiveScene(@orderedScenes[newActiveIndex]?.uuid)
getActiveScene: ->
return @scenes[@activeScene]
setActiveScene: (uuid) ->
@activeScene = uuid
@emit "scene:switched", @scenes[uuid]