import { Box, Debug, Vector3D } from '@awayjs/core'; import { Billboard, TextField as AwayTextField, DisplayObjectContainer as AwayDisplayObjectContainer, Sprite as AwaySprite, MovieClip as AwayMovieClip, DisplayObject as AwayDisplayObject, IDisplayObjectAdapter, TextSprite, } from '@awayjs/scene'; import { DisplayObject } from './DisplayObject'; import { InteractiveObject } from './InteractiveObject'; import { Event } from '../events/Event'; import { IContainer, PickGroup } from '@awayjs/view'; import { constructClassFromSymbol, Errors, OrphanManager } from '@awayfl/avm2'; import { Point } from '../geom/Point'; import { SecurityDomain } from '../SecurityDomain'; import { StaticEvents } from '../events/StaticEvents'; import { AVMStage } from '@awayfl/swf-loader'; export class DisplayObjectContainer extends InteractiveObject { // for AVM1: public _children: any[]; public addTimelineObjectAtDepth(child: any, depth: number) { } public getTimelineObjectAtDepth(depth: number): any { return null; } /** * The DisplayObjectContainer class is the base class for all objects that can serve as display object containers on * the display list. The display list manages all objects displayed in the Flash runtimes. * Use the DisplayObjectContainer class to arrange the display objects in the display list. * Each DisplayObjectContainer object has its own child list for organizing the z-order of the objects. * The z-order is the front-to-back order that determines which object is drawn in front, which is behind, * and so on. * *

DisplayObject is an abstract base class; therefore, * you cannot call DisplayObject directly. Invoking * new DisplayObject() throws an * ArgumentError exception.

* * The DisplayObjectContainer class is an abstract base class for all objects that can contain child objects. * It cannot be instantiated directly; calling the * new DisplayObjectContainer() constructor * throws an ArgumentError exception. * *

For more information, see the "Display Programming" chapter of the * ActionScript 3.0 Developer's Guide.

/** * Calling the new DisplayObjectContainer() constructor throws an * ArgumentError exception. You can, however, call constructors for * the following subclasses of DisplayObjectContainer: * * new Loader()new Sprite()new MovieClip() */ constructor() { super(); if (this.adaptee) { for (let i: number = 0; i < ( this.adaptee).numChildren; i++) { const mc = ( this.adaptee).getChildAt(i); const mcadapter = mc.adapter; const constructorFunc = (mcadapter).executeConstructor; if (constructorFunc) { (mcadapter).executeConstructor = null; //console.log(randomVal, "call constructor for ", mc.parent.name, mc.name); constructorFunc(); //(mcadapter).constructorHasRun=true; } if ((mc).just_added_to_timeline && mc._sessionID != -1 && mcadapter && (mcadapter).dispatchStaticEvent) { (mcadapter).dispatchStaticEvent('added', mcadapter); (mc).just_added_to_timeline = false; mc.hasDispatchedAddedToStage = mc.isOnDisplayList(); if (mc.hasDispatchedAddedToStage) (mcadapter).dispatchStaticEvent('addedToStage', mcadapter); } } } } protected createAdaptee(): AwayDisplayObject { return new AwayDisplayObjectContainer(); } //---------------------------stuff added to make it work: public clone(): DisplayObjectContainer { if (!( this)._symbol) { throw ('_symbol not defined when cloning movieclip'); } const clone = constructClassFromSymbol(( this)._symbol, ( this)._symbol.symbolClass); clone.axInitializer(); this.adaptee.copyTo(clone.adaptee); return clone; } public debugDisplayGraph(obj: any) { obj.object = this; obj.rectangle = 'x:' + this.x + ', y:' + this.y + ', width:' + this.width + ', height:' + this.height; obj.children = {}; let i: number = 0; for (i = 0;i < ( this._adaptee).numChildren;i++) { const oneChild: AwayDisplayObject = ( this._adaptee).getChildAt(i); const childname = 'child_' + i + ' ' + (oneChild.adapter).constructor.name; if (oneChild.isAsset(AwaySprite) || oneChild.isAsset(AwayDisplayObjectContainer)) { if (oneChild.adapter) { obj.children[childname] = {}; (oneChild.adapter).debugDisplayGraph(obj.children[childname]); } } else if (oneChild.isAsset(Billboard)) { obj.children[childname] = {}; obj.children[childname].object = oneChild.adapter; obj.children[childname].name = oneChild.name; obj.children[childname].rectangle = 'x:' + oneChild.x + ', y:' + oneChild.y; } else if (oneChild.isAsset(AwayMovieClip)) { obj.children[childname] = {}; obj.children[childname].object = oneChild.adapter; obj.children[childname].name = oneChild.name; obj.children[childname].rectangle = 'x:' + oneChild.x + ', y:' + oneChild.y; } else if (oneChild.isAsset(AwayTextField)) { obj.children[childname] = {}; obj.children[childname].object = oneChild.adapter; obj.children[childname].text = (oneChild).text; obj.children[childname].rectangle = 'x:' + oneChild.x + ', y:' + oneChild.y; const box: Box = PickGroup.getInstance() .getBoundsPicker(AVMStage.instance().view.getNode(oneChild)).getBoxBounds(AVMStage.instance().view.getNode(oneChild)); obj.children[childname].width = (box == null) ? 0 : box.width; obj.children[childname].height = (box == null) ? 0 : box.height; } } } // overwrite public dispatch_ADDED_TO_STAGE(dispatchForThisChild: boolean = false) { if (dispatchForThisChild) { if (!StaticEvents.events[Event.ADDED_TO_STAGE]) StaticEvents.events[Event.ADDED_TO_STAGE] = new ( this.sec).flash.events.Event(Event.ADDED_TO_STAGE); StaticEvents.events[Event.ADDED_TO_STAGE].target = this; this.dispatchEvent(StaticEvents.events[Event.ADDED_TO_STAGE]); } if (this._adaptee) { for (let i = 0;i < ( this._adaptee).numChildren; i++) { const oneChild: AwayDisplayObject = ( this._adaptee).getChildAt(i); if (oneChild.adapter && (oneChild.adapter).dispatchEventRecursive && !oneChild.hasDispatchedAddedToStage) { oneChild.hasDispatchedAddedToStage = true; (oneChild.adapter).dispatch_ADDED_TO_STAGE(true); } } } } public dispatch_REMOVED_FROM_STAGE(dispatchForThisChild: boolean = false) { if (dispatchForThisChild) { if (!StaticEvents.events[Event.REMOVED_FROM_STAGE]) StaticEvents.events[Event.REMOVED_FROM_STAGE] = new ( this.sec).flash.events.Event(Event.REMOVED_FROM_STAGE); StaticEvents.events[Event.REMOVED_FROM_STAGE].target = this; this.dispatchEvent(StaticEvents.events[Event.REMOVED_FROM_STAGE]); } if (this._adaptee) { for (let i = 0;i < ( this._adaptee).numChildren; i++) { const oneChild: AwayDisplayObject = ( this._adaptee).getChildAt(i); if (oneChild.adapter && (oneChild.adapter).dispatchEventRecursive && oneChild.hasDispatchedAddedToStage) { oneChild.hasDispatchedAddedToStage = false; (oneChild.adapter).dispatch_REMOVED_FROM_STAGE(true); } } } } //---------------------------original as3 properties / methods: /** * Determines whether or not the children of the object are mouse, or user input device, enabled. * If an object is enabled, a user can interact with it by using a mouse or user input device. The default is true. * * This property is useful when you create a button with an instance of the Sprite class * (instead of using the SimpleButton class). When you use a Sprite instance to create a button, * you can choose to decorate the button by using the addChild() method to add additional * Sprite instances. This process can cause unexpected behavior with mouse events because * the Sprite instances you add as children can become the target object of a mouse event * when you expect the parent instance to be the target object. To ensure that the parent * instance serves as the target objects for mouse events, you can set the * mouseChildren property of the parent instance to false. * No event is dispatched by setting this property. You must use the * addEventListener() method to create interactive functionality. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 */ public get mouseChildren (): boolean { return ( this._adaptee).mouseChildren; } public set mouseChildren (enable: boolean) { ( this._adaptee).mouseChildren = enable; } /** * Returns the number of children of this object. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 */ public get numChildren (): number { return (( this._adaptee)).numChildren; } /** * Determines whether the children of the object are tab enabled. Enables or disables tabbing for the * children of the object. The default is true. * Note: Do not use the tabChildren property with Flex. * Instead, use the mx.core.UIComponent.hasFocusableChildren property. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 * @throws IllegalOperationError Calling this property of the Stage object * throws an exception. The Stage object does not implement this property. */ public get tabChildren (): boolean { // @todo Debug.throwPIR('playerglobals/display/DisplayObjectContainer', 'get tabChildren', ''); return false; } public set tabChildren (enable: boolean) { // @todo Debug.throwPIR('playerglobals/display/DisplayObjectContainer', 'set tabChildren', ''); } /** * Returns a TextSnapshot object for this DisplayObjectContainer instance. * @langversion 3.0 * @playerversion Flash 9 */ public get textSnapshot (): any { // @todo Debug.throwPIR('playerglobals/display/DisplayObjectContainer', 'get textSnapshot', ''); return null; } /** * Adds a child DisplayObject instance to this DisplayObjectContainer instance. The child is added * to the front (top) of all other children in this DisplayObjectContainer instance. (To add a child to a * specific index position, use the addChildAt() method.) * * If you add a child object that already has a different display object container as * a parent, the object is removed from the child list of the other display object container. * Note: The command stage.addChild() can cause problems with a published SWF file, * including security problems and conflicts with other loaded SWF files. * There is only one Stage within a Flash runtime instance, * no matter how many SWF files you load into the runtime. So, generally, objects * should not be added to the Stage, directly, at all. The only object the Stage should * contain is the root object. Create a DisplayObjectContainer to contain all of the items on the * display list. Then, if necessary, add that DisplayObjectContainer instance to the Stage. * @param child The DisplayObject instance to add as a child of this DisplayObjectContainer instance. * @return The DisplayObject instance that you pass in the * child parameter. * @throws ArgumentError Throws if the child is the same as the parent. Also throws if * the caller is a child (or grandchild etc.) of the child being added. */ public addChild (child: DisplayObject): DisplayObject { // it can happen that we addChild a existing timeline-child // in this case the timeline-child gets unregistered, and we need to register it again const registerChild: boolean = (child.adaptee._sessionID >= 0 && ( this).registerScriptObject); const dispatchAdded = !(child.adaptee.parent == this.adaptee); ( this.adaptee).addChild(child.adaptee); if (registerChild) { ( this).registerScriptObject(child.adaptee); } if (dispatchAdded) { child.dispatchStaticEvent(Event.ADDED, child); if (this.adaptee.isOnDisplayList()) { child.dispatch_ADDED_TO_STAGE(true); } } OrphanManager.removeOrphan(child.adaptee); return child; } /** * Adds a child DisplayObject instance to this DisplayObjectContainer * instance. The child is added * at the index position specified. An index of 0 represents the back (bottom) * of the display list for this DisplayObjectContainer object. * * For example, the following example shows three display objects, labeled a, b, and c, at * index positions 0, 2, and 1, respectively * If you add a child object that already has a different display object container as * a parent, the object is removed from the child list of the other display object container. * @param child The DisplayObject instance to add as a child of this * DisplayObjectContainer instance. * @param index The index position to which the child is added. If you specify a * currently occupied index position, the child object that exists at that position and all * higher positions are moved up one position in the child list. * @return The DisplayObject instance that you pass in the * child parameter. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 * @throws RangeError Throws if the index position does not exist in the child list. * @throws ArgumentError Throws if the child is the same as the parent. Also throws if * the caller is a child (or grandchild etc.) of the child being added. */ public addChildAt (child: DisplayObject, index: number): DisplayObject { // it can happen that we addChild a existing timeline-child // in this case the timeline-child gets unregistered, and we need to register it again const registerChild: boolean = (child.adaptee._sessionID >= 0 && ( this).registerScriptObject); const dispatchAdded = !(child.adaptee.parent == this.adaptee); // todo: this should be done much more efficient (in awayjs) ( this.adaptee).addChildAt(child.adaptee, index); if (registerChild) { ( this).registerScriptObject(child.adaptee); } if (dispatchAdded) { child.dispatchStaticEvent(Event.ADDED); if (this.adaptee.isOnDisplayList()) { child.dispatch_ADDED_TO_STAGE(true); } } OrphanManager.removeOrphan(child.adaptee); return child; } /** * Indicates whether the security restrictions * would cause any display objects to be omitted from the list returned by calling * the DisplayObjectContainer.getObjectsUnderPoint() method * with the specified point point. By default, content from one domain cannot * access objects from another domain unless they are permitted to do so with a call to the * Security.allowDomain() method. For more information, related to security, * see the Flash Player Developer Center Topic: * Security. * * The point parameter is in the coordinate space of the Stage, * which may differ from the coordinate space of the display object container (unless the * display object container is the Stage). You can use the globalToLocal() and * the localToGlobal() methods to convert points between these coordinate * spaces. * @param point The point under which to look. * @return true if the point contains child display objects with security restrictions. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 */ public areInaccessibleObjectsUnderPoint (point: Point): boolean { // @todo Debug.throwPIR('playerglobals/display/DisplayObjectContainer', 'areInaccessibleObjectsUnderPoint', ''); return false; } /** * Determines whether the specified display object is a child of the DisplayObjectContainer instance or * the instance itself. * The search includes the entire display list including this DisplayObjectContainer instance. Grandchildren, * great-grandchildren, and so on each return true. * @param child The child object to test. * @return true if the child object is a child of the DisplayObjectContainer * or the container itself; otherwise false. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 */ public contains(child: DisplayObject): boolean { return ( this._adaptee).contains(child.adaptee); } /** * Returns the child display object instance that exists at the specified index. * @param index The index position of the child object. * @return The child display object at the specified index position. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 * @throws RangeError Throws if the index does not exist in the child list. * @throws SecurityError This child display object belongs to a sandbox * to which you do not have access. You can avoid this situation by having * the child movie call Security.allowDomain(). */ public getChildAt (index: number): DisplayObject { const child = ( this._adaptee).getChildAt(index); if (!child.adapter || child.adapter == child) { // todo: this looks like it might cause trouble: // it adds a Sprite-adapter to the child, regardsless what type the child really is const sprite = child.adapter = new ( this.sec).flash.display.Shape(); sprite.adaptee = child; } return child.adapter; } /** * Returns the child display object that exists with the specified name. * If more that one child display object has the specified name, * the method returns the first object in the child list. * * The getChildAt() method is faster than the * getChildByName() method. The getChildAt() method accesses * a child from a cached array, whereas the getChildByName() method * has to traverse a linked list to access a child. * @param name The name of the child to return. * @return The child display object with the specified name. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 * @throws SecurityError This child display object belongs to a sandbox * to which you do not have access. You can avoid this situation by having * the child movie call the Security.allowDomain() method. */ public getChildByName (name: string): DisplayObject { return ( this._adaptee).getChildByName(name) ? (( this._adaptee).getChildByName(name).adapter) : null; } /** * Returns the index position of a child DisplayObject instance. * @param child The DisplayObject instance to identify. * @return The index position of the child display object to identify. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 * @throws ArgumentError Throws if the child parameter is not a child of this object. */ public getChildIndex (child: DisplayObject): number { return ( this._adaptee).getChildIndex(child.adaptee); } /** * Returns an array of objects that lie under the specified point and are children * (or grandchildren, and so on) of this DisplayObjectContainer instance. Any child objects that * are inaccessible for security reasons are omitted from the returned array. To determine whether * this security restriction affects the returned array, call the * areInaccessibleObjectsUnderPoint() method. * * The point parameter is in the coordinate space of the Stage, * which may differ from the coordinate space of the display object container (unless the * display object container is the Stage). You can use the globalToLocal() and * the localToGlobal() methods to convert points between these coordinate * spaces. * @param point The point under which to look. * @return An array of objects that lie under the specified point and are children * (or grandchildren, and so on) of this DisplayObjectContainer instance. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 */ public getObjectsUnderPoint(point: Point): DisplayObject[] { const raycastPicker = PickGroup.getInstance().getRaycastPicker(AVMStage.instance().view.getNode(this.adaptee)); const awayChildren: IContainer[] = raycastPicker.getObjectsUnderPoint( new Vector3D(0, 0, -1000), new Vector3D(point.x, point.y, 1000)); const avm2Children: DisplayObject[] = []; let i = awayChildren.length; while (i > 0) { i--; let child = awayChildren[i]; if (child instanceof TextSprite) { child = child.parent; } else if (child.isAsset(AwaySprite) && (child.adapter) == child) { child.adapter = new ( this.sec).flash.display.Sprite(); child.adapter.adaptee = child; } avm2Children.push(child.adapter); } return avm2Children; } /** * Removes the specified child DisplayObject instance from the child list of the DisplayObjectContainer instance. * The parent property of the removed child is set to null * , and the object is garbage collected if no other * references to the child exist. The index positions of any display objects above the child in the * DisplayObjectContainer are decreased by 1. * * The garbage collector reallocates unused memory space. When a variable * or object is no longer actively referenced or stored somewhere, the garbage collector sweeps * through and wipes out the memory space it used to occupy if no other references to it exist. * @param child The DisplayObject instance to remove. * @return The DisplayObject instance that you pass in the * child parameter. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 * @throws ArgumentError Throws if the child parameter is not a child of this object. */ public removeChild (child: DisplayObject): DisplayObject { try { ( this._adaptee).removeChild(child.adaptee); } catch (e) { throw ( this.sec).createError('ArgumentError', Errors.NotAChildError); } //OrphanManager.addOrphan(child.adaptee); return child; } /** * Removes a child DisplayObject from the specified index position in the child list of * the DisplayObjectContainer. The parent property of the removed child is set to * null, and the object is garbage collected if no other references to the child exist. The index * positions of any display objects above the child in the DisplayObjectContainer are decreased by 1. * * The garbage collector reallocates unused memory space. When a variable or * object is no longer actively referenced or stored somewhere, the garbage collector sweeps * through and wipes out the memory space it used to occupy if no other references to it exist. * @param index The child index of the DisplayObject to remove. * @return The DisplayObject instance that was removed. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 * @throws SecurityError This child display object belongs to a sandbox * to which the calling object does not have access. You can avoid this situation by having * the child movie call the Security.allowDomain() method. * @throws RangeError Throws if the index does not exist in the child list. */ public removeChildAt (index: number): DisplayObject { const child = ( this._adaptee).removeChildAt(index).adapter; //OrphanManager.addOrphan(child.adaptee); return child; } public removeChildren (beginIndex: number = 0, endIndex: number = 2147483647) { if (endIndex >= ( this._adaptee).numChildren) endIndex = ( this._adaptee).numChildren - 1; for (let i: number = endIndex - 1;i >= beginIndex; i--) this.removeChildAt(i); } /** * Changes the position of an existing child in the display object container. * This affects the layering of child objects. For example, the following example shows three * display objects, labeled a, b, and c, at index positions 0, 1, and 2, respectively: * * When you use the setChildIndex() method and specify an index position * that is already occupied, the only positions that change are those in between the display object's * former and new position. * All others will stay the same. * If a child is moved to an index LOWER than its current index, * all children in between will INCREASE by 1 for their index reference. * If a child is moved to an index HIGHER than its current index, * all children in between will DECREASE by 1 for their index reference. * For example, if the display object container * in the previous example is named container, you can swap the position * of the display objects labeled a and b by calling the following code: * * container.setChildIndex(container.getChildAt(1), 0); * * This code results in the following arrangement of objects: * @param child The child DisplayObject instance for which you want to change * the index number. * @param index The resulting index number for the child display object. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 * @throws RangeError Throws if the index does not exist in the child list. * @throws ArgumentError Throws if the child parameter is not a child of this object. */ public setChildIndex (child: DisplayObject, index: number) { this.setChildIndexInternal(child, index); } protected setChildIndexInternal(child: DisplayObject, index: number) { const idx = ( this.adaptee).getChildIndex(child.adaptee); if (idx < 0) throw ('[DisplayObjectContainer.setChildindex] \ - todo: throw as3 error when child is not child of this obj'); if (idx == index) return; if (index > ( this._adaptee).numChildren) throw ('[DisplayObjectContainer.setChildindex] - todo: throw as3 error when index is out of bounds'); ( this.adaptee).setChildIndex(child.adaptee, index); } public stopAllMovieClips () { // @todo Debug.throwPIR('playerglobals/display/DisplayObjectContainer', 'stopAllMovieClips', ''); } /** * Swaps the z-order (front-to-back order) of the two specified child objects. All other child * objects in the display object container remain in the same index positions. * @param child1 The first child object. * @param child2 The second child object. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 * @throws ArgumentError Throws if either child parameter is not a child of this object. */ public swapChildren (child1: DisplayObject, child2: DisplayObject) { ( this._adaptee).swapChildren(child1.adaptee, child2.adaptee); } /** * Swaps the z-order (front-to-back order) of the child objects at the two specified index positions in the * child list. All other child objects in the display object container remain in the same index positions. * @param index1 The index position of the first child object. * @param index2 The index position of the second child object. * @langversion 3.0 * @playerversion Flash 9 * @playerversion Lite 4 * @throws RangeError If either index does not exist in the child list. */ public swapChildrenAt (index1: number, index2: number) { ( this._adaptee).swapChildrenAt(index1, index2); } }