├── .gitignore ├── .vscode ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── dist ├── fairygui.js ├── fairygui.min.js └── fairygui.module.js ├── gulpfile.js ├── images └── 20200610-084916.png ├── package.json ├── src ├── GButton.ts ├── GComboBox.ts ├── GComponent.ts ├── GGraph.ts ├── GGroup.ts ├── GImage.ts ├── GLabel.ts ├── GList.ts ├── GLoader.ts ├── GMovieClip.ts ├── GObject.ts ├── GProgressBar.ts ├── GRichTextField.ts ├── GRoot.ts ├── GScrollBar.ts ├── GSlider.ts ├── GTextField.ts ├── GTextInput.ts ├── GTimer.ts ├── PopupMenu.ts ├── RelationItem.ts ├── Relations.ts ├── ScrollPane.ts ├── Transition.ts ├── Window.ts ├── config │ ├── Definitions.ts │ └── UIConfig.ts ├── controller │ ├── Action.ts │ ├── ChangePageAction.ts │ ├── Controller.ts │ ├── PageOption.ts │ └── PlayTransitionAction.ts ├── createjs │ └── extras │ │ ├── Bitmap.ts │ │ ├── Resource.ts │ │ ├── ScaleBitmap.ts │ │ ├── Sprite.ts │ │ ├── TextMetrics.ts │ │ ├── TextStyle.ts │ │ └── TilingBitmap.ts ├── display │ ├── BitmapFont.ts │ ├── Frame.ts │ ├── HTMLInput.ts │ ├── IUISource.ts │ ├── InputElement.ts │ ├── MovieClip.ts │ ├── MovieClipData.ts │ ├── MovieClipSettings.ts │ ├── UIContainer.ts │ ├── UIImage.ts │ ├── UISprite.ts │ ├── UIStage.ts │ └── UITextField.ts ├── events │ ├── DisplayObjectEvent.ts │ ├── DragEvent.ts │ ├── FocusEvent.ts │ ├── GearEvent.ts │ ├── ListEvent.ts │ ├── ScrollEvent.ts │ ├── StateChangeEvent.ts │ └── TextEvent.ts ├── fairygui-createjs.ts ├── gear │ ├── GearAnimation.ts │ ├── GearBase.ts │ ├── GearColor.ts │ ├── GearDisplay.ts │ ├── GearIcon.ts │ ├── GearLook.ts │ ├── GearSize.ts │ ├── GearText.ts │ └── GearXY.ts ├── interface.ts ├── interface │ ├── IAnimationGear.ts │ ├── IColorGear.ts │ ├── IColorableTitle.ts │ ├── IMovieClip.ts │ └── IUIObject.ts ├── res │ ├── DisplayListItem.ts │ ├── PackageItem.ts │ ├── UIObjectFactory.ts │ └── UIPackage.ts └── utils │ ├── AssetLoader.ts │ ├── Binder.ts │ ├── ByteArray.ts │ ├── ByteBuffer.ts │ ├── DOMEventManager.ts │ ├── DragIndicator.ts │ ├── GObjectRecycler.ts │ ├── InputDelegate.ts │ ├── Margin.ts │ ├── MathUtil.ts │ ├── NumberUtil.ts │ ├── RawByte.ts │ ├── RawInflate.ts │ ├── Recycler.ts │ ├── StringUtil.ts │ ├── UBBParser.ts │ ├── Utils.ts │ ├── XMLParser.ts │ └── isMobile.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .objs 4 | build -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5501 3 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "build", 7 | "path": "/", 8 | "group": "build", 9 | "problemMatcher": [], 10 | "label": "build source" 11 | }, 12 | 13 | { 14 | "type": "npm", 15 | "script": "test", 16 | "path": "/", 17 | "group": "build", 18 | "problemMatcher": [], 19 | "label": "build test" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 FairyGUI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FairyGUI-createjs 2 | 3 | #### A GUI Editor&framework for Create.js #### 4 | 5 | Official website: [www.fairygui.com](https://www.fairygui.com) 6 | 7 | #### Install 8 | ``` 9 | npm install fairygui-createjs 10 | ``` 11 | 12 | #### Live Demo #### 13 | [example](https://blog.krapnik.cn/FairyGUI-createjs-example/dist/index.html) 14 | 15 | ### Usage ### 16 | 17 | Step 1, we use the editor to create the UI. 18 | 19 | ![](images/20200610-084916.png) 20 | 21 | Step 2, we only need a little code to display it. 22 | 23 | ```javascript 24 | import * as fgui from 'fairygui-createjs' 25 | 26 | // create stage 27 | let canvas = document.query("#canvas") 28 | let stage = new createjs.Stage(canvas); 29 | let loader = new fgui.AssetLoader(false, "", "Anonymous"); 30 | let manifest = [ 31 | { id: "test", src: 'ui/test.fui', type: "binary" }, 32 | { id: 'test@atlas0', src: 'ui/test@atlas0.png', type: "image" }, 33 | { id: 'test@atlas0_1', src: 'ui/test@atlas0_1.png', type: "image" } 34 | ]; 35 | 36 | function createStage(){ 37 | fgui.GRoot.inst.attachTo(this.stage, { 38 | designWidth: 1136, 39 | designHeight: 640, 40 | scaleMode: fgui.StageScaleMode.FIXED_WIDTH, 41 | orientation: fgui.StageOrientation.LANDSCAPE, 42 | alignV: fgui.StageAlign.TOP, 43 | alignH: fgui.StageAlign.LEFT 44 | }); 45 | 46 | createjs.Ticker.framerate = 60; 47 | createjs.Ticker.timingMode = createjs.Ticker.RAF_SYNCHED; 48 | createjs.Ticker.on('tick', ()=>{ 49 | if (e.paused !== 1) { 50 | stage.update(); 51 | } else { 52 | console.log('pause!'); 53 | }, this); 54 | } 55 | 56 | // createLoader 57 | function createLoader() { 58 | createjs.Sound.alternateExtensions = ["mp3"]; 59 | loader.installPlugin(createjs.Sound); 60 | loader.loadManifest(this.manifest); 61 | loader.on("complete", resLoaded, this); 62 | } 63 | 64 | // createObject 65 | function resLoaded(){ 66 | loader.destroy(); 67 | fgui.UIPackage.addPackage("test"); 68 | let ins = fgui.UIPackage.createObject("test", "main"); 69 | ins.setSize(fgui.GRoot.inst.width, fgui.GRoot.inst.height); 70 | ins.addRelation(fgui.GRoot.inst, fgui.RelationType.Size); 71 | fgui.GRoot.inst.addChild(ins); 72 | } 73 | 74 | createStage(); 75 | createLoader(); 76 | 77 | ``` 78 | 79 | # License 80 | MIT 81 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp') 2 | const rollup = require('rollup') 3 | const ts = require('gulp-typescript'); 4 | const rename = require("gulp-rename"); 5 | const uglify = require('gulp-uglify-es').default; 6 | const tsProject = ts.createProject('tsconfig.json', { declaration: true }); 7 | 8 | const onwarn = warning => { 9 | // Silence circular dependency warning for moment package 10 | if (warning.code === 'CIRCULAR_DEPENDENCY') 11 | return 12 | 13 | console.warn(`(!) ${warning.message}`) 14 | } 15 | 16 | gulp.task('buildJs', () => { 17 | return tsProject.src().pipe(tsProject()).pipe(gulp.dest('./build')); 18 | }) 19 | 20 | gulp.task("rollup", async function () { 21 | let config = { 22 | input: "build/fairygui-createjs.js", 23 | external: ['createjs'], 24 | onwarn : onwarn, 25 | output: { 26 | file: 'dist/fairygui.js', 27 | format: 'umd', 28 | extend: true, 29 | name: 'fgui', 30 | globals: { createjs: 'createjs' } 31 | } 32 | }; 33 | const subTask = await rollup.rollup(config); 34 | await subTask.write(config); 35 | 36 | let config2 = { 37 | input: "build/fairygui-createjs.js", 38 | external: ['createjs'], 39 | output: { 40 | file: 'dist/fairygui.module.js', 41 | format: 'esm', 42 | extend: true, 43 | name: 'fgui', 44 | globals: { createjs: 'createjs' } 45 | } 46 | }; 47 | const subTask2 = await rollup.rollup(config2); 48 | await subTask2.write(config2); 49 | }); 50 | 51 | gulp.task("uglify", function () { 52 | return gulp.src("dist/fairygui.js") 53 | .pipe(rename({ suffix: '.min' })) 54 | .pipe(uglify(/* options */)) 55 | .pipe(gulp.dest("dist/")); 56 | }); 57 | 58 | gulp.task('build' 59 | , gulp.series( 60 | gulp.parallel('buildJs'), 61 | gulp.parallel('rollup'), 62 | gulp.parallel('uglify') 63 | ) 64 | ) -------------------------------------------------------------------------------- /images/20200610-084916.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krapnikkk/FairyGUI-createjs/cc6b3871963231aab010a7a9524887491c2c80da/images/20200610-084916.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fairygui-createjs", 3 | "version": "0.0.5", 4 | "description": "A GUI Editor & framework for create.js", 5 | "main": "dist/fairygui.js", 6 | "module": "dist/fairygui.module.js", 7 | "types": "build/fairygui-createjs.d.ts", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/krapnikkk/FairyGUI-createjs" 11 | }, 12 | "files": [ 13 | "dist/fairygui.js", 14 | "dist/fairygui.min.js", 15 | "dist/fairygui.module.js", 16 | "test", 17 | "build" 18 | ], 19 | "scripts": { 20 | "build": "gulp build" 21 | }, 22 | "keywords": [ 23 | "createjs", 24 | "create.js", 25 | "fairygui", 26 | "fgui", 27 | "gui" 28 | ], 29 | "author": "", 30 | "license": "MIT", 31 | "homepage": "https://fairygui.com/", 32 | "dependencies": { 33 | "ismobilejs": "^1.1.1" 34 | }, 35 | "devDependencies": { 36 | "@rollup/plugin-node-resolve": "^8.0.1", 37 | "@types/createjs": "0.0.29", 38 | "gulp": "^4.0.2", 39 | "gulp-clean": "^0.4.0", 40 | "gulp-concat": "^2.6.1", 41 | "gulp-minify": "^3.1.0", 42 | "gulp-rename": "^2.0.0", 43 | "gulp-typescript": "^3.1.6", 44 | "gulp-uglify": "^3.0.2", 45 | "gulp-uglify-es": "^2.0.0", 46 | "rollup": "^1.27.5", 47 | "ts-loader": "^7.0.4", 48 | "typescript": "^3.9.2" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/GGraph.ts: -------------------------------------------------------------------------------- 1 | 2 | import { UISprite } from './display/UISprite' 3 | import { GObject } from './GObject' 4 | import { IColorGear } from './interface/IColorGear' 5 | import { StringUtil } from './utils/StringUtil' 6 | import { Utils } from './utils/Utils' 7 | import { XmlNode } from './utils/XMLParser' 8 | 9 | export class GGraph extends GObject implements IColorGear { 10 | private $type: number = 0 11 | private $lineSize: number = 1 12 | private $lineColor: string 13 | private $sides: number = 0 14 | private $fillColor: string 15 | private $corner: number[] 16 | private $startAngle: number = 0 17 | private $points: Array = [] 18 | 19 | public constructor() { 20 | super() 21 | this.$lineSize = 1 22 | this.$lineColor = '#000000' 23 | this.$fillColor = '#FFFFFF' 24 | } 25 | 26 | public drawRect(lineSize: number, lineColor: string, fillColor: string): void { 27 | this.$type = 1 28 | this.$lineSize = lineSize 29 | this.$lineColor = lineColor 30 | this.$fillColor = fillColor 31 | this.drawGraph() 32 | } 33 | 34 | public drawEllipse(lineSize: number, lineColor: string, fillColor: string): void { 35 | this.$type = 2 36 | this.$lineSize = lineSize 37 | this.$lineColor = lineColor 38 | this.$fillColor = fillColor 39 | this.drawGraph() 40 | } 41 | 42 | public get color(): string { 43 | return this.$fillColor 44 | } 45 | 46 | public set color(value: string) { 47 | this.$fillColor = value 48 | if (this.$type != 0) this.drawGraph() 49 | } 50 | 51 | private drawGraph(): void { 52 | let shape: createjs.Shape = this.$displayObject as createjs.Shape 53 | let g: createjs.Graphics = shape.graphics 54 | g.clear() 55 | 56 | let w: number = this.width 57 | let h: number = this.height 58 | if (w == 0 || h == 0) return 59 | 60 | g.beginStroke(this.$lineColor) 61 | 62 | if (this.$lineSize == 0) { 63 | g.setStrokeStyle(0.1) // see https://github.com/CreateJS/EaselJS/issues/734 64 | } else { 65 | g.setStrokeStyle(this.$lineSize) 66 | w -= this.$lineSize 67 | h -= this.$lineSize 68 | } 69 | g.beginFill(this.$fillColor) 70 | if (this.$type == 1) { 71 | if (this.$corner && this.$corner.length >= 1) { 72 | if (this.$corner.length == 1) { 73 | g.drawRoundRect(this.$lineSize / 2, this.$lineSize / 2, w, h, this.$corner[0]) 74 | } else { 75 | g.drawRoundRectComplex( 76 | this.$lineSize / 2, 77 | this.$lineSize / 2, 78 | w, 79 | h, 80 | this.$corner[0], 81 | this.$corner[1], 82 | this.$corner[3], 83 | this.$corner[2] 84 | ) 85 | } 86 | } else { 87 | g.drawRect(this.$lineSize / 2, this.$lineSize / 2, w, h) 88 | } 89 | } else if (this.$type == 2) { 90 | let halfW: number = w * 0.5 91 | if (w == h) g.drawCircle(halfW + this.$lineSize / 2, halfW + this.$lineSize / 2, halfW) 92 | else { 93 | w = w - this.$lineSize 94 | h = h - this.$lineSize 95 | g.drawEllipse(this.$lineSize / 2, this.$lineSize / 2, w, h) 96 | } 97 | } else if (this.$type == 3) { 98 | let radius = w > h ? w / 2 : h / 2 99 | g.drawPolyStar(0 + radius, 0 + radius, radius, this.$sides, 0, this.$startAngle) 100 | } else if (this.$type == 4) { 101 | Utils.fillPath(g, this.$points, 0, 0) 102 | } 103 | g.endFill() 104 | shape.cache(0, 0, this.$width, this.$height) 105 | } 106 | 107 | public replaceMe(target: GObject): void { 108 | if (!this.$parent) throw new Error('parent not set') 109 | 110 | target.name = this.name 111 | target.alpha = this.alpha 112 | target.rotation = this.rotation 113 | target.visible = this.visible 114 | target.touchable = this.touchable 115 | target.grayed = this.grayed 116 | target.setXY(this.x, this.y) 117 | target.setSize(this.width, this.height) 118 | 119 | let index: number = this.$parent.getChildIndex(this) 120 | this.$parent.addChildAt(target, index) 121 | target.relations.copyFrom(this.relations) 122 | 123 | this.$parent.removeChild(this, true) 124 | } 125 | 126 | public addBeforeMe(target: GObject): void { 127 | if (this.$parent == null) throw new Error('parent not set') 128 | 129 | let index: number = this.$parent.getChildIndex(this) 130 | this.$parent.addChildAt(target, index) 131 | } 132 | 133 | public addAfterMe(target: GObject): void { 134 | if (this.$parent == null) throw new Error('parent not set') 135 | 136 | let index: number = this.$parent.getChildIndex(this) 137 | index++ 138 | this.$parent.addChildAt(target, index) 139 | } 140 | 141 | protected createDisplayObject(): void { 142 | this.$displayObject = new UISprite(this) 143 | this.$displayObject.mouseEnabled = true; 144 | } 145 | 146 | protected handleSizeChanged(): void { 147 | if (this.$type != 0) this.drawGraph() 148 | } 149 | 150 | public setupBeforeAdd(xml: XmlNode): void { 151 | super.setupBeforeAdd(xml) 152 | 153 | let type: string = xml.attributes.type 154 | if (type && type != 'empty') { 155 | let str: string 156 | str = xml.attributes.lineSize 157 | if (str) this.$lineSize = parseInt(str) 158 | 159 | let c: string 160 | str = xml.attributes.lineColor 161 | if (str) { 162 | c = StringUtil.convertToRGBA(str) 163 | this.$lineColor = c 164 | } 165 | 166 | str = xml.attributes.fillColor 167 | if (str) { 168 | c = StringUtil.convertToRGBA(str) 169 | this.$fillColor = c 170 | } 171 | 172 | let arr: string[] 173 | str = xml.attributes.corner 174 | if (str) { 175 | arr = str.split(',') 176 | if (arr.length > 1) 177 | this.$corner = [parseInt(arr[0]), parseInt(arr[1]), parseInt(arr[2]), parseInt(arr[3])] 178 | else this.$corner = [parseInt(arr[0])] 179 | } 180 | 181 | if (type == 'rect') { 182 | this.$type = 1 183 | } else if (type == 'eclipse') { 184 | this.$type = 2 185 | } else if (type == 'regular_polygon') { 186 | this.$type = 3 187 | str = xml.attributes.sides 188 | if (str) { 189 | this.$sides = parseInt(str) 190 | } 191 | str = xml.attributes.startAngle 192 | if (str) { 193 | this.$startAngle = parseInt(str) 194 | } 195 | } else if (type == 'polygon') { 196 | this.$type = 4 197 | str = xml.attributes.points 198 | if (str) { 199 | arr = str.split(',') 200 | this.$points = arr.map(point => { 201 | return parseInt(point) 202 | }) 203 | } 204 | } 205 | 206 | this.drawGraph() 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/GGroup.ts: -------------------------------------------------------------------------------- 1 | 2 | import { UIContainer } from './display/UIContainer' 3 | import { GObject } from './GObject' 4 | 5 | export class GGroup extends GObject { 6 | protected $empty: boolean 7 | 8 | /**@internal */ 9 | $updating: boolean 10 | 11 | protected createDisplayObject(): void { 12 | let c = new UIContainer(this) 13 | /** 14 | * todo 15 | */ 16 | // c.interactive = false; 17 | c.mouseEnabled = false; 18 | this.setDisplayObject(c) 19 | } 20 | 21 | public updateBounds(): void { 22 | if (this.$updating || !this.parent) return 23 | 24 | let cnt: number = this.$parent.numChildren 25 | let i: number = 0 26 | let ax: number = Number.POSITIVE_INFINITY, 27 | ay: number = Number.POSITIVE_INFINITY 28 | let ar: number = Number.NEGATIVE_INFINITY, 29 | ab: number = Number.NEGATIVE_INFINITY 30 | this.$empty = true 31 | let child: GObject 32 | let tmp: number = 0 33 | for (i = 0; i < cnt; i++) { 34 | child = this.$parent.getChildAt(i) 35 | if (child.group == this) { 36 | tmp = child.x 37 | if (tmp < ax) ax = tmp 38 | tmp = child.y 39 | if (tmp < ay) ay = tmp 40 | tmp = child.x + child.width 41 | if (tmp > ar) ar = tmp 42 | tmp = child.y + child.height 43 | if (tmp > ab) ab = tmp 44 | this.$empty = false 45 | } 46 | } 47 | 48 | this.$updating = true 49 | if (!this.$empty) { 50 | this.setXY(ax, ay) 51 | this.setSize(ar - ax, ab - ay) 52 | } else this.setSize(0, 0) 53 | this.$updating = false 54 | } 55 | 56 | public setXY(xv: number, yv: number): void { 57 | if (this.$x != xv || this.$y != yv) { 58 | let dx: number = xv - this.$x 59 | let dy: number = yv - this.$y 60 | super.setXY(xv, yv) 61 | this.moveChildren(dx, dy) 62 | } 63 | } 64 | 65 | public moveChildren(dx: number, dy: number): void { 66 | if (this.$updating || !this.$parent) return 67 | 68 | this.$updating = true 69 | let cnt: number = this.$parent.numChildren 70 | let i: number = 0 71 | let child: GObject 72 | for (i = 0; i < cnt; i++) { 73 | child = this.$parent.getChildAt(i) 74 | if (child.group == this) { 75 | child.setXY(child.x + dx, child.y + dy) 76 | } 77 | } 78 | this.$updating = false 79 | } 80 | 81 | protected updateAlpha(): void { 82 | super.updateAlpha() 83 | 84 | if (this.$inProgressBuilding) return 85 | 86 | let cnt: number = this.$parent.numChildren 87 | let i: number 88 | let child: GObject 89 | for (i = 0; i < cnt; i++) { 90 | child = this.$parent.getChildAt(i) 91 | if (child.group == this) child.alpha = this.alpha 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/GImage.ts: -------------------------------------------------------------------------------- 1 | 2 | import { FlipType, GearType, ParseFlipType } from './config/Definitions' 3 | import { UIImage } from './display/UIImage' 4 | 5 | import { GObject } from './GObject' 6 | import { IColorGear } from './interface/IColorGear' 7 | import { StringUtil } from './utils/StringUtil' 8 | import { XmlNode } from './utils/XMLParser' 9 | 10 | export class GImage extends GObject implements IColorGear { 11 | private $content: UIImage 12 | private $flip: FlipType 13 | 14 | public constructor() { 15 | super(); 16 | } 17 | 18 | public get touchable(): boolean { 19 | return false 20 | } 21 | 22 | public set touchable(value: boolean) { 23 | this.$touchable = false //GImage has no interaction 24 | } 25 | 26 | public get color(): string { 27 | return this.$content.tint 28 | } 29 | 30 | public set color(value: string) { 31 | if (this.color != value) { 32 | this.updateGear(GearType.Color) 33 | this.$content.tint = value 34 | this.$content.setCache(0, 0, this.$width, this.$height) 35 | } 36 | } 37 | 38 | public get flip(): FlipType { 39 | return this.$flip 40 | } 41 | 42 | public set flip(value: FlipType) { 43 | if (this.$flip != value) { 44 | this.$flip = value 45 | this.$content.scaleX = this.$content.scaleY = 1 46 | if (this.$flip == FlipType.Horizontal || this.$flip == FlipType.Both) { 47 | this.$content.scaleX = -1 48 | } 49 | if (this.$flip == FlipType.Vertical || this.$flip == FlipType.Both) { 50 | this.$content.scaleY = -1 51 | } 52 | this.handleXYChanged() 53 | } 54 | } 55 | 56 | public get texture(): HTMLImageElement { 57 | return this.$content.texture 58 | } 59 | 60 | public set texture(value: HTMLImageElement) { 61 | if (value != null) { 62 | this.$sourceWidth = value.width 63 | this.$sourceHeight = value.height 64 | } else this.$sourceWidth = this.$sourceHeight = 0 65 | this.$initWidth = this.$sourceWidth 66 | this.$initHeight = this.$sourceHeight 67 | this.$content.texture = value 68 | } 69 | 70 | protected createDisplayObject(): void { 71 | this.$content = new UIImage(this) 72 | this.setDisplayObject(this.$content) 73 | } 74 | 75 | public dispose(): void { 76 | this.$content.destroy() 77 | super.dispose() 78 | } 79 | 80 | public constructFromResource(): void { 81 | this.$sourceWidth = this.packageItem.width 82 | this.$sourceHeight = this.packageItem.height 83 | this.$initWidth = this.$sourceWidth 84 | this.$initHeight = this.$sourceHeight 85 | this.$content.$initDisp(this.packageItem) 86 | this.setSize(this.$sourceWidth, this.$sourceHeight) 87 | } 88 | 89 | protected handleXYChanged(): void { 90 | super.handleXYChanged() 91 | if (this.$flip != FlipType.None) { 92 | if (this.$content.scaleX == -1) this.$content.x += this.width 93 | if (this.$content.scaleY == -1) this.$content.y += this.height 94 | } 95 | } 96 | 97 | protected handleSizeChanged(): void { 98 | this.$content.width = this.width 99 | this.$content.height = this.height 100 | let rect = new createjs.Rectangle(this.x, this.y, this.width, this.height) 101 | this.$content.sourceRect = rect 102 | } 103 | 104 | public setupBeforeAdd(xml: XmlNode): void { 105 | super.setupBeforeAdd(xml) 106 | 107 | let str: string 108 | str = xml.attributes.color 109 | if (str) { 110 | this.color = StringUtil.HEX2RGB(str) 111 | } 112 | str = xml.attributes.flip 113 | if (str) this.flip = ParseFlipType(str) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/GLabel.ts: -------------------------------------------------------------------------------- 1 | 2 | import { GearType } from './config/Definitions' 3 | import { GComponent } from './GComponent' 4 | import { GObject } from './GObject' 5 | import { GTextInput } from './GTextInput' 6 | import { IColorableTitle, isColorableTitle } from './interface/IColorableTitle' 7 | import { XmlNode, XmlParser } from './utils/XMLParser' 8 | 9 | export class GLabel extends GComponent implements IColorableTitle { 10 | protected $titleObject: GObject 11 | protected $iconObject: GObject 12 | 13 | public constructor() { 14 | super() 15 | } 16 | 17 | public get icon(): string { 18 | if (this.$iconObject != null) return this.$iconObject.icon 19 | return null 20 | } 21 | 22 | public set icon(value: string) { 23 | if (this.$iconObject != null) this.$iconObject.icon = value 24 | this.updateGear(GearType.Icon) 25 | } 26 | 27 | public get title(): string { 28 | if (this.$titleObject) return this.$titleObject.text 29 | else return null 30 | } 31 | 32 | public set title(value: string) { 33 | if (this.$titleObject) this.$titleObject.text = value 34 | this.updateGear(GearType.Text) 35 | } 36 | 37 | public get text(): string { 38 | return this.title 39 | } 40 | 41 | public set text(value: string) { 42 | this.title = value 43 | } 44 | 45 | public get titleColor(): string { 46 | if (isColorableTitle(this.$titleObject)) return this.$titleObject.titleColor 47 | return '' 48 | } 49 | 50 | public set titleColor(value: string) { 51 | if (isColorableTitle(this.$titleObject)) this.$titleObject.titleColor = value 52 | } 53 | 54 | public get fontSize(): number { 55 | if (isColorableTitle(this.$titleObject)) return this.$titleObject.fontSize 56 | return 0 57 | } 58 | 59 | public set fontSize(value: number) { 60 | if (isColorableTitle(this.$titleObject)) this.$titleObject.fontSize = value 61 | } 62 | 63 | public set editable(val: boolean) { 64 | if (this.$titleObject) (this.$titleObject as GTextInput).editable = val 65 | } 66 | 67 | public get editable(): boolean { 68 | if (this.$titleObject && this.$titleObject instanceof GTextInput) 69 | return (this.$titleObject as GTextInput).editable 70 | else return false 71 | } 72 | 73 | protected constructFromXML(xml: XmlNode): void { 74 | super.constructFromXML(xml) 75 | 76 | this.$titleObject = this.getChild('title') 77 | this.$iconObject = this.getChild('icon') 78 | } 79 | 80 | public setupAfterAdd(xml: XmlNode): void { 81 | super.setupAfterAdd(xml) 82 | 83 | let cs = XmlParser.getChildNodes(xml, 'Label') 84 | if (cs && cs.length > 0) { 85 | xml = cs[0] 86 | 87 | let str: string 88 | str = xml.attributes.title 89 | if (str) this.text = str 90 | str = xml.attributes.icon 91 | if (str) this.icon = str 92 | 93 | str = xml.attributes.titleColor 94 | if (str) 95 | this.titleColor = str; 96 | 97 | if (this.$titleObject instanceof GTextInput) { 98 | str = xml.attributes.prompt 99 | let ti = this.$titleObject as GTextInput 100 | if (str) ti.promptText = str 101 | str = xml.attributes.maxLength 102 | if (str) ti.maxLength = parseInt(str) 103 | str = xml.attributes.restrict 104 | if (str) ti.restrict = str 105 | str = xml.attributes.password 106 | if (str) ti.password = str == 'true' 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/GMovieClip.ts: -------------------------------------------------------------------------------- 1 | 2 | import { GearType } from './config/Definitions' 3 | import { MovieClip } from './display/MovieClip' 4 | import { GObject } from './GObject' 5 | import { IAnimationGear } from './interface/IAnimationGear' 6 | import { IColorGear } from './interface/IColorGear' 7 | import { StringUtil } from './utils/StringUtil' 8 | import { XmlNode } from './utils/XMLParser' 9 | 10 | export class GMovieClip extends GObject implements IAnimationGear, IColorGear { 11 | private $movieClip: MovieClip 12 | 13 | public constructor() { 14 | super() 15 | } 16 | 17 | protected mapPivotWidth(scale: number): number { 18 | return scale * this.$sourceWidth 19 | } 20 | 21 | protected mapPivotHeight(scale: number): number { 22 | return scale * this.$sourceHeight 23 | } 24 | 25 | protected handleSizeChanged(): void { 26 | if (this.$displayObject != null && this.$sourceWidth != 0 && this.$sourceHeight != 0) 27 | this.$displayObject.set({ 28 | scaleX: (this.$width / this.$sourceWidth) * this.$scaleX, 29 | scaleY: (this.$height / this.$sourceHeight) * this.$scaleY 30 | }) 31 | } 32 | 33 | public handleScaleChanged(): void { 34 | if (this.$displayObject != null) { 35 | this.$displayObject.set({ 36 | scaleX: (this.$width / this.$sourceWidth) * this.$scaleX, 37 | scaleY: (this.$height / this.$sourceHeight) * this.$scaleY 38 | }) 39 | } 40 | } 41 | 42 | public get touchable(): boolean { 43 | return false 44 | } 45 | 46 | public set touchable(value: boolean) { 47 | this.$touchable = false //GMovieClip has no interaction 48 | } 49 | 50 | public get color(): string { 51 | return this.$movieClip.tint 52 | } 53 | 54 | public set color(value: string) { 55 | this.$movieClip.tint = value 56 | } 57 | 58 | protected createDisplayObject(): void { 59 | this.$movieClip = new MovieClip(this) 60 | this.setDisplayObject(this.$movieClip) 61 | } 62 | 63 | public get playing(): boolean { 64 | return this.$movieClip.playing 65 | } 66 | 67 | public set playing(value: boolean) { 68 | if (this.$movieClip.playing != value) { 69 | this.$movieClip.playing = value 70 | this.updateGear(GearType.Animation) 71 | } 72 | } 73 | 74 | public get frame(): number { 75 | return this.$movieClip.currentFrame 76 | } 77 | 78 | public set frame(value: number) { 79 | if (this.$movieClip.currentFrame != value) { 80 | this.$movieClip.currentFrame = value 81 | this.updateGear(GearType.Animation) 82 | } 83 | } 84 | 85 | /** 86 | * Modify the playing settings for the current MovieClip object, there are two ways to call this method: 87 | * 1) pass whole parameters: 88 | startFrame: number; 89 | endFrame: number; 90 | repeatCount: number; 91 | loopEndAt: number; 92 | endCallback: (target?: MovieClip) => void; 93 | endCallbackContext: any; 94 | * 2) just pass 1 object which implements MovieClipSettings (recommended) 95 | */ 96 | public setPlaySettings(...args: any[]): void { 97 | this.$movieClip.setPlaySettings.apply(this.$movieClip, args) 98 | } 99 | 100 | public constructFromResource(): void { 101 | this.$sourceWidth = this.packageItem.width 102 | this.$sourceHeight = this.packageItem.height 103 | this.$initWidth = this.$sourceWidth 104 | this.$initHeight = this.$sourceHeight 105 | 106 | this.setSize(this.$sourceWidth, this.$sourceHeight) 107 | 108 | this.packageItem.load() 109 | 110 | this.$movieClip.interval = this.packageItem.interval 111 | this.$movieClip.swing = this.packageItem.swing 112 | this.$movieClip.repeatDelay = this.packageItem.repeatDelay 113 | this.$movieClip.frames = this.packageItem.frames 114 | this.$movieClip.boundsRect = new createjs.Rectangle(0, 0, this.$sourceWidth, this.$sourceHeight) 115 | } 116 | 117 | public setupBeforeAdd(xml: XmlNode): void { 118 | super.setupBeforeAdd(xml) 119 | 120 | let str: string 121 | str = xml.attributes.frame 122 | if (str) this.$movieClip.currentFrame = parseInt(str) 123 | str = xml.attributes.playing 124 | this.$movieClip.playing = str != 'false' 125 | 126 | str = xml.attributes.color 127 | if (str) { 128 | this.color = StringUtil.HEX2RGB(str) 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/GProgressBar.ts: -------------------------------------------------------------------------------- 1 | 2 | import { ParseEaseType, ParseProgressTitleType, ProgressTitleType } from './config/Definitions' 3 | import { GComponent } from './GComponent' 4 | import { GMovieClip } from './GMovieClip' 5 | import { GObject } from './GObject' 6 | import { GTextField } from './GTextField' 7 | import { Binder } from './utils/Binder' 8 | import { XmlNode, XmlParser } from './utils/XMLParser' 9 | 10 | export class GProgressBar extends GComponent { 11 | private $max: number = 0 12 | private $value: number = 0 13 | private $titleType: ProgressTitleType 14 | private $reverse: boolean 15 | 16 | private $titleObject: GTextField 17 | private $aniObject: GObject 18 | private $barObjectH: GObject 19 | private $barObjectV: GObject 20 | private $barMaxWidth: number = 0 21 | private $barMaxHeight: number = 0 22 | private $barMaxWidthDelta: number = 0 23 | private $barMaxHeightDelta: number = 0 24 | private $barStartX: number = 0 25 | private $barStartY: number = 0 26 | 27 | private $tweener: createjs.Tween 28 | private $tweenValue: number = 0 29 | 30 | private static easeLinear: (amount: number) => number = ParseEaseType('linear') // createjs.Ease.getPowIn(1); 31 | 32 | public constructor() { 33 | super() 34 | 35 | this.$titleType = ProgressTitleType.Percent 36 | this.$value = 50 37 | this.$max = 100 38 | } 39 | 40 | public get titleType(): ProgressTitleType { 41 | return this.$titleType 42 | } 43 | 44 | public set titleType(value: ProgressTitleType) { 45 | if (this.$titleType != value) { 46 | this.$titleType = value 47 | this.update(this.$value) 48 | } 49 | } 50 | 51 | public get max(): number { 52 | return this.$max 53 | } 54 | 55 | public set max(value: number) { 56 | if (this.$max != value) { 57 | this.$max = value 58 | this.update(this.$value) 59 | } 60 | } 61 | 62 | public get value(): number { 63 | return this.$value 64 | } 65 | 66 | public set value(value: number) { 67 | if (this.$tweener != null) { 68 | this.$tweener.paused = true 69 | this.$tweener = null 70 | } 71 | 72 | if (this.$value != value) { 73 | this.$value = value 74 | this.update(this.$value) 75 | } 76 | } 77 | 78 | public tweenValue(value: number, duration: number): createjs.Tween { 79 | if (this.$value != value) { 80 | if (this.$tweener) { 81 | this.$tweener.paused = true 82 | this.$tweener.removeAllEventListeners() 83 | createjs.Tween.removeTweens(this) 84 | } 85 | 86 | this.$tweenValue = this.$value 87 | this.$value = value 88 | this.$tweener = createjs.Tween.get(this, { 89 | onChange: Binder.create(this.onUpdateTween, this) 90 | }).to({ $tweenValue: value }, duration * 1000, GProgressBar.easeLinear) 91 | return this.$tweener 92 | } else return null 93 | } 94 | 95 | private onUpdateTween(): void { 96 | this.update(this.$tweenValue) 97 | } 98 | 99 | public update(val: number): void { 100 | let percent: number = this.$max != 0 ? Math.min(val / this.$max, 1) : 0 101 | if (this.$titleObject) { 102 | switch (this.$titleType) { 103 | case ProgressTitleType.Percent: 104 | this.$titleObject.text = `${Math.round(percent * 100)}%` 105 | break 106 | 107 | case ProgressTitleType.ValueAndMax: 108 | this.$titleObject.text = `${Math.round(val)}/${Math.round(this.$max)}` 109 | break 110 | 111 | case ProgressTitleType.Value: 112 | this.$titleObject.text = `${Math.round(val)}` 113 | break 114 | 115 | case ProgressTitleType.Max: 116 | this.$titleObject.text = `${Math.round(this.$max)}` 117 | break 118 | } 119 | } 120 | 121 | let fullWidth: number = this.width - this.$barMaxWidthDelta 122 | let fullHeight: number = this.height - this.$barMaxHeightDelta 123 | if (!this.$reverse) { 124 | if (this.$barObjectH) this.$barObjectH.width = fullWidth * percent 125 | if (this.$barObjectV) this.$barObjectV.height = fullHeight * percent 126 | } else { 127 | if (this.$barObjectH) { 128 | this.$barObjectH.width = fullWidth * percent 129 | this.$barObjectH.x = this.$barStartX + (fullWidth - this.$barObjectH.width) 130 | } 131 | if (this.$barObjectV) { 132 | this.$barObjectV.height = fullHeight * percent 133 | this.$barObjectV.y = this.$barStartY + (fullHeight - this.$barObjectV.height) 134 | } 135 | } 136 | if (this.$aniObject instanceof GMovieClip) 137 | (this.$aniObject as GMovieClip).frame = Math.round(percent * 100) 138 | } 139 | 140 | protected constructFromXML(xml: XmlNode): void { 141 | super.constructFromXML(xml) 142 | 143 | xml = XmlParser.getChildNodes(xml, 'ProgressBar')[0] 144 | 145 | let str: string 146 | str = xml.attributes.titleType 147 | if (str) this.$titleType = ParseProgressTitleType(str) 148 | 149 | this.$reverse = xml.attributes.reverse == 'true' 150 | 151 | this.$titleObject = this.getChild('title') as GTextField 152 | this.$barObjectH = this.getChild('bar') 153 | this.$barObjectV = this.getChild('bar_v') 154 | this.$aniObject = this.getChild('ani') 155 | 156 | if (this.$barObjectH) { 157 | this.$barMaxWidth = this.$barObjectH.width 158 | this.$barMaxWidthDelta = this.width - this.$barMaxWidth 159 | this.$barStartX = this.$barObjectH.x 160 | } 161 | if (this.$barObjectV) { 162 | this.$barMaxHeight = this.$barObjectV.height 163 | this.$barMaxHeightDelta = this.height - this.$barMaxHeight 164 | this.$barStartY = this.$barObjectV.y 165 | } 166 | } 167 | 168 | protected handleSizeChanged(): void { 169 | super.handleSizeChanged() 170 | 171 | if (this.$barObjectH) this.$barMaxWidth = this.width - this.$barMaxWidthDelta 172 | if (this.$barObjectV) this.$barMaxHeight = this.height - this.$barMaxHeightDelta 173 | if (!this.$inProgressBuilding) this.update(this.$value) 174 | } 175 | 176 | public setupAfterAdd(xml: XmlNode): void { 177 | super.setupAfterAdd(xml) 178 | 179 | xml = XmlParser.getChildNodes(xml, 'ProgressBar')[0] 180 | if (xml) { 181 | this.$value = parseInt(xml.attributes.value) || 0 182 | this.$max = parseInt(xml.attributes.max) || 0 183 | } 184 | this.update(this.$value) 185 | } 186 | 187 | public dispose(): void { 188 | if (this.$tweener) { 189 | this.$tweener.paused = true 190 | this.$tweener.removeAllEventListeners() 191 | } 192 | createjs.Tween.removeTweens(this) 193 | this.$tweener = null 194 | super.dispose() 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/GRichTextField.ts: -------------------------------------------------------------------------------- 1 | import { TextStyle } from './createjs/extras/TextStyle' 2 | import { GTextField } from './GTextField' 3 | import { XmlNode } from './utils/XMLParser' 4 | import { TextEvent } from './events/TextEvent' 5 | import { GearType } from './config/Definitions' 6 | 7 | 8 | export class TextBlock { 9 | public text: string 10 | public style: TextStyle 11 | } 12 | 13 | //TOOD: impl 14 | export class GRichTextField extends GTextField { 15 | protected $ubbEnabled: boolean 16 | protected $textFlow: TextBlock[] 17 | 18 | public set ubbEnabled(value: boolean) { 19 | if (this.$ubbEnabled != value) { 20 | this.$ubbEnabled = value 21 | this.render() 22 | } 23 | } 24 | 25 | public get ubbEnabled(): boolean { 26 | return this.$ubbEnabled 27 | } 28 | 29 | public setupBeforeAdd(xml: XmlNode): void { 30 | super.setupBeforeAdd(xml) 31 | this.$ubbEnabled = xml.attributes.ubb == 'true' 32 | } 33 | 34 | public constructor() { 35 | super() 36 | 37 | // this.$textField.interactive = true; 38 | // this.$textField.interactiveChildren = false; 39 | this.on(TextEvent.LinkClick, this.$clickLink, this) 40 | } 41 | 42 | public set textFlow(flow: TextBlock[]) { 43 | this.$textFlow = flow 44 | this.render() 45 | } 46 | 47 | public set text(value: string) { 48 | this.$text = value 49 | if (this.$text == null) this.$text = '' 50 | this.$textField.width = this.width 51 | // if(this.$ubbEnabled) 52 | // this.textFlow = StringUtil.parseUBB(this.$text); //TODO: parser impl 53 | this.updateGear(GearType.Text) 54 | this.render() 55 | } 56 | 57 | private $clickLink(block: TextBlock) { 58 | let event = new createjs.Event(TextEvent.LinkClick, true, false) 59 | event.data = block.text 60 | this.dispatchEvent(event, this) 61 | } 62 | 63 | public dispose(): void { 64 | this.off(TextEvent.LinkClick, this.$clickLink) 65 | super.dispose() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/GScrollBar.ts: -------------------------------------------------------------------------------- 1 | import { InteractiveEvents } from './config/Definitions' 2 | import { GComponent } from './GComponent' 3 | import { GObject } from './GObject' 4 | import { GRoot } from './GRoot' 5 | import { ScrollPane } from './ScrollPane' 6 | import { XmlNode, XmlParser } from './utils/XMLParser' 7 | 8 | export class GScrollBar extends GComponent { 9 | private $grip: GObject 10 | private $arrowButton1: GObject 11 | private $arrowButton2: GObject 12 | private $bar: GObject 13 | private $target: ScrollPane 14 | 15 | private $vertical: boolean 16 | private $scrollPerc: number 17 | private $fixedGripSize: boolean 18 | 19 | private $dragOffset: createjs.Point 20 | 21 | public constructor() { 22 | super() 23 | this.$dragOffset = new createjs.Point() 24 | this.$scrollPerc = 0 25 | } 26 | 27 | public setScrollPane(target: ScrollPane, vertical: boolean): void { 28 | this.$target = target 29 | this.$vertical = vertical 30 | } 31 | 32 | public set displayPerc(val: number) { 33 | if (this.$vertical) { 34 | if (!this.$fixedGripSize) this.$grip.height = val * this.$bar.height 35 | this.$grip.y = this.$bar.y + (this.$bar.height - this.$grip.height) * this.$scrollPerc 36 | } else { 37 | if (!this.$fixedGripSize) this.$grip.width = val * this.$bar.width 38 | this.$grip.x = this.$bar.x + (this.$bar.width - this.$grip.width) * this.$scrollPerc 39 | } 40 | } 41 | 42 | public get scrollPerc(): number { 43 | return this.$scrollPerc 44 | } 45 | 46 | public set scrollPerc(val: number) { 47 | this.$scrollPerc = val 48 | if (this.$vertical) 49 | this.$grip.y = this.$bar.y + (this.$bar.height - this.$grip.height) * this.$scrollPerc 50 | else this.$grip.x = this.$bar.x + (this.$bar.width - this.$grip.width) * this.$scrollPerc 51 | } 52 | 53 | public get minSize(): number { 54 | if (this.$vertical) 55 | return ( 56 | (this.$arrowButton1 != null ? this.$arrowButton1.height : 0) + 57 | (this.$arrowButton2 != null ? this.$arrowButton2.height : 0) 58 | ) 59 | else 60 | return ( 61 | (this.$arrowButton1 != null ? this.$arrowButton1.width : 0) + 62 | (this.$arrowButton2 != null ? this.$arrowButton2.width : 0) 63 | ) 64 | } 65 | 66 | protected constructFromXML(xml: XmlNode): void { 67 | super.constructFromXML(xml) 68 | 69 | xml = XmlParser.getChildNodes(xml, 'ScrollBar')[0] 70 | if (xml != null) this.$fixedGripSize = xml.attributes.fixedGripSize == 'true' 71 | 72 | this.$grip = this.getChild('grip') 73 | if (!this.$grip) { 74 | console.error("please create and define 'grip' in the Editor for the scrollbar") 75 | return 76 | } 77 | 78 | this.$bar = this.getChild('bar') 79 | if (!this.$bar) { 80 | console.error("please create and define 'bar' in the Editor for the scrollbar") 81 | return 82 | } 83 | 84 | this.$arrowButton1 = this.getChild('arrow1') 85 | this.$arrowButton2 = this.getChild('arrow2') 86 | 87 | this.$grip.on(InteractiveEvents.Down, this.$gripMouseDown, this) 88 | 89 | if (this.$arrowButton1) 90 | this.$arrowButton1.on(InteractiveEvents.Down, this.$arrowButton1Click, this) 91 | if (this.$arrowButton2) 92 | this.$arrowButton2.on(InteractiveEvents.Down, this.$arrowButton2Click, this) 93 | 94 | this.on(InteractiveEvents.Down, this.$barMouseDown, this) 95 | } 96 | 97 | private $gripMouseDown(evt: any): void { 98 | if (!this.$bar) return 99 | 100 | evt.stopPropagation() 101 | 102 | // this.$dragOffset = evt.data.getLocalPosition(this.displayObject, this.$dragOffset); 103 | this.$dragOffset = new createjs.Point(evt.localX, evt.localY) 104 | this.$dragOffset.x -= this.$grip.x 105 | this.$dragOffset.y -= this.$grip.y 106 | 107 | this.$mouseMoveEvent = GRoot.inst.nativeStage.on( 108 | InteractiveEvents.Move, 109 | this.$gripDragging, 110 | this 111 | ) 112 | this.$mouseUpEvent = GRoot.inst.nativeStage.on( 113 | InteractiveEvents.Up, 114 | this.$gripDraggingEnd, 115 | this 116 | ) 117 | } 118 | 119 | private static sScrollbarHelperPoint: createjs.Point = new createjs.Point() 120 | private $gripDragging(evt: any): void { 121 | let pt: createjs.Point = evt.target.localToLocal(evt.localX, evt.localY, this.$displayObject); // todo 122 | if (GScrollBar.sScrollbarHelperPoint.x == 0 && GScrollBar.sScrollbarHelperPoint.y == 0) { 123 | GScrollBar.sScrollbarHelperPoint.x = this.$target['$xPos']; 124 | GScrollBar.sScrollbarHelperPoint.y = this.$target['$yPos']; 125 | } 126 | pt.x -= GScrollBar.sScrollbarHelperPoint.x; 127 | pt.y -= GScrollBar.sScrollbarHelperPoint.y; 128 | 129 | 130 | if (this.$vertical) { 131 | let curY: number = pt.y - this.$dragOffset.y 132 | this.$target.setPercY((curY - this.$bar.y) / (this.$bar.height - this.$grip.height), false) 133 | } else { 134 | let curX: number = pt.x - this.$dragOffset.x 135 | this.$target.setPercX((curX - this.$bar.x) / (this.$bar.width - this.$grip.width), false) 136 | } 137 | } 138 | 139 | private $gripDraggingEnd(evt: createjs.Event): void { 140 | GScrollBar.sScrollbarHelperPoint = new createjs.Point(); 141 | GRoot.inst.nativeStage.off(InteractiveEvents.Move, this.$mouseMoveEvent) 142 | GRoot.inst.nativeStage.off(InteractiveEvents.Up, this.$mouseUpEvent) 143 | } 144 | 145 | private $arrowButton1Click(evt: createjs.Event): void { 146 | evt.stopPropagation() 147 | 148 | if (this.$vertical) this.$target.scrollUp() 149 | else this.$target.scrollLeft() 150 | } 151 | 152 | private $arrowButton2Click(evt: createjs.Event): void { 153 | evt.stopPropagation() 154 | 155 | if (this.$vertical) this.$target.scrollDown() 156 | else this.$target.scrollRight() 157 | } 158 | 159 | private $barMouseDown(evt: any): void { 160 | let pt: createjs.Point = new createjs.Point(evt.localX, evt.localY) 161 | // let pt: createjs.Point = evt.data.getLocalPosition(this.$grip.displayObject, GScrollBar.sScrollbarHelperPoint); 162 | if (this.$vertical) { 163 | if (pt.y < 0) this.$target.scrollUp(4) 164 | else this.$target.scrollDown(4) 165 | } else { 166 | if (pt.x < 0) this.$target.scrollLeft(4) 167 | else this.$target.scrollRight(4) 168 | } 169 | } 170 | 171 | public dispose(): void { 172 | this.off(InteractiveEvents.Down, this.$barMouseDown) 173 | GScrollBar.sScrollbarHelperPoint = new createjs.Point(); 174 | if (this.$arrowButton1) this.$arrowButton1.off(InteractiveEvents.Down, this.$arrowButton1Click) 175 | if (this.$arrowButton2) this.$arrowButton2.off(InteractiveEvents.Down, this.$arrowButton2Click) 176 | 177 | this.$grip.off(InteractiveEvents.Down, this.$gripMouseDown) 178 | this.$gripDraggingEnd(null) 179 | 180 | super.dispose() 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/GSlider.ts: -------------------------------------------------------------------------------- 1 | import { InteractiveEvents, ParseProgressTitleType, ProgressTitleType } from './config/Definitions' 2 | import { StateChangeEvent } from './events/StateChangeEvent' 3 | import { GComponent } from './GComponent' 4 | import { GMovieClip } from './GMovieClip' 5 | import { GObject } from './GObject' 6 | import { GRoot } from './GRoot' 7 | import { GTextField } from './GTextField' 8 | import { XmlNode, XmlParser } from './utils/XMLParser' 9 | 10 | export class GSlider extends GComponent { 11 | protected $max: number = 0 12 | protected $value: number = 0 13 | protected $titleType: ProgressTitleType 14 | 15 | protected $titleObject: GTextField 16 | protected $aniObject: GObject 17 | protected $barObjectH: GObject 18 | protected $barObjectV: GObject 19 | 20 | protected $barMaxWidth: number = 0 21 | protected $barMaxHeight: number = 0 22 | protected $barMaxWidthDelta: number = 0 23 | protected $barMaxHeightDelta: number = 0 24 | protected $gripObject: GObject 25 | 26 | private $clickPos: createjs.Point 27 | private $clickPercent: number 28 | 29 | public constructor() { 30 | super() 31 | 32 | this.$titleType = ProgressTitleType.Percent 33 | this.$value = 50 34 | this.$max = 100 35 | this.$clickPos = new createjs.Point() 36 | } 37 | 38 | public get titleType(): ProgressTitleType { 39 | return this.$titleType 40 | } 41 | 42 | public set titleType(value: ProgressTitleType) { 43 | this.$titleType = value 44 | } 45 | 46 | public get max(): number { 47 | return this.$max 48 | } 49 | 50 | public set max(value: number) { 51 | if (this.$max != value) { 52 | this.$max = value 53 | this.update() 54 | } 55 | } 56 | 57 | public get value(): number { 58 | return this.$value 59 | } 60 | 61 | public set value(value: number) { 62 | if (this.$value != value) { 63 | this.$value = value 64 | this.update() 65 | } 66 | } 67 | 68 | public update(): void { 69 | let percent: number = Math.min(this.$value / this.$max, 1) 70 | this.updateWidthPercent(percent) 71 | } 72 | 73 | private updateWidthPercent(percent: number): void { 74 | if (this.$titleObject) { 75 | switch (this.$titleType) { 76 | case ProgressTitleType.Percent: 77 | this.$titleObject.text = `${Math.round(percent * 100)}%` 78 | break 79 | 80 | case ProgressTitleType.ValueAndMax: 81 | this.$titleObject.text = `${this.$value}/${this.$max}` 82 | break 83 | 84 | case ProgressTitleType.Value: 85 | this.$titleObject.text = `${this.$value}` 86 | break 87 | 88 | case ProgressTitleType.Max: 89 | this.$titleObject.text = `${this.$max}` 90 | break 91 | } 92 | } 93 | 94 | if (this.$barObjectH) this.$barObjectH.width = (this.width - this.$barMaxWidthDelta) * percent 95 | if (this.$barObjectV) 96 | this.$barObjectV.height = (this.height - this.$barMaxHeightDelta) * percent 97 | 98 | if (this.$aniObject instanceof GMovieClip) 99 | (this.$aniObject as GMovieClip).frame = Math.round(percent * 100) 100 | } 101 | 102 | protected handleSizeChanged(): void { 103 | super.handleSizeChanged() 104 | 105 | if (this.$barObjectH) this.$barMaxWidth = this.width - this.$barMaxWidthDelta 106 | if (this.$barObjectV) this.$barMaxHeight = this.height - this.$barMaxHeightDelta 107 | if (!this.$inProgressBuilding) this.update() 108 | } 109 | 110 | public setupAfterAdd(xml: XmlNode): void { 111 | super.setupAfterAdd(xml) 112 | 113 | xml = XmlParser.getChildNodes(xml, 'Slider')[0] 114 | if (xml) { 115 | this.$value = parseInt(xml.attributes.value) 116 | this.$max = parseInt(xml.attributes.max) 117 | } 118 | 119 | this.update() 120 | } 121 | 122 | protected constructFromXML(xml: XmlNode): void { 123 | super.constructFromXML(xml) 124 | 125 | xml = XmlParser.getChildNodes(xml, 'Slider')[0] 126 | 127 | let str: string 128 | if (xml) { 129 | str = xml.attributes.titleType 130 | if (str) this.$titleType = ParseProgressTitleType(str) 131 | } 132 | 133 | this.$titleObject = this.getChild('title') as GTextField 134 | this.$barObjectH = this.getChild('bar') 135 | this.$barObjectV = this.getChild('bar_v') 136 | this.$aniObject = this.getChild('ani') 137 | this.$gripObject = this.getChild('grip') 138 | 139 | if (this.$barObjectH) { 140 | this.$barMaxWidth = this.$barObjectH.width 141 | this.$barMaxWidthDelta = this.width - this.$barMaxWidth 142 | } 143 | if (this.$barObjectV) { 144 | this.$barMaxHeight = this.$barObjectV.height 145 | this.$barMaxHeightDelta = this.height - this.$barMaxHeight 146 | } 147 | if (this.$gripObject) this.$gripObject.on(InteractiveEvents.Down, this.$gripMouseDown, this) 148 | } 149 | 150 | private $gripMouseDown(evt: any): void { 151 | this.$clickPos = this.globalToLocal(evt.stageX, evt.stageY) 152 | this.$clickPercent = this.$value / this.$max 153 | 154 | this.$mouseMoveEvent = GRoot.inst.nativeStage.on( 155 | InteractiveEvents.Move, 156 | this.$gripMouseMove, 157 | this 158 | ) 159 | this.$mouseUpEvent = GRoot.inst.nativeStage.on(InteractiveEvents.Up, this.$gripMouseUp, this) 160 | } 161 | 162 | private static sSilderHelperPoint: createjs.Point = new createjs.Point() 163 | 164 | private $gripMouseMove(evt): void { 165 | let pt: createjs.Point = this.globalToLocal(evt.stageX, evt.stageY, GSlider.sSilderHelperPoint) 166 | let deltaX: number = pt.x - this.$clickPos.x 167 | let deltaY: number = pt.y - this.$clickPos.y 168 | 169 | let percent: number 170 | if (this.$barObjectH) percent = this.$clickPercent + deltaX / this.$barMaxWidth 171 | else percent = this.$clickPercent + deltaY / this.$barMaxHeight 172 | if (percent > 1) percent = 1 173 | else if (percent < 0) percent = 0 174 | let newValue: number = Math.round(this.$max * percent) 175 | if (newValue != this.$value) { 176 | this.$value = newValue 177 | let evt = new createjs.Event(StateChangeEvent.CHANGED, true, false) 178 | this.dispatchEvent(evt, this) 179 | } 180 | this.updateWidthPercent(percent) 181 | } 182 | 183 | private $gripMouseUp(evt: createjs.Event): void { 184 | let percent: number = this.$value / this.$max 185 | this.updateWidthPercent(percent) 186 | 187 | GRoot.inst.nativeStage.off(InteractiveEvents.Move, this.$mouseMoveEvent) 188 | GRoot.inst.nativeStage.off(InteractiveEvents.Up, this.$mouseUpEvent) 189 | } 190 | 191 | public dispose(): void { 192 | if (this.$gripObject) this.$gripObject.off(InteractiveEvents.Down, this.$gripMouseDown) 193 | GRoot.inst.nativeStage.off(InteractiveEvents.Move, this.$mouseMoveEvent) 194 | GRoot.inst.nativeStage.off(InteractiveEvents.Up, this.$mouseUpEvent) 195 | super.dispose() 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/GTextInput.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Decls } from './GObject' 3 | import { GTextField } from './GTextField' 4 | import { InputDelegate } from './utils/InputDelegate' 5 | import { XmlNode } from './utils/XMLParser' 6 | 7 | export const enum InputType { 8 | TEXT = 'text', 9 | PASSWORD = 'password', 10 | NUMBER = 'number', 11 | EMAIL = 'email', 12 | TEL = 'tel', 13 | URL = 'url' 14 | } 15 | 16 | export class GTextInput extends GTextField { 17 | protected $editable: boolean 18 | protected $util: InputDelegate = null 19 | 20 | /**@internal */ 21 | $isTyping: boolean = false 22 | 23 | public constructor() { 24 | super() 25 | this.focusable = true 26 | this.editable = true //init 27 | 28 | this.type = InputType.TEXT 29 | 30 | this.on('removed', this.removed, this) 31 | this.on('added', this.added, this) 32 | this.$util.initialize() 33 | } 34 | 35 | private $hitArea: createjs.Shape 36 | protected createDisplayObject(): void { 37 | super.createDisplayObject() 38 | this.$hitArea = new createjs.Shape() 39 | this.$hitArea.graphics.beginFill('#000').drawRect(0, 0, this.$width, this.$height) 40 | this.$displayObject.hitArea = this.$hitArea 41 | } 42 | 43 | protected handleSizeChanged(): void { 44 | super.handleSizeChanged() 45 | this.$hitArea.graphics.drawRect(0, 0, this.$width, this.$height) 46 | this.$displayObject.hitArea = this.$hitArea 47 | } 48 | 49 | private removed(disp: createjs.DisplayObject): void { 50 | if (this.$util) this.$util.destroy() 51 | } 52 | 53 | private added(disp: createjs.DisplayObject): void { 54 | if (this.$util) this.$util.initialize() 55 | } 56 | 57 | public requestFocus(): void { 58 | //tab or call actively 59 | Decls.GRoot.inst.focus = this 60 | this.$util.$onFocus() 61 | } 62 | 63 | public get editable(): boolean { 64 | return this.$editable 65 | } 66 | 67 | public set editable(v: boolean) { 68 | if (v != this.$editable) { 69 | this.$editable = v 70 | 71 | if (this.$editable) { 72 | if (!this.$util) this.$util = new InputDelegate(this) 73 | this.$util.initialize() 74 | } else { 75 | if (this.$util) this.$util.destroy() 76 | } 77 | 78 | this.touchable = this.$editable 79 | } 80 | } 81 | 82 | private changeToPassText(text: string): string { 83 | let passText: string = '' 84 | for (let i: number = 0, num = text.length; i < num; i++) { 85 | switch (text.charAt(i)) { 86 | case '\n': 87 | passText += '\n' 88 | break 89 | case '\r': 90 | break 91 | default: 92 | passText += '*' 93 | } 94 | } 95 | return passText 96 | } 97 | 98 | protected getText(): string { 99 | return this.$util.text 100 | } 101 | 102 | protected setText(value: string): void { 103 | if (value == null) value = '' 104 | if (this.$text == value) return 105 | this.$util.text = value 106 | super.setText(value) 107 | } 108 | 109 | protected setColor(value: string): void { 110 | super.setColor(value) 111 | this.$util.setColor(value) 112 | } 113 | 114 | public get promptText(): string { 115 | return this.$util.$getProperty('placeholder') 116 | } 117 | 118 | public set promptText(v: string) { 119 | if (v == null) v = '' 120 | this.$util.$setProperty('placeholder', v) 121 | } 122 | 123 | public get maxLength(): number { 124 | return parseInt(this.$util.$getProperty('maxlength')) || 0 125 | } 126 | 127 | public set maxLength(v: number) { 128 | this.$util.$setProperty('maxlength', String(v)) 129 | } 130 | 131 | public get restrict(): string { 132 | return this.$util.$restrict 133 | } 134 | 135 | public set restrict(v: string) { 136 | this.$util.$restrict = v 137 | } 138 | 139 | public get password(): boolean { 140 | return this.type == InputType.PASSWORD 141 | } 142 | 143 | public set password(v: boolean) { 144 | this.type = InputType.PASSWORD 145 | } 146 | 147 | public get type(): InputType { 148 | return this.$util.type 149 | } 150 | 151 | public set type(t: InputType) { 152 | this.$util.type = t 153 | } 154 | 155 | public dispose(): void { 156 | super.dispose() 157 | this.off('removed', this.removed) 158 | this.off('added', this.added) 159 | this.$util.destroy() 160 | this.$util = null 161 | } 162 | 163 | protected renderNow(updateBounds: boolean = true): void { 164 | this.$requireRender = false 165 | this.$sizeDirty = false 166 | this.$util.$updateProperties() 167 | if (this.$isTyping) this.decorateInputbox() 168 | let origText = this.$text 169 | if (this.type == InputType.PASSWORD) this.$text = this.changeToPassText(this.$text) 170 | super.renderNow(updateBounds) 171 | this.$text = origText 172 | } 173 | 174 | private decorateInputbox(): void { 175 | //draw underlines? 176 | } 177 | 178 | public setupBeforeAdd(xml: XmlNode): void { 179 | super.setupBeforeAdd(xml) 180 | 181 | //this.promptText = xml.attributes.prompt; //this will be available once UBB has implemented. 182 | var str: string = xml.attributes.maxLength 183 | if (str != null) this.maxLength = parseInt(str) 184 | str = xml.attributes.restrict 185 | if (str != null) this.restrict = str 186 | str = xml.attributes.password 187 | if (str == 'true') this.password = true 188 | else { 189 | str = xml.attributes.keyboardType 190 | if (str == '4') this.type = InputType.NUMBER 191 | else if (str == '3') this.type = InputType.URL 192 | else if (str == '5') this.type = InputType.TEL 193 | else if (str == '6') this.type = InputType.EMAIL 194 | } 195 | } 196 | 197 | /*public setupAfterAdd(xml: XmlNode): void { 198 | super.setupAfterAdd(xml); 199 | f (!this.$text && this.promptText) { 200 | if(this.type != InputType.PASSWORD) { 201 | this.$textField.text = this.promptText; //take UBB into account 202 | } 203 | } 204 | }*/ 205 | } 206 | -------------------------------------------------------------------------------- /src/GTimer.ts: -------------------------------------------------------------------------------- 1 | 2 | export class GTimer { 3 | private $items: TimerItem[]; 4 | private $itemPool: TimerItem[]; 5 | 6 | private $enumIdx: number = 0; 7 | private $enumCount: number = 0; 8 | private $curTime: number = Date.now(); 9 | // private $lastTime:number = 0; 10 | private $ticker: createjs.Ticker; 11 | 12 | public static inst: GTimer = new GTimer(); 13 | 14 | public constructor() { 15 | this.$items = []; 16 | this.$itemPool = []; 17 | // this.$lastTime = createjs.Ticker.getTime(); 18 | // GTimer.time = this.$lastTime; 19 | createjs.Ticker.on('tick', this.advance, this); 20 | } 21 | 22 | private getItem(): TimerItem { 23 | if (this.$itemPool.length) 24 | return this.$itemPool.pop(); 25 | else 26 | return new TimerItem(); 27 | } 28 | 29 | private findItem(callback: (...args: any[]) => void, thisObj: any): TimerItem { 30 | let len: number = this.$items.length; 31 | for (let i: number = 0; i < len; i++) { 32 | let item: TimerItem = this.$items[i]; 33 | if (item.callback == callback && item.thisObj == thisObj) 34 | return item; 35 | } 36 | return null; 37 | } 38 | 39 | //repeat <= 0 means loop 40 | public add(delayInMs: number, repeat: number, callback: (...args: any[]) => void, thisObj: any, callbackParam?: any): void { 41 | let item: TimerItem = this.findItem(callback, thisObj); 42 | if (!item) { 43 | item = this.getItem(); 44 | item.callback = callback; 45 | item.thisObj = thisObj; 46 | this.$items.push(item); 47 | } 48 | item.delay = delayInMs; 49 | item.counter = 0; 50 | item.repeat = repeat; 51 | item.param = callbackParam; 52 | item.end = false; 53 | } 54 | 55 | public addLoop(delayInMs: number, callback: (...args: any[]) => void, thisObj: any, callbackParam?: any): void { 56 | this.add(delayInMs, 0, callback, thisObj, callbackParam); 57 | } 58 | 59 | public callLater(callback: (...args: any[]) => void, thisObj: any, callbackParam?: any): void { 60 | this.add(1, 1, callback, thisObj, callbackParam); 61 | } 62 | 63 | public callDelay(delayInMs: number, callback: (...args: any[]) => void, thisObj: any, callbackParam?: any): void { 64 | this.add(delayInMs, 1, callback, thisObj, callbackParam); 65 | } 66 | 67 | public exists(callback: (...args: any[]) => void, thisObj: any): boolean { 68 | let item: TimerItem = this.findItem(callback, thisObj); 69 | return item != null; 70 | } 71 | 72 | public remove(callback: (...args: any[]) => void, thisObj: any): void { 73 | let item: TimerItem = this.findItem(callback, thisObj); 74 | if (item) { 75 | let i: number = this.$items.indexOf(item); 76 | this.$items.splice(i, 1); 77 | if (i < this.$enumIdx) 78 | this.$enumIdx--; 79 | this.$enumCount--; 80 | 81 | item.callback = null; 82 | item.param = null; 83 | this.$itemPool.push(item); 84 | } 85 | } 86 | 87 | public get ticker(): createjs.Ticker { 88 | return this.$ticker; 89 | } 90 | 91 | public get curTime(): number { 92 | return this.$curTime; 93 | } 94 | 95 | public advance(): void { 96 | this.$enumIdx = 0; 97 | this.$enumCount = this.$items.length; 98 | 99 | while (this.$enumIdx < this.$enumCount) { 100 | let item: TimerItem = this.$items[this.$enumIdx]; 101 | this.$enumIdx++; 102 | 103 | let ms = createjs.Ticker.interval; 104 | this.$curTime += ms; 105 | 106 | if (item.advance(ms)) { 107 | if (item.end) { 108 | this.$enumIdx--; 109 | this.$enumCount--; 110 | this.$items.splice(this.$enumIdx, 1); 111 | this.$itemPool.push(item); 112 | } 113 | 114 | if (item.callback) { 115 | let args = [ms]; 116 | if (item.param && item.param instanceof Array) 117 | args = item.param.concat(args); 118 | else if (item.param !== void 0) 119 | args.unshift(item.param); 120 | item.callback.apply(item.thisObj, args); 121 | } 122 | 123 | if (item.end) 124 | item.callback = item.thisObj = item.param = null; 125 | } 126 | } 127 | } 128 | } 129 | 130 | class TimerItem { 131 | public delay: number = 0; 132 | public counter: number = 0; 133 | public repeat: number = 0; 134 | public callback: (...args: any[]) => void; 135 | public thisObj: any; 136 | public param: any; 137 | public end: boolean; 138 | 139 | public advance(delta: number = 0): boolean { 140 | this.counter += delta; 141 | if (this.counter >= this.delay) { 142 | this.counter -= this.delay; 143 | if (this.counter > this.delay) 144 | this.counter = this.delay; 145 | 146 | if (this.repeat > 0) { 147 | this.repeat--; 148 | if (this.repeat == 0) 149 | this.end = true; 150 | } 151 | 152 | return true; 153 | } 154 | else 155 | return false; 156 | } 157 | } -------------------------------------------------------------------------------- /src/PopupMenu.ts: -------------------------------------------------------------------------------- 1 | 2 | import { RelationType, PopupDirection } from './config/Definitions' 3 | import { UIConfig } from './config/UIConfig' 4 | import { Controller } from './controller/Controller' 5 | import { ListEvent } from './events/ListEvent' 6 | import { GButton } from './GButton' 7 | import { GComponent } from './GComponent' 8 | import { GList } from './GList' 9 | import { GObject } from './GObject' 10 | import { GRoot } from './GRoot' 11 | import { GTimer } from './GTimer' 12 | import { UIPackage } from './res/UIPackage' 13 | 14 | export class PopupMenu { 15 | protected $contentPane: GComponent 16 | protected $list: GList 17 | 18 | public constructor(resourceURL: string = null) { 19 | if (!resourceURL) { 20 | resourceURL = UIConfig.popupMenu 21 | if (!resourceURL) throw new Error('UIConfig.popupMenu not defined') 22 | } 23 | this.$contentPane = UIPackage.createObjectFromURL(resourceURL) as GComponent 24 | this.$contentPane.on('added', this.$addedToStage, this) 25 | this.$list = this.$contentPane.getChild('list') as GList 26 | this.$list.removeChildrenToPool() 27 | this.$list.addRelation(this.$contentPane, RelationType.Width) 28 | this.$list.removeRelation(this.$contentPane, RelationType.Height) 29 | this.$contentPane.addRelation(this.$list, RelationType.Height) 30 | this.$list.on(ListEvent.ItemClick, this.$clickItem, this) 31 | } 32 | 33 | public dispose(): void { 34 | GTimer.inst.remove(this.$delayClickItem, this) 35 | this.$list.off(ListEvent.ItemClick, this.$clickItem) 36 | this.$contentPane.off('added', this.$addedToStage) 37 | this.$contentPane.dispose() 38 | } 39 | 40 | public addItem(caption: string, handler?: Function): GButton { 41 | let item: GButton = this.$list.addItemFromPool() as GButton 42 | item.title = caption 43 | item.data = handler 44 | item.grayed = false 45 | let c: Controller = item.getController('checked') 46 | if (c != null) c.selectedIndex = 0 47 | return item 48 | } 49 | 50 | public addItemAt(caption: string, index: number, handler?: Function): GButton { 51 | let item: GButton = this.$list.getFromPool() as GButton 52 | this.$list.addChildAt(item, index) 53 | item.title = caption 54 | item.data = handler 55 | item.grayed = false 56 | let c: Controller = item.getController('checked') 57 | if (c != null) c.selectedIndex = 0 58 | return item 59 | } 60 | 61 | public addSeperator(): void { 62 | if (UIConfig.popupMenuSeperator == null) 63 | throw new Error('UIConfig.popupMenuSeperator not defined') 64 | this.$list.addItemFromPool(UIConfig.popupMenuSeperator) 65 | } 66 | 67 | public getItemName(index: number): string { 68 | let item: GObject = this.$list.getChildAt(index) 69 | return item.name 70 | } 71 | 72 | public setItemText(name: string, caption: string): void { 73 | let item: GButton = this.$list.getChild(name) as GButton 74 | item.title = caption 75 | } 76 | 77 | public setItemVisible(name: string, visible: boolean): void { 78 | let item: GButton = this.$list.getChild(name) as GButton 79 | if (item.visible != visible) { 80 | item.visible = visible 81 | this.$list.setBoundsChangedFlag() 82 | } 83 | } 84 | 85 | public setItemGrayed(name: string, grayed: boolean): void { 86 | let item: GButton = this.$list.getChild(name) as GButton 87 | item.grayed = grayed 88 | } 89 | 90 | public setItemCheckable(name: string, checkable: boolean): void { 91 | let item: GButton = this.$list.getChild(name) as GButton 92 | let c: Controller = item.getController('checked') 93 | if (c != null) { 94 | if (checkable) { 95 | if (c.selectedIndex == 0) c.selectedIndex = 1 96 | } else c.selectedIndex = 0 97 | } 98 | } 99 | 100 | public setItemChecked(name: string, checked: boolean): void { 101 | let item: GButton = this.$list.getChild(name) as GButton 102 | let c: Controller = item.getController('checked') 103 | if (c != null) c.selectedIndex = checked ? 2 : 1 104 | } 105 | 106 | public isItemChecked(name: string): boolean { 107 | let item: GButton = this.$list.getChild(name) as GButton 108 | let c: Controller = item.getController('checked') 109 | if (c != null) return c.selectedIndex == 2 110 | else return false 111 | } 112 | 113 | public removeItem(name: string): boolean { 114 | let item: GButton = this.$list.getChild(name) as GButton 115 | if (item != null) { 116 | let index: number = this.$list.getChildIndex(item) 117 | this.$list.removeChildToPoolAt(index) 118 | return true 119 | } else return false 120 | } 121 | 122 | public clearItems(): void { 123 | this.$list.removeChildrenToPool() 124 | } 125 | 126 | public get itemCount(): Number { 127 | return this.$list.numChildren 128 | } 129 | 130 | public get contentPane(): GComponent { 131 | return this.$contentPane 132 | } 133 | 134 | public get list(): GList { 135 | return this.$list 136 | } 137 | 138 | public show(target: GObject = null, dir?: PopupDirection): void { 139 | let r: GRoot = target != null ? GRoot.findFor(target) : GRoot.inst 140 | r.showPopup(this.contentPane, target instanceof GRoot ? null : target, dir) 141 | } 142 | 143 | private $clickItem(evt: createjs.Event): void { 144 | let item: GObject = evt.data.item 145 | GTimer.inst.add(100, 1, this.$delayClickItem, this, item) 146 | } 147 | 148 | private $delayClickItem(itemObject: GObject): void { 149 | if (!(itemObject instanceof GButton)) return 150 | if (itemObject.grayed) { 151 | this.$list.selectedIndex = -1 152 | return 153 | } 154 | let c: Controller = itemObject.getController('checked') 155 | if (c != null && c.selectedIndex != 0) { 156 | if (c.selectedIndex == 1) c.selectedIndex = 2 157 | else c.selectedIndex = 1 158 | } 159 | let r: GRoot = this.$contentPane.parent as GRoot 160 | if (r) r.hidePopup(this.contentPane) 161 | if (itemObject.data != null) (itemObject.data as Function).call(null) 162 | 163 | GTimer.inst.remove(this.$delayClickItem, this) 164 | } 165 | 166 | private $addedToStage(): void { 167 | this.$list.selectedIndex = -1 168 | this.$list.resizeToFit(100000, 10) 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/Relations.ts: -------------------------------------------------------------------------------- 1 | 2 | import { GComponent } from './GComponent' 3 | import { GObject } from './GObject' 4 | import { RelationItem } from './RelationItem' 5 | import { XmlNode } from './utils/XMLParser' 6 | 7 | export class Relations { 8 | protected $owner: GObject 9 | protected $items: RelationItem[] 10 | 11 | public sizeDirty: boolean = false 12 | 13 | /**@internal */ 14 | $dealing: GObject //currently deal with 15 | 16 | private static RELATION_NAMES: string[] = [ 17 | 'left-left', //0 18 | 'left-center', 19 | 'left-right', 20 | 'center-center', 21 | 'right-left', 22 | 'right-center', 23 | 'right-right', 24 | 'top-top', //7 25 | 'top-middle', 26 | 'top-bottom', 27 | 'middle-middle', 28 | 'bottom-top', 29 | 'bottom-middle', 30 | 'bottom-bottom', 31 | 'width-width', //14 32 | 'height-height', //15 33 | 'leftext-left', //16 34 | 'leftext-right', 35 | 'rightext-left', 36 | 'rightext-right', 37 | 'topext-top', //20 38 | 'topext-bottom', 39 | 'bottomext-top', 40 | 'bottomext-bottom' //23 41 | ] 42 | public constructor(owner: GObject) { 43 | this.$owner = owner 44 | this.$items = [] 45 | } 46 | 47 | public add(target: GObject, relationType: number, usePercent: boolean = false): void { 48 | let length: number = this.$items.length 49 | for (let i: number = 0; i < length; i++) { 50 | let item: RelationItem = this.$items[i] 51 | if (item.target == target) { 52 | item.add(relationType, usePercent) 53 | return 54 | } 55 | } 56 | let newItem: RelationItem = new RelationItem(this.$owner) 57 | newItem.target = target 58 | newItem.add(relationType, usePercent) 59 | this.$items.push(newItem) 60 | } 61 | 62 | public addItems(target: GObject, sidePairs: string): void { 63 | let arr: string[] = sidePairs.split(',') 64 | let s: string 65 | let usePercent: boolean 66 | 67 | for (let i = 0; i < 2; i++) { 68 | s = arr[i] 69 | if (!s) continue 70 | 71 | if (s.charAt(s.length - 1) == '%') { 72 | s = s.substr(0, s.length - 1) 73 | usePercent = true 74 | } else usePercent = false 75 | if (s.indexOf('-') == -1) s = `${s}-${s}` 76 | 77 | let t: number = Relations.RELATION_NAMES.indexOf(s) 78 | if (t == -1) throw new Error('Invalid relation type') 79 | 80 | this.add(target, t, usePercent) 81 | } 82 | } 83 | 84 | public remove(target: GObject, relationType: number = 0): void { 85 | let cnt: number = this.$items.length 86 | let i: number = 0 87 | while (i < cnt) { 88 | let item: RelationItem = this.$items[i] 89 | if (item.target == target) { 90 | item.remove(relationType) 91 | if (item.isEmpty) { 92 | item.dispose() 93 | this.$items.splice(i, 1) 94 | cnt-- 95 | } else i++ 96 | } else i++ 97 | } 98 | } 99 | 100 | public contains(target: GObject): boolean { 101 | let length: number = this.$items.length 102 | for (let i: number = 0; i < length; i++) { 103 | if (this.$items[i].target == target) return true 104 | } 105 | return false 106 | } 107 | 108 | public clearFor(target: GObject): void { 109 | let cnt: number = this.$items.length 110 | let i: number = 0 111 | while (i < cnt) { 112 | let item: RelationItem = this.$items[i] 113 | if (item.target == target) { 114 | item.dispose() 115 | this.$items.splice(i, 1) 116 | cnt-- 117 | } else i++ 118 | } 119 | } 120 | 121 | public clearAll(): void { 122 | this.$items.forEach(item => { 123 | item.dispose() 124 | }, this) 125 | this.$items.length = 0 126 | } 127 | 128 | public copyFrom(source: Relations): void { 129 | this.clearAll() 130 | source.$items.forEach(ri => { 131 | let item: RelationItem = new RelationItem(this.$owner) 132 | item.copyFrom(ri) 133 | this.$items.push(item) 134 | }, this) 135 | } 136 | 137 | public dispose(): void { 138 | this.clearAll() 139 | } 140 | 141 | public onOwnerSizeChanged(dWidth: number, dHeight: number): void { 142 | if (this.$items.length <= 0) return 143 | this.$items.forEach(item => { 144 | item.applyOnSelfResized(dWidth, dHeight); 145 | }, this) 146 | } 147 | 148 | public ensureRelationsSizeCorrect(): void { 149 | if (this.$items.length == 0) return 150 | 151 | this.sizeDirty = false 152 | this.$items.forEach(item => { 153 | item.target.ensureSizeCorrect() 154 | }, this) 155 | } 156 | 157 | public get empty(): boolean { 158 | return this.$items.length == 0 159 | } 160 | 161 | public setup(xml: XmlNode): void { 162 | xml.children.forEach(cxml => { 163 | if (cxml.nodeName != 'relation') return 164 | 165 | let targetId: string 166 | let target: GObject 167 | 168 | targetId = cxml.attributes.target 169 | if (this.$owner.parent) { 170 | if (targetId) target = this.$owner.parent.getChildById(targetId) 171 | else target = this.$owner.parent 172 | } else { 173 | //call from the component's constructor 174 | target = (this.$owner as GComponent).getChildById(targetId) 175 | } 176 | if (target) this.addItems(target, cxml.attributes.sidePair) 177 | }, this) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/Window.ts: -------------------------------------------------------------------------------- 1 | 2 | import { InteractiveEvents, RelationType } from './config/Definitions' 3 | import { UIConfig } from './config/UIConfig' 4 | import { GComponent } from './GComponent' 5 | import { GGraph } from './GGraph' 6 | import { GObject, Decls } from './GObject' 7 | import { UIPackage } from './res/UIPackage' 8 | import { DragEvent } from './events/DragEvent' 9 | import { IUISource } from './display/IUISource' 10 | import { IGRoot } from './interface' 11 | 12 | export class Window extends GComponent { 13 | private $contentPane: GComponent 14 | private $modalWaitPane: GObject 15 | private $closeButton: GObject 16 | private $dragArea: GObject 17 | private $contentArea: GObject 18 | private $frame: GComponent 19 | private $modal: boolean 20 | 21 | private $uiSources: IUISource[] 22 | private $inited: boolean 23 | private $loading: boolean 24 | 25 | protected $requestingCmd: number = 0 26 | 27 | public bringToFrontOnClick: boolean 28 | 29 | public constructor() { 30 | super() 31 | this.focusable = true 32 | this.$uiSources = [] 33 | this.bringToFrontOnClick = UIConfig.bringWindowToFrontOnClick 34 | 35 | this.on('added', this.$onShown, this) 36 | this.on('removed', this.$onHidden, this) 37 | this.on(InteractiveEvents.Down, this.$mouseDown, this) 38 | } 39 | 40 | public addUISource(source: IUISource): void { 41 | this.$uiSources.push(source) 42 | } 43 | 44 | public set contentPane(val: GComponent) { 45 | if (this.$contentPane != val) { 46 | if (this.$contentPane != null) this.removeChild(this.$contentPane) 47 | this.$contentPane = val 48 | if (this.$contentPane != null) { 49 | this.addChild(this.$contentPane) 50 | this.setSize(this.$contentPane.width, this.$contentPane.height) 51 | this.$contentPane.addRelation(this, RelationType.Size) 52 | this.$frame = this.$contentPane.getChild('frame') as GComponent 53 | if (this.$frame != null) { 54 | this.closeButton = this.$frame.getChild('closeButton') 55 | this.dragArea = this.$frame.getChild('dragArea') 56 | this.contentArea = this.$frame.getChild('contentArea') 57 | } 58 | } 59 | } 60 | } 61 | 62 | public get contentPane(): GComponent { 63 | return this.$contentPane 64 | } 65 | 66 | public get frame(): GComponent { 67 | return this.$frame 68 | } 69 | 70 | public get closeButton(): GObject { 71 | return this.$closeButton 72 | } 73 | 74 | public set closeButton(value: GObject) { 75 | if (this.$closeButton != null) this.$closeButton.removeClick(this.closeEventHandler) 76 | this.$closeButton = value 77 | if (this.$closeButton != null) this.$closeButton.click(this.closeEventHandler, this) 78 | } 79 | 80 | public get dragArea(): GObject { 81 | return this.$dragArea 82 | } 83 | 84 | public set dragArea(value: GObject) { 85 | if (this.$dragArea != value) { 86 | if (this.$dragArea != null) { 87 | this.$dragArea.draggable = false 88 | this.$dragArea.off(DragEvent.START, this.$dragStart) 89 | } 90 | 91 | this.$dragArea = value 92 | if (this.$dragArea != null) { 93 | if (this.$dragArea instanceof GGraph) this.$dragArea.drawRect(0, '#000', '#000') 94 | this.$dragArea.draggable = true 95 | this.$dragArea.on(DragEvent.START, this.$dragStart, this) 96 | } 97 | } 98 | } 99 | 100 | public get contentArea(): GObject { 101 | return this.$contentArea 102 | } 103 | 104 | public set contentArea(value: GObject) { 105 | this.$contentArea = value 106 | } 107 | 108 | public show(): void { 109 | Decls.GRoot.inst.showWindow(this) 110 | } 111 | 112 | public showOn(root: IGRoot): void { 113 | root.showWindow(this) 114 | } 115 | 116 | public hide(): void { 117 | if (this.isShowing) this.doHideAnimation() 118 | } 119 | 120 | public hideImmediately(): void { 121 | Decls.GRoot.inst.hideWindowImmediately(this) 122 | } 123 | 124 | public centerOn(r: GObject, autoUpdate: boolean = false): void { 125 | this.setXY(Math.round((r.width - this.width) * 0.5), Math.round((r.height - this.height) * 0.5)) 126 | if (autoUpdate) { 127 | this.addRelation(r, RelationType.Center_Center) 128 | this.addRelation(r, RelationType.Middle_Middle) 129 | } 130 | } 131 | 132 | public toggleVisible(): void { 133 | if (this.isTop) this.hide() 134 | else this.show() 135 | } 136 | 137 | public get isShowing(): boolean { 138 | return this.parent != null 139 | } 140 | 141 | public get isTop(): boolean { 142 | return this.parent != null && this.parent.getChildIndex(this) == this.parent.numChildren - 1 143 | } 144 | 145 | public get modal(): boolean { 146 | return this.$modal 147 | } 148 | 149 | public set modal(val: boolean) { 150 | this.$modal = val 151 | } 152 | 153 | public bringToFront(): void { 154 | Decls.GRoot.inst.bringToFront(this) 155 | } 156 | 157 | public showModalWait(msg?: string, cmd: number = 0): void { 158 | if (cmd != 0) this.$requestingCmd = cmd 159 | 160 | if (UIConfig.windowModalWaiting) { 161 | if (!this.$modalWaitPane) 162 | this.$modalWaitPane = UIPackage.createObjectFromURL(UIConfig.windowModalWaiting) 163 | 164 | this.layoutModalWaitPane(msg) 165 | 166 | this.addChild(this.$modalWaitPane) 167 | } 168 | } 169 | 170 | protected layoutModalWaitPane(msg?: string): void { 171 | if (this.$contentArea != null) { 172 | let pt: createjs.Point = this.$frame.localToGlobal() 173 | pt = this.globalToLocal(pt.x, pt.y, pt) 174 | this.$modalWaitPane.setXY(pt.x + this.$contentArea.x, pt.y + this.$contentArea.y) 175 | this.$modalWaitPane.setSize(this.$contentArea.width, this.$contentArea.height) 176 | if (msg && msg.length) this.$modalWaitPane.text = msg 177 | } else this.$modalWaitPane.setSize(this.width, this.height) 178 | } 179 | 180 | public closeModalWait(cmd: number = 0): boolean { 181 | if (cmd != 0) { 182 | if (this.$requestingCmd != cmd) return false 183 | } 184 | this.$requestingCmd = 0 185 | 186 | if (this.$modalWaitPane && this.$modalWaitPane.parent != null) 187 | this.removeChild(this.$modalWaitPane) 188 | 189 | return true 190 | } 191 | 192 | public get modalWaiting(): boolean { 193 | return this.$modalWaitPane && this.$modalWaitPane.parent != null 194 | } 195 | 196 | public init(): void { 197 | if (this.$inited || this.$loading) return 198 | 199 | if (this.$uiSources.length > 0) { 200 | this.$loading = false 201 | this.$uiSources.forEach(o => { 202 | if (!o.loaded) { 203 | o.load(this.$uiLoadComplete, this) 204 | this.$loading = true 205 | } 206 | }, this) 207 | 208 | if (!this.$loading) this.$init() 209 | } else this.$init() 210 | } 211 | 212 | protected onInit(): void {} 213 | 214 | protected onShown(): void {} 215 | 216 | protected onHide(): void {} 217 | 218 | protected doShowAnimation(): void { 219 | this.onShown() 220 | } 221 | 222 | protected doHideAnimation(): void { 223 | this.hideImmediately() 224 | } 225 | 226 | private $uiLoadComplete(): void { 227 | let cnt: number = this.$uiSources.length 228 | for (let i: number = 0; i < cnt; i++) { 229 | if (!this.$uiSources[i].loaded) return 230 | } 231 | 232 | this.$loading = false 233 | this.$init() 234 | } 235 | 236 | private $init(): void { 237 | this.$inited = true 238 | this.onInit() 239 | 240 | if (this.isShowing) this.doShowAnimation() 241 | } 242 | 243 | public dispose(): void { 244 | this.off('added', this.$onShown) 245 | this.off('removed', this.$onHidden) 246 | this.off(InteractiveEvents.Down, this.$mouseDown) 247 | if (this.$dragArea) this.$dragArea.off(DragEvent.START, this.$dragStart) 248 | 249 | if (this.parent != null) this.hideImmediately() 250 | 251 | if (this.$modalWaitPane) this.$modalWaitPane.dispose() 252 | if (this.$contentPane) this.$contentPane.dispose() 253 | 254 | super.dispose() 255 | } 256 | 257 | protected closeEventHandler(evt: createjs.Event): void { 258 | this.hide() 259 | } 260 | 261 | private $onShown(target: createjs.DisplayObject): void { 262 | if (!this.$inited) this.init() 263 | else this.doShowAnimation() 264 | } 265 | 266 | private $onHidden(target: createjs.DisplayObject): void { 267 | this.closeModalWait() 268 | this.onHide() 269 | } 270 | 271 | private $mouseDown(evt: createjs.Event): void { 272 | if (this.isShowing && this.bringToFrontOnClick) this.bringToFront() 273 | } 274 | 275 | private $dragStart(evt: createjs.Event): void { 276 | let currentTarget = evt.data.currentTarget 277 | GObject.castFromNativeObject(currentTarget).stopDrag() 278 | this.startDrag(evt.data.pointerID) 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/config/UIConfig.ts: -------------------------------------------------------------------------------- 1 | import { ScrollBarDisplayType } from "./Definitions"; 2 | 3 | 4 | /**global ui configuration */ 5 | export class UIConfig { 6 | 7 | /**default font name of your project. */ 8 | public static defaultFont: string = "Arial"; 9 | 10 | /** resource used by Window.showModalWait to lock the certain window with modal mode.*/ 11 | public static windowModalWaiting: string; 12 | /** resource used by GRoot.showModalWait to lock the global screen with modal mode. */ 13 | public static globalModalWaiting: string; 14 | 15 | /** modal layer background configuration. */ 16 | public static modalLayerColor: string = "#333333"; 17 | public static modalLayerAlpha: number = 0.2; 18 | 19 | //Default button click sound 20 | public static buttonSound: string; 21 | public static buttonSoundVolumeScale: number = 1; 22 | 23 | /** global scrollbar name */ 24 | public static horizontalScrollBar: string; 25 | public static verticalScrollBar: string; 26 | /** scrolling distance per action in pixel*/ 27 | public static defaultScrollSpeed: number = 25; 28 | /** default scrollbar display mode. It's recommended to set ScrollBarDisplayType.Visible for Desktop environment and ScrollBarDisplayType.Auto for mobile environment.*/ 29 | public static defaultScrollBarDisplay: number = ScrollBarDisplayType.Visible; 30 | /** allow user to drag the content of a container. Set to true for mobile is recommended.*/ 31 | public static defaultScrollTouchEffect: boolean = true; 32 | /** enable bounce effect when the scrolling reaches to the edge of a container. Set to true for mobile is recommended.*/ 33 | public static defaultScrollBounceEffect: boolean = true; 34 | /** Deceleration ratio of scrollpane when its in touch dragging.*/ 35 | public static defaultScrollDecelerationRate: number = .967; 36 | 37 | /** global PopupMenu name.*/ 38 | public static popupMenu: string; 39 | /** seperator resource name to be created to seperate each items on the global PopupMenu.*/ 40 | public static popupMenuSeperator: string; 41 | /** the error symbol for the error status of the GLoader object.*/ 42 | public static loaderErrorSign: string; 43 | /** the widget name to create global popup tip-box to contain some messages.*/ 44 | public static tooltipsWin: string; 45 | 46 | /** maximum count of items to be displayed in the visible viewport of the GCombobox.*/ 47 | public static defaultComboBoxVisibleItemCount: number = 10; 48 | 49 | /** the finger moving threshold in pixel to trigger the scrolling action.*/ 50 | public static touchScrollSensitivity: number = 20; 51 | 52 | /** the finger moving threshold in pixel to trigger the dragging event.*/ 53 | public static touchDragSensitivity: number = 10; 54 | 55 | /** auto bring the window you clicked to the topmost level of the GRoot children list.*/ 56 | public static bringWindowToFrontOnClick: boolean = true; 57 | } -------------------------------------------------------------------------------- /src/controller/Action.ts: -------------------------------------------------------------------------------- 1 | import { XmlNode } from "../utils/XMLParser"; 2 | import { Controller } from "./Controller"; 3 | 4 | export class Action { 5 | public fromPage: string[]; 6 | public toPage: string[]; 7 | public execute(controller: Controller, prevPage: string, curPage: string): void { 8 | if ((!this.fromPage || this.fromPage.length == 0 || this.fromPage.indexOf(prevPage) != -1) 9 | && (!this.toPage || this.toPage.length == 0 || this.toPage.indexOf(curPage) != -1)) 10 | this.enter(controller); 11 | else 12 | this.leave(controller); 13 | } 14 | 15 | protected enter(controller: Controller): void { 16 | } 17 | 18 | protected leave(controller: Controller): void { 19 | } 20 | 21 | public setup(xml: XmlNode): void { 22 | let str: String; 23 | 24 | str = xml.attributes.fromPage; 25 | if (str) 26 | this.fromPage = str.split(","); 27 | 28 | str = xml.attributes.toPage; 29 | if (str) 30 | this.toPage = str.split(","); 31 | } 32 | } -------------------------------------------------------------------------------- /src/controller/ChangePageAction.ts: -------------------------------------------------------------------------------- 1 | import { GComponent } from '../GComponent' 2 | import { XmlNode } from '../utils/XMLParser' 3 | import { Action } from './Action' 4 | import { Controller } from './Controller' 5 | 6 | export class ChangePageAction extends Action { 7 | public objectId: string 8 | public controllerName: string 9 | public targetPage: string 10 | 11 | protected enter(controller: Controller): void { 12 | if (!this.controllerName) return 13 | 14 | let gcom: GComponent 15 | if (this.objectId) gcom = controller.parent.getChildById(this.objectId) as GComponent 16 | else gcom = controller.parent 17 | if (gcom) { 18 | let cc: Controller = gcom.getController(this.controllerName) 19 | if (cc && cc != controller && !cc.$updating) cc.selectedPageId = this.targetPage 20 | } 21 | } 22 | 23 | public setup(xml: XmlNode): void { 24 | super.setup(xml) 25 | 26 | this.objectId = xml.attributes.objectId 27 | this.controllerName = xml.attributes.controller 28 | this.targetPage = xml.attributes.targetPage 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/controller/Controller.ts: -------------------------------------------------------------------------------- 1 | 2 | import { StateChangeEvent } from '../events/StateChangeEvent' 3 | import { GComponent } from '../GComponent' 4 | import { XmlNode } from '../utils/XMLParser' 5 | import { Action } from './Action' 6 | import { ChangePageAction } from './ChangePageAction' 7 | import { PlayTransitionAction } from './PlayTransitionAction' 8 | 9 | export class Controller extends createjs.EventDispatcher { 10 | private $name: string 11 | private $selectedIndex: number = 0 12 | private $previousIndex: number = 0 13 | private $pageIds: string[] 14 | private $pageNames: string[] 15 | private $actions: Action[] 16 | 17 | /**@internal */ 18 | $parent: GComponent 19 | /**@internal */ 20 | $autoRadioGroupDepth: boolean 21 | /**@internal */ 22 | $updating: boolean 23 | 24 | private static $nextPageId: number = 0 25 | 26 | public constructor() { 27 | super() 28 | this.$pageIds = [] 29 | this.$pageNames = [] 30 | this.$selectedIndex = -1 31 | this.$previousIndex = -1 32 | } 33 | 34 | public get name(): string { 35 | return this.$name 36 | } 37 | 38 | public set name(value: string) { 39 | this.$name = value 40 | } 41 | 42 | public get parent(): GComponent { 43 | return this.$parent 44 | } 45 | 46 | public get selectedIndex(): number { 47 | return this.$selectedIndex 48 | } 49 | 50 | public set selectedIndex(value: number) { 51 | if (this.$selectedIndex != value) { 52 | if (value > this.$pageIds.length - 1) throw new Error(`index out of range: ${value}`) 53 | 54 | this.$updating = true 55 | this.$previousIndex = this.$selectedIndex 56 | this.$selectedIndex = value 57 | this.$parent.applyController(this) 58 | let event = new createjs.Event(StateChangeEvent.CHANGED, true, false) 59 | this.dispatchEvent(event, this) 60 | this.$updating = false 61 | } 62 | } 63 | 64 | //same effect as selectedIndex but without event emitted 65 | public setSelectedIndex(value: number = 0): void { 66 | if (this.$selectedIndex != value) { 67 | if (value > this.$pageIds.length - 1) throw new Error(`index out of range: ${value}`) 68 | 69 | this.$updating = true 70 | this.$previousIndex = this.$selectedIndex 71 | this.$selectedIndex = value 72 | this.$parent.applyController(this) 73 | this.$updating = false 74 | } 75 | } 76 | 77 | public get previsousIndex(): number { 78 | return this.$previousIndex 79 | } 80 | 81 | public get selectedPage(): string { 82 | if (this.$selectedIndex == -1) return null 83 | else return this.$pageNames[this.$selectedIndex] 84 | } 85 | 86 | public set selectedPage(val: string) { 87 | this.selectedIndex = Math.max(0, this.$pageNames.indexOf(val)) 88 | } 89 | 90 | public setSelectedPage(value: string): void { 91 | this.setSelectedIndex(Math.max(0, this.$pageNames.indexOf(value))) 92 | } 93 | 94 | public get previousPage(): string { 95 | if (this.$previousIndex == -1) return null 96 | else return this.$pageNames[this.$previousIndex] 97 | } 98 | 99 | public get pageCount(): number { 100 | return this.$pageIds.length 101 | } 102 | 103 | public getPageName(index: number = 0): string { 104 | return this.$pageNames[index] 105 | } 106 | 107 | public addPage(name: string = ''): void { 108 | this.addPageAt(name, this.$pageIds.length) 109 | } 110 | 111 | public addPageAt(name: string, index: number = 0): void { 112 | let nid: string = `${Controller.$nextPageId++}` 113 | if (index == this.$pageIds.length) { 114 | this.$pageIds.push(nid) 115 | this.$pageNames.push(name) 116 | } else { 117 | this.$pageIds.splice(index, 0, nid) 118 | this.$pageNames.splice(index, 0, name) 119 | } 120 | } 121 | 122 | public removePage(name: string): void { 123 | let i: number = this.$pageNames.indexOf(name) 124 | if (i != -1) { 125 | this.$pageIds.splice(i, 1) 126 | this.$pageNames.splice(i, 1) 127 | if (this.$selectedIndex >= this.$pageIds.length) this.selectedIndex = this.$selectedIndex - 1 128 | else this.$parent.applyController(this) 129 | } 130 | } 131 | 132 | public removePageAt(index: number = 0): void { 133 | this.$pageIds.splice(index, 1) 134 | this.$pageNames.splice(index, 1) 135 | if (this.$selectedIndex >= this.$pageIds.length) this.selectedIndex = this.$selectedIndex - 1 136 | else this.$parent.applyController(this) 137 | } 138 | 139 | public clearPages(): void { 140 | this.$pageIds.length = 0 141 | this.$pageNames.length = 0 142 | if (this.$selectedIndex != -1) this.selectedIndex = -1 143 | else this.$parent.applyController(this) 144 | } 145 | 146 | public hasPage(aName: string): boolean { 147 | return this.$pageNames.indexOf(aName) >= 0 148 | } 149 | 150 | public getPageIndexById(aId: string): number { 151 | return this.$pageIds.indexOf(aId) 152 | } 153 | 154 | public getPageIdByName(aName: string): string { 155 | let i: number = this.$pageNames.indexOf(aName) 156 | if (i != -1) return this.$pageIds[i] 157 | else return null 158 | } 159 | 160 | public getPageNameById(aId: string): string { 161 | let i: number = this.$pageIds.indexOf(aId) 162 | if (i != -1) return this.$pageNames[i] 163 | else return null 164 | } 165 | 166 | public getPageId(index: number = 0): string { 167 | return this.$pageIds[index] 168 | } 169 | 170 | public get selectedPageId(): string { 171 | if (this.$selectedIndex == -1) return null 172 | else return this.$pageIds[this.$selectedIndex] 173 | } 174 | 175 | public set selectedPageId(val: string) { 176 | this.selectedIndex = this.$pageIds.indexOf(val) 177 | } 178 | 179 | public set oppositePageId(val: string) { 180 | let i: number = this.$pageIds.indexOf(val) 181 | if (i > 0) this.selectedIndex = 0 182 | else if (this.$pageIds.length > 1) this.selectedIndex = 1 183 | } 184 | 185 | public get previousPageId(): string { 186 | if (this.$previousIndex == -1) return null 187 | else return this.$pageIds[this.$previousIndex] 188 | } 189 | 190 | public executeActions(): void { 191 | if (this.$actions && this.$actions.length > 0) { 192 | this.$actions.forEach(a => { 193 | a.execute(this, this.previousPageId, this.selectedPageId) 194 | }) 195 | } 196 | } 197 | 198 | public createAction(type: string): Action { 199 | switch (type) { 200 | case 'play_transition': 201 | return new PlayTransitionAction() 202 | 203 | case 'change_page': 204 | return new ChangePageAction() 205 | } 206 | return null 207 | } 208 | 209 | public setup(xml: XmlNode): void { 210 | this.$name = xml.attributes.name 211 | this.$autoRadioGroupDepth = xml.attributes.autoRadioGroupDepth == 'true' 212 | 213 | let str: string = xml.attributes.pages 214 | if (str) { 215 | let arr = str.split(',') 216 | let cnt = arr.length 217 | for (let i = 0; i < cnt; i += 2) { 218 | this.$pageIds.push(arr[i]) 219 | this.$pageNames.push(arr[i + 1]) 220 | } 221 | } 222 | 223 | let col: XmlNode[] = xml.children 224 | if (col.length > 0) { 225 | this.$actions = this.$actions || [] 226 | col.forEach(cxml => { 227 | let action: Action = this.createAction(cxml.attributes.type) 228 | action.setup(cxml) 229 | this.$actions.push(action) 230 | }) 231 | } 232 | 233 | str = xml.attributes.transitions 234 | if (str) { 235 | this.$actions = this.$actions || [] 236 | let k: number, e: number 237 | str.split(',').forEach(str => { 238 | if (str && str.length) { 239 | let pt: PlayTransitionAction = new PlayTransitionAction() 240 | k = str.indexOf('=') 241 | pt.transitionName = str.substr(k + 1) 242 | str = str.substring(0, k) 243 | k = str.indexOf('-') 244 | e = parseInt(str.substring(k + 1)) 245 | if (e < this.$pageIds.length) pt.toPage = [this.$pageIds[e]] 246 | str = str.substring(0, k) 247 | if (str != '*') { 248 | e = parseInt(str) 249 | if (e < this.$pageIds.length) pt.fromPage = [this.$pageIds[e]] 250 | } 251 | pt.stopOnExit = true 252 | this.$actions.push(pt) 253 | } 254 | }) 255 | } 256 | 257 | if (this.$parent && this.$pageIds.length > 0) this.$selectedIndex = 0 258 | else this.$selectedIndex = -1 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /src/controller/PageOption.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from "./Controller"; 2 | 3 | export class PageOption { 4 | private $controller: Controller; 5 | private $id: string; 6 | 7 | public set controller(val: Controller) { 8 | this.$controller = val; 9 | } 10 | 11 | public set name(pageName: string) { 12 | this.$id = this.$controller.getPageIdByName(pageName); 13 | } 14 | 15 | public get name(): string { 16 | if (this.$id) 17 | return this.$controller.getPageNameById(this.$id); 18 | else 19 | return null; 20 | } 21 | 22 | public set index(pageIndex: number) { 23 | this.$id = this.$controller.getPageId(pageIndex); 24 | } 25 | 26 | public get index(): number { 27 | if (this.$id) 28 | return this.$controller.getPageIndexById(this.$id); 29 | else 30 | return -1; 31 | } 32 | 33 | public clear(): void { 34 | this.$id = null; 35 | } 36 | 37 | public set id(id: string) { 38 | this.$id = id; 39 | } 40 | 41 | public get id(): string { 42 | return this.$id; 43 | } 44 | } -------------------------------------------------------------------------------- /src/controller/PlayTransitionAction.ts: -------------------------------------------------------------------------------- 1 | // import { Controller } from "../interface/Controller"; 2 | import { Transition } from "../Transition"; 3 | import { XmlNode } from "../utils/XMLParser"; 4 | import { Action } from "./Action"; 5 | import { Controller } from "./Controller"; 6 | 7 | 8 | export class PlayTransitionAction extends Action { 9 | public transitionName: string; 10 | public repeat: number = 1; 11 | public delay: number = 0; 12 | public stopOnExit: boolean = false; 13 | 14 | private $currentTransition: Transition; 15 | 16 | protected enter(controller: Controller): void { 17 | let trans: Transition = controller.parent.getTransition(this.transitionName); 18 | if (trans) { 19 | if (this.$currentTransition && this.$currentTransition.playing) 20 | trans.changeRepeat(this.repeat); 21 | else 22 | trans.play({ 23 | times: this.repeat, 24 | delay: this.delay 25 | }); 26 | this.$currentTransition = trans; 27 | } 28 | } 29 | 30 | protected leave(): void { 31 | if (this.stopOnExit && this.$currentTransition) { 32 | this.$currentTransition.stop(); 33 | this.$currentTransition = null; 34 | } 35 | } 36 | 37 | /**@internal */ 38 | public setup(xml: XmlNode): void { 39 | super.setup(xml); 40 | 41 | this.transitionName = xml.attributes.transition; 42 | 43 | let str: string; 44 | 45 | str = xml.attributes.repeat; 46 | if (str) 47 | this.repeat = parseInt(str); 48 | 49 | str = xml.attributes.delay; 50 | if (str) 51 | this.delay = parseFloat(str); 52 | 53 | this.stopOnExit = xml.attributes.stopOnExit == "true"; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/createjs/extras/Bitmap.ts: -------------------------------------------------------------------------------- 1 | 2 | import { PackageItem } from '../../res/PackageItem' 3 | import { Sprite } from './Sprite' 4 | 5 | export class Bitmap extends Sprite { 6 | public constructor(item?: PackageItem) { 7 | super() 8 | if (item) { 9 | let { width, height, texture, id } = item 10 | this.$frameId = id 11 | if (typeof texture == 'string') { 12 | this.texture = document.createElement('img') 13 | this.texture.src = texture 14 | } else { 15 | this.texture = (texture as createjs.SpriteSheetFrame).image 16 | } 17 | this.sourceRect = new createjs.Rectangle(0, 0, width, height) 18 | this.textureRect = (texture as createjs.SpriteSheetFrame).rect 19 | } 20 | } 21 | 22 | draw(ctx: CanvasRenderingContext2D, ignoreCache: boolean): boolean { 23 | let flag = super.draw(ctx, ignoreCache) 24 | if (flag) { 25 | return flag 26 | } 27 | if (this.sourceRect && this.textureRect) { 28 | let { x, y, width, height } = this.sourceRect 29 | x = this.$isTrim ? x : 0 30 | y = this.$isTrim ? y : 0 31 | ctx.drawImage( 32 | this.texture, 33 | this.textureRect.x, 34 | this.textureRect.y, 35 | this.textureRect.width, 36 | this.textureRect.height, 37 | x, 38 | y, 39 | width, 40 | height 41 | ) //GObject来控制位置坐标 42 | } else { 43 | ctx.drawImage(this.texture, 0, 0) 44 | } 45 | return true 46 | } 47 | 48 | getBounds() { 49 | var rect = super.getBounds() 50 | if (rect) { 51 | return rect 52 | } 53 | var texture = this.texture, 54 | o = this.sourceRect || texture 55 | var hasContent = 56 | texture && (texture['naturalWidth'] || texture['getContext'] || texture['readyState'] >= 2) 57 | return hasContent ? this.sourceRect.setValues(0, 0, o.width, o.height) : null 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/createjs/extras/Resource.ts: -------------------------------------------------------------------------------- 1 | export class Resource { 2 | data: ArrayBuffer; 3 | constructor(data: ArrayBuffer) { 4 | this.data = data; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/createjs/extras/ScaleBitmap.ts: -------------------------------------------------------------------------------- 1 | 2 | import { PackageItem } from '../../res/PackageItem' 3 | import { Sprite } from './Sprite' 4 | 5 | export default class ScaleBitmap extends Sprite { 6 | scale9Grid: createjs.Rectangle 7 | snapToPixel: boolean 8 | 9 | constructor(item: PackageItem, scale9Grid: createjs.Rectangle) { 10 | super() 11 | if (item) { 12 | let { width, height, texture, id } = item 13 | this.$frameId = id 14 | if (typeof texture == 'string') { 15 | this.texture = document.createElement('img') 16 | this.texture.src = texture 17 | } else { 18 | this.texture = (texture as createjs.SpriteSheetFrame).image 19 | } 20 | this.sourceRect = new createjs.Rectangle(0, 0, width, height) 21 | this.textureRect = (texture as createjs.SpriteSheetFrame).rect 22 | } 23 | this.scale9Grid = scale9Grid 24 | this.snapToPixel = true 25 | } 26 | 27 | draw(ctx: CanvasRenderingContext2D, ignoreCache: boolean): boolean { 28 | let flag = super.draw(ctx, ignoreCache) 29 | if (flag) { 30 | return true 31 | } 32 | var centerX = this.scale9Grid.width 33 | var centerY = this.scale9Grid.height 34 | var scaledCenterX: number 35 | var scaledCenterY: number 36 | var imageHeight = this.sourceRect.height 37 | var imageWidth = this.sourceRect.width 38 | if (centerX == 0) { 39 | //vertical 40 | if (centerY == 0) { 41 | throw 'One of scale9Grid width or height must be greater than zero.' 42 | } 43 | var scale3Region2 = this.textureRect.y + this.scale9Grid.y 44 | var scale3Region3 = this.textureRect.y + this.scale9Grid.y + this.scale9Grid.height 45 | var scaledFirstRegion = this.scale9Grid.y 46 | var scaledSecondRegion = this.scale9Grid.height 47 | var scaledThirdRegion = this.textureRect.height - scaledFirstRegion - scaledSecondRegion 48 | scaledCenterY = imageHeight - scaledFirstRegion - scaledThirdRegion 49 | 50 | ctx.drawImage( 51 | this.texture, 52 | this.textureRect.x, 53 | this.textureRect.y, 54 | this.textureRect.width, 55 | scaledFirstRegion, 56 | 0, 57 | 0, 58 | imageWidth, 59 | scaledFirstRegion 60 | ) 61 | ctx.drawImage( 62 | this.texture, 63 | this.textureRect.x, 64 | scale3Region2, 65 | this.textureRect.width, 66 | scaledSecondRegion, 67 | 0, 68 | scaledFirstRegion, 69 | imageWidth, 70 | scaledCenterY 71 | ) 72 | ctx.drawImage( 73 | this.texture, 74 | this.textureRect.x, 75 | scale3Region3, 76 | this.textureRect.width, 77 | scaledThirdRegion, 78 | 0, 79 | scaledCenterY + scaledFirstRegion, 80 | imageWidth, 81 | scaledThirdRegion 82 | ) 83 | } else if (centerY == 0) { 84 | //horizontal 85 | var scale3Region2 = this.textureRect.x + this.scale9Grid.x 86 | var scale3Region3 = this.textureRect.x + this.scale9Grid.x + this.scale9Grid.width 87 | var scaledFirstRegion = this.scale9Grid.x 88 | var scaledSecondRegion = this.scale9Grid.width 89 | var scaledThirdRegion = this.textureRect.width - scaledFirstRegion - scaledSecondRegion 90 | scaledCenterX = imageWidth - scaledFirstRegion - scaledThirdRegion 91 | 92 | ctx.drawImage( 93 | this.texture, 94 | this.textureRect.x, 95 | this.textureRect.y, 96 | scaledFirstRegion, 97 | this.textureRect.height, 98 | 0, 99 | 0, 100 | scaledFirstRegion, 101 | imageHeight 102 | ) 103 | ctx.drawImage( 104 | this.texture, 105 | scale3Region2, 106 | this.textureRect.y, 107 | scaledSecondRegion, 108 | this.textureRect.height, 109 | scaledFirstRegion, 110 | 0, 111 | scaledCenterX, 112 | imageHeight 113 | ) 114 | ctx.drawImage( 115 | this.texture, 116 | scale3Region3, 117 | this.textureRect.y, 118 | scaledThirdRegion, 119 | this.textureRect.height, 120 | scaledFirstRegion + scaledCenterX, 121 | 0, 122 | scaledThirdRegion, 123 | imageHeight 124 | ) 125 | } else { 126 | var left = this.scale9Grid.x 127 | var top = this.scale9Grid.y 128 | var right = this.textureRect.width - centerX - left 129 | var bottom = this.textureRect.height - centerY - top 130 | scaledCenterX = imageWidth - left - right 131 | scaledCenterY = imageHeight - top - bottom 132 | 133 | ctx.drawImage( 134 | this.texture, 135 | this.textureRect.x, 136 | this.textureRect.y, 137 | left, 138 | top, 139 | 0, 140 | 0, 141 | left, 142 | top 143 | ) 144 | ctx.drawImage( 145 | this.texture, 146 | this.textureRect.x + left, 147 | this.textureRect.y, 148 | centerX, 149 | top, 150 | left, 151 | 0, 152 | scaledCenterX, 153 | top 154 | ) 155 | ctx.drawImage( 156 | this.texture, 157 | this.textureRect.x + left + centerX, 158 | this.textureRect.y, 159 | right, 160 | top, 161 | left + scaledCenterX, 162 | 0, 163 | right, 164 | top 165 | ) 166 | 167 | ctx.drawImage( 168 | this.texture, 169 | this.textureRect.x, 170 | this.textureRect.y + top, 171 | left, 172 | centerY, 173 | 0, 174 | top, 175 | left, 176 | scaledCenterY 177 | ) 178 | ctx.drawImage( 179 | this.texture, 180 | this.textureRect.x + left, 181 | this.textureRect.y + top, 182 | centerX, 183 | centerY, 184 | left, 185 | top, 186 | scaledCenterX, 187 | scaledCenterY 188 | ) 189 | ctx.drawImage( 190 | this.texture, 191 | this.textureRect.x + left + centerX, 192 | this.textureRect.y + top, 193 | right, 194 | centerY, 195 | left + scaledCenterX, 196 | top, 197 | right, 198 | scaledCenterY 199 | ) 200 | 201 | ctx.drawImage( 202 | this.texture, 203 | this.textureRect.x, 204 | this.textureRect.y + top + centerY, 205 | left, 206 | bottom, 207 | 0, 208 | top + scaledCenterY, 209 | left, 210 | bottom 211 | ) 212 | ctx.drawImage( 213 | this.texture, 214 | this.textureRect.x + left, 215 | this.textureRect.y + top + centerY, 216 | centerX, 217 | bottom, 218 | left, 219 | top + scaledCenterY, 220 | scaledCenterX, 221 | bottom 222 | ) 223 | ctx.drawImage( 224 | this.texture, 225 | this.textureRect.x + left + centerX, 226 | this.textureRect.y + top + centerY, 227 | right, 228 | bottom, 229 | left + scaledCenterX, 230 | top + scaledCenterY, 231 | right, 232 | bottom 233 | ) 234 | } 235 | return true 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/createjs/extras/Sprite.ts: -------------------------------------------------------------------------------- 1 | 2 | export class Sprite extends createjs.DisplayObject { 3 | protected $tint: string = '#fff' 4 | protected $frameId: string 5 | public texture: HTMLImageElement 6 | protected $sourceRect: createjs.Rectangle // tips:图片元件在组件中的坐标和宽高信息 7 | protected $textureRect: createjs.Rectangle // tips:绘制的图片在纹理集中的坐标和宽高信息 8 | protected $isTrim: boolean = false 9 | 10 | protected static $cachedTexturePool: { 11 | [key: string]: { refCount: number; texture: HTMLImageElement } 12 | } = {} 13 | 14 | public constructor() { 15 | super() 16 | } 17 | 18 | public get tint(): string { 19 | return this.$tint 20 | } 21 | 22 | public set tint(v: string) { 23 | this.$tint = v 24 | let rgb = v.split(',') 25 | this.filters = [ 26 | new createjs.ColorFilter(+rgb[0] / 255, +rgb[1] / 255, +rgb[2] / 255, 1, 0, 0, 0, 1) 27 | ] 28 | } 29 | 30 | public set sourceRect(rect: createjs.Rectangle) { 31 | this.$sourceRect = rect 32 | } 33 | 34 | public get sourceRect(): createjs.Rectangle { 35 | return this.$sourceRect 36 | } 37 | 38 | public set textureRect(rect: createjs.Rectangle) { 39 | this.$textureRect = rect 40 | } 41 | 42 | public get textureRect(): createjs.Rectangle { 43 | return this.$textureRect 44 | } 45 | 46 | isVisible() { 47 | var hasContent = 48 | this.cacheCanvas || 49 | (this.texture && 50 | (this.texture['naturalWidth'] || 51 | this.texture['getContext'] || 52 | this.texture['readyState'] >= 2)) 53 | return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent) 54 | } 55 | 56 | getBounds() { 57 | var rect = super.getBounds() 58 | if (rect) { 59 | return rect 60 | } 61 | var texture = this.texture, 62 | o = this.sourceRect || texture 63 | var hasContent = 64 | texture && (texture['naturalWidth'] || texture['getContext'] || texture['readyState'] >= 2) 65 | return hasContent ? this.sourceRect.setValues(0, 0, o.width, o.height) : null 66 | } 67 | 68 | public destroy(): void { 69 | //todo 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/createjs/extras/TilingBitmap.ts: -------------------------------------------------------------------------------- 1 | 2 | import { PackageItem } from '../../res/PackageItem' 3 | import { Sprite } from './Sprite' 4 | 5 | export class TilingBitmap extends Sprite { 6 | private _pattern: CanvasPattern 7 | public constructor(item?: PackageItem) { 8 | super() 9 | if (item) { 10 | let { width, height, texture, id } = item 11 | this.$frameId = id 12 | if (typeof texture == 'string') { 13 | this.texture = document.createElement('img') 14 | this.texture.src = texture 15 | } else { 16 | this.texture = (texture as createjs.SpriteSheetFrame).image 17 | } 18 | this.sourceRect = new createjs.Rectangle(0, 0, width, height) 19 | this.textureRect = (texture as createjs.SpriteSheetFrame).rect 20 | let offsetCanvas: HTMLCanvasElement = document.createElement('canvas') as HTMLCanvasElement 21 | let offsetCanvasContext: CanvasRenderingContext2D = offsetCanvas.getContext('2d') 22 | offsetCanvas.width = this.textureRect.width 23 | offsetCanvas.height = this.textureRect.height 24 | offsetCanvasContext.drawImage( 25 | this.texture, 26 | this.textureRect.x, 27 | this.textureRect.y, 28 | this.textureRect.width, 29 | this.textureRect.height, 30 | 0, 31 | 0, 32 | this.textureRect.width, 33 | this.textureRect.height 34 | ) 35 | this._pattern = offsetCanvasContext.createPattern(offsetCanvas, 'repeat') 36 | } 37 | } 38 | 39 | draw(ctx: CanvasRenderingContext2D, ignoreCache: boolean): boolean { 40 | let flag = super.draw(ctx, ignoreCache) 41 | if (flag) { 42 | return flag 43 | } 44 | // ctx.save(); 45 | let { width, height } = this.sourceRect 46 | ctx.fillStyle = this._pattern 47 | ctx.fillRect(0, 0, width, height) 48 | // ctx.restore(); 49 | return true 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/display/BitmapFont.ts: -------------------------------------------------------------------------------- 1 | 2 | export class BMGlyph { 3 | public x: number = 0; 4 | public y: number = 0; 5 | public offsetX: number = 0; 6 | public offsetY: number = 0; 7 | public width: number = 0; 8 | public height: number = 0; 9 | public advance: number = 0; 10 | public lineHeight: number = 0; 11 | public channel: number = 0; 12 | public texture: HTMLImageElement | createjs.SpriteSheetFrame; 13 | } 14 | 15 | export type GlyphDictionary = { 16 | [key: string]: BMGlyph 17 | } 18 | 19 | export class BitmapFont { 20 | public id: string; 21 | public size: number = 0; 22 | public ttf: boolean; 23 | public glyphs: GlyphDictionary; 24 | public resizable: boolean; 25 | public colorable: boolean; 26 | 27 | public constructor() { 28 | this.glyphs = {}; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/display/Frame.ts: -------------------------------------------------------------------------------- 1 | import { Texture } from "../config/Definitions"; 2 | 3 | export class Frame { 4 | public addDelay: number = 0; 5 | public texture: Texture; 6 | } 7 | -------------------------------------------------------------------------------- /src/display/HTMLInput.ts: -------------------------------------------------------------------------------- 1 | import { InteractiveEvents } from '../config/Definitions' 2 | import { InputElement } from './InputElement' 3 | import { Decls } from '../GObject' 4 | import { GTimer } from '../GTimer' 5 | export class HTMLInput { 6 | private $input: InputElement 7 | private $singleLine: HTMLInputElement 8 | private $multiLine: HTMLTextAreaElement 9 | private $curEle: HTMLInputElement | HTMLTextAreaElement 10 | 11 | /**@internal */ 12 | $wrapper: HTMLDivElement 13 | private $delegateDiv: HTMLDivElement 14 | private $canvas: HTMLCanvasElement 15 | 16 | /**@internal */ 17 | $requestToShow: boolean = false 18 | /**@internal */ 19 | $scaleX: number = 1 20 | /**@internal */ 21 | $scaleY: number = 1 22 | 23 | public static isTyping: boolean = false 24 | 25 | private constructor() {} 26 | 27 | private static $instance: HTMLInput 28 | 29 | public static get inst(): HTMLInput { 30 | if (!HTMLInput.$instance) HTMLInput.$instance = new HTMLInput() 31 | return HTMLInput.$instance 32 | } 33 | 34 | public initialize(container: HTMLElement, view: HTMLCanvasElement): void { 35 | this.$canvas = view 36 | let div 37 | if (!this.$delegateDiv) { 38 | div = document.createElement('div') 39 | this.$delegateDiv = div 40 | div.id = '__delegateDiv' 41 | container.appendChild(div) 42 | this.initDomPos(div) 43 | this.$wrapper = document.createElement('div') 44 | this.initDomPos(this.$wrapper) 45 | this.$wrapper.style.width = '0px' 46 | this.$wrapper.style.height = '0px' 47 | this.$wrapper.style.left = '0px' 48 | this.$wrapper.style.top = '-100px' 49 | 50 | this.setTransform(this.$wrapper, '0% 0% 0px') 51 | div.appendChild(this.$wrapper) 52 | 53 | Decls.GRoot.inst.on(InteractiveEvents.Click, this.canvasClickHandler, this) 54 | 55 | this.initInputElement(true) //input 56 | this.initInputElement(false) //textarea 57 | } 58 | } 59 | 60 | public isInputOn(): boolean { 61 | return this.$input != null 62 | } 63 | 64 | private canvasClickHandler(e: Event): void { 65 | if (this.$requestToShow) { 66 | this.$requestToShow = false 67 | this.$input.onClickHandler(e) 68 | this.show() 69 | } else { 70 | if (this.$curEle) { 71 | this.clearInputElement() 72 | this.$curEle.blur() 73 | this.$curEle = null 74 | } 75 | } 76 | } 77 | 78 | public isInputShown(): boolean { 79 | return this.$input != null 80 | } 81 | 82 | public isCurrentInput(input: InputElement): boolean { 83 | return this.$input == input 84 | } 85 | 86 | private initDomPos(dom: HTMLElement): void { 87 | dom.style.position = 'absolute' 88 | dom.style.left = '0px' 89 | dom.style.top = '0px' 90 | dom.style.border = 'none' 91 | dom.style.padding = '0' 92 | } 93 | 94 | private setTransform(el: HTMLElement, origin: string, transform?: string): void { 95 | let style: any = el.style 96 | style.transformOrigin = style.webkitTransformOrigin = style.msTransformOrigin = style.mozTransformOrigin = style.oTransformOrigin = origin 97 | if (transform && transform.length > 0) 98 | style.transform = style.webkitTransform = style.msTransform = style.mozTransform = style.oTransform = transform 99 | } 100 | 101 | /**@internal */ 102 | updateSize(sx: number, sy: number): void { 103 | if (!this.$canvas) return 104 | this.$scaleX = sx 105 | this.$scaleY = sy 106 | 107 | this.$delegateDiv.style.left = this.$canvas.style.left 108 | this.$delegateDiv.style.top = this.$canvas.style.top 109 | 110 | let cvsStyle: any = this.$canvas.style 111 | this.setTransform( 112 | this.$delegateDiv, 113 | '0% 0% 0px', 114 | cvsStyle.transform || 115 | cvsStyle.webkitTransform || 116 | cvsStyle.msTransform || 117 | cvsStyle.mozTransform || 118 | cvsStyle.oTransform 119 | ) 120 | } 121 | 122 | private initInputElement(multiline: boolean): void { 123 | let inputElement: HTMLInputElement | HTMLTextAreaElement 124 | if (multiline) { 125 | inputElement = document.createElement('textarea') 126 | inputElement.style.resize = 'none' 127 | this.$multiLine = inputElement 128 | inputElement.id = 'stageTextAreaEle' 129 | } else { 130 | inputElement = document.createElement('input') 131 | this.$singleLine = inputElement 132 | inputElement.type = 'text' 133 | inputElement.id = 'stageInputEle' 134 | } 135 | 136 | this.$wrapper.appendChild(inputElement) 137 | inputElement.setAttribute('tabindex', '-1') 138 | inputElement.style.width = '1px' 139 | inputElement.style.height = '12px' 140 | 141 | this.initDomPos(inputElement) 142 | let style: any = inputElement.style 143 | style.outline = 'thin' 144 | style.background = 'none' 145 | style.overflow = 'hidden' 146 | style.wordBreak = 'break-all' 147 | style.opacity = 0 148 | 149 | inputElement.oninput = e => { 150 | if (this.$input) this.$input.onInputHandler() 151 | } 152 | } 153 | 154 | public show(): void { 155 | GTimer.inst.callLater(() => { 156 | this.$curEle.style.opacity = '1' 157 | }, this) 158 | } 159 | 160 | public disconnect(ele: InputElement): void { 161 | if (this.$input == null || this.$input == ele) { 162 | this.clearInputElement() 163 | 164 | if (this.$curEle) this.$curEle.blur() 165 | } 166 | } 167 | 168 | public clearAttributes(obj: any): void { 169 | if (this.$curEle) { 170 | for (let key in obj) { 171 | this.$curEle.removeAttribute(key) 172 | } 173 | } 174 | } 175 | 176 | public clearInputElement(): void { 177 | if (this.$curEle) { 178 | this.$curEle.value = '' 179 | 180 | this.$curEle.onblur = null 181 | let style = this.$curEle.style 182 | style.width = '1px' 183 | style.height = '12px' 184 | style.left = '0px' 185 | style.top = '0px' 186 | style.opacity = '0' 187 | 188 | let el2 189 | if (this.$singleLine == this.$curEle) el2 = this.$multiLine 190 | else el2 = this.$singleLine 191 | el2.style.display = 'block' 192 | 193 | this.$wrapper.style.left = '0px' 194 | this.$wrapper.style.top = '-100px' 195 | this.$wrapper.style.height = '0px' 196 | this.$wrapper.style.width = '0px' 197 | } 198 | 199 | if (this.$input) { 200 | this.$input.onDisconnect() 201 | this.$input = null 202 | HTMLInput.isTyping = false 203 | } 204 | } 205 | 206 | public requestInput(ele: InputElement): HTMLInputElement | HTMLTextAreaElement { 207 | this.clearInputElement() 208 | this.$input = ele 209 | HTMLInput.isTyping = true 210 | 211 | let el2 212 | if (this.$input.textField.multipleLine) { 213 | this.$curEle = this.$multiLine 214 | el2 = this.$singleLine 215 | } else { 216 | this.$curEle = this.$singleLine 217 | el2 = this.$multiLine 218 | } 219 | el2.style.display = 'none' 220 | 221 | return this.$curEle 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/display/IUISource.ts: -------------------------------------------------------------------------------- 1 | export interface IUISource { 2 | fileName: string; 3 | loaded: boolean; 4 | 5 | load(callback: () => void, thisObj: any): void; 6 | } 7 | -------------------------------------------------------------------------------- /src/display/MovieClip.ts: -------------------------------------------------------------------------------- 1 | 2 | import { MovieClipStatus } from '../config/Definitions' 3 | import { Bitmap } from '../createjs/extras/Bitmap' 4 | import { GObject } from '../GObject' 5 | import { GTimer } from '../GTimer' 6 | import { IMovieClip } from '../interface/IMovieClip' 7 | import { IUIObject } from '../interface/IUIObject' 8 | import { MathUtil } from '../utils/MathUtil' 9 | import { Frame } from './Frame' 10 | import { MovieClipData } from './MovieClipData' 11 | import { DefaultMovieClipSettings, MovieClipSettings } from './MovieClipSettings' 12 | 13 | export class MovieClip extends Bitmap implements IUIObject, IMovieClip { 14 | public interval: number = 0 15 | public swing: boolean 16 | public repeatDelay: number = 0 17 | 18 | private $playing: boolean 19 | private $frameCount: number = 0 20 | private $frames: Frame[] 21 | private $currentFrame: number = 0 22 | private $status: number = MovieClipStatus.NORMAL 23 | private $settings: DefaultMovieClipSettings 24 | private data: MovieClipData 25 | 26 | public UIOwner: GObject 27 | 28 | public constructor(owner: GObject) { 29 | super() 30 | this.UIOwner = owner 31 | this.data = new MovieClipData() 32 | this.$playing = true 33 | this.mouseEnabled = false; 34 | this.$settings = new DefaultMovieClipSettings() 35 | this.$isTrim = true 36 | 37 | this.on('added', this.added, this) 38 | this.on('removed', this.removed, this) 39 | } 40 | 41 | // draw(ctx: CanvasRenderingContext2D, ignoreCache: boolean): boolean { 42 | // // let flag = super.draw(ctx, ignoreCache); 43 | // // if (flag) { 44 | // // return flag; 45 | // // } 46 | // if (this.sourceRect && this.textureRect) { 47 | // let { x, y, width, height } = this.textureRect; 48 | // ctx.drawImage(this.texture, x, y, width, height, this.sourceRect.x, this.sourceRect.y, this.sourceRect.width, this.sourceRect.height);//GObject来控制位置坐标 49 | // } else { 50 | // ctx.drawImage(this.texture, 0, 0); 51 | // } 52 | // return true; 53 | // } 54 | 55 | public get frames(): Frame[] { 56 | return this.$frames 57 | } 58 | 59 | public set frames(value: Frame[]) { 60 | this.$frames = value 61 | if (this.$frames != null) this.$frameCount = this.$frames.length 62 | else this.$frameCount = 0 63 | 64 | if (this.$settings.endFrame == -1 || this.$settings.endFrame > this.$frameCount - 1) 65 | this.$settings.endFrame = this.$frameCount - 1 66 | if (this.$settings.loopEndAt == -1 || this.$settings.loopEndAt > this.$frameCount - 1) 67 | this.$settings.loopEndAt = this.$frameCount - 1 68 | 69 | if (this.$currentFrame < 0 || this.$currentFrame > this.$frameCount - 1) 70 | this.$currentFrame = this.$frameCount - 1 71 | 72 | if (this.$frameCount > 0) { 73 | this.setFrame(this.$frames[this.$currentFrame]) 74 | } else { 75 | this.setFrame(null) 76 | } 77 | this.data.rewind() 78 | } 79 | 80 | public get frameCount(): number { 81 | return this.$frameCount 82 | } 83 | 84 | // public get boundsRect(): createjs.Rectangle { 85 | // return this.getBounds(); 86 | // // return this._boundsRect; 87 | // } 88 | 89 | public set boundsRect(value: createjs.Rectangle) { 90 | // this._boundsRect = value; 91 | // this.sourceRect = value; 92 | } 93 | 94 | public get currentFrame(): number { 95 | return this.$currentFrame 96 | } 97 | 98 | public set currentFrame(value: number) { 99 | if (this.$currentFrame != value) { 100 | this.$currentFrame = value 101 | this.data.currentFrame = value 102 | this.setFrame(this.$currentFrame < this.$frameCount ? this.$frames[this.$currentFrame] : null) 103 | } 104 | } 105 | 106 | public get playing(): boolean { 107 | return this.$playing 108 | } 109 | 110 | public set playing(value: boolean) { 111 | this.$playing = value 112 | 113 | if (value && GObject.isDisplayObjectOnStage(this)) GTimer.inst.add(0, 0, this.update, this) 114 | else GTimer.inst.remove(this.update, this) 115 | } 116 | 117 | /** 118 | * Modify the playing settings for the current MovieClip object, there are two ways to call this method: 119 | * 1) pass whole parameters: 120 | startFrame: number; 121 | endFrame: number; 122 | repeatCount: number; 123 | loopEndAt: number; 124 | endCallback: (target?: MovieClip) => void; 125 | endCallbackContext: any; 126 | * 2) just pass 1 object which implements MovieClipSettings (recommended) 127 | */ 128 | public setPlaySettings(...args: any[]): void { 129 | if (args.length == 1 && typeof args[0] == 'object') this.$settings.mix(args[0]) 130 | else { 131 | let s: any = args[0], 132 | e: any = args[1], 133 | r: any = args[2], 134 | l: any = args[3], 135 | ec: () => void = args[4], 136 | ecc: any = args[5] 137 | 138 | let o: MovieClipSettings = {} 139 | if (MathUtil.isNumber(s)) o.startFrame = s 140 | if (MathUtil.isNumber(e)) o.endFrame = e 141 | if (MathUtil.isNumber(r)) o.repeatCount = r 142 | if (MathUtil.isNumber(l)) o.loopEndAt = l 143 | if (ec && typeof ec == 'function') o.endCallback = ec 144 | if (ecc) o.endCallbackContext = ecc 145 | 146 | this.$settings.mix(o) 147 | } 148 | 149 | if (this.$settings.endFrame == -1 || this.$settings.endFrame > this.$frameCount - 1) 150 | this.$settings.endFrame = this.$frameCount - 1 151 | if (this.$settings.loopEndAt == -1) this.$settings.loopEndAt = this.$settings.endFrame 152 | 153 | this.$status = MovieClipStatus.NORMAL 154 | 155 | this.currentFrame = this.$settings.startFrame 156 | } 157 | 158 | private update(): void { 159 | if (this.UIOwner.$inProgressBuilding) return 160 | if (this.$playing && this.$frameCount != 0 && this.$status != MovieClipStatus.ENDED) { 161 | this.data.update(this) 162 | if (this.$currentFrame != this.data.currentFrame) { 163 | if (this.$status == MovieClipStatus.LOOPING) { 164 | this.$currentFrame = this.$settings.startFrame 165 | this.data.currentFrame = this.$currentFrame 166 | this.$status = MovieClipStatus.NORMAL 167 | } else if (this.$status == MovieClipStatus.STOPPING) { 168 | this.$currentFrame = this.$settings.loopEndAt 169 | this.data.currentFrame = this.$currentFrame 170 | this.$status = MovieClipStatus.ENDED 171 | 172 | //play end 173 | if (this.$settings.endCallback != null) GTimer.inst.callLater(this.$playEnd, this) 174 | } else { 175 | this.$currentFrame = this.data.currentFrame 176 | if (this.$currentFrame == this.$settings.endFrame) { 177 | if (this.$settings.repeatCount > 0) { 178 | this.$settings.repeatCount-- 179 | if (this.$settings.repeatCount == 0) this.$status = MovieClipStatus.STOPPING 180 | else this.$status = MovieClipStatus.LOOPING 181 | } 182 | } 183 | } 184 | 185 | this.setFrame(this.$frames[this.$currentFrame]) 186 | } 187 | } 188 | } 189 | 190 | private $playEnd(): void { 191 | if (this.$settings.endCallback != null) { 192 | let f: (mc: MovieClip) => void = this.$settings.endCallback 193 | let fObj: any = this.$settings.endCallbackContext 194 | 195 | this.$settings.endCallback = this.$settings.endCallbackContext = null 196 | this.$settings.endCallbackContext = null 197 | 198 | if (f) f.call(fObj, this) 199 | } 200 | } 201 | 202 | private setFrame(frame: Frame): void { 203 | this.texture = frame == null ? null : frame.texture.image 204 | this.textureRect = frame.texture.rect 205 | this.sourceRect = frame.texture.trim 206 | let { x, y, width, height } = this.sourceRect 207 | this.cache(x, y, width, height) 208 | // this._textureID = -1; 209 | } 210 | 211 | private added(disp: createjs.DisplayObject): void { 212 | if (this.$playing) GTimer.inst.add(0, 0, this.update, this) 213 | } 214 | 215 | private removed(disp: createjs.DisplayObject): void { 216 | if (this.$playing) GTimer.inst.remove(this.update, this) 217 | } 218 | 219 | public destroy(): void { 220 | GTimer.inst.remove(this.update, this) 221 | this.off('added', this.added) 222 | this.off('removed', this.removed) 223 | super.destroy() 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/display/MovieClipData.ts: -------------------------------------------------------------------------------- 1 | import { IMovieClip } from "../interface/IMovieClip"; 2 | 3 | export class MovieClipData { 4 | public reachesEnd: boolean; 5 | public reversed: boolean; 6 | public repeatedCount: number = 0; 7 | 8 | private $curFrame: number = 0; 9 | private $lastTime: number = 0; 10 | private $curFrameDelay: number = 0; 11 | 12 | public constructor() { 13 | this.$lastTime = Date.now(); 14 | } 15 | 16 | public update(mc: IMovieClip): void { 17 | let t: number = Date.now(); 18 | let elapsed: number = t - this.$lastTime; 19 | this.$lastTime = t; 20 | 21 | let cur: number = this.$curFrame; 22 | if (cur >= mc.frameCount) 23 | cur = mc.frameCount - 1; 24 | 25 | this.reachesEnd = false; 26 | this.$curFrameDelay += elapsed; 27 | let interval: number = mc.interval + mc.frames[cur].addDelay 28 | + ((cur == 0 && this.repeatedCount > 0) ? mc.repeatDelay : 0); 29 | if (this.$curFrameDelay < interval) 30 | return; 31 | 32 | this.$curFrameDelay -= interval; 33 | if (this.$curFrameDelay > mc.interval) 34 | this.$curFrameDelay = mc.interval; 35 | 36 | if (mc.swing) { 37 | if (this.reversed) { 38 | this.$curFrame--; 39 | if (this.$curFrame < 0) { 40 | this.$curFrame = Math.min(1, mc.frameCount - 1); 41 | this.repeatedCount++; 42 | this.reversed = !this.reversed; 43 | } 44 | } 45 | else { 46 | this.$curFrame++; 47 | if (this.$curFrame > mc.frameCount - 1) { 48 | this.$curFrame = Math.max(0, mc.frameCount - 2); 49 | this.repeatedCount++; 50 | this.reachesEnd = true; 51 | this.reversed = !this.reversed; 52 | } 53 | } 54 | } 55 | else { 56 | this.$curFrame++; 57 | if (this.$curFrame > mc.frameCount - 1) { 58 | this.$curFrame = 0; 59 | this.repeatedCount++; 60 | this.reachesEnd = true; 61 | } 62 | } 63 | } 64 | 65 | public get currentFrame(): number { 66 | return this.$curFrame; 67 | } 68 | 69 | public set currentFrame(value: number) { 70 | this.$curFrame = value; 71 | this.$curFrameDelay = 0; 72 | } 73 | 74 | public rewind(): void { 75 | this.$curFrame = 0; 76 | this.$curFrameDelay = 0; 77 | this.reversed = false; 78 | this.reachesEnd = false; 79 | } 80 | 81 | public reset(): void { 82 | this.$curFrame = 0; 83 | this.$curFrameDelay = 0; 84 | this.repeatedCount = 0; 85 | this.reachesEnd = false; 86 | this.reversed = false; 87 | } 88 | 89 | public copy(src: MovieClipData): void { 90 | this.$curFrame = src.$curFrame; 91 | this.$curFrameDelay = src.$curFrameDelay; 92 | this.repeatedCount = src.repeatedCount; 93 | this.reachesEnd = src.reachesEnd; 94 | this.reversed = src.reversed; 95 | } 96 | } -------------------------------------------------------------------------------- /src/display/MovieClipSettings.ts: -------------------------------------------------------------------------------- 1 | import { IMovieClip } from "../interface/IMovieClip"; 2 | 3 | export interface MovieClipSettings { 4 | startFrame?: number; 5 | endFrame?: number; 6 | repeatCount?: number; 7 | loopEndAt?: number; 8 | endCallback?: (target?: IMovieClip) => void; 9 | endCallbackContext?: any; 10 | [key: string]: any; 11 | } 12 | 13 | export class DefaultMovieClipSettings implements MovieClipSettings { 14 | /**the first frame number to start to play */ 15 | public startFrame: number = 0; 16 | /**the end frame the playing will end at, -1 means to the tail */ 17 | public endFrame: number = -1; 18 | /**play count, 0 means endeless */ 19 | public repeatCount: number = 0; 20 | /**once the repeated playing completes, the playing will end at, -1 means to the tail */ 21 | public loopEndAt: number = -1; 22 | /**complete callback handler */ 23 | public endCallback: (target?: IMovieClip) => void = null; 24 | /**context object for the callback function */ 25 | public endCallbackContext: any = null; 26 | 27 | /**modify the current settings without whole parameters provided */ 28 | public mix(other: MovieClipSettings): MovieClipSettings { 29 | let ret: MovieClipSettings = this; 30 | for (let key in other) { 31 | if (key == "mix") continue; 32 | ret[key] = other[key]; 33 | } 34 | return this; 35 | } 36 | } -------------------------------------------------------------------------------- /src/display/UIContainer.ts: -------------------------------------------------------------------------------- 1 | 2 | import { GObject } from '../GObject' 3 | import { IUIObject } from '../interface/IUIObject' 4 | 5 | export class UIContainer extends createjs.Container implements IUIObject { 6 | protected $scrollRect: createjs.Rectangle 7 | protected $rectMask: createjs.Graphics 8 | 9 | public UIOwner: GObject 10 | 11 | public constructor(owner?: GObject) { 12 | super() 13 | this.UIOwner = owner 14 | this.mouseEnabled = true; 15 | this.mouseChildren = true; 16 | } 17 | 18 | public get scrollRect(): createjs.Rectangle { 19 | return this.$scrollRect 20 | } 21 | 22 | public set scrollRect(rect: createjs.Rectangle) { 23 | this.$scrollRect = rect 24 | if (rect != null) { 25 | if (!this.$rectMask) { 26 | this.$rectMask = new createjs.Graphics() 27 | var shape = new createjs.Shape(this.$rectMask) 28 | this.mask = shape; 29 | } 30 | this.$rectMask.clear() 31 | if (rect.width > 0 && rect.height > 0) { 32 | this.$rectMask.beginFill('#000') 33 | this.$rectMask.drawRect( 34 | this.$scrollRect.x, 35 | this.$scrollRect.y, 36 | this.$scrollRect.width, 37 | this.$scrollRect.height 38 | ) 39 | this.$rectMask.endFill(); 40 | } 41 | } else this.mask = null 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/display/UIImage.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Bitmap } from '../createjs/extras/Bitmap' 3 | import ScaleBitmap from '../createjs/extras/ScaleBitmap' 4 | import { TilingBitmap } from '../createjs/extras/TilingBitmap' 5 | import { GObject } from '../GObject' 6 | import { IUIObject } from '../interface/IUIObject' 7 | import { PackageItem } from '../res/PackageItem' 8 | 9 | export class UIImage extends createjs.Container implements IUIObject { 10 | public UIOwner: GObject 11 | protected $disp: Bitmap | ScaleBitmap | TilingBitmap 12 | 13 | public constructor(owner?: GObject) { 14 | super() 15 | this.UIOwner = owner; 16 | // this.mouseEnabled = this.mouseChildren = false; 17 | } 18 | 19 | public set sourceRect(rect: createjs.Rectangle) { 20 | this.$disp.sourceRect = rect 21 | } 22 | 23 | public setCache(x: number, y: number, width: number, height: number) { 24 | this.$disp.cache(x, y, width, height) 25 | } 26 | 27 | /**@internal */ 28 | $initDisp(item?: PackageItem): void { 29 | if (this.$disp) return 30 | 31 | if (item) { 32 | item.load() 33 | if (item.scaleByTile) { 34 | this.$disp = new TilingBitmap(item) 35 | } else if (item.scale9Grid) { 36 | let rect = new createjs.Rectangle( 37 | item.scale9Grid.x, 38 | item.scale9Grid.y, 39 | Math.max(0, item.scale9Grid.width), 40 | Math.max(0, item.scale9Grid.height) 41 | ) 42 | this.$disp = new ScaleBitmap(item, rect) 43 | } else { 44 | this.$disp = new Bitmap(item) 45 | } 46 | } else { 47 | this.$disp = new Bitmap() 48 | } 49 | 50 | this.addChild(this.$disp) 51 | } 52 | 53 | public get tint(): string { 54 | return this.$disp.tint 55 | } 56 | 57 | public set tint(value: string) { 58 | this.$disp.tint = value 59 | } 60 | 61 | public get height(): number { 62 | return this.$disp.sourceRect.height 63 | } 64 | 65 | public set height(v: number) { 66 | this.$disp.sourceRect.height = v 67 | } 68 | 69 | public get width(): number { 70 | return this.$disp.sourceRect.width 71 | } 72 | 73 | public set width(v: number) { 74 | this.$disp.sourceRect.width = v 75 | } 76 | 77 | public get texture(): HTMLImageElement { 78 | return this.$disp.texture 79 | } 80 | 81 | public set texture(v: HTMLImageElement) { 82 | this.$disp.texture = v 83 | } 84 | 85 | /** 86 | * rect = x,y,w,h = l,t,r,b 87 | */ 88 | public get scale9Grid(): createjs.Rectangle { 89 | if (this.$disp instanceof ScaleBitmap) { 90 | return this.$disp.scale9Grid 91 | } 92 | return null 93 | } 94 | 95 | /** 96 | * rect = x,y,w,h = l,t,r,b 97 | */ 98 | public set scale9Grid(rect: createjs.Rectangle) { 99 | if (this.$disp instanceof ScaleBitmap) { 100 | this.$disp.scale9Grid = rect 101 | } 102 | } 103 | 104 | /** 105 | * todo 106 | */ 107 | public destroy(): void { 108 | // if(this.$disp) { 109 | // this.$disp.destroy(options); 110 | // this.$disp = null; 111 | // } 112 | // super.destroy(options); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/display/UISprite.ts: -------------------------------------------------------------------------------- 1 | 2 | import { GObject } from '../GObject' 3 | import { IUIObject } from '../interface/IUIObject' 4 | 5 | export class UISprite extends createjs.Shape implements IUIObject { 6 | public UIOwner: GObject 7 | 8 | public constructor(owner?: GObject) { 9 | super() 10 | this.UIOwner = owner 11 | this.mouseEnabled = false; 12 | // this.interactive = false; 13 | // this.interactiveChildren = false; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/display/UITextField.ts: -------------------------------------------------------------------------------- 1 | 2 | import { TextStyle } from '../createjs/extras/TextStyle' 3 | import { GObject, Decls } from '../GObject' 4 | import { IUIObject } from '../interface/IUIObject' 5 | 6 | export class UITextField extends createjs.Text implements IUIObject { 7 | public style: TextStyle 8 | public UIOwner: GObject 9 | public multiple: createjs.Text 10 | protected $minHeight: number 11 | protected $minHeightID: number = -1 12 | 13 | public constructor(owner?: GObject) { 14 | super() 15 | this.UIOwner = owner 16 | this.style = new TextStyle({}) 17 | // this.mouseEnabled = false; 18 | // this.interactive = this.interactiveChildren = false; 19 | // this._texture.noFrame = false; 20 | // this._width = this._texture.frame.width; 21 | // this._height = this._texture.frame.height; 22 | // this.$minHeight = -1; 23 | // this._texture.on("update", this.updateFrame, this); 24 | } 25 | 26 | public get minHeight(): number { 27 | return this.$minHeight 28 | } 29 | 30 | public applyStyle() { 31 | let canvas = Decls.GRoot.inst.applicationContext.canvas 32 | this.font = this.style.toFontString() 33 | this.color = this.style.fill as string 34 | let letterSpacing = this.style.letterSpacing 35 | if (letterSpacing) { 36 | canvas.style.letterSpacing = `${letterSpacing}px` 37 | } else { 38 | canvas.style.letterSpacing = `0px` 39 | } 40 | let leading = this.style.leading 41 | if (leading) { 42 | this.lineHeight = this.getMeasuredLineHeight() + leading 43 | } else { 44 | this.lineHeight = this.getMeasuredLineHeight() 45 | } 46 | let strokeThickness = this.style.strokeThickness 47 | if (strokeThickness) { 48 | if (!this.multiple) { 49 | this.multiple = new createjs.Text(this.text, this.font, this.color); 50 | (this.UIOwner.parent.displayObject as createjs.Container).addChild(this.multiple); 51 | } 52 | 53 | this.multiple.outline = strokeThickness 54 | if (this.style.stroke != this.color) { 55 | this.multiple.color = this.style.stroke 56 | } 57 | this.multiple.lineHeight = this.lineHeight 58 | this.multiple.lineWidth = this.lineWidth 59 | } 60 | 61 | let shadow = new createjs.Shadow( 62 | this.style.dropShadowColor, 63 | this.style.dropShadowOffsetX, 64 | this.style.dropShadowOffsetY, 65 | this.style.dropShadowBlur 66 | ) 67 | this.shadow = shadow 68 | let { x, y, width, height } = this 69 | this.setBounds(x, y, width, height) 70 | } 71 | 72 | /**@internal */ 73 | $updateMinHeight(): void { 74 | this.$minHeight = this.getMeasuredLineHeight() 75 | } 76 | 77 | // protected updateFrame(): void { 78 | // GTimer.inst.callLater(this.internalUpdateFrame, this); 79 | // } 80 | 81 | // private internalUpdateFrame(): void { 82 | // if(this._texture) { 83 | // let frm = this._texture.frame; 84 | // this._height = Math.max(this._height, this.$minHeight); 85 | // let w = frm.x + this._width, h = frm.y + this._height; 86 | // if(w > this._texture.baseTexture.width) 87 | // w = this._texture.baseTexture.width - frm.x; 88 | // if(h > this._texture.baseTexture.height) 89 | // h = this._texture.baseTexture.height - frm.y; 90 | 91 | // frm.width = w / this.resolution; 92 | // frm.height = h / this.resolution; 93 | 94 | // this._texture.trim.width = frm.width; 95 | // this._texture.trim.height = frm.height; 96 | 97 | // let padding = this._style.trim ? 0 : this._style.padding; 98 | // this._texture.trim.x = -padding; 99 | // this._texture.trim.y = -padding; 100 | 101 | // this._texture.frame = frm; 102 | // } 103 | // } 104 | 105 | //cancel scaling update 106 | // protected _onTextureUpdate(): void { 107 | // this._textureID = -1; 108 | // this._textureTrimmedID = -1; 109 | // } 110 | 111 | public get width(): number { 112 | return this.getMetrics()['width'] 113 | } 114 | 115 | public set width(v: number) { 116 | this.lineWidth = v 117 | if (this.multiple) { 118 | this.multiple.lineWidth = v 119 | } 120 | } 121 | 122 | public get height(): number { 123 | return this.lineHeight 124 | } 125 | 126 | public set height(v: number) { 127 | this.lineHeight = v; 128 | if (this.multiple) { 129 | this.multiple.lineHeight = v; 130 | } 131 | } 132 | 133 | public get textHeight(): number { 134 | return this.getMeasuredHeight() 135 | } 136 | 137 | public set textHeight(v: number) {} 138 | 139 | public get textWidth(): number { 140 | return this.getMeasuredWidth() 141 | } 142 | 143 | public set content(v: string) { 144 | this.text = v 145 | if (this.multiple) { 146 | this.multiple.text = v 147 | } 148 | } 149 | 150 | public get content(): string { 151 | return this.text 152 | } 153 | 154 | public updateMultiplePosition(x: number, y: number) { 155 | if (this.multiple) { 156 | this.multiple.x = x 157 | this.multiple.y = y 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/events/DisplayObjectEvent.ts: -------------------------------------------------------------------------------- 1 | 2 | export const enum DisplayObjectEvent { 3 | XY_CHANGED = "__xyChanged", 4 | SIZE_CHANGED = "__sizeChanged", 5 | VISIBLE_CHANGED = "__visibleChanged", 6 | SIZE_DELAY_CHANGE = "__sizeDelayChange", 7 | MOUSE_WHEEL = "__mouseWheel" 8 | } 9 | -------------------------------------------------------------------------------- /src/events/DragEvent.ts: -------------------------------------------------------------------------------- 1 | 2 | export const enum DragEvent { 3 | START = "__dragStart", 4 | END = "__dragEnd", 5 | MOVING = "__dragMoving", 6 | DROP = "__dragDrop" 7 | } 8 | -------------------------------------------------------------------------------- /src/events/FocusEvent.ts: -------------------------------------------------------------------------------- 1 | export const enum FocusEvent { 2 | CHANGED = "__focusChanged" 3 | } 4 | -------------------------------------------------------------------------------- /src/events/GearEvent.ts: -------------------------------------------------------------------------------- 1 | export const enum GearEvent { 2 | GEAR_STOP = "__gearStop" 3 | } 4 | -------------------------------------------------------------------------------- /src/events/ListEvent.ts: -------------------------------------------------------------------------------- 1 | export const enum ListEvent { 2 | ItemClick = '__itemClick' 3 | } 4 | -------------------------------------------------------------------------------- /src/events/ScrollEvent.ts: -------------------------------------------------------------------------------- 1 | export const enum ScrollEvent { 2 | SCROLL = '__scroll', 3 | SCROLL_END = '__scrollEnd', 4 | PULL_DOWN_RELEASE = '__pullDownRelease', 5 | PULL_UP_RELEASE = '__pullUpRelease' 6 | } 7 | -------------------------------------------------------------------------------- /src/events/StateChangeEvent.ts: -------------------------------------------------------------------------------- 1 | 2 | export const enum StateChangeEvent { 3 | CHANGED = "__stateChanged" 4 | } 5 | 6 | // export class StateChangeEvent extends createjs.Event{ 7 | // constructor(type: string, bubbles: boolean, cancelable: boolean){ 8 | // super(type,bubbles,cancelable); 9 | 10 | // } 11 | // } 12 | -------------------------------------------------------------------------------- /src/events/TextEvent.ts: -------------------------------------------------------------------------------- 1 | export const enum TextEvent { 2 | LinkClick = "__linkClick", 3 | Change = "__textChange", 4 | FocusIn = "__textFocusIn", 5 | FocusOut = "__textFocusOut" 6 | } 7 | 8 | -------------------------------------------------------------------------------- /src/fairygui-createjs.ts: -------------------------------------------------------------------------------- 1 | export { GGroup } from './GGroup' 2 | export { GObject } from './GObject' 3 | export { GGraph } from './GGraph' 4 | export { GImage } from './GImage' 5 | export { GMovieClip } from './GMovieClip' 6 | export { GRoot } from './GRoot' 7 | export { GTextField } from './GTextField' 8 | export { GRichTextField } from './GRichTextField' 9 | export { GTextInput } from './GTextInput' 10 | export { GLoader } from './GLoader' 11 | export { GComponent } from './GComponent' 12 | export { GLabel } from './GLabel' 13 | export { GButton } from './GButton' 14 | export { GComboBox } from './GComboBox' 15 | export { GSlider } from './GSlider' 16 | export { GProgressBar } from './GProgressBar' 17 | export { GScrollBar } from './GScrollBar' 18 | export { GList } from './GList' 19 | export { Window } from './Window' 20 | export { Controller } from './controller/Controller' 21 | export { Transition } from './Transition' 22 | export { ScrollPane } from './ScrollPane' 23 | export { PopupMenu } from './PopupMenu' 24 | export { UIPackage } from './res/UIPackage' 25 | export { 26 | UIStage, 27 | StageScaleMode, 28 | StageOrientation, 29 | StageAlign, 30 | UIStageOptions, 31 | DefaultUIStageOptions 32 | } from './display/UIStage' 33 | export { PackageItem } from './res/PackageItem' 34 | export { UIObjectFactory } from './res/UIObjectFactory' 35 | export { UIConfig } from './config/UIConfig' 36 | 37 | export { AssetLoader } from './utils/AssetLoader' 38 | 39 | export { StringUtil } from './utils/StringUtil' 40 | export { Binder } from './utils/Binder' 41 | 42 | export { StateChangeEvent } from './events/StateChangeEvent' 43 | export { DragEvent } from './events/DragEvent' 44 | export { DragIndicator } from './utils/DragIndicator' 45 | export { GTimer } from './GTimer' 46 | export * from './config/Definitions' 47 | -------------------------------------------------------------------------------- /src/gear/GearAnimation.ts: -------------------------------------------------------------------------------- 1 | import { GObject } from '../GObject' 2 | import { IAnimationGear } from '../interface/IAnimationGear' 3 | import GearBase from './GearBase' 4 | 5 | export class GearAnimation extends GearBase { 6 | private $storage: { [key: string]: GearAnimationValue } 7 | private $default: GearAnimationValue 8 | 9 | public constructor(owner: GObject & IAnimationGear) { 10 | super(owner) 11 | } 12 | 13 | protected init(): void { 14 | this.$default = new GearAnimationValue(this.$owner['playing'], this.$owner['frame']) 15 | this.$storage = {} 16 | } 17 | 18 | protected addStatus(pageId: string, value: string): void { 19 | if (value == '-') return 20 | 21 | let gv: GearAnimationValue 22 | if (pageId == null) gv = this.$default 23 | else { 24 | gv = new GearAnimationValue() 25 | this.$storage[pageId] = gv 26 | } 27 | let arr: string[] = value.split(',') 28 | gv.frame = parseInt(arr[0]) 29 | gv.playing = arr[1] == 'p' 30 | } 31 | 32 | public apply(): void { 33 | this.$owner.$gearLocked = true 34 | 35 | let gv: GearAnimationValue = this.$storage[this.$controller.selectedPageId] 36 | if (!gv) gv = this.$default 37 | 38 | this.$owner['frame'] = gv.frame 39 | this.$owner['playing'] = gv.playing 40 | 41 | this.$owner.$gearLocked = false 42 | } 43 | 44 | public updateState(): void { 45 | if (this.$controller == null || this.$owner.$gearLocked || this.$owner.$inProgressBuilding) 46 | return 47 | 48 | let gv: GearAnimationValue = this.$storage[this.$controller.selectedPageId] 49 | if (!gv) { 50 | gv = new GearAnimationValue() 51 | this.$storage[this.$controller.selectedPageId] = gv 52 | } 53 | 54 | gv.frame = this.$owner['frame'] 55 | gv.playing = this.$owner['playing'] 56 | } 57 | } 58 | 59 | class GearAnimationValue implements IAnimationGear { 60 | public playing: boolean 61 | public frame: number 62 | 63 | public constructor(playing: boolean = true, frame: number = 0) { 64 | this.playing = playing 65 | this.frame = frame 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/gear/GearBase.ts: -------------------------------------------------------------------------------- 1 | import { GObject } from '../GObject' 2 | import { ParseEaseType } from '../config/Definitions' 3 | import { Controller } from '../controller/Controller' 4 | import { XmlNode } from '../utils/XMLParser' 5 | 6 | export default class GearBase { 7 | public static disableAllTweenEffect: boolean = false 8 | 9 | protected $tween: boolean 10 | protected $easeType: (t: number) => number 11 | protected $tweenTime: number 12 | protected $tweenDelay: number 13 | protected $lockToken: number = 0 14 | 15 | protected $owner: GObject 16 | protected $controller: Controller 17 | 18 | public constructor(owner: GObject) { 19 | this.$owner = owner 20 | this.$easeType = ParseEaseType('Quad.Out') 21 | this.$tweenTime = 0.3 22 | this.$tweenDelay = 0 23 | } 24 | 25 | public get controller(): Controller { 26 | return this.$controller 27 | } 28 | 29 | public set controller(val: Controller) { 30 | if (val != this.$controller) { 31 | this.$controller = val 32 | if (this.$controller) this.init() 33 | } 34 | } 35 | 36 | public get tween(): boolean { 37 | return this.$tween 38 | } 39 | 40 | public set tween(val: boolean) { 41 | this.$tween = val 42 | } 43 | 44 | public get tweenDelay(): number { 45 | return this.$tweenDelay 46 | } 47 | 48 | public set tweenDelay(val: number) { 49 | this.$tweenDelay = val 50 | } 51 | 52 | public get tweenTime(): number { 53 | return this.$tweenTime 54 | } 55 | 56 | public set tweenTime(value: number) { 57 | this.$tweenTime = value 58 | } 59 | 60 | public get easeType(): (t: number) => number { 61 | return this.$easeType 62 | } 63 | 64 | public set easeType(value: (t: number) => number) { 65 | this.$easeType = value 66 | } 67 | 68 | public setup(xml: XmlNode): void { 69 | this.$controller = this.$owner.parent.getController(xml.attributes.controller) 70 | if (this.$controller == null) return 71 | 72 | this.init() 73 | 74 | let str: string 75 | 76 | str = xml.attributes.tween 77 | if (str) this.$tween = true 78 | 79 | str = xml.attributes.ease 80 | if (str) this.$easeType = ParseEaseType(str) 81 | 82 | str = xml.attributes.duration 83 | if (str) this.$tweenTime = parseFloat(str) 84 | 85 | str = xml.attributes.delay 86 | if (str) this.$tweenDelay = parseFloat(str) 87 | if (this['$vid'] != null) { 88 | str = xml.attributes.pages 89 | if (str) this['pages'] = str.split(',') 90 | } else { 91 | let pages: string[] 92 | let values: string[] 93 | 94 | str = xml.attributes.pages 95 | if (str) pages = str.split(',') 96 | 97 | str = xml.attributes.values 98 | if (str) values = str.split('|') 99 | 100 | if (pages && values) { 101 | values.forEach((s, i) => { 102 | this.addStatus(pages[i], s) 103 | }) 104 | } 105 | 106 | str = xml.attributes.default 107 | if (str) this.addStatus(null, str) 108 | } 109 | } 110 | 111 | public updateFromRelations(dx: number, dy: number): void {} 112 | 113 | protected addStatus(pageId: string, value: string): void {} 114 | 115 | protected init(): void {} 116 | 117 | public apply(): void {} 118 | 119 | public updateState(): void {} 120 | } 121 | -------------------------------------------------------------------------------- /src/gear/GearColor.ts: -------------------------------------------------------------------------------- 1 | import { GObject } from '../GObject' 2 | import GearBase from './GearBase' 3 | import { IColorGear } from '../interface/IColorGear' 4 | import { StringUtil } from '../utils/StringUtil' 5 | 6 | export default class GearColor extends GearBase { 7 | private $storage: { [key: string]: string } 8 | private $default: string = '#ffffff' 9 | 10 | public constructor(owner: GObject & IColorGear) { 11 | super(owner) 12 | } 13 | 14 | protected init(): void { 15 | this.$default = this.$owner['color'] 16 | this.$storage = {} 17 | } 18 | 19 | protected addStatus(pageId: string, value: string): void { 20 | if (value == '-') return 21 | 22 | let col: string = value 23 | if (pageId == null) this.$default = col 24 | else this.$storage[pageId] = col 25 | } 26 | 27 | public apply(): void { 28 | this.$owner.$gearLocked = true 29 | 30 | let data: string = this.$storage[this.$controller.selectedPageId] 31 | if (data != undefined) this.$owner['color'] = StringUtil.HEX2RGB(data) 32 | else this.$owner['color'] = StringUtil.HEX2RGB(this.$default) 33 | 34 | this.$owner.$gearLocked = false 35 | } 36 | 37 | public updateState(): void { 38 | if (this.$controller == null || this.$owner.$gearLocked || this.$owner.$inProgressBuilding) 39 | return 40 | 41 | this.$storage[this.$controller.selectedPageId] = this.$owner['color'] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/gear/GearDisplay.ts: -------------------------------------------------------------------------------- 1 | import GearBase from './GearBase' 2 | import { GObject } from '../GObject' 3 | 4 | export default class GearDisplay extends GearBase { 5 | private $vid: number = 0 6 | public pages: string[] 7 | 8 | public constructor(owner: GObject) { 9 | super(owner) 10 | this.$lockToken = 1 11 | } 12 | 13 | protected init(): void { 14 | this.pages = null 15 | } 16 | 17 | public lock(): number { 18 | this.$vid++ 19 | return this.$lockToken 20 | } 21 | 22 | public release(token: number): void { 23 | if (token == this.$lockToken) this.$vid-- 24 | } 25 | 26 | public get connected(): boolean { 27 | return this.controller == null || this.$vid > 0 28 | } 29 | 30 | public apply(): void { 31 | this.$lockToken++ 32 | if (this.$lockToken <= 0) this.$lockToken = 1 33 | 34 | if ( 35 | this.pages == null || 36 | this.pages.length == 0 || 37 | this.pages.indexOf(this.$controller.selectedPageId) != -1 38 | ) 39 | this.$vid = 1 40 | else this.$vid = 0 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/gear/GearIcon.ts: -------------------------------------------------------------------------------- 1 | import { GObject } from '../GObject' 2 | import GearBase from './GearBase' 3 | 4 | export class GearIcon extends GearBase { 5 | private $storage: { [key: string]: string } 6 | private $default: string 7 | 8 | public constructor(owner: GObject) { 9 | super(owner) 10 | } 11 | 12 | protected init(): void { 13 | this.$default = this.$owner.icon 14 | this.$storage = {} 15 | } 16 | 17 | protected addStatus(pageId: string, value: string): void { 18 | if (pageId == null) this.$default = value 19 | else this.$storage[pageId] = value 20 | } 21 | 22 | public apply(): void { 23 | this.$owner.$gearLocked = true 24 | 25 | let data: string = this.$storage[this.$controller.selectedPageId] 26 | if (data != undefined) this.$owner.icon = data 27 | else this.$owner.icon = this.$default 28 | 29 | this.$owner.$gearLocked = false 30 | } 31 | 32 | public updateState(): void { 33 | if (this.$controller == null || this.$owner.$gearLocked || this.$owner.$inProgressBuilding) 34 | return 35 | 36 | this.$storage[this.$controller.selectedPageId] = this.$owner.icon 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/gear/GearLook.ts: -------------------------------------------------------------------------------- 1 | import { GearEvent } from '../events/GearEvent' 2 | import GearBase from './GearBase' 3 | import { GObject } from '../GObject' 4 | import { UIPackage } from '../res/UIPackage' 5 | 6 | export class GearLook extends GearBase { 7 | private $tweener: createjs.Tween 8 | 9 | private $storage: { [key: string]: GearLookValue } 10 | private $default: GearLookValue 11 | private $tweenValue: createjs.Point 12 | private $tweenTarget: GearLookValue 13 | 14 | public constructor(owner: GObject) { 15 | super(owner) 16 | } 17 | 18 | protected init(): void { 19 | this.$default = new GearLookValue(this.$owner.alpha, this.$owner.rotation, this.$owner.grayed) 20 | this.$storage = {} 21 | } 22 | 23 | protected addStatus(pageId: string, value: string): void { 24 | if (value == '-') return 25 | 26 | let arr: string[] = value.split(',') 27 | let gv: GearLookValue 28 | if (pageId == null) gv = this.$default 29 | else { 30 | gv = new GearLookValue() 31 | this.$storage[pageId] = gv 32 | } 33 | gv.alpha = parseFloat(arr[0]) 34 | gv.rotation = parseInt(arr[1]) 35 | gv.grayed = arr[2] == '1' ? true : false 36 | } 37 | 38 | public apply(): void { 39 | let gv: GearLookValue = this.$storage[this.$controller.selectedPageId] 40 | if (!gv) gv = this.$default 41 | 42 | if (this.$tween && !UIPackage.$constructingObjects && !GearBase.disableAllTweenEffect) { 43 | this.$owner.$gearLocked = true 44 | this.$owner.grayed = gv.grayed 45 | this.$owner.$gearLocked = false 46 | 47 | if (this.$tweener) { 48 | if (this.$tweenTarget.alpha === gv.alpha && this.$tweenTarget.rotation === gv.rotation) 49 | return 50 | this.$tweener.gotoAndStop(this.$tweener.duration) //set to end 51 | this.$tweener = null 52 | } 53 | 54 | let a: boolean = gv.alpha != this.$owner.alpha 55 | let b: boolean = gv.rotation != this.$owner.rotation 56 | if (a || b) { 57 | if (this.$owner.hasGearController(0, this.$controller)) 58 | this.$lockToken = this.$owner.lockGearDisplay() 59 | 60 | this.$tweenTarget = gv 61 | 62 | let vars: any = { 63 | onChange: () => { 64 | this.$owner.$gearLocked = true 65 | if (a) this.$owner.alpha = this.$tweenValue.x 66 | if (b) this.$owner.rotation = this.$tweenValue.y 67 | this.$owner.$gearLocked = false 68 | } 69 | } 70 | 71 | if (this.$tweenValue == null) this.$tweenValue = new createjs.Point() 72 | this.$tweenValue.x = this.$owner.alpha 73 | this.$tweenValue.y = this.$owner.rotation 74 | this.$tweener = createjs.Tween.get(this.$tweenValue, vars) 75 | .wait(this.$tweenDelay * 1000) 76 | .to({ x: gv.alpha, y: gv.rotation }, this.$tweenTime * 1000, this.$easeType) 77 | .call(this.tweenComplete, null, this) 78 | } 79 | } else { 80 | this.$owner.$gearLocked = true 81 | this.$owner.grayed = gv.grayed 82 | this.$owner.alpha = gv.alpha 83 | this.$owner.rotation = gv.rotation 84 | this.$owner.$gearLocked = false 85 | } 86 | } 87 | 88 | private tweenComplete(): void { 89 | if (this.$lockToken != 0) { 90 | this.$owner.releaseGearDisplay(this.$lockToken) 91 | this.$lockToken = 0 92 | } 93 | this.$tweener = null 94 | let evt = new createjs.Event(GearEvent.GEAR_STOP, true, false) 95 | this.$owner.dispatchEvent(evt, this) 96 | } 97 | 98 | public updateState(): void { 99 | if (this.$controller == null || this.$owner.$gearLocked || this.$owner.$inProgressBuilding) 100 | return 101 | 102 | let gv: GearLookValue = this.$storage[this.$controller.selectedPageId] 103 | if (!gv) { 104 | gv = new GearLookValue() 105 | this.$storage[this.$controller.selectedPageId] = gv 106 | } 107 | 108 | gv.alpha = this.$owner.alpha 109 | gv.rotation = this.$owner.rotation 110 | gv.grayed = this.$owner.grayed 111 | } 112 | } 113 | 114 | class GearLookValue { 115 | public alpha: number 116 | public rotation: number 117 | public grayed: boolean 118 | 119 | public constructor(alpha: number = 0, rotation: number = 0, grayed: boolean = false) { 120 | this.alpha = alpha 121 | this.rotation = rotation 122 | this.grayed = grayed 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/gear/GearSize.ts: -------------------------------------------------------------------------------- 1 | import { GearEvent } from '../events/GearEvent' 2 | import GearBase from './GearBase' 3 | import { GObject } from '../GObject' 4 | import { UIPackage } from '../res/UIPackage' 5 | 6 | export class GearSize extends GearBase { 7 | private $tweener: createjs.Tween 8 | 9 | private $storage: { [key: string]: GearSizeValue } 10 | private $default: GearSizeValue 11 | private $tweenValue: GearSizeValue 12 | private $tweenTarget: GearSizeValue 13 | 14 | public constructor(owner: GObject) { 15 | super(owner) 16 | } 17 | 18 | protected init(): void { 19 | this.$default = new GearSizeValue( 20 | this.$owner.width, 21 | this.$owner.height, 22 | this.$owner.scaleX, 23 | this.$owner.scaleY 24 | ) 25 | this.$storage = {} 26 | } 27 | 28 | protected addStatus(pageId: string, value: string): void { 29 | if (value == '-') return 30 | 31 | let arr: string[] = value.split(',') 32 | let gv: GearSizeValue 33 | if (pageId == null) gv = this.$default 34 | else { 35 | gv = new GearSizeValue() 36 | this.$storage[pageId] = gv 37 | } 38 | gv.width = parseInt(arr[0]) 39 | gv.height = parseInt(arr[1]) 40 | if (arr.length > 2) { 41 | gv.scaleX = parseFloat(arr[2]) 42 | gv.scaleY = parseFloat(arr[3]) 43 | } 44 | } 45 | 46 | public apply(): void { 47 | let gv: GearSizeValue = this.$storage[this.$controller.selectedPageId] 48 | if (!gv) gv = this.$default 49 | 50 | if (this.$tween && !UIPackage.$constructingObjects && !GearBase.disableAllTweenEffect) { 51 | if (this.$tweener) { 52 | if ( 53 | this.$tweenTarget.width != gv.width || 54 | this.$tweenTarget.height != gv.height || 55 | this.$tweenTarget.scaleX != gv.scaleX || 56 | this.$tweenTarget.scaleY != gv.scaleY 57 | ) { 58 | this.$tweener.gotoAndStop(this.$tweener.duration) //set to end 59 | this.$tweener = null 60 | } else return 61 | } 62 | 63 | let a: boolean = gv.width != this.$owner.width || gv.height != this.$owner.height 64 | let b: boolean = gv.scaleX != this.$owner.scaleX || gv.scaleY != this.$owner.scaleY 65 | if (a || b) { 66 | if (this.$owner.hasGearController(0, this.$controller)) 67 | this.$lockToken = this.$owner.lockGearDisplay() 68 | 69 | this.$tweenTarget = gv 70 | 71 | let vars: any = { 72 | onChange: () => { 73 | this.$owner.$gearLocked = true 74 | if (a) 75 | this.$owner.setSize( 76 | this.$tweenValue.width, 77 | this.$tweenValue.height, 78 | this.$owner.gearXY.controller == this.$controller 79 | ) 80 | if (b) this.$owner.setScale(this.$tweenValue.scaleX, this.$tweenValue.scaleY) 81 | this.$owner.$gearLocked = false 82 | } 83 | } 84 | if (this.$tweenValue == null) this.$tweenValue = new GearSizeValue() 85 | this.$tweenValue.width = this.$owner.width 86 | this.$tweenValue.height = this.$owner.height 87 | this.$tweenValue.scaleX = this.$owner.scaleX 88 | this.$tweenValue.scaleY = this.$owner.scaleY 89 | this.$tweener = createjs.Tween.get(this.$tweenValue, vars) 90 | .wait(this.$tweenDelay * 1000) 91 | .to( 92 | { width: gv.width, height: gv.height, scaleX: gv.scaleX, scaleY: gv.scaleY }, 93 | this.$tweenTime * 1000, 94 | this.$easeType 95 | ) 96 | .call(this.tweenComplete, null, this) 97 | } 98 | } else { 99 | this.$owner.$gearLocked = true 100 | this.$owner.setSize(gv.width, gv.height, this.$owner.gearXY.controller == this.$controller) 101 | this.$owner.setScale(gv.scaleX, gv.scaleY) 102 | this.$owner.$gearLocked = false 103 | } 104 | } 105 | 106 | private tweenComplete(): void { 107 | if (this.$lockToken != 0) { 108 | this.$owner.releaseGearDisplay(this.$lockToken) 109 | this.$lockToken = 0 110 | } 111 | this.$tweener = null 112 | let evt = new createjs.Event(GearEvent.GEAR_STOP, true, false) 113 | this.$owner.dispatchEvent(evt, this) 114 | } 115 | 116 | public updateState(): void { 117 | if (this.$controller == null || this.$owner.$gearLocked || this.$owner.$inProgressBuilding) 118 | return 119 | 120 | let gv: GearSizeValue = this.$storage[this.$controller.selectedPageId] 121 | if (!gv) { 122 | gv = new GearSizeValue() 123 | this.$storage[this.$controller.selectedPageId] = gv 124 | } 125 | 126 | gv.width = this.$owner.width 127 | gv.height = this.$owner.height 128 | gv.scaleX = this.$owner.scaleX 129 | gv.scaleY = this.$owner.scaleY 130 | } 131 | 132 | public updateFromRelations(dx: number, dy: number): void { 133 | if (this.$controller == null || this.$storage == null) return 134 | 135 | for (let key in this.$storage) { 136 | let gv: GearSizeValue = this.$storage[key] 137 | gv.width += dx 138 | gv.height += dy 139 | } 140 | this.$default.width += dx 141 | this.$default.height += dy 142 | 143 | this.updateState() 144 | } 145 | } 146 | 147 | class GearSizeValue { 148 | public width: number 149 | public height: number 150 | public scaleX: number 151 | public scaleY: number 152 | 153 | public constructor( 154 | width: number = 0, 155 | height: number = 0, 156 | scaleX: number = 0, 157 | scaleY: number = 0 158 | ) { 159 | this.width = width 160 | this.height = height 161 | this.scaleX = scaleX 162 | this.scaleY = scaleY 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/gear/GearText.ts: -------------------------------------------------------------------------------- 1 | import { GObject } from '../GObject' 2 | import GearBase from './GearBase' 3 | 4 | export class GearText extends GearBase { 5 | private $storage: { [key: string]: string } 6 | private $default: string 7 | 8 | public constructor(owner: GObject) { 9 | super(owner) 10 | } 11 | 12 | protected init(): void { 13 | this.$default = this.$owner.text 14 | this.$storage = {} 15 | } 16 | 17 | protected addStatus(pageId: string, value: string): void { 18 | if (pageId == null) this.$default = value 19 | else this.$storage[pageId] = value 20 | } 21 | 22 | public apply(): void { 23 | this.$owner.$gearLocked = true 24 | 25 | let data: string = this.$storage[this.$controller.selectedPageId] 26 | if (data != undefined) this.$owner.text = data 27 | else this.$owner.text = this.$default 28 | 29 | this.$owner.$gearLocked = false 30 | } 31 | 32 | public updateState(): void { 33 | if (this.$controller == null || this.$owner.$gearLocked || this.$owner.$inProgressBuilding) 34 | return 35 | 36 | this.$storage[this.$controller.selectedPageId] = this.$owner.text 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/gear/GearXY.ts: -------------------------------------------------------------------------------- 1 | 2 | import { GearEvent } from '../events/GearEvent' 3 | import GearBase from './GearBase' 4 | import { GObject } from '../GObject' 5 | import { UIPackage } from '../res/UIPackage' 6 | 7 | export class GearXY extends GearBase { 8 | private $tweener: createjs.Tween 9 | 10 | private $storage: { [key: string]: createjs.Point } 11 | private $default: createjs.Point 12 | private $tweenValue: createjs.Point 13 | private $tweenTarget: createjs.Point 14 | 15 | public constructor(owner: GObject) { 16 | super(owner) 17 | } 18 | 19 | protected init(): void { 20 | this.$default = new createjs.Point(this.$owner.x, this.$owner.y) 21 | this.$storage = {} 22 | } 23 | 24 | protected addStatus(pageId: string, value: string): void { 25 | if (value == '-') return 26 | 27 | let arr: string[] = value.split(',') 28 | let pt: createjs.Point 29 | if (pageId == null) pt = this.$default 30 | else { 31 | pt = new createjs.Point() 32 | this.$storage[pageId] = pt 33 | } 34 | pt.x = parseInt(arr[0]) 35 | pt.y = parseInt(arr[1]) 36 | } 37 | 38 | public apply(): void { 39 | let pt: createjs.Point = this.$storage[this.$controller.selectedPageId] 40 | if (!pt) pt = this.$default 41 | 42 | if (this.$tween && !UIPackage.$constructingObjects && !GearBase.disableAllTweenEffect) { 43 | if (this.$tweener) { 44 | if (this.$tweenTarget.x === pt.x && this.$tweenTarget.y === pt.y) return 45 | this.$tweener.gotoAndStop(this.$tweener.duration) //set to end 46 | this.$tweener = null 47 | } 48 | if (this.$owner.x != pt.x || this.$owner.y != pt.y) { 49 | this.$owner.hasGearController(0, this.$controller) 50 | this.$lockToken = this.$owner.lockGearDisplay() 51 | 52 | this.$tweenTarget = pt 53 | 54 | let vars: any = { 55 | onChange: () => { 56 | this.$owner.$gearLocked = true 57 | this.$owner.setXY(this.$tweenValue.x, this.$tweenValue.y) 58 | this.$owner.$gearLocked = false 59 | } 60 | } 61 | if (this.$tweenValue == null) this.$tweenValue = new createjs.Point() 62 | this.$tweenValue.x = this.$owner.x 63 | this.$tweenValue.y = this.$owner.y 64 | this.$tweener = createjs.Tween.get(this.$tweenValue, vars) 65 | .wait(this.$tweenDelay * 1000) 66 | .to({ x: pt.x, y: pt.y }, this.$tweenTime * 1000, this.$easeType) 67 | .call(this.tweenComplete, null, this) 68 | } 69 | } else { 70 | this.$owner.$gearLocked = true 71 | this.$owner.setXY(pt.x, pt.y) 72 | this.$owner.$gearLocked = false 73 | } 74 | } 75 | 76 | private tweenComplete(): void { 77 | if (this.$lockToken != 0) { 78 | this.$owner.releaseGearDisplay(this.$lockToken) 79 | this.$lockToken = 0 80 | } 81 | this.$tweener = null 82 | let evt = new createjs.Event(GearEvent.GEAR_STOP, true, false) 83 | this.$owner.dispatchEvent(evt, this) 84 | } 85 | 86 | public updateState(): void { 87 | if (this.$controller == null || this.$owner.$gearLocked || this.$owner.$inProgressBuilding) 88 | return 89 | 90 | let pt: createjs.Point = this.$storage[this.$controller.selectedPageId] 91 | if (!pt) { 92 | pt = new createjs.Point() 93 | this.$storage[this.$controller.selectedPageId] = pt 94 | } 95 | 96 | pt.x = this.$owner.x 97 | pt.y = this.$owner.y 98 | } 99 | 100 | public updateFromRelations(dx: number, dy: number): void { 101 | if (this.$controller == null || this.$storage == null) return 102 | 103 | for (let key in this.$storage) { 104 | let pt: createjs.Point = this.$storage[key] 105 | pt.x += dx 106 | pt.y += dy 107 | } 108 | this.$default.x += dx 109 | this.$default.y += dy 110 | 111 | this.updateState() 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/interface.ts: -------------------------------------------------------------------------------- 1 | import { GObject } from './GObject' 2 | import { PackageItem } from './res/PackageItem' 3 | 4 | export interface IGRoot { 5 | [x: string]: any 6 | inst: any 7 | findFor(obj: GObject): any 8 | } 9 | export interface IObjectFactoryType { 10 | newObjectDirectly(type: string): GObject 11 | newObject(type: number | PackageItem, userClass?: new () => GObject): GObject 12 | } 13 | -------------------------------------------------------------------------------- /src/interface/IAnimationGear.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface IAnimationGear { 3 | playing: boolean; 4 | frame: number; 5 | } 6 | 7 | export let isAnimationGear = function (obj: any): obj is IAnimationGear { 8 | return obj && "playing" in obj && "frame" in obj; 9 | } 10 | -------------------------------------------------------------------------------- /src/interface/IColorGear.ts: -------------------------------------------------------------------------------- 1 | export interface IColorGear { 2 | color: string|number; 3 | } 4 | 5 | export let isColorGear = function (obj: any): obj is IColorGear { 6 | return obj && "color" in obj; 7 | } 8 | -------------------------------------------------------------------------------- /src/interface/IColorableTitle.ts: -------------------------------------------------------------------------------- 1 | export interface IColorableTitle { 2 | titleColor: string; 3 | fontSize: number; 4 | } 5 | 6 | export let isColorableTitle = function (obj: any): obj is IColorableTitle { 7 | return obj && "titleColor" in obj && "fontSize" in obj; 8 | } 9 | -------------------------------------------------------------------------------- /src/interface/IMovieClip.ts: -------------------------------------------------------------------------------- 1 | import { Frame } from "../display/Frame"; 2 | import { MovieClipData } from "../display/MovieClipData"; 3 | import { DefaultMovieClipSettings } from "../display/MovieClipSettings"; 4 | 5 | export interface IMovieClip { 6 | interval: number; 7 | swing: boolean; 8 | repeatDelay: number; 9 | 10 | // playing: boolean; 11 | frameCount: number; 12 | frames: Frame[]; 13 | // currentFrame: number; 14 | // data: MovieClipData; 15 | } -------------------------------------------------------------------------------- /src/interface/IUIObject.ts: -------------------------------------------------------------------------------- 1 | import { GObject } from '../GObject' 2 | 3 | export interface IUIObject { 4 | UIOwner: GObject 5 | } 6 | 7 | export let isUIObject = function(obj: any): obj is IUIObject { 8 | return obj && 'UIOwner' in obj && obj.UIOwner != null 9 | } 10 | -------------------------------------------------------------------------------- /src/res/DisplayListItem.ts: -------------------------------------------------------------------------------- 1 | import { XmlNode } from "../utils/XMLParser"; 2 | import { PackageItem } from "./PackageItem"; 3 | 4 | export class DisplayListItem { 5 | public packageItem: PackageItem; 6 | public type: string; 7 | public desc: XmlNode; 8 | public listItemCount: number; 9 | 10 | public constructor(packageItem: PackageItem, type: string) { 11 | this.packageItem = packageItem; 12 | this.type = type; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/res/PackageItem.ts: -------------------------------------------------------------------------------- 1 | 2 | import { PackageItemType } from "../config/Definitions"; 3 | import { BitmapFont } from "../display/BitmapFont"; 4 | import { Frame } from "../display/Frame"; 5 | import { XmlNode } from "../utils/XMLParser"; 6 | import { DisplayListItem } from "./DisplayListItem"; 7 | import { UIPackage, AssetTypes } from "./UIPackage"; 8 | 9 | export class PackageItem { 10 | 11 | public owner: UIPackage; 12 | 13 | public type: PackageItemType; 14 | public id: string; 15 | public name: string; 16 | public width: number = 0; 17 | public height: number = 0; 18 | public file: string; 19 | public decoded: boolean; 20 | 21 | //image 22 | public scale9Grid: createjs.Rectangle; 23 | public scaleByTile: boolean; 24 | public tiledSlices: number = 0; 25 | public texture: HTMLImageElement | createjs.SpriteSheetFrame; 26 | 27 | //movieclip 28 | public interval: number = 0; 29 | public repeatDelay: number = 0; 30 | public swing: boolean; 31 | public frames: Frame[]; 32 | 33 | //sound 34 | public sound: createjs.Sound; 35 | 36 | //componenet 37 | public componentData: XmlNode; 38 | public displayList: DisplayListItem[]; 39 | 40 | //font 41 | public bitmapFont: BitmapFont; 42 | 43 | public load(): AssetTypes { 44 | return this.owner.getItemAsset(this); 45 | } 46 | 47 | public toString(): string { 48 | return this.name; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/res/UIObjectFactory.ts: -------------------------------------------------------------------------------- 1 | import { PackageItemType } from '../config/Definitions' 2 | import { GButton } from '../GButton' 3 | import { GComboBox } from '../GComboBox' 4 | import { GComponent } from '../GComponent' 5 | import { GGraph } from '../GGraph' 6 | import { GGroup } from '../GGroup' 7 | import { GImage } from '../GImage' 8 | import { GLabel } from '../GLabel' 9 | import { GList } from '../GList' 10 | import { GLoader } from '../GLoader' 11 | import { GMovieClip } from '../GMovieClip' 12 | import { GObject } from '../GObject' 13 | import { GProgressBar } from '../GProgressBar' 14 | import { GRichTextField } from '../GRichTextField' 15 | import { GScrollBar } from '../GScrollBar' 16 | import { GSlider } from '../GSlider' 17 | import { GTextField } from '../GTextField' 18 | import { GTextInput } from '../GTextInput' 19 | import { XmlNode } from '../utils/XMLParser' 20 | import { PackageItem } from './PackageItem' 21 | import { Decls } from './UIPackage' 22 | 23 | type ExtensionClassDictionary = { 24 | [key: string]: new () => GComponent 25 | } 26 | export class UIObjectFactory { 27 | private static packageItemExtensions: ExtensionClassDictionary = {} 28 | private static loaderExtension: new () => GLoader 29 | 30 | public static setPackageItemExtension(url: string, type: { new (): GComponent }): void { 31 | UIObjectFactory.packageItemExtensions[url.substring(5)] = type 32 | } 33 | 34 | // public static setLoaderExtension(type: { new(): GLoader }): void { 35 | // UIObjectFactory.loaderExtension = type; 36 | // } 37 | 38 | public static newObject(pi: PackageItem): GObject { 39 | switch (pi.type) { 40 | case PackageItemType.Image: 41 | return new GImage() 42 | 43 | case PackageItemType.MovieClip: 44 | return new GMovieClip() 45 | 46 | case PackageItemType.Component: 47 | let cls: { new (): GObject } = UIObjectFactory.packageItemExtensions[pi.owner.id + pi.id] 48 | if (cls) return new cls() 49 | 50 | let xml: XmlNode = pi.owner.getItemAsset(pi) as XmlNode 51 | let extention: string = xml.attributes.extention 52 | if (extention != null) { 53 | switch (extention) { 54 | case 'Button': 55 | return new GButton() 56 | case 'ProgressBar': 57 | return new GProgressBar() 58 | case 'Label': 59 | return new GLabel() 60 | case 'Slider': 61 | return new GSlider() 62 | case 'ScrollBar': 63 | return new GScrollBar() 64 | case 'ComboBox': 65 | return new GComboBox() 66 | default: 67 | return new GComponent() 68 | } 69 | } else return new GComponent() 70 | } 71 | return null 72 | } 73 | 74 | static newObjectDirectly(type: string): GObject { 75 | switch (type) { 76 | case 'image': 77 | return new GImage() 78 | 79 | case 'movieclip': 80 | return new GMovieClip() 81 | 82 | case 'component': 83 | return new GComponent() 84 | 85 | case 'text': 86 | return new GTextField() 87 | 88 | case 'list': 89 | return new GList() 90 | 91 | case 'richtext': 92 | return new GRichTextField() 93 | 94 | case 'inputtext': 95 | return new GTextInput() 96 | 97 | case 'group': 98 | return new GGroup() 99 | 100 | case 'graph': 101 | return new GGraph() 102 | 103 | case 'loader': 104 | if (UIObjectFactory.loaderExtension != null) return new UIObjectFactory.loaderExtension() 105 | else return new GLoader() 106 | } 107 | return null 108 | } 109 | } 110 | 111 | Decls.UIObjectFactory = UIObjectFactory 112 | -------------------------------------------------------------------------------- /src/utils/AssetLoader.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Resource } from "../createjs/extras/Resource"; 3 | 4 | export interface ResourceDictionary { 5 | 6 | [index: string]: Resource|HTMLImageElement; 7 | 8 | } 9 | export class AssetLoader extends createjs.LoadQueue { 10 | protected static $resources: ResourceDictionary = {}; 11 | public constructor(preferXHR?: boolean, basePath?: string,crossOrigin?:string) { 12 | super(preferXHR, basePath); 13 | 14 | this.on('complete', this._onComplete, this); 15 | } 16 | 17 | protected _onComplete(event: any): void { 18 | var loader = event.target; 19 | var result = loader["_loadedResults"]; 20 | AssetLoader.addResources(result); 21 | }; 22 | 23 | public static get resourcesPool(): ResourceDictionary { 24 | return AssetLoader.$resources; 25 | } 26 | 27 | public static destroyResource(key: string): void { 28 | let res = AssetLoader.$resources[key]; 29 | if (res) { 30 | delete AssetLoader.$resources[key]; 31 | } 32 | } 33 | 34 | public static addResources(res: ResourceDictionary): void { 35 | if (!res) return; 36 | for (let key in res) //override the item which has same key name 37 | AssetLoader.$resources[key] = res[key]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/utils/Binder.ts: -------------------------------------------------------------------------------- 1 | export class Binder { 2 | public static create(func: Function, context: any, ...args: any[]): T { 3 | if (!context) 4 | return func as T; 5 | return (function (): void { 6 | let fullargs = arguments.length > 0 ? [].concat(Array.prototype.slice.call(arguments)).concat(args) : [].concat(args); 7 | func.apply(context, fullargs); 8 | }) as Function as T; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/ByteBuffer.ts: -------------------------------------------------------------------------------- 1 | import { ByteArray } from "./ByteArray"; 2 | 3 | export class ByteBuffer extends ByteArray { 4 | public stringTable: Array = null; 5 | public version: number = 0; 6 | 7 | public constructor(buffer?: ArrayBuffer | Uint8Array, bufferExtSize?: number) { 8 | super(buffer, bufferExtSize); 9 | } 10 | 11 | public skip(count: number): void { 12 | this.position += count; 13 | } 14 | 15 | public readBool(): boolean { 16 | return this.readByte() == 1; 17 | } 18 | 19 | public readS(): string { 20 | var index: number = this.readUnsignedShort(); 21 | if (index == 65534) //null 22 | return null; 23 | else if (index == 65533) 24 | return "" 25 | else 26 | return this.stringTable[index]; 27 | } 28 | 29 | public readSArray(cnt: number): Array { 30 | var ret: Array = new Array(cnt); 31 | for (var i: number = 0; i < cnt; i++) 32 | ret[i] = this.readS(); 33 | 34 | return ret; 35 | } 36 | 37 | public writeS(value: string): void { 38 | var index: number = this.readUnsignedShort(); 39 | if (index != 65534 && index != 65533) 40 | this.stringTable[index] = value; 41 | } 42 | 43 | public readColor(hasAlpha: boolean = false): number { 44 | var r: number = this.readUnsignedByte(); 45 | var g: number = this.readUnsignedByte(); 46 | var b: number = this.readUnsignedByte(); 47 | var a: number = this.readUnsignedByte(); 48 | 49 | return (hasAlpha ? (a << 24) : 0) + (r << 16) + (g << 8) + b; 50 | } 51 | 52 | public readChar(): string { 53 | var i: number = this.readUnsignedShort(); 54 | return String.fromCharCode(i); 55 | } 56 | 57 | public readBuffer(): ByteBuffer { 58 | var count: number = this.readUnsignedInt(); 59 | var ba: ByteBuffer = new ByteBuffer(new Uint8Array(this.buffer, this.position, count)); 60 | ba.stringTable = this.stringTable; 61 | ba.version = this.version; 62 | this.position += count; 63 | return ba; 64 | } 65 | 66 | public seek(indexTablePos: number, blockIndex: number): boolean { 67 | var tmp: number = this.position; 68 | this.position = indexTablePos; 69 | var segCount: number = this.readByte(); 70 | if (blockIndex < segCount) { 71 | var useShort: boolean = this.readByte() == 1; 72 | var newPos: number; 73 | if (useShort) { 74 | this.position += 2 * blockIndex; 75 | newPos = this.readUnsignedShort(); 76 | } 77 | else { 78 | this.position += 4 * blockIndex; 79 | newPos = this.readUnsignedInt(); 80 | } 81 | 82 | if (newPos > 0) { 83 | this.position = indexTablePos + newPos; 84 | return true; 85 | } 86 | else { 87 | this.position = tmp; 88 | return false; 89 | } 90 | } 91 | else { 92 | this.position = tmp; 93 | return false; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/utils/DOMEventManager.ts: -------------------------------------------------------------------------------- 1 | 2 | import { DisplayObjectEvent } from '../events/DisplayObjectEvent' 3 | 4 | export class DOMEventManager extends createjs.EventDispatcher { 5 | public static inst: DOMEventManager = new DOMEventManager() 6 | 7 | public constructor() { 8 | super() 9 | 10 | //resize 11 | window.addEventListener('resize', e => this.notifyResizeEvents(e), false) 12 | 13 | //modifer keys 14 | window.addEventListener('keydown', e => this.onWindowKeyDown(e), false) 15 | window.addEventListener('keyup', e => this.onWindowKeyUp(e), false) 16 | 17 | //mouse wheel 18 | const toBind: string[] = 19 | 'onwheel' in document || (document)['documentMode'] >= 9 20 | ? ['wheel'] 21 | : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'] 22 | for (let i: number = toBind.length; i; ) { 23 | window.addEventListener(toBind[--i], e => this.onMouseWheel(e), false) 24 | } 25 | } 26 | 27 | //resize 28 | private notifyResizeEvents(e: any): void { 29 | this.dispatchEvent('resize') 30 | } 31 | 32 | private onMouseWheel(event: any): void { 33 | let orgEvent: any = event || window.event, 34 | delta: number = 0, 35 | deltaX: number = 0, 36 | deltaY: number = 0, 37 | absDelta: number = 0 38 | 39 | if ('detail' in orgEvent) { 40 | deltaY = orgEvent.detail * -1 41 | } 42 | if ('wheelDelta' in orgEvent) { 43 | deltaY = orgEvent.wheelDelta 44 | } 45 | if ('wheelDeltaY' in orgEvent) { 46 | deltaY = orgEvent.wheelDeltaY 47 | } 48 | if ('wheelDeltaX' in orgEvent) { 49 | deltaX = orgEvent.wheelDeltaX * -1 50 | } 51 | 52 | //FF DOMMouseScroll 53 | if ('axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS) { 54 | deltaX = deltaY * -1 55 | deltaY = 0 56 | } 57 | 58 | delta = deltaY === 0 ? deltaX : deltaY 59 | 60 | if ('deltaY' in orgEvent) { 61 | deltaY = orgEvent.deltaY * -1 62 | delta = deltaY 63 | } 64 | if ('deltaX' in orgEvent) { 65 | deltaX = orgEvent.deltaX 66 | if (deltaY === 0) { 67 | delta = deltaX * -1 68 | } 69 | } 70 | 71 | if (deltaY === 0 && deltaX === 0) { 72 | return 73 | } 74 | 75 | // Delta modes: 76 | // * deltaMode 0 is by pixels, nothing to do 77 | // * deltaMode 1 is by lines 78 | // * deltaMode 2 is by pages 79 | if (orgEvent.deltaMode === 1) { 80 | const lineHeight = 16 //fontSize - line-height; 81 | delta *= lineHeight 82 | deltaY *= lineHeight 83 | deltaX *= lineHeight 84 | } else if (orgEvent.deltaMode === 2) { 85 | const pageHeight = 16 //dom.clientHeight = page-height 86 | delta *= pageHeight 87 | deltaY *= pageHeight 88 | deltaX *= pageHeight 89 | } 90 | 91 | absDelta = Math.max(Math.abs(deltaY), Math.abs(deltaX)) 92 | 93 | if (!this.lowestDelta || absDelta < this.lowestDelta) { 94 | this.lowestDelta = absDelta 95 | 96 | if (orgEvent.type === 'mousewheel' && absDelta % 120 === 0) this.lowestDelta /= 40 97 | } 98 | 99 | if (orgEvent.type === 'mousewheel' && absDelta % 120 === 0) { 100 | delta /= 40 101 | deltaX /= 40 102 | deltaY /= 40 103 | } 104 | 105 | delta = Math[delta >= 1 ? 'floor' : 'ceil'](delta / this.lowestDelta) 106 | deltaX = Math[deltaX >= 1 ? 'floor' : 'ceil'](deltaX / this.lowestDelta) 107 | deltaY = Math[deltaY >= 1 ? 'floor' : 'ceil'](deltaY / this.lowestDelta) 108 | 109 | this.retEvent.delta = delta 110 | this.retEvent.deltaX = deltaX 111 | this.retEvent.deltaY = deltaY 112 | this.retEvent.deltaFactor = this.lowestDelta 113 | this.retEvent.deltaMode = 0 114 | 115 | if (this.nullLowestDeltaTimeout) { 116 | clearTimeout(this.nullLowestDeltaTimeout) 117 | } 118 | this.nullLowestDeltaTimeout = window.setTimeout(() => this.nullLowestDelta(), 200) 119 | let evt = new createjs.Event(DisplayObjectEvent.MOUSE_WHEEL, true, false) 120 | evt.data = { event: this.retEvent } 121 | this.dispatchEvent(evt, this) 122 | } 123 | 124 | private retEvent: any = {} 125 | private lowestDelta: number 126 | private nullLowestDeltaTimeout: number = NaN 127 | 128 | private nullLowestDelta(): void { 129 | this.lowestDelta = null 130 | } 131 | 132 | /*******************keys*******************/ 133 | private $pressedKeys: { [key: number]: boolean } = {} 134 | private $releasedKeys: { [key: number]: boolean } = {} 135 | private $downKeys: number[] = [] 136 | 137 | public isKeyDown(key: number): boolean { 138 | return this.$downKeys.indexOf(key) >= 0 139 | } 140 | 141 | public isKeyPressed(key: number): boolean { 142 | return !!this.$pressedKeys[key] 143 | } 144 | 145 | public isKeyReleased(key: number): boolean { 146 | return !!this.$releasedKeys[key] 147 | } 148 | 149 | private onWindowKeyDown(evt: any): void { 150 | let key: number = evt.which || evt.keyCode 151 | 152 | if (!this.isKeyDown(key)) { 153 | this.$downKeys.push(key) 154 | this.$pressedKeys[key] = true 155 | 156 | this.dispatchEvent('keyPressed', key) 157 | } 158 | } 159 | 160 | private onWindowKeyUp(evt: any): void { 161 | let key: number = evt.which || evt.keyCode 162 | 163 | if (this.isKeyDown(key)) { 164 | this.$pressedKeys[key] = false 165 | this.$releasedKeys[key] = true 166 | 167 | let index: number = this.$downKeys.indexOf(key) 168 | if (index >= 0) this.$downKeys.splice(index, 1) 169 | 170 | this.dispatchEvent('keyReleased', key) 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/utils/DragIndicator.ts: -------------------------------------------------------------------------------- 1 | 2 | import { AlignType, VertAlignType } from '../config/Definitions' 3 | import { GLoader } from '../GLoader' 4 | import { GObject } from '../GObject' 5 | import { GRoot } from '../GRoot' 6 | import { DragEvent } from '../events/DragEvent' 7 | 8 | export class DragIndicator { 9 | protected $agent: GLoader 10 | protected $sourceData: any 11 | protected $sourceObject: GObject 12 | 13 | public constructor() { 14 | this.$agent = new GLoader() 15 | this.$agent.draggable = true 16 | this.$agent.touchable = false 17 | this.$agent.setSize(100, 100) 18 | this.$agent.setPivot(0.5, 0.5, true) 19 | this.$agent.align = AlignType.Center 20 | this.$agent.verticalAlign = VertAlignType.Middle 21 | this.$agent.sortingOrder = 1000000 //top most 22 | this.$agent.on(DragEvent.END, this.$dragEnd, this) 23 | } 24 | 25 | public get dragAgent(): GObject { 26 | return this.$agent 27 | } 28 | 29 | public get isDragging(): boolean { 30 | return this.$agent.parent != null 31 | } 32 | 33 | public get sourceObject(): GObject { 34 | return this.$sourceObject 35 | } 36 | 37 | public startDrag( 38 | source: GObject, 39 | icon: string, 40 | sourceData: any, 41 | touchPointID: number = -1 42 | ): void { 43 | if (this.isDragging) return 44 | 45 | this.$sourceObject = source 46 | this.$sourceData = sourceData 47 | this.$agent.url = icon 48 | GRoot.inst.addChild(this.$agent) 49 | const pt: createjs.Point = GRoot.inst.globalToLocal( 50 | GRoot.globalMouseStatus.mouseX, 51 | GRoot.globalMouseStatus.mouseY 52 | ) 53 | this.$agent.setXY(pt.x, pt.y) 54 | this.$agent.startDrag(touchPointID) 55 | } 56 | 57 | public cancel(): void { 58 | if (this.$agent.parent != null) { 59 | this.$agent.stopDrag() 60 | GRoot.inst.removeChild(this.$agent) 61 | this.$sourceData = null 62 | } 63 | } 64 | 65 | private $dragEnd(evt: any): void { 66 | if (!this.isDragging) return 67 | 68 | GRoot.inst.removeChild(this.$agent) 69 | 70 | let sourceData: any = this.$sourceData 71 | this.$sourceData = null 72 | let obj: GObject = GRoot.inst.getObjectUnderPoint(evt.target.x, evt.target.y) 73 | while (obj != null) { 74 | if (obj.hasListener(DragEvent.DROP)) { 75 | obj.requestFocus() 76 | let currentTarget = obj.displayObject; 77 | let event = new createjs.Event(DragEvent.DROP, true, false) 78 | event.data = { currentTarget, sourceData } 79 | obj.dispatchEvent(event, this) 80 | return 81 | } 82 | obj = obj.parent 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/utils/GObjectRecycler.ts: -------------------------------------------------------------------------------- 1 | import { UIPackage } from '../res/UIPackage' 2 | import { Recycler } from './Recycler' 3 | 4 | export class GObjectRecycler extends Recycler { 5 | public constructor() { 6 | super() 7 | } 8 | 9 | public clear(): void { 10 | for (let key in this.$pool) { 11 | let arr = this.$pool[key] 12 | if (arr) { 13 | arr.forEach((v: any) => { 14 | v.dispose() 15 | }) 16 | } 17 | } 18 | super.clear() 19 | } 20 | 21 | protected createObject(id: string): any { 22 | return UIPackage.createObjectFromURL(id) //id = url 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/InputDelegate.ts: -------------------------------------------------------------------------------- 1 | 2 | import { InputElement } from '../display/InputElement' 3 | import { GObject } from '../GObject' 4 | import { GRoot } from '../GRoot' 5 | import { GTextInput, InputType } from '../GTextInput' 6 | import { GTimer } from '../GTimer' 7 | import { FocusEvent } from '../events/FocusEvent' 8 | import { TextEvent } from '../events/TextEvent' 9 | import { InteractiveEvents } from '../config/Definitions' 10 | 11 | export class InputDelegate { 12 | protected $inited: boolean = false 13 | protected $textField: GTextInput 14 | protected $input: InputElement 15 | protected $restrictString: string = null 16 | protected $restrictRegex: RegExp = null 17 | protected $type: InputType 18 | 19 | private $focused: boolean = false 20 | 21 | public constructor(tf: GTextInput) { 22 | this.$textField = tf 23 | this.$input = new InputElement(tf) 24 | } 25 | 26 | public initialize(): void { 27 | if (this.$inited) return 28 | 29 | this.$input.$addToStage() 30 | 31 | this.$input.on('updateText', this.updateText, this) 32 | this.$input.on(FocusEvent.CHANGED, this.focusHandler, this) 33 | this.$textField.on(InteractiveEvents.Down, this.textFieldDownHandler, this) 34 | this.$inited = true 35 | } 36 | 37 | private textFieldDownHandler(): void { 38 | this.$onFocus() 39 | } 40 | 41 | public destroy(): void { 42 | if (!this.$inited) return 43 | 44 | this.$input.$removeFromStage() 45 | 46 | this.$textField.off(InteractiveEvents.Down, this.textFieldDownHandler) 47 | GRoot.inst.off(InteractiveEvents.Down, this.onStageDown) 48 | 49 | this.$input.off('updateText', this.updateText) 50 | this.$input.off(FocusEvent.CHANGED, this.focusHandler) 51 | 52 | this.$inited = false 53 | } 54 | 55 | public get text(): string { 56 | return this.$input.text 57 | } 58 | 59 | public set text(v: string) { 60 | this.$input.text = v 61 | } 62 | 63 | public setColor(v: string) { 64 | return this.$input.setColor(v) 65 | } 66 | 67 | private updateText(): void { 68 | let textValue = this.$input.text 69 | let isChanged: boolean = false 70 | if (this.$restrictRegex != null) { 71 | let result: string[] = textValue.match(this.$restrictRegex) 72 | if (result) textValue = result.join('') 73 | else textValue = '' 74 | isChanged = true 75 | } 76 | 77 | if (isChanged && this.$input.text != textValue) this.$input.text = textValue 78 | 79 | this.$textField.text = this.$input.text 80 | let evt = new createjs.Event(TextEvent.Change, true, false) 81 | evt.data = { textField: this.$textField } 82 | this.$textField.dispatchEvent(evt, this) 83 | } 84 | 85 | private onStageDown(e: createjs.Event): void { 86 | let target = GObject.castFromNativeObject(e.currentTarget) 87 | if (target != this.$textField) this.$input.$hide() 88 | } 89 | 90 | private focusHandler(evt: createjs.Event): void { 91 | let { data } = evt 92 | if (data == 'focus') { 93 | if (!this.$focused) { 94 | this.$focused = true 95 | this.$textField.$isTyping = true 96 | this.$textField.alpha = 0.01 97 | this.$textField.dispatchEvent(FocusEvent.CHANGED, 'focus') 98 | this.$textField.dispatchEvent(TextEvent.FocusIn, this.$textField) 99 | } 100 | } else if (data == 'blur') { 101 | if (this.$focused) { 102 | this.$focused = false 103 | GRoot.inst.off(InteractiveEvents.Down, this.onStageDown) 104 | this.$textField.$isTyping = false 105 | this.$textField.alpha = 1 106 | this.$input.$onBlur() 107 | this.$textField.dispatchEvent(FocusEvent.CHANGED, 'blur') 108 | this.$textField.dispatchEvent(TextEvent.FocusOut, this.$textField) 109 | } 110 | } 111 | } 112 | 113 | public get isFocused(): boolean { 114 | return this.$focused 115 | } 116 | 117 | /**@internal */ 118 | $getProperty(name: string): string { 119 | return (this.$inited && this.$input.getAttribute(name)) || null 120 | } 121 | 122 | /**@internal */ 123 | $setProperty(name: string, value: string): void { 124 | if (!this.$inited) return 125 | this.$input.setAttribute(name, value) 126 | } 127 | 128 | get $restrict(): string { 129 | return this.$restrictString 130 | } 131 | 132 | set $restrict(v: string) { 133 | this.$restrictString = v 134 | if (this.$restrictString != null && this.$restrictString.length > 0) 135 | this.$restrictRegex = new RegExp(this.$restrictString) 136 | else this.$restrictRegex = null 137 | } 138 | 139 | public get type(): InputType { 140 | return this.$type 141 | } 142 | 143 | public set type(v: InputType) { 144 | if (v != this.$type) this.$type = v 145 | } 146 | 147 | private tryHideInput(): void { 148 | if (!this.$textField.visible && this.$input) this.$input.$removeFromStage() 149 | } 150 | 151 | /**@internal */ 152 | $updateProperties(): void { 153 | if (this.isFocused) { 154 | this.$input.resetInput() 155 | this.tryHideInput() 156 | return 157 | } 158 | 159 | this.$input.text = this.$textField.text 160 | this.$input.resetInput() 161 | this.tryHideInput() 162 | } 163 | 164 | /**@internal */ 165 | $onFocus(): void { 166 | if (!this.$textField.visible || this.$focused) return 167 | 168 | GRoot.inst.off(InteractiveEvents.Down, this.onStageDown) 169 | GTimer.inst.callLater(() => { 170 | GRoot.inst.on(InteractiveEvents.Down, this.onStageDown, this) 171 | }, this) 172 | 173 | this.$input.$show() 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/utils/Margin.ts: -------------------------------------------------------------------------------- 1 | 2 | export class Margin { 3 | public left: number = 0; 4 | public right: number = 0; 5 | public top: number = 0; 6 | public bottom: number = 0; 7 | 8 | public parse(str: string): void { 9 | if (!str) { 10 | this.left = this.right = this.top = this.bottom = 0; 11 | return; 12 | } 13 | let arr: string[] = str.split(","); 14 | if (arr.length == 1) { 15 | let k: number = parseInt(arr[0]); 16 | this.left = this.right = this.top = this.bottom = k; 17 | } 18 | else { 19 | this.top = parseInt(arr[0]); 20 | this.bottom = parseInt(arr[1]); 21 | this.left = parseInt(arr[2]); 22 | this.right = parseInt(arr[3]); 23 | } 24 | } 25 | 26 | public copy(source: Margin): void { 27 | this.top = source.top; 28 | this.bottom = source.bottom; 29 | this.left = source.left; 30 | this.right = source.right; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/utils/MathUtil.ts: -------------------------------------------------------------------------------- 1 | 2 | export class MathUtil { 3 | public static RADIAN: number = Math.PI / 180; 4 | 5 | public static clamp(value: number, min: number, max: number): number { 6 | if (value < min) 7 | value = min; 8 | else if (value > max) 9 | value = max; 10 | return value; 11 | } 12 | 13 | public static clamp01(value: number): number { 14 | if (value > 1) 15 | value = 1; 16 | else if (value < 0) 17 | value = 0; 18 | return value; 19 | } 20 | 21 | public static isNumber(n: any): n is number { 22 | if (typeof (n) != "number") return false; 23 | if (isNaN(n)) return false; 24 | return true; 25 | } 26 | 27 | public static sign(x: number): number { 28 | x = Number(x); 29 | 30 | if (x === 0 || isNaN(x)) 31 | return x; 32 | 33 | return x > 0 ? 1 : -1; 34 | } 35 | 36 | public static angleToRadian(n: number): number { 37 | return n * MathUtil.RADIAN; 38 | } 39 | 40 | public static lerp(s: number, e: number, p: number): number { 41 | return s + p * (e - s); 42 | } 43 | } -------------------------------------------------------------------------------- /src/utils/NumberUtil.ts: -------------------------------------------------------------------------------- 1 | export class NumberUtil { 2 | public static RADIAN: number = Math.PI / 180 3 | 4 | public static clamp(value: number, min: number, max: number): number { 5 | if (value < min) value = min 6 | else if (value > max) value = max 7 | return value 8 | } 9 | 10 | public static clamp01(value: number): number { 11 | if (value > 1) value = 1 12 | else if (value < 0) value = 0 13 | return value 14 | } 15 | 16 | public static isNumber(n: any): n is number { 17 | if (typeof n != 'number') return false 18 | if (isNaN(n)) return false 19 | return true 20 | } 21 | 22 | public static sign(x: number): number { 23 | x = Number(x) 24 | 25 | if (x === 0 || isNaN(x)) return x 26 | 27 | return x > 0 ? 1 : -1 28 | } 29 | 30 | public static angleToRadian(n: number): number { 31 | return n * NumberUtil.RADIAN 32 | } 33 | 34 | public static lerp(s: number, e: number, p: number): number { 35 | return s + p * (e - s) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/utils/RawByte.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | const enum RawByteFlags { 4 | EOF_byte = -1, 5 | EOF_code_point = -1, 6 | Fatal_code_point = 0xFFFD 7 | } 8 | 9 | export class RawByte { 10 | 11 | private static inRange(a: number, min: number, max: number): boolean { 12 | return min <= a && a <= max; 13 | } 14 | 15 | public static decodeUTF8(data: Uint8Array): string { 16 | let pos: number = 0; 17 | let result: string = ""; 18 | let code_point: number; 19 | let utf8_code_point = 0; 20 | let utf8_bytes_needed = 0; 21 | let utf8_bytes_seen = 0; 22 | let utf8_lower_boundary = 0; 23 | 24 | while (data.length > pos) { 25 | let _byte = data[pos++]; 26 | 27 | if (_byte == RawByteFlags.EOF_byte) { 28 | if (utf8_bytes_needed != 0) { 29 | code_point = RawByteFlags.Fatal_code_point; 30 | } else { 31 | code_point = RawByteFlags.EOF_code_point; 32 | } 33 | } else { 34 | 35 | if (utf8_bytes_needed == 0) { 36 | if (RawByte.inRange(_byte, 0x00, 0x7F)) { 37 | code_point = _byte; 38 | } else { 39 | if (RawByte.inRange(_byte, 0xC2, 0xDF)) { 40 | utf8_bytes_needed = 1; 41 | utf8_lower_boundary = 0x80; 42 | utf8_code_point = _byte - 0xC0; 43 | } else if (RawByte.inRange(_byte, 0xE0, 0xEF)) { 44 | utf8_bytes_needed = 2; 45 | utf8_lower_boundary = 0x800; 46 | utf8_code_point = _byte - 0xE0; 47 | } else if (RawByte.inRange(_byte, 0xF0, 0xF4)) { 48 | utf8_bytes_needed = 3; 49 | utf8_lower_boundary = 0x10000; 50 | utf8_code_point = _byte - 0xF0; 51 | } else { 52 | throw new Error("failed to decode the raw binary data"); 53 | } 54 | utf8_code_point = utf8_code_point * Math.pow(64, utf8_bytes_needed); 55 | code_point = null; 56 | } 57 | } else if (!RawByte.inRange(_byte, 0x80, 0xBF)) { 58 | utf8_code_point = 0; 59 | utf8_bytes_needed = 0; 60 | utf8_bytes_seen = 0; 61 | utf8_lower_boundary = 0; 62 | pos--; 63 | code_point = RawByteFlags.Fatal_code_point; 64 | } else { 65 | 66 | utf8_bytes_seen += 1; 67 | utf8_code_point = utf8_code_point + (_byte - 0x80) * Math.pow(64, utf8_bytes_needed - utf8_bytes_seen); 68 | 69 | if (utf8_bytes_seen !== utf8_bytes_needed) { 70 | code_point = null; 71 | } else { 72 | 73 | let cp = utf8_code_point; 74 | let lower_boundary = utf8_lower_boundary; 75 | utf8_code_point = 0; 76 | utf8_bytes_needed = 0; 77 | utf8_bytes_seen = 0; 78 | utf8_lower_boundary = 0; 79 | if (RawByte.inRange(cp, lower_boundary, 0x10FFFF) && !this.inRange(cp, 0xD800, 0xDFFF)) { 80 | code_point = cp; 81 | } else { 82 | code_point = _byte; 83 | } 84 | } 85 | 86 | } 87 | } 88 | //Decode string 89 | if (code_point !== null && code_point !== RawByteFlags.EOF_code_point) { 90 | if (code_point <= 0xFFFF) { 91 | if (code_point > 0) 92 | result += String.fromCharCode(code_point); 93 | } else { 94 | code_point -= 0x10000; 95 | result += String.fromCharCode(0xD800 + ((code_point >> 10) & 0x3ff)); 96 | result += String.fromCharCode(0xDC00 + (code_point & 0x3ff)); 97 | } 98 | } 99 | } 100 | return result; 101 | } 102 | 103 | } -------------------------------------------------------------------------------- /src/utils/Recycler.ts: -------------------------------------------------------------------------------- 1 | export abstract class Recycler { 2 | protected $pool: { [name: string]: T[] }; 3 | protected $count: number = 0; 4 | 5 | public constructor() { 6 | this.$pool = {}; 7 | } 8 | 9 | public get count(): number { 10 | return this.$count; 11 | } 12 | 13 | public clear(): void { 14 | for (let key in this.$pool) { 15 | let arr = this.$pool[key]; 16 | if (arr) { 17 | arr.length = 0; 18 | arr = null; 19 | } 20 | } 21 | this.$pool = {}; 22 | this.$count = 0; 23 | } 24 | 25 | public get(id: string): T { 26 | let arr: T[] = this.$pool[id]; 27 | if (arr == null) { 28 | arr = []; 29 | this.$pool[id] = arr; 30 | } 31 | if (arr.length) { 32 | this.$count--; 33 | return arr.shift(); 34 | } 35 | return this.createObject(id); 36 | } 37 | 38 | protected abstract createObject(id: string): T; 39 | 40 | public recycle(id: string, obj: T): void { 41 | if (!id) return; 42 | let arr: T[] = this.$pool[id]; 43 | if (arr == null) { 44 | arr = []; 45 | this.$pool[id] = arr; 46 | } 47 | this.$count++; 48 | arr.push(obj); 49 | } 50 | } -------------------------------------------------------------------------------- /src/utils/StringUtil.ts: -------------------------------------------------------------------------------- 1 | 2 | export class StringUtil { 3 | 4 | public static encodeHTML(str: string): string { 5 | if (!str) 6 | return ""; 7 | else 8 | return str.replace("&", "&").replace("<", "<").replace(">", ">").replace("'", "'"); 9 | } 10 | 11 | public static getFileName(source: string): string { 12 | let i: number = source.lastIndexOf("/"); 13 | if (i != -1) 14 | source = source.substr(i + 1); 15 | i = source.lastIndexOf("\\"); 16 | if (i != -1) 17 | source = source.substr(i + 1); 18 | i = source.lastIndexOf("."); 19 | if (i != -1) 20 | return source.substring(0, i); 21 | else 22 | return source; 23 | } 24 | 25 | public static startsWith(source: string, str: string, ignoreCase: boolean = false): boolean { 26 | if (!source) 27 | return false; 28 | else if (source.length < str.length) 29 | return false; 30 | else { 31 | source = source.substring(0, str.length); 32 | if (!ignoreCase) 33 | return source == str; 34 | else 35 | return source.toLowerCase() == str.toLowerCase(); 36 | } 37 | } 38 | 39 | public static endsWith(source: string, str: string, ignoreCase: boolean = false): boolean { 40 | if (!source) 41 | return false; 42 | else if (source.length < str.length) 43 | return false; 44 | else { 45 | source = source.substring(source.length - str.length); 46 | if (!ignoreCase) 47 | return source == str; 48 | else 49 | return source.toLowerCase() == str.toLowerCase(); 50 | } 51 | } 52 | 53 | public static trim(targetString: string): string { 54 | return StringUtil.trimLeft(StringUtil.trimRight(targetString)); 55 | } 56 | 57 | public static trimLeft(targetString: string): string { 58 | let tempChar: string = ""; 59 | let i: number; 60 | for (i = 0; i < targetString.length; i++) { 61 | tempChar = targetString.charAt(i); 62 | if (tempChar != " " && tempChar != "\n" && tempChar != "\r") 63 | break; 64 | } 65 | return targetString.substr(i); 66 | } 67 | 68 | public static trimRight(targetString: string): string { 69 | let tempChar: string = ""; 70 | let i: number; 71 | for (i = targetString.length - 1; i >= 0; i--) { 72 | tempChar = targetString.charAt(i); 73 | if (tempChar != " " && tempChar != "\n" && tempChar != "\r") 74 | break; 75 | } 76 | return targetString.substring(0, i + 1); 77 | } 78 | 79 | public static convertToHtmlColor(argb: number, hasAlpha: boolean = false): string { 80 | let alpha: string; 81 | if (hasAlpha) 82 | alpha = (argb >> 24 & 0xFF).toString(16); 83 | else 84 | alpha = ""; 85 | let red: string = (argb >> 16 & 0xFF).toString(16); 86 | let green: string = (argb >> 8 & 0xFF).toString(16); 87 | let blue: string = (argb & 0xFF).toString(16); 88 | if (alpha.length == 1) 89 | alpha = `0${alpha}`; 90 | if (red.length == 1) 91 | red = `0${red}`; 92 | if (green.length == 1) 93 | green = `0${green}`; 94 | if (blue.length == 1) 95 | blue = `0${blue}`; 96 | return `#${alpha}${red}${green}${blue}`; 97 | } 98 | 99 | public static convertFromHtmlColor(str: string, hasAlpha: boolean = false): number { 100 | if (str.length < 1) 101 | return 0; 102 | 103 | if (str.charAt(0) == "#") 104 | str = str.substr(1); 105 | 106 | if (str.length == 8) 107 | return (parseInt(str.substr(0, 2), 16) << 24) + parseInt(str.substr(2), 16); 108 | else if (hasAlpha) 109 | return 0xFF000000 + parseInt(str, 16); 110 | else 111 | return parseInt(str, 16); 112 | } 113 | 114 | public static convertToRGBA(str: string): string { 115 | if (str.charAt(0) == "#") 116 | str = str.substr(1); 117 | 118 | let alpha = +(parseInt('0x' + str.slice(0, 2)) / 255).toFixed(2); 119 | str = str.substr(2); 120 | let r = parseInt(str.substring(0, 2), 16); 121 | let g = parseInt(str.substring(2, 4), 16); 122 | let b = parseInt(str.substring(4), 16); 123 | return createjs.Graphics.getRGB(r, g, b, alpha); 124 | } 125 | 126 | public static HEX2RGB(hex: string): string { 127 | if (hex.charAt(0) == "#") 128 | hex = hex.substr(1); 129 | 130 | if (hex.length === 3) { 131 | hex += hex; 132 | } 133 | return `${this.HEX2DEC(hex.substring(0, 2))},${this.HEX2DEC(hex.substring(2, 4))},${this.HEX2DEC(hex.substring(4))}`; 134 | 135 | } 136 | 137 | public static HEX2DEC(hex: string): string { 138 | return parseInt(hex, 16).toString(); 139 | 140 | } 141 | 142 | } -------------------------------------------------------------------------------- /src/utils/UBBParser.ts: -------------------------------------------------------------------------------- 1 | import { TextBlock } from "../GRichTextField"; 2 | 3 | export type UBBParserHandlerMap = { 4 | [key: string]: (tag: string, end: boolean, attr: string) => string 5 | } 6 | 7 | export class UBBParser { 8 | private $text: string; 9 | private $readPos: number = 0; 10 | 11 | protected $handlers: UBBParserHandlerMap; 12 | 13 | public smallFontSize: number = 12; 14 | public normalFontSize: number = 14; 15 | public largeFontSize: number = 16; 16 | 17 | public defaultImgWidth: number = 0; 18 | public defaultImgHeight: number = 0; 19 | 20 | public static inst: UBBParser = new UBBParser(); 21 | 22 | public constructor() { 23 | this.$handlers = { 24 | url: this.onTag_URL, 25 | img: this.onTag_IMG, 26 | b: this.onTag_Simple, 27 | i: this.onTag_Simple, 28 | u: this.onTag_Simple, 29 | sup: this.onTag_Simple, 30 | sub: this.onTag_Simple, 31 | color: this.onTag_COLOR, 32 | font: this.onTag_FONT, 33 | size: this.onTag_SIZE 34 | }; 35 | } 36 | 37 | protected onTag_URL(tagName: string, end: boolean, attr: string): string { 38 | if (!end) { 39 | if (attr != null) 40 | return ``; 41 | else { 42 | let href: string = this.getTagText(); 43 | return ``; 44 | } 45 | } 46 | else 47 | return ""; 48 | } 49 | 50 | protected onTag_IMG(tagName: string, end: boolean, attr: string): string { 51 | if (!end) { 52 | let src: string = this.getTagText(true); 53 | if (!src) 54 | return null; 55 | 56 | if (this.defaultImgWidth) 57 | return ``; 58 | else 59 | return ``; 60 | } 61 | else 62 | return null; 63 | } 64 | 65 | protected onTag_Simple(tagName: string, end: boolean, attr: string): string { 66 | return end ? `` : `<${tagName}>`; 67 | } 68 | 69 | protected onTag_COLOR(tagName: string, end: boolean, attr: string): string { 70 | if (!end) 71 | return ``; 72 | else 73 | return ""; 74 | } 75 | 76 | protected onTag_FONT(tagName: string, end: boolean, attr: string): string { 77 | if (!end) 78 | return ``; 79 | else 80 | return ""; 81 | } 82 | 83 | protected onTag_SIZE(tagName: string, end: boolean, attr: string): string { 84 | if (!end) { 85 | if (attr == "normal") 86 | attr = `${this.normalFontSize}`; 87 | else if (attr == "small") 88 | attr = `${this.smallFontSize}`; 89 | else if (attr == "large") 90 | attr = `${this.largeFontSize}`; 91 | else if (attr.length && attr.charAt(0) == "+") 92 | attr = `${this.smallFontSize + parseInt(attr.substr(1))}`; 93 | else if (attr.length && attr.charAt(0) == "-") 94 | attr = `${this.smallFontSize - parseInt(attr.substr(1))}`; 95 | return ``; 96 | } 97 | else 98 | return ""; 99 | } 100 | 101 | protected getTagText(remove: boolean = false): string { 102 | let pos: number = this.$text.indexOf("[", this.$readPos); 103 | if (pos == -1) 104 | return null; 105 | 106 | let ret: string = this.$text.substring(this.$readPos, pos); 107 | if (remove) 108 | this.$readPos = pos; 109 | return ret; 110 | } 111 | 112 | //TODO: impl for GRichTextField 113 | public parseStyle(text: string): TextBlock[] { 114 | return []; 115 | } 116 | } -------------------------------------------------------------------------------- /src/utils/Utils.ts: -------------------------------------------------------------------------------- 1 | export class Utils { 2 | static deepCopyProperties(target: any, source: any, propertyObj: Object) { 3 | for (var prop in propertyObj) { 4 | if (Array.isArray(source[prop])) { 5 | target[prop] = source[prop].slice(); 6 | } else { 7 | target[prop] = source[prop]; 8 | } 9 | } 10 | } 11 | 12 | static TextureCache: { [key: string]: HTMLImageElement|createjs.SpriteSheetFrame } = {}; 13 | 14 | public static fillPath(ctx: createjs.Graphics, points: number[], px: number, py: number): void { 15 | var cnt: number = points.length; 16 | ctx.moveTo(points[0] + px, points[1] + py); 17 | for (var i: number = 2; i < cnt; i += 2) { 18 | ctx.lineTo(points[i] + px, points[i + 1] + py); 19 | } 20 | ctx.lineTo(points[0] + px, points[1] + py); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/XMLParser.ts: -------------------------------------------------------------------------------- 1 | export type AttributeDictionary = { 2 | [key: string]: string; 3 | } 4 | 5 | export class XmlNode { 6 | public context: Node; 7 | public nodeName: string; 8 | public type: number; 9 | public text: string; 10 | 11 | private $children: XmlNode[]; 12 | private $attributes: AttributeDictionary; 13 | 14 | public constructor(ele: Node) { 15 | this.nodeName = ele.nodeName; 16 | this.context = ele; 17 | this.type = ele.nodeType; 18 | this.text = (this.type == Node.COMMENT_NODE || this.type == Node.TEXT_NODE) ? this.context.textContent : null; 19 | } 20 | 21 | public get children(): XmlNode[] { 22 | if (!this.$children) 23 | this.$children = XmlParser.getChildNodes(this); 24 | return this.$children; 25 | } 26 | 27 | public get attributes(): AttributeDictionary { 28 | if (!this.$attributes) 29 | this.$attributes = XmlParser.getNodeAttributes(this); 30 | return this.$attributes; 31 | } 32 | } 33 | 34 | export class XmlParser { 35 | 36 | private static $parser: DOMParser = new DOMParser(); 37 | 38 | public static tryParse(xmlstring: string, mimeType: any = "application/xml"): XmlNode { 39 | let doc: Document = XmlParser.$parser.parseFromString(xmlstring, mimeType); 40 | if (doc && doc.childNodes && doc.childNodes.length >= 1) 41 | return new XmlNode(doc.firstChild); 42 | return null; 43 | } 44 | 45 | public static getXmlRoot(xml: XmlNode): XmlNode { 46 | if (!xml || !xml.context) 47 | throw new Error("Invalid xml node"); 48 | let p: Node = xml.context; 49 | while (p.parentNode != null) 50 | p = p.parentNode; 51 | return p == xml.context ? xml : new XmlNode(p); 52 | } 53 | 54 | public static getChildNodes(xml: XmlNode, matchName: string = null): XmlNode[] { 55 | let nodes: NodeList = xml.context.childNodes; 56 | let ret: XmlNode[] = []; 57 | if (!nodes || nodes.length <= 0) return ret; 58 | let len: number = nodes.length; 59 | for (let i: number = 0; i < len; i++) { 60 | let n: Node = nodes.item(i); 61 | if (n.nodeType == Node.TEXT_NODE) 62 | continue; 63 | if (!matchName || (matchName && matchName.length > 0 && n.nodeName.toLowerCase() == matchName.toLowerCase())) 64 | ret.push(new XmlNode(n)); 65 | } 66 | return ret; 67 | } 68 | 69 | public static getNodeAttributes(xml: XmlNode): AttributeDictionary { 70 | let context: Element = xml.context as Element; 71 | let asList: NamedNodeMap = context.attributes; 72 | let ret: AttributeDictionary = {}; 73 | if (!asList || asList.length <= 0) return ret; 74 | let len: number = asList.length; 75 | for (let i = 0; i < len; i++) { 76 | let a: Attr = asList.item(i); 77 | ret[a.nodeName] = a.nodeValue; 78 | } 79 | return ret; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/utils/isMobile.ts: -------------------------------------------------------------------------------- 1 | import isMobileCall from 'ismobilejs' 2 | 3 | const isMobile = isMobileCall(window.navigator) 4 | 5 | export { isMobile } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "target": "es6", 5 | "module": "es6", 6 | "moduleResolution":"Node", 7 | "sourceMap":true, 8 | "declarationDir": "dist/types", 9 | }, 10 | 11 | "include": [ 12 | "./src/**/*" 13 | ], 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | --------------------------------------------------------------------------------