UNPKG

neft

Version:

Universal Platform

331 lines (274 loc) 11.5 kB
'use strict' utils = require 'src/utils' signal = require 'src/signal' assert = require 'src/assert' module.exports = (Renderer, Impl, itemUtils) -> class Component @getCloneFunction = (func, name) -> if typeof func is 'function' if (func::) instanceof Renderer.Item utils.bindFunctionContext(func.New, func) else func else if func and typeof func._main is 'function' func._main else throw new Error "'#{name}' is not an item definition" constructor: (original, opts) -> unless original instanceof Component opts = original original = null if original? assert.instanceOf original, Component while original.parent original = original.parent @id = original?.id or utils.uid() @item = null @itemId = original?.itemId or '' @fileName = opts?.fileName or original?.fileName or 'unknown' @objects = {} @idsOrder = original?.idsOrder or null @objectsOrder = [] @objectsOrderSignalArr = null @isClone = !!original @isDeepClone = false @ready = false @mirror = false @belongsToComponent = null @objectsInitQueue = [] @parent = original @disabledObjects = original?.disabledObjects or Object.create(null) # if original # @createItem = original.createItem # @cloneItem = original.cloneItem # @cacheItem = original.cacheItem # else @clone = utils.bindFunctionContext @clone, @ @createItem = utils.bindFunctionContext @createItem, @ @createItem.getComponent = @clone # @cloneItem = utils.bindFunctionContext @cloneItem, @ # @cacheItem = utils.bindFunctionContext @cacheItem, @ @onObjectChange = null Object.preventExtensions @ initSignalArr = -> for id, i in @idsOrder @objectsOrder[i] ||= @objects[id] or null @objectsOrderSignalArr = utils.clone(@objectsOrder) @objectsOrderSignalArr.push null, null initAsEmptyDefinition: -> initSignalArr.call @ Object.freeze @ @initObjects() return init: -> assert.notOk @ready assert.ok @isClone @onObjectChange ?= signal.create() initSignalArr.call @ @ready = true # init objects {objectsInitQueue} = @ i = 0; n = objectsInitQueue.length while i < n objectsInitQueue[i].apply objectsInitQueue[i+1], objectsInitQueue[i+2] i += 3 @objectsInitQueue = null Object.freeze @ initObjects: -> # init extensions for id, item of @objects if @objects.hasOwnProperty(id) and not (item instanceof Renderer.Class) extensions = item._extensions i = 0 {length} = extensions while extension = extensions[i++] if extension._bindings?.when continue if extension instanceof Renderer.Class and extension.name continue if extension instanceof Renderer.Animation continue extension.enable() if extensions.length < length i-- length-- # init objects for id, item of @objects if @objects.hasOwnProperty(id) and item instanceof Renderer.Item and id isnt @itemId item.onReady.emit() return endComponentCloning = (comp, components, createdComponents) -> # clone no children objects (e.g. links) for id, obj of comp.parent.objects if not comp.objects[id] and id isnt comp.itemId and not comp.disabledObjects[id] newObj = cloneObject obj, components, createdComponents, comp # initialize component comp.init() return cloneObject = (item, components, createdComponents, parentComponent) -> # get cloned item component needsNewComp = false itemCompId = item._component.id unless component = components[itemCompId] needsNewComp = true component = components[itemCompId] = new Component item._component if belongsToComponent = item._component.belongsToComponent component.belongsToComponent = components[belongsToComponent.id] component.mirror = parentComponent.mirror createdComponents.push component # create default class in required component # used when main item (only this type can have opts) extends other item clone = item.clone component if item._component.item is item component.item = clone # save object in the cloned component if clone.id if item._component.item is item if belongsToComponent = item._component.belongsToComponent components[belongsToComponent.id].setObject clone, clone.id component.setObject clone, component.itemId else component.setObject clone, clone.id # clone extensions of this object unless item instanceof Renderer.Class for ext in item._extensions # extension can be already cloned if it has an id cloneExt = components[ext._component.id]?.objects[ext.id] cloneExt ?= cloneObject ext, components, createdComponents, parentComponent cloneExt.target = clone if item instanceof Renderer.Item # if we extend another item, # we process it in reversed order (from top to bottom - basic item); # extending require that extended item is less important, that's why # we put his children at the bottom if clone.children.length firstChildren = Array::slice.call clone.children clone.children.clear() child = item.children.firstChild while child # child can be already cloned cloneChild = components[child._component.id]?.objects[child.id] cloneChild ?= cloneItem(child, components, createdComponents, component) cloneChild.parent = clone child = child.nextSibling if firstChildren for child in firstChildren child.parent = clone clone cloneItem = (item, components, createdComponents, parentComponent) -> itemCompId = item._component.id needsNewComp = not components[itemCompId] clone = cloneObject item, components, createdComponents, parentComponent # one item can have different extending items, # but their can't use the same component; # this function is recursive, so our deep component are not available for parents if needsNewComp endComponentCloning components[itemCompId], components, createdComponents components[itemCompId] = null clone clone: (parentComponent, itemOpts) -> unless parentComponent instanceof Component itemOpts = parentComponent parentComponent = null component = new Component @ component.mirror = not parentComponent component.belongsToComponent = parentComponent components = {} components[component.id] = component if parentComponent components[parentComponent.id] = parentComponent createdComponents = [component] item = cloneItem @item, components, createdComponents, component for comp in createdComponents unless comp.item comp.item = item unless comp.objects[comp.itemId] comp.setObject item, comp.itemId `//<development>` Object.freeze createdComponents `//</development>` if itemOpts itemUtils.Object.setOpts component.item, parentComponent, itemOpts # for comp in createdComponents # components[comp.id] = comp # endComponentCloning comp, components, createdComponents for comp in createdComponents unless comp.ready endComponentCloning comp, components, createdComponents if component.mirror for comp in createdComponents assert.ok comp.ready comp.initObjects() item.onReady.emit() component setObject: (object, id) -> assert.isString id assert.notLengthOf id, 0 assert.ok @parent.objects[id] assert.notOk @objects.hasOwnProperty(id) @objects[id] = object index = @idsOrder.indexOf id if index isnt -1 @objectsOrder[index] = object return createItem: (arg1, arg2) -> component = @clone arg1, arg2 component.item cloneRawObject: (item, opts=0) -> assert.instanceOf item, itemUtils.Object assert.isString item.id assert.notLengthOf item.id, 0 assert.ok item.id isnt @itemId assert.ok @objects[item.id] or @parent?.objects[item.id] {id} = item if id is @itemId clone = @createItem() else component = new Component @ component.objects = Object.create @objects component.item = @item component.objectsOrderSignalArr = new Array @objectsOrder.length+2 component.isDeepClone = true component.ready = true component.mirror = true components = {} components[component.id] = component createdComponents = [component] clone = cloneItem item, components, createdComponents, component for val, i in @idsOrder component.objectsOrder[i] = component.objectsOrderSignalArr[i] ||= @objectsOrder[i] clone cloneObject: (item, opts) -> clone = @cloneRawObject item, opts clone._component.initObjects() clone cloneComponentObject: -> comp = new Component @ comp.objects = Object.create @objects comp.item = @item comp.objectsOrder = Object.create @objectsOrder comp.objectsOrderSignalArr = Object.create @objectsOrderSignalArr comp.onObjectChange = @onObjectChange comp.isDeepClone = true comp.ready = true comp.mirror = true assert.is comp.objectsOrder.length, comp.idsOrder.length comp setObjectById: (object, id) -> assert.instanceOf object, itemUtils.Object assert.isString id assert.ok object.id is id assert.ok @objects[id] or @parent?.objects[id] if (oldVal = @objects[id]) is object return @objects[id] = object index = @idsOrder.indexOf id if index isnt -1 @objectsOrder[index] = @objectsOrderSignalArr[index] = object @onObjectChange?.emit id, oldVal object @Link = class Link constructor: (@id) -> Object.preventExtensions @ getItem: (component) -> obj = component.objects[@id] obj