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
187 lines
4.4 KiB
CoffeeScript
10 years ago
|
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]
|
||
|
|