wgge 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/ActivatedTreeNode.js +187 -0
- package/core/Collection.js +156 -0
- package/core/Dictionary.js +83 -0
- package/core/animation/3d/AnimatedVector3.js +35 -0
- package/core/animation/3d/AnimationHelper.js +103 -0
- package/core/animation/3d/ProgressVector3.js +26 -0
- package/core/animation/AnimatedRotation.js +44 -0
- package/core/animation/AnimatedValue.js +33 -0
- package/core/animation/AnimatedVector2.js +35 -0
- package/core/animation/ProgressRotation.js +40 -0
- package/core/animation/ProgressValue.js +27 -0
- package/core/animation/ProgressVector2.js +23 -0
- package/core/audio/AudioModel.js +36 -0
- package/core/audio/AudioRenderer.js +120 -0
- package/core/audio/global/GlobalAudioModel.js +88 -0
- package/core/audio/global/GlobalAudioRenderer.js +108 -0
- package/core/controller/AnimationController.js +37 -0
- package/core/controller/AnimationFloatController.js +39 -0
- package/core/controller/AnimationVector2Controller.js +39 -0
- package/core/controller/CollectionController.js +47 -0
- package/core/controller/ConditionalNodeController.js +51 -0
- package/core/controller/ControllerBase.js +64 -0
- package/core/controller/ControllerWithSaveGame.js +27 -0
- package/core/controller/ControllerWithTimeout.js +10 -0
- package/core/controller/NullableNodeController.js +35 -0
- package/core/controller/TimeoutController.js +26 -0
- package/core/helper/ArrayHelper.js +19 -0
- package/core/helper/DOMHelper.js +167 -0
- package/core/helper/GUIHelper.js +53 -0
- package/core/helper/HttpHelper.js +33 -0
- package/core/helper/ImageHelper.js +36 -0
- package/core/helper/NumberHelper.js +28 -0
- package/core/helper/ObjectHelper.js +10 -0
- package/core/helper/StringHelper.js +59 -0
- package/core/model/ModelBase.js +91 -0
- package/core/model/ObjectModel.js +97 -0
- package/core/model/collection/ModelNodeCollection.js +232 -0
- package/core/model/collection/table/IdentifiedModelNode.js +24 -0
- package/core/model/collection/table/ModelNodeTable.js +53 -0
- package/core/model/event/ChangeEvent.js +24 -0
- package/core/model/event/EventManager.js +44 -0
- package/core/model/event/NodeWithEvents.js +32 -0
- package/core/model/template/TemplateNode.js +45 -0
- package/core/model/value/BoolValue.js +17 -0
- package/core/model/value/CapacityValue.js +49 -0
- package/core/model/value/DirtyValue.js +66 -0
- package/core/model/value/FloatValue.js +8 -0
- package/core/model/value/IntValue.js +20 -0
- package/core/model/value/NullableNode.js +66 -0
- package/core/model/value/StringValue.js +21 -0
- package/core/model/vector/Quaternion.js +82 -0
- package/core/model/vector/Rotation.js +61 -0
- package/core/model/vector/Vector2.js +177 -0
- package/core/model/vector/Vector3.js +98 -0
- package/core/renderer/RendererBase.js +46 -0
- package/core/renderer/canvas/CanvasRenderer.js +60 -0
- package/core/renderer/canvas/ImageToCanvasRenderer.js +94 -0
- package/core/renderer/dom/DirtyValueRenderer.js +36 -0
- package/core/renderer/dom/DomRenderer.js +32 -0
- package/core/renderer/dom/ImageRenderer.js +41 -0
- package/core/renderer/dom/Vector2Renderer.js +37 -0
- package/core/renderer/generic/CollectionRenderer.js +50 -0
- package/core/renderer/generic/ConditionalNodeRenderer.js +57 -0
- package/core/renderer/generic/NullableNodeRenderer.js +43 -0
- package/core/renderer/svg/SvgRenderer.js +130 -0
- package/editor/EditorController.js +135 -0
- package/editor/EditorModel.js +38 -0
- package/editor/EditorRenderer.js +116 -0
- package/editor/form/NodeFormRenderer.js +180 -0
- package/editor/material/MaterialPreviewRenderer.js +136 -0
- package/editor/table/TableRenderer.js +123 -0
- package/editor/table/lookup/TableLookupRenderer.js +120 -0
- package/editor/table/row/TableRowRenderer.js +97 -0
- package/game/GameController.js +144 -0
- package/game/GameModel.js +73 -0
- package/game/GameRenderer.js +108 -0
- package/game/controls/ControlsController.js +104 -0
- package/game/controls/ControlsModel.js +60 -0
- package/game/menu/MenuModel.js +25 -0
- package/game/menu/MenuRenderer.js +37 -0
- package/game/menu/item/MenuItemModel.js +31 -0
- package/game/menu/item/MenuItemRenderer.js +40 -0
- package/game/resources/3d/3DModel.js +30 -0
- package/game/resources/ResourcesModel.js +26 -0
- package/game/resources/assets/AssetCache.js +193 -0
- package/game/resources/assets/loader/AssetLoader.js +56 -0
- package/game/resources/assets/loader/AudioLoader.js +15 -0
- package/game/resources/assets/loader/GlbLoader.js +16 -0
- package/game/resources/assets/loader/ImageLoader.js +15 -0
- package/game/resources/assets/loader/MaterialLoader.js +49 -0
- package/game/resources/assets/loader/Model3dLoader.js +21 -0
- package/game/resources/assets/loader/SpriteLoader.js +40 -0
- package/game/resources/image/DualImageModel.js +34 -0
- package/game/resources/image/ImageModel.js +43 -0
- package/game/resources/material/MaterialModel.js +69 -0
- package/game/save/SaveGameController.js +21 -0
- package/game/save/SaveGameModel.js +5 -0
- package/game/save/SaveGameRenderer.js +17 -0
- package/game/save/editor/SaveGameEditorRenderer.js +17 -0
- package/index.html +42 -0
- package/index.js +73 -0
- package/package.json +35 -0
- package/style/basic/editor.less +290 -0
- package/style/basic/game.less +149 -0
- package/style/basic/utilities.less +215 -0
- package/style/style.less +3 -0
- package/webpack.config.js +29 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import ActivatedTreeNode from "../ActivatedTreeNode";
|
|
2
|
+
|
|
3
|
+
export default class ControllerBase extends ActivatedTreeNode {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @type DemoGameModel
|
|
7
|
+
*/
|
|
8
|
+
game;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @type ObjectModel
|
|
12
|
+
*/
|
|
13
|
+
model;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @type array[(delta) => any]
|
|
17
|
+
*/
|
|
18
|
+
updateActions;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {DemoGameModel} game
|
|
22
|
+
* @param {ObjectModel} model
|
|
23
|
+
*/
|
|
24
|
+
constructor(game, model) {
|
|
25
|
+
super();
|
|
26
|
+
this.game = game;
|
|
27
|
+
this.model = model;
|
|
28
|
+
|
|
29
|
+
this.updateActions = null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
update(delta) {
|
|
33
|
+
if (!this.isActivated) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (this.updateActions) {
|
|
38
|
+
while (this.updateActions.length > 0) {
|
|
39
|
+
const action = this.updateActions.shift();
|
|
40
|
+
action(delta);
|
|
41
|
+
}
|
|
42
|
+
this.updateActions = null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.updateInternal(delta);
|
|
46
|
+
this.children.forEach((c) => c.update(delta));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
updateInternal(delta) {
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
*
|
|
55
|
+
* @param action (delta) => any
|
|
56
|
+
*/
|
|
57
|
+
runOnUpdate(action) {
|
|
58
|
+
if (!this.updateActions) {
|
|
59
|
+
this.updateActions = [];
|
|
60
|
+
}
|
|
61
|
+
this.updateActions.push(action);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import ControllerWithTimeout from "./ControllerWithTimeout";
|
|
2
|
+
|
|
3
|
+
export default class ControllerWithSaveGame extends ControllerWithTimeout {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @type DemoSaveGameModel
|
|
7
|
+
*/
|
|
8
|
+
saveGame;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {DemoGameModel} game
|
|
12
|
+
* @param {ObjectModel} model
|
|
13
|
+
* @param {DemoSaveGameModel} saveGame
|
|
14
|
+
*/
|
|
15
|
+
constructor(game, model, saveGame = null) {
|
|
16
|
+
super(game, model);
|
|
17
|
+
if (!saveGame) {
|
|
18
|
+
saveGame = this.game.saveGame.get();
|
|
19
|
+
}
|
|
20
|
+
this.saveGame = saveGame;
|
|
21
|
+
|
|
22
|
+
if (!this.saveGame) {
|
|
23
|
+
console.error('no SaveGameModel provided for controller!');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import ControllerBase from "./ControllerBase";
|
|
2
|
+
import TimeoutController from "./TimeoutController";
|
|
3
|
+
|
|
4
|
+
export default class ControllerWithTimeout extends ControllerBase {
|
|
5
|
+
|
|
6
|
+
runAfterTimeout(timeout, action) {
|
|
7
|
+
return this.addChild(new TimeoutController(this.game, timeout, action));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import ControllerBase from "./ControllerBase";
|
|
2
|
+
|
|
3
|
+
export default class NullableNodeController extends ControllerBase {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @type NullableNode
|
|
7
|
+
*/
|
|
8
|
+
model;
|
|
9
|
+
|
|
10
|
+
constructor(game, model, controllerFactory, defaultControllerFactory = null) {
|
|
11
|
+
super(game, model);
|
|
12
|
+
|
|
13
|
+
this.model = model;
|
|
14
|
+
|
|
15
|
+
this.controllerFactory = controllerFactory;
|
|
16
|
+
this.defaultControllerFactory = defaultControllerFactory;
|
|
17
|
+
|
|
18
|
+
this.addAutoEvent(
|
|
19
|
+
this.model,
|
|
20
|
+
'change',
|
|
21
|
+
() => this.updateController(),
|
|
22
|
+
true
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
updateController() {
|
|
27
|
+
this.resetChildren();
|
|
28
|
+
if (this.model.isSet()) {
|
|
29
|
+
this.addChild(this.controllerFactory(this.model.get()));
|
|
30
|
+
} else if (this.defaultControllerFactory) {
|
|
31
|
+
this.addChild(this.defaultControllerFactory());
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import ControllerBase from "./ControllerBase";
|
|
2
|
+
|
|
3
|
+
export default class TimeoutController extends ControllerBase {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @param {DemoGameModel} game
|
|
8
|
+
* @param {Number} duration
|
|
9
|
+
* @param {function} onFinished
|
|
10
|
+
*/
|
|
11
|
+
constructor(game, duration, onFinished) {
|
|
12
|
+
super(game, null);
|
|
13
|
+
this.duration = duration;
|
|
14
|
+
this.timeout = duration;
|
|
15
|
+
this.onFinished = onFinished;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
updateInternal(delta) {
|
|
19
|
+
this.timeout -= delta;
|
|
20
|
+
if (this.timeout <= 0) {
|
|
21
|
+
this.removeMyself();
|
|
22
|
+
this.onFinished();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export default class ArrayHelper {
|
|
2
|
+
|
|
3
|
+
static remove(arr, el) {
|
|
4
|
+
arr.splice(arr.indexOf(el), 1);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
static random(arr) {
|
|
8
|
+
return arr[ArrayHelper.randomIndex(arr.length)];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static randomIndex(length) {
|
|
12
|
+
return Math.floor(Math.random() * length);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static unique(arr) {
|
|
16
|
+
return arr.filter((value, index, self) => self.indexOf(value) === index);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import StringHelper from "./StringHelper";
|
|
2
|
+
|
|
3
|
+
export default class DOMHelper {
|
|
4
|
+
|
|
5
|
+
static addClass(element, css) {
|
|
6
|
+
if (Array.isArray((css)) && css.length > 0) {
|
|
7
|
+
css.forEach((cls) => {
|
|
8
|
+
if (typeof cls === 'string' && cls.length > 0) element.classList.add(cls);
|
|
9
|
+
});
|
|
10
|
+
} else if (css) {
|
|
11
|
+
css.split(' ').forEach((cls) => {
|
|
12
|
+
if (typeof cls === 'string' && cls.length > 0) element.classList.add(cls);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static removeClass(element, css) {
|
|
18
|
+
element.classList.remove(css);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static hasClass(element, css) {
|
|
22
|
+
return element.classList.contains(css);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static toggleClass(element, css, classActive = null) {
|
|
26
|
+
const hasClass = DOMHelper.hasClass(element, css);
|
|
27
|
+
if (hasClass && classActive !== true) {
|
|
28
|
+
DOMHelper.removeClass(element, css);
|
|
29
|
+
} else if (classActive !== false && !hasClass) {
|
|
30
|
+
DOMHelper.addClass(element, css);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
*
|
|
36
|
+
* @param parent Element
|
|
37
|
+
* @param tag string
|
|
38
|
+
* @param css string|array|null
|
|
39
|
+
* @param innerText string|null
|
|
40
|
+
* @param onClick ()=>any|null
|
|
41
|
+
* @returns Element
|
|
42
|
+
*/
|
|
43
|
+
static createElement(parent, tag, css = null, innerText = null, onClick = null) {
|
|
44
|
+
const el = document.createElement(tag);
|
|
45
|
+
this.addClass(el, css);
|
|
46
|
+
if (parent) {
|
|
47
|
+
parent.appendChild(el);
|
|
48
|
+
}
|
|
49
|
+
if (innerText) {
|
|
50
|
+
el.innerText = innerText;
|
|
51
|
+
}
|
|
52
|
+
if (onClick) {
|
|
53
|
+
el.addEventListener('click', onClick);
|
|
54
|
+
}
|
|
55
|
+
return el;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static destroyElement(el) {
|
|
59
|
+
if (el && el.parentNode && typeof el.parentNode.removeChild === 'function') {
|
|
60
|
+
el.parentNode.removeChild(el);
|
|
61
|
+
}
|
|
62
|
+
if (el && typeof el.remove === 'function') {
|
|
63
|
+
el.remove();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static emptyElement(el) {
|
|
68
|
+
if (el && el.textContent !== undefined) {
|
|
69
|
+
el.textContent = '';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static isFullscreen() {
|
|
74
|
+
return (window.fullScreen || (window.innerWidth == screen.width && window.innerHeight == screen.height))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static openFullscreen(elem) {
|
|
78
|
+
if (elem.requestFullscreen) {
|
|
79
|
+
elem.requestFullscreen();
|
|
80
|
+
} else if (elem.webkitRequestFullscreen) { /* Safari */
|
|
81
|
+
elem.webkitRequestFullscreen();
|
|
82
|
+
} else if (elem.msRequestFullscreen) { /* IE11 */
|
|
83
|
+
elem.msRequestFullscreen();
|
|
84
|
+
}
|
|
85
|
+
DOMHelper.addClass(document.body, 'fullscreen');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static closeFullscreen() {
|
|
89
|
+
if (document.exitFullscreen) {
|
|
90
|
+
document.exitFullscreen();
|
|
91
|
+
} else if (document.webkitExitFullscreen) { /* Safari */
|
|
92
|
+
document.webkitExitFullscreen();
|
|
93
|
+
} else if (document.msExitFullscreen) { /* IE11 */
|
|
94
|
+
document.msExitFullscreen();
|
|
95
|
+
}
|
|
96
|
+
DOMHelper.removeClass(document.body, 'fullscreen');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static instantEditor(element, setter, textarea = false) {
|
|
100
|
+
if (element.dataset.instantEditor === '1') {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
element.dataset.instantEditor = '1';
|
|
104
|
+
const text = element.innerText;
|
|
105
|
+
DOMHelper.emptyElement(element);
|
|
106
|
+
const editor = DOMHelper.createElement(element, 'form', 'instant-editor');
|
|
107
|
+
const input = DOMHelper.createElement(editor, textarea ? 'textarea' : 'input');
|
|
108
|
+
input.value = text;
|
|
109
|
+
if (textarea) {
|
|
110
|
+
const cancel = DOMHelper.createElement(editor, 'button');
|
|
111
|
+
cancel.innerText = 'Cancel';
|
|
112
|
+
cancel.addEventListener('click', (e) => {
|
|
113
|
+
e.stopPropagation();
|
|
114
|
+
DOMHelper.destroyElement(editor);
|
|
115
|
+
element.innerText = text;
|
|
116
|
+
element.dataset.instantEditor = '0';
|
|
117
|
+
});
|
|
118
|
+
const save = DOMHelper.createElement(editor, 'input');
|
|
119
|
+
save.setAttribute('type', 'submit');
|
|
120
|
+
save.value = 'Save';
|
|
121
|
+
} else {
|
|
122
|
+
input.setAttribute('type', 'text');
|
|
123
|
+
}
|
|
124
|
+
input.focus();
|
|
125
|
+
editor.addEventListener('submit', (e) => {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
e.stopPropagation();
|
|
128
|
+
const value = input.value;
|
|
129
|
+
DOMHelper.destroyElement(editor);
|
|
130
|
+
element.dataset.instantEditor = '0';
|
|
131
|
+
element.innerText = value;
|
|
132
|
+
setter(value);
|
|
133
|
+
});
|
|
134
|
+
if (!textarea) {
|
|
135
|
+
editor.addEventListener('focusout', (e) => {
|
|
136
|
+
DOMHelper.destroyElement(editor);
|
|
137
|
+
element.innerText = text;
|
|
138
|
+
element.dataset.instantEditor = '0';
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static magicEditor(element, setter, textarea = false) {
|
|
144
|
+
const addHintIfNeeded = () => {
|
|
145
|
+
if (element.innerText.length === 0) {
|
|
146
|
+
element.innerText = '(edit)';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
addHintIfNeeded();
|
|
150
|
+
element.style.cursor = 'text';
|
|
151
|
+
element.addEventListener('click', () => {
|
|
152
|
+
DOMHelper.instantEditor(
|
|
153
|
+
element,
|
|
154
|
+
(value) => {
|
|
155
|
+
setter(value);
|
|
156
|
+
addHintIfNeeded();
|
|
157
|
+
},
|
|
158
|
+
textarea
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static stripHtmlTags(text) {
|
|
164
|
+
return StringHelper.stripHtmlTags(text);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as dat from "dat.gui";
|
|
2
|
+
|
|
3
|
+
export default class GUIHelper {
|
|
4
|
+
|
|
5
|
+
static createGUI() {
|
|
6
|
+
return new dat.GUI();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
static addVector2(gui, vector, name, min, max, step) {
|
|
10
|
+
const folder = gui.addFolder(name);
|
|
11
|
+
GUIHelper.addScaler(folder, vector, 'x', min, max, step);
|
|
12
|
+
GUIHelper.addScaler(folder, vector, 'y', min, max, step);
|
|
13
|
+
return folder;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static addVector3(gui, vector, name, min, max, step) {
|
|
17
|
+
const folder = gui.addFolder(name);
|
|
18
|
+
GUIHelper.addScaler(folder, vector, 'x', min, max, step);
|
|
19
|
+
GUIHelper.addScaler(folder, vector, 'y', min, max, step);
|
|
20
|
+
GUIHelper.addScaler(folder, vector, 'z', min, max, step);
|
|
21
|
+
return folder;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static addQuaternion(gui, quat, name) {
|
|
25
|
+
const min = -Math.PI;
|
|
26
|
+
const max = Math.PI;
|
|
27
|
+
const step = Math.PI/180;
|
|
28
|
+
const folder = gui.addFolder(name);
|
|
29
|
+
GUIHelper.addScaler(folder, quat, 'x', min, max, step);
|
|
30
|
+
GUIHelper.addScaler(folder, quat, 'y', min, max, step);
|
|
31
|
+
GUIHelper.addScaler(folder, quat, 'z', min, max, step);
|
|
32
|
+
GUIHelper.addScaler(folder, quat, 'w', min, max, step);
|
|
33
|
+
return folder;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static addRotationVector3(gui, object, name) {
|
|
37
|
+
return GUIHelper.addVector3(gui, object, name, -Math.PI, Math.PI, Math.PI / 180);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static addScaleVector3(gui, object, name) {
|
|
41
|
+
return GUIHelper.addVector3(gui, object, name, 0, 25, 0.1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static addScaler(gui, object, property, min, max, step, onChange = null) {
|
|
45
|
+
const item = gui.add(object, property, min, max, step).listen();
|
|
46
|
+
item.onChange(() => {
|
|
47
|
+
if (onChange) onChange();
|
|
48
|
+
if (typeof object.makeDirty === 'function') object.makeDirty();
|
|
49
|
+
});
|
|
50
|
+
return item;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export default class HttpHelper {
|
|
2
|
+
|
|
3
|
+
static async post(url, body) {
|
|
4
|
+
return fetch(url, {
|
|
5
|
+
method: 'POST', // *GET, POST, PUT, DELETE, etc.
|
|
6
|
+
mode: 'cors', // no-cors, *cors, same-origin
|
|
7
|
+
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
|
|
8
|
+
credentials: 'same-origin', // include, *same-origin, omit
|
|
9
|
+
headers: {
|
|
10
|
+
'Content-Type': 'application/json'
|
|
11
|
+
},
|
|
12
|
+
redirect: 'follow', // manual, *follow, error
|
|
13
|
+
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-uri
|
|
14
|
+
body: body // body data type must match "Content-Type" header
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param {string} fileName
|
|
21
|
+
* @param {string} data
|
|
22
|
+
*/
|
|
23
|
+
static fakeDownload(fileName, data) {
|
|
24
|
+
const element = document.createElement('a');
|
|
25
|
+
element.style.display = 'none';
|
|
26
|
+
element.setAttribute('href', 'data:text/json;charset=utf-8,' + encodeURIComponent(data));
|
|
27
|
+
element.setAttribute('download', 'resources.json');
|
|
28
|
+
document.body.appendChild(element);
|
|
29
|
+
element.click();
|
|
30
|
+
document.body.removeChild(element);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import NumberHelper from "./NumberHelper";
|
|
2
|
+
import Vector2 from "../model/vector/Vector2";
|
|
3
|
+
|
|
4
|
+
export default class ImageHelper {
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param {Vector2} size
|
|
9
|
+
* @param {Vector2} viewBoxSize
|
|
10
|
+
* @param {Number} zoom
|
|
11
|
+
* @param {Number} min
|
|
12
|
+
* @param {Number} max
|
|
13
|
+
* @returns {number}
|
|
14
|
+
*/
|
|
15
|
+
static sanitizeZoom(size, viewBoxSize, zoom = 0.0, min = 0, max = Number.MAX_VALUE) {
|
|
16
|
+
if (size.size() === 0) return 1;
|
|
17
|
+
|
|
18
|
+
const minZoomX = viewBoxSize.x / size.x;
|
|
19
|
+
const minZoomY = viewBoxSize.y / size.y;
|
|
20
|
+
|
|
21
|
+
min = Math.max(min, minZoomX, minZoomY);
|
|
22
|
+
return NumberHelper.between(min, max, zoom);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static sanitizeCenter(size, viewBoxSize, zoom, coordinates) {
|
|
26
|
+
const result = new Vector2(coordinates);
|
|
27
|
+
const vbs = viewBoxSize.multiply((1 / zoom) * 0.5);
|
|
28
|
+
result.x = Math.max(vbs.x, result.x);
|
|
29
|
+
result.x = Math.min(size.x - vbs.x, result.x);
|
|
30
|
+
result.y = Math.max(vbs.y, result.y);
|
|
31
|
+
result.y = Math.min(size.y - vbs.y, result.y);
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export default class NumberHelper {
|
|
2
|
+
|
|
3
|
+
static round(number, decimals) {
|
|
4
|
+
const d = Math.pow(10, decimals);
|
|
5
|
+
return Math.round(number * d) / d;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
static random(min, max) {
|
|
9
|
+
const diff = max - min;
|
|
10
|
+
return min + (diff * Math.random());
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
Returns n if n is between min and max. Return min if n lower than min or return max if n is greater than max.
|
|
15
|
+
*/
|
|
16
|
+
static between(min, max, n) {
|
|
17
|
+
const minimum = Math.min(min, max);
|
|
18
|
+
const maximum = Math.max(min, max);
|
|
19
|
+
return Math.min(maximum, Math.max(minimum, n));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static isBetween(n, min, max) {
|
|
23
|
+
const minimum = Math.min(min, max);
|
|
24
|
+
const maximum = Math.max(min, max);
|
|
25
|
+
return n >= minimum && n <= maximum;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export default class StringHelper {
|
|
2
|
+
|
|
3
|
+
static shorten(text, length = 25, ellipsis = '...') {
|
|
4
|
+
if (text === null || text === undefined || typeof text !== 'string') {
|
|
5
|
+
return text;
|
|
6
|
+
}
|
|
7
|
+
if (text.length <= length) {
|
|
8
|
+
return text;
|
|
9
|
+
}
|
|
10
|
+
let short = text.substring(0, length - ellipsis.length);
|
|
11
|
+
short += ellipsis;
|
|
12
|
+
return short;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static hash(value) {
|
|
16
|
+
let hash = 0;
|
|
17
|
+
if (value.length === 0) return hash;
|
|
18
|
+
for (let i = 0; i < value.length; i++) {
|
|
19
|
+
let ch = value.charCodeAt(i);
|
|
20
|
+
hash = ((hash << 5) - hash) + ch;
|
|
21
|
+
hash = hash & hash;
|
|
22
|
+
}
|
|
23
|
+
return hash;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static token(value) {
|
|
27
|
+
let hash = StringHelper.hash(value);
|
|
28
|
+
if (hash === 0) return null;
|
|
29
|
+
let token = 'a';
|
|
30
|
+
if (hash < 0) {
|
|
31
|
+
token = 'b';
|
|
32
|
+
hash = -hash;
|
|
33
|
+
}
|
|
34
|
+
return token + hash;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static paragraphize(text) {
|
|
38
|
+
if ((!text) || typeof text !== 'string') return '';
|
|
39
|
+
const arr = text.split(/(\<br\>|\<br\/\>|\n)/);
|
|
40
|
+
const strings = arr.filter((t) => StringHelper.stripHtmlTags(t.trim()).length > 0);
|
|
41
|
+
return strings.reduce((prev, current) => prev.concat(`<p>${current}</p>`), '');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static extractWord(str, pos = 0, sep = ' ') {
|
|
45
|
+
const words = str.split(sep);
|
|
46
|
+
if (words.length > pos) return words[pos];
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static extractId(str, pos = 1) {
|
|
51
|
+
return StringHelper.extractWord(str, pos, '/');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static stripHtmlTags(text) {
|
|
55
|
+
if ((!text) || typeof text !== 'string') return '';
|
|
56
|
+
return text.replace(/(<([^>]+)>)/gi, '');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import NodeWithEvents from "./event/NodeWithEvents";
|
|
2
|
+
|
|
3
|
+
export default class ModelBase extends NodeWithEvents {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @type boolean
|
|
7
|
+
*/
|
|
8
|
+
isDirty;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @type boolean
|
|
12
|
+
*/
|
|
13
|
+
isPersistent;
|
|
14
|
+
|
|
15
|
+
constructor(persistent = true) {
|
|
16
|
+
super();
|
|
17
|
+
this.isPersistent = persistent;
|
|
18
|
+
this.isDirty = true;
|
|
19
|
+
|
|
20
|
+
this.propertyValueDirtyHandler = () => this.makeDirty();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
restoreState(state) {
|
|
24
|
+
this.restoreStateInternal(state);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Override with node specific code
|
|
29
|
+
* @param {object} state
|
|
30
|
+
*/
|
|
31
|
+
restoreStateInternal(state) {
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getState() {
|
|
36
|
+
if (!this.isPersistent) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return this.getStateInternal();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Override with node specific state
|
|
45
|
+
* @returns {any}
|
|
46
|
+
*/
|
|
47
|
+
getStateInternal() {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
makeDirty() {
|
|
52
|
+
if (!this.isDirty) {
|
|
53
|
+
this.isDirty = true;
|
|
54
|
+
this.triggerEvent('dirty');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
clean() {
|
|
59
|
+
if (!this.isDirty) return false;
|
|
60
|
+
this.isDirty = false;
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
clone() {
|
|
65
|
+
const n = new this.constructor();
|
|
66
|
+
n.restoreState(this.getState());
|
|
67
|
+
return n;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
addOnDirtyListener(handler) {
|
|
71
|
+
this.addEventListener('dirty', handler);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
removeOnDirtyListener(handler) {
|
|
75
|
+
this.removeEventListener('dirty', handler);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
subscribeToOnDirtyEvent(node) {
|
|
79
|
+
node.addOnDirtyListener(this.propertyValueDirtyHandler);
|
|
80
|
+
return node;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
unsubscribeFromOnDirtyEvent(node) {
|
|
84
|
+
node.removeOnDirtyListener(this.propertyValueDirtyHandler);
|
|
85
|
+
return node;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
equalsTo(ch) {
|
|
89
|
+
return this === ch;
|
|
90
|
+
}
|
|
91
|
+
}
|