| /** | 
|  * Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上 | 
|  * @module zrender/graphic/Group | 
|  * @example | 
|  *     const Group = require('zrender/graphic/Group'); | 
|  *     const Circle = require('zrender/graphic/shape/Circle'); | 
|  *     const g = new Group(); | 
|  *     g.position[0] = 100; | 
|  *     g.position[1] = 100; | 
|  *     g.add(new Circle({ | 
|  *         style: { | 
|  *             x: 100, | 
|  *             y: 100, | 
|  *             r: 20, | 
|  *         } | 
|  *     })); | 
|  *     zr.add(g); | 
|  */ | 
|   | 
| import * as zrUtil from '../core/util'; | 
| import Element, { ElementProps } from '../Element'; | 
| import BoundingRect from '../core/BoundingRect'; | 
| import { MatrixArray } from '../core/matrix'; | 
| import Displayable from './Displayable'; | 
| import { ZRenderType } from '../zrender'; | 
|   | 
| export interface GroupProps extends ElementProps { | 
| } | 
|   | 
| class Group extends Element<GroupProps> { | 
|   | 
|     readonly isGroup = true | 
|   | 
|     private _children: Element[] = [] | 
|   | 
|   | 
|     constructor(opts?: GroupProps) { | 
|         super(); | 
|   | 
|         this.attr(opts); | 
|     } | 
|   | 
|     /** | 
|      * Get children reference. | 
|      */ | 
|     childrenRef() { | 
|         return this._children; | 
|     } | 
|   | 
|     /** | 
|      * Get children copy. | 
|      */ | 
|     children() { | 
|         return this._children.slice(); | 
|     } | 
|   | 
|     /** | 
|      * 获取指定 index 的儿子节点 | 
|      */ | 
|     childAt(idx: number): Element { | 
|         return this._children[idx]; | 
|     } | 
|   | 
|     /** | 
|      * 获取指定名字的儿子节点 | 
|      */ | 
|     childOfName(name: string): Element { | 
|         const children = this._children; | 
|         for (let i = 0; i < children.length; i++) { | 
|             if (children[i].name === name) { | 
|                 return children[i]; | 
|             } | 
|         } | 
|     } | 
|   | 
|     childCount(): number { | 
|         return this._children.length; | 
|     } | 
|   | 
|     /** | 
|      * 添加子节点到最后 | 
|      */ | 
|     add(child: Element): Group { | 
|         if (child) { | 
|             if (child !== this && child.parent !== this) { | 
|                 this._children.push(child); | 
|                 this._doAdd(child); | 
|             } | 
|             if (process.env.NODE_ENV !== 'production') { | 
|                 if (child.__hostTarget) { | 
|                     throw 'This elemenet has been used as an attachment'; | 
|                 } | 
|             } | 
|         } | 
|   | 
|         return this; | 
|     } | 
|   | 
|     /** | 
|      * 添加子节点在 nextSibling 之前 | 
|      */ | 
|     addBefore(child: Element, nextSibling: Element) { | 
|         if (child && child !== this && child.parent !== this | 
|             && nextSibling && nextSibling.parent === this) { | 
|   | 
|             const children = this._children; | 
|             const idx = children.indexOf(nextSibling); | 
|   | 
|             if (idx >= 0) { | 
|                 children.splice(idx, 0, child); | 
|                 this._doAdd(child); | 
|             } | 
|         } | 
|   | 
|         return this; | 
|     } | 
|   | 
|     replace(oldChild: Element, newChild: Element) { | 
|         const idx = zrUtil.indexOf(this._children, oldChild); | 
|         if (idx >= 0) { | 
|             this.replaceAt(newChild, idx); | 
|         } | 
|         return this; | 
|     } | 
|   | 
|     replaceAt(child: Element, index: number) { | 
|         const children = this._children; | 
|         const old = children[index]; | 
|   | 
|         if (child && child !== this && child.parent !== this && child !== old) { | 
|             children[index] = child; | 
|   | 
|             old.parent = null; | 
|             const zr = this.__zr; | 
|             if (zr) { | 
|                 old.removeSelfFromZr(zr); | 
|             } | 
|   | 
|             this._doAdd(child); | 
|         } | 
|   | 
|         return this; | 
|     } | 
|   | 
|     _doAdd(child: Element) { | 
|         if (child.parent) { | 
|             // Parent must be a group | 
|             (child.parent as Group).remove(child); | 
|         } | 
|   | 
|         child.parent = this; | 
|   | 
|         const zr = this.__zr; | 
|         if (zr && zr !== (child as Group).__zr) {    // Only group has __storage | 
|   | 
|             child.addSelfToZr(zr); | 
|         } | 
|   | 
|         zr && zr.refresh(); | 
|     } | 
|   | 
|     /** | 
|      * Remove child | 
|      * @param child | 
|      */ | 
|     remove(child: Element) { | 
|         const zr = this.__zr; | 
|         const children = this._children; | 
|   | 
|         const idx = zrUtil.indexOf(children, child); | 
|         if (idx < 0) { | 
|             return this; | 
|         } | 
|         children.splice(idx, 1); | 
|   | 
|         child.parent = null; | 
|   | 
|         if (zr) { | 
|   | 
|             child.removeSelfFromZr(zr); | 
|         } | 
|   | 
|         zr && zr.refresh(); | 
|   | 
|         return this; | 
|     } | 
|   | 
|     /** | 
|      * Remove all children | 
|      */ | 
|     removeAll() { | 
|         const children = this._children; | 
|         const zr = this.__zr; | 
|         for (let i = 0; i < children.length; i++) { | 
|             const child = children[i]; | 
|             if (zr) { | 
|                 child.removeSelfFromZr(zr); | 
|             } | 
|             child.parent = null; | 
|         } | 
|         children.length = 0; | 
|   | 
|         return this; | 
|     } | 
|   | 
|     /** | 
|      * 遍历所有子节点 | 
|      */ | 
|     eachChild<Context>( | 
|         cb: (this: Context, el: Element, index?: number) => void, | 
|         context?: Context | 
|     ) { | 
|         const children = this._children; | 
|         for (let i = 0; i < children.length; i++) { | 
|             const child = children[i]; | 
|             cb.call(context, child, i); | 
|         } | 
|         return this; | 
|     } | 
|   | 
|     /** | 
|      * Visit all descendants. | 
|      * Return false in callback to stop visit descendants of current node | 
|      */ | 
|     // TODO Group itself should also invoke the callback. | 
|     traverse<T>( | 
|         cb: (this: T, el: Element) => boolean | void, | 
|         context?: T | 
|     ) { | 
|         for (let i = 0; i < this._children.length; i++) { | 
|             const child = this._children[i]; | 
|             const stopped = cb.call(context, child); | 
|   | 
|             if (child.isGroup && !stopped) { | 
|                 child.traverse(cb, context); | 
|             } | 
|         } | 
|         return this; | 
|     } | 
|   | 
|     addSelfToZr(zr: ZRenderType) { | 
|         super.addSelfToZr(zr); | 
|         for (let i = 0; i < this._children.length; i++) { | 
|             const child = this._children[i]; | 
|             child.addSelfToZr(zr); | 
|         } | 
|     } | 
|   | 
|     removeSelfFromZr(zr: ZRenderType) { | 
|         super.removeSelfFromZr(zr); | 
|         for (let i = 0; i < this._children.length; i++) { | 
|             const child = this._children[i]; | 
|             child.removeSelfFromZr(zr); | 
|         } | 
|     } | 
|   | 
|     getBoundingRect(includeChildren?: Element[]): BoundingRect { | 
|         // TODO Caching | 
|         const tmpRect = new BoundingRect(0, 0, 0, 0); | 
|         const children = includeChildren || this._children; | 
|         const tmpMat: MatrixArray = []; | 
|         let rect = null; | 
|   | 
|         for (let i = 0; i < children.length; i++) { | 
|             const child = children[i]; | 
|             // TODO invisible? | 
|             if (child.ignore || (child as Displayable).invisible) { | 
|                 continue; | 
|             } | 
|   | 
|             const childRect = child.getBoundingRect(); | 
|             const transform = child.getLocalTransform(tmpMat); | 
|             // TODO | 
|             // The boundingRect cacluated by transforming original | 
|             // rect may be bigger than the actual bundingRect when rotation | 
|             // is used. (Consider a circle rotated aginst its center, where | 
|             // the actual boundingRect should be the same as that not be | 
|             // rotated.) But we can not find better approach to calculate | 
|             // actual boundingRect yet, considering performance. | 
|             if (transform) { | 
|                 BoundingRect.applyTransform(tmpRect, childRect, transform); | 
|                 rect = rect || tmpRect.clone(); | 
|                 rect.union(tmpRect); | 
|             } | 
|             else { | 
|                 rect = rect || childRect.clone(); | 
|                 rect.union(childRect); | 
|             } | 
|         } | 
|         return rect || tmpRect; | 
|     } | 
| } | 
|   | 
| Group.prototype.type = 'group'; | 
| // Storage will use childrenRef to get children to render. | 
| export interface GroupLike extends Element { | 
|     childrenRef(): Element[] | 
| } | 
|   | 
| export default Group; |