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.
Files changed (107) hide show
  1. package/core/ActivatedTreeNode.js +187 -0
  2. package/core/Collection.js +156 -0
  3. package/core/Dictionary.js +83 -0
  4. package/core/animation/3d/AnimatedVector3.js +35 -0
  5. package/core/animation/3d/AnimationHelper.js +103 -0
  6. package/core/animation/3d/ProgressVector3.js +26 -0
  7. package/core/animation/AnimatedRotation.js +44 -0
  8. package/core/animation/AnimatedValue.js +33 -0
  9. package/core/animation/AnimatedVector2.js +35 -0
  10. package/core/animation/ProgressRotation.js +40 -0
  11. package/core/animation/ProgressValue.js +27 -0
  12. package/core/animation/ProgressVector2.js +23 -0
  13. package/core/audio/AudioModel.js +36 -0
  14. package/core/audio/AudioRenderer.js +120 -0
  15. package/core/audio/global/GlobalAudioModel.js +88 -0
  16. package/core/audio/global/GlobalAudioRenderer.js +108 -0
  17. package/core/controller/AnimationController.js +37 -0
  18. package/core/controller/AnimationFloatController.js +39 -0
  19. package/core/controller/AnimationVector2Controller.js +39 -0
  20. package/core/controller/CollectionController.js +47 -0
  21. package/core/controller/ConditionalNodeController.js +51 -0
  22. package/core/controller/ControllerBase.js +64 -0
  23. package/core/controller/ControllerWithSaveGame.js +27 -0
  24. package/core/controller/ControllerWithTimeout.js +10 -0
  25. package/core/controller/NullableNodeController.js +35 -0
  26. package/core/controller/TimeoutController.js +26 -0
  27. package/core/helper/ArrayHelper.js +19 -0
  28. package/core/helper/DOMHelper.js +167 -0
  29. package/core/helper/GUIHelper.js +53 -0
  30. package/core/helper/HttpHelper.js +33 -0
  31. package/core/helper/ImageHelper.js +36 -0
  32. package/core/helper/NumberHelper.js +28 -0
  33. package/core/helper/ObjectHelper.js +10 -0
  34. package/core/helper/StringHelper.js +59 -0
  35. package/core/model/ModelBase.js +91 -0
  36. package/core/model/ObjectModel.js +97 -0
  37. package/core/model/collection/ModelNodeCollection.js +232 -0
  38. package/core/model/collection/table/IdentifiedModelNode.js +24 -0
  39. package/core/model/collection/table/ModelNodeTable.js +53 -0
  40. package/core/model/event/ChangeEvent.js +24 -0
  41. package/core/model/event/EventManager.js +44 -0
  42. package/core/model/event/NodeWithEvents.js +32 -0
  43. package/core/model/template/TemplateNode.js +45 -0
  44. package/core/model/value/BoolValue.js +17 -0
  45. package/core/model/value/CapacityValue.js +49 -0
  46. package/core/model/value/DirtyValue.js +66 -0
  47. package/core/model/value/FloatValue.js +8 -0
  48. package/core/model/value/IntValue.js +20 -0
  49. package/core/model/value/NullableNode.js +66 -0
  50. package/core/model/value/StringValue.js +21 -0
  51. package/core/model/vector/Quaternion.js +82 -0
  52. package/core/model/vector/Rotation.js +61 -0
  53. package/core/model/vector/Vector2.js +177 -0
  54. package/core/model/vector/Vector3.js +98 -0
  55. package/core/renderer/RendererBase.js +46 -0
  56. package/core/renderer/canvas/CanvasRenderer.js +60 -0
  57. package/core/renderer/canvas/ImageToCanvasRenderer.js +94 -0
  58. package/core/renderer/dom/DirtyValueRenderer.js +36 -0
  59. package/core/renderer/dom/DomRenderer.js +32 -0
  60. package/core/renderer/dom/ImageRenderer.js +41 -0
  61. package/core/renderer/dom/Vector2Renderer.js +37 -0
  62. package/core/renderer/generic/CollectionRenderer.js +50 -0
  63. package/core/renderer/generic/ConditionalNodeRenderer.js +57 -0
  64. package/core/renderer/generic/NullableNodeRenderer.js +43 -0
  65. package/core/renderer/svg/SvgRenderer.js +130 -0
  66. package/editor/EditorController.js +135 -0
  67. package/editor/EditorModel.js +38 -0
  68. package/editor/EditorRenderer.js +116 -0
  69. package/editor/form/NodeFormRenderer.js +180 -0
  70. package/editor/material/MaterialPreviewRenderer.js +136 -0
  71. package/editor/table/TableRenderer.js +123 -0
  72. package/editor/table/lookup/TableLookupRenderer.js +120 -0
  73. package/editor/table/row/TableRowRenderer.js +97 -0
  74. package/game/GameController.js +144 -0
  75. package/game/GameModel.js +73 -0
  76. package/game/GameRenderer.js +108 -0
  77. package/game/controls/ControlsController.js +104 -0
  78. package/game/controls/ControlsModel.js +60 -0
  79. package/game/menu/MenuModel.js +25 -0
  80. package/game/menu/MenuRenderer.js +37 -0
  81. package/game/menu/item/MenuItemModel.js +31 -0
  82. package/game/menu/item/MenuItemRenderer.js +40 -0
  83. package/game/resources/3d/3DModel.js +30 -0
  84. package/game/resources/ResourcesModel.js +26 -0
  85. package/game/resources/assets/AssetCache.js +193 -0
  86. package/game/resources/assets/loader/AssetLoader.js +56 -0
  87. package/game/resources/assets/loader/AudioLoader.js +15 -0
  88. package/game/resources/assets/loader/GlbLoader.js +16 -0
  89. package/game/resources/assets/loader/ImageLoader.js +15 -0
  90. package/game/resources/assets/loader/MaterialLoader.js +49 -0
  91. package/game/resources/assets/loader/Model3dLoader.js +21 -0
  92. package/game/resources/assets/loader/SpriteLoader.js +40 -0
  93. package/game/resources/image/DualImageModel.js +34 -0
  94. package/game/resources/image/ImageModel.js +43 -0
  95. package/game/resources/material/MaterialModel.js +69 -0
  96. package/game/save/SaveGameController.js +21 -0
  97. package/game/save/SaveGameModel.js +5 -0
  98. package/game/save/SaveGameRenderer.js +17 -0
  99. package/game/save/editor/SaveGameEditorRenderer.js +17 -0
  100. package/index.html +42 -0
  101. package/index.js +73 -0
  102. package/package.json +35 -0
  103. package/style/basic/editor.less +290 -0
  104. package/style/basic/game.less +149 -0
  105. package/style/basic/utilities.less +215 -0
  106. package/style/style.less +3 -0
  107. 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,10 @@
1
+ export default class ObjectHelper {
2
+
3
+ static clone(obj) {
4
+ if (obj === null || obj === undefined) {
5
+ return obj;
6
+ }
7
+ return JSON.parse(JSON.stringify(obj));
8
+ }
9
+
10
+ }
@@ -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
+ }