lexgui 0.7.15 → 8.1.0

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 (134) hide show
  1. package/LICENSE +201 -21
  2. package/README.md +14 -5
  3. package/build/components/AlertDialog.d.ts +7 -0
  4. package/build/components/ArrayInput.d.ts +9 -0
  5. package/build/components/BaseComponent.d.ts +73 -0
  6. package/build/components/Button.d.ts +14 -0
  7. package/build/components/Calendar.d.ts +41 -0
  8. package/build/components/CalendarRange.d.ts +16 -0
  9. package/build/components/CanvasCurve.d.ts +10 -0
  10. package/build/components/CanvasDial.d.ts +11 -0
  11. package/build/components/CanvasMap2D.d.ts +61 -0
  12. package/build/components/Card.d.ts +8 -0
  13. package/build/components/Checkbox.d.ts +8 -0
  14. package/build/components/Color.d.ts +20 -0
  15. package/build/components/ColorInput.d.ts +13 -0
  16. package/build/components/ColorPicker.d.ts +29 -0
  17. package/build/components/ComboButtons.d.ts +8 -0
  18. package/build/components/ContextMenu.d.ts +16 -0
  19. package/build/components/Counter.d.ts +9 -0
  20. package/build/components/Curve.d.ts +10 -0
  21. package/build/components/DatePicker.d.ts +13 -0
  22. package/build/components/Dial.d.ts +10 -0
  23. package/build/components/Dialog.d.ts +20 -0
  24. package/build/components/DropdownMenu.d.ts +32 -0
  25. package/build/components/FileInput.d.ts +8 -0
  26. package/build/components/Footer.d.ts +14 -0
  27. package/build/components/Form.d.ts +8 -0
  28. package/build/components/Layers.d.ts +9 -0
  29. package/build/components/List.d.ts +9 -0
  30. package/build/components/Map2D.d.ts +12 -0
  31. package/build/components/Menubar.d.ts +59 -0
  32. package/build/components/NodeTree.d.ts +26 -0
  33. package/build/components/NumberInput.d.ts +9 -0
  34. package/build/components/OTPInput.d.ts +8 -0
  35. package/build/components/Pad.d.ts +8 -0
  36. package/build/components/Pagination.d.ts +26 -0
  37. package/build/components/PocketDialog.d.ts +11 -0
  38. package/build/components/Popover.d.ts +20 -0
  39. package/build/components/Progress.d.ts +8 -0
  40. package/build/components/RadioGroup.d.ts +8 -0
  41. package/build/components/RangeInput.d.ts +11 -0
  42. package/build/components/Rate.d.ts +8 -0
  43. package/build/components/Select.d.ts +10 -0
  44. package/build/components/Sheet.d.ts +10 -0
  45. package/build/components/Sidebar.d.ts +84 -0
  46. package/build/components/SizeInput.d.ts +8 -0
  47. package/build/components/Skeleton.d.ts +5 -0
  48. package/build/components/Spinner.d.ts +9 -0
  49. package/build/components/TabSections.d.ts +11 -0
  50. package/build/components/Table.d.ts +34 -0
  51. package/build/components/Tabs.d.ts +20 -0
  52. package/build/components/Tags.d.ts +9 -0
  53. package/build/components/TextArea.d.ts +8 -0
  54. package/build/components/TextInput.d.ts +11 -0
  55. package/build/components/Title.d.ts +8 -0
  56. package/build/components/Toggle.d.ts +8 -0
  57. package/build/components/Tour.d.ts +36 -0
  58. package/build/components/Vector.d.ts +9 -0
  59. package/build/core/Area.d.ts +143 -0
  60. package/build/core/Branch.d.ts +19 -0
  61. package/build/core/Core.d.ts +1 -0
  62. package/build/core/Event.d.ts +26 -0
  63. package/build/core/Icons.d.ts +4 -0
  64. package/build/core/Namespace.d.ts +2 -0
  65. package/build/core/Namespace.js +34 -0
  66. package/build/core/Namespace.js.map +1 -0
  67. package/build/core/Panel.d.ts +538 -0
  68. package/build/core/Utils.d.ts +1 -0
  69. package/build/core/Vec2.d.ts +21 -0
  70. package/build/extensions/AssetView.d.ts +136 -0
  71. package/build/extensions/AssetView.js +1367 -0
  72. package/build/extensions/AssetView.js.map +1 -0
  73. package/build/extensions/Audio.d.ts +9 -0
  74. package/build/extensions/Audio.js +163 -0
  75. package/build/extensions/Audio.js.map +1 -0
  76. package/build/extensions/CodeEditor.d.ts +350 -0
  77. package/build/extensions/CodeEditor.js +5022 -0
  78. package/build/extensions/CodeEditor.js.map +1 -0
  79. package/build/extensions/DocMaker.d.ts +27 -0
  80. package/build/extensions/DocMaker.js +327 -0
  81. package/build/extensions/DocMaker.js.map +1 -0
  82. package/build/extensions/GraphEditor.d.ts +276 -0
  83. package/build/extensions/GraphEditor.js +2770 -0
  84. package/build/extensions/GraphEditor.js.map +1 -0
  85. package/build/extensions/ImUi.d.ts +46 -0
  86. package/build/extensions/ImUi.js +227 -0
  87. package/build/extensions/ImUi.js.map +1 -0
  88. package/build/extensions/Timeline.d.ts +670 -0
  89. package/build/extensions/Timeline.js +3955 -0
  90. package/build/extensions/Timeline.js.map +1 -0
  91. package/build/extensions/VideoEditor.d.ts +128 -0
  92. package/build/extensions/VideoEditor.js +898 -0
  93. package/build/extensions/VideoEditor.js.map +1 -0
  94. package/build/extensions/index.d.ts +8 -0
  95. package/build/extensions/index.js +10 -0
  96. package/build/extensions/index.js.map +1 -0
  97. package/build/index.all.d.ts +2 -0
  98. package/build/index.css.d.ts +4 -0
  99. package/build/index.d.ts +56 -0
  100. package/build/lexgui.all.js +28498 -0
  101. package/build/lexgui.all.js.map +1 -0
  102. package/build/lexgui.all.min.js +1 -0
  103. package/build/lexgui.all.module.js +28422 -0
  104. package/build/lexgui.all.module.js.map +1 -0
  105. package/build/lexgui.all.module.min.js +1 -0
  106. package/build/lexgui.css +939 -346
  107. package/build/lexgui.js +13406 -17286
  108. package/build/lexgui.js.map +1 -0
  109. package/build/lexgui.min.css +3 -10
  110. package/build/lexgui.min.js +1 -1
  111. package/build/lexgui.module.js +12762 -16698
  112. package/build/lexgui.module.js.map +1 -0
  113. package/build/lexgui.module.min.js +1 -1
  114. package/changelog.md +170 -74
  115. package/demo.js +162 -48
  116. package/examples/all-components.html +45 -14
  117. package/examples/asset-view.html +110 -47
  118. package/examples/code-editor.html +5 -5
  119. package/examples/dialogs.html +3 -3
  120. package/examples/editor.html +27 -13
  121. package/examples/index.html +19 -14
  122. package/examples/node-graph.html +2 -2
  123. package/examples/previews/video-editor.png +0 -0
  124. package/examples/timeline.html +1 -1
  125. package/examples/video-editor.html +2 -2
  126. package/package.json +25 -9
  127. package/build/extensions/audio.js +0 -212
  128. package/build/extensions/codeeditor.js +0 -6319
  129. package/build/extensions/docmaker.js +0 -432
  130. package/build/extensions/imui.js +0 -325
  131. package/build/extensions/nodegraph.js +0 -3696
  132. package/build/extensions/timeline.js +0 -4636
  133. package/build/extensions/videoeditor.js +0 -953
  134. package/build/lexgui-docs.css +0 -352
@@ -1,953 +0,0 @@
1
- import { LX } from 'lexgui';
2
-
3
- if(!LX) {
4
- throw("lexgui.js missing!");
5
- }
6
-
7
- LX.extensions.push( 'TimeBar' );
8
- LX.extensions.push( 'VideoEditor' );
9
-
10
- /**
11
- * @class TimeBar
12
- */
13
-
14
- class TimeBar {
15
-
16
- static TIMEBAR_PLAY = 1;
17
- static TIMEBAR_TRIM = 2;
18
-
19
- static BACKGROUND_COLOR = LX.getThemeColor("global-branch-darker");
20
- static COLOR = LX.getThemeColor("global-button-color");
21
- static ACTIVE_COLOR = "#668ee4";
22
-
23
- constructor( area, type, options = {} ) {
24
-
25
- this.type = type;
26
- this.duration = options.duration || 1.0;
27
-
28
- // Create canvas
29
- this.canvas = document.createElement( 'canvas' );
30
- this.canvas.width = area.size[0];
31
- this.canvas.height = area.size[1];
32
- area.attach( this.canvas );
33
-
34
- this.ctx = this.canvas.getContext("2d");
35
-
36
- this.markerWidth = options.markerWidth ?? 8;
37
- this.markerHeight = options.markerHeight ?? (this.canvas.height * 0.5);
38
- this.offset = options.offset || (this.markerWidth*0.5 + 5);
39
-
40
- // dimensions of line (not canvas)
41
- this.lineWidth = this.canvas.width - this.offset * 2;
42
- this.lineHeight = options.barHeight ?? 5;
43
-
44
- this.position = new LX.vec2( this.offset, this.canvas.height * 0.5 - this.lineHeight * 0.5);
45
- this.startX = this.position.x;
46
- this.endX = this.position.x + this.lineWidth;
47
- this.currentX = this.startX;
48
-
49
- this._draw();
50
-
51
- this.updateTheme();
52
- LX.addSignal( "@on_new_color_scheme", (el, value) => {
53
- // Retrieve again the color using LX.getThemeColor, which checks the applied theme
54
- this.updateTheme();
55
- } )
56
-
57
- this.canvas.onmousedown = (e) => this.onMouseDown(e);
58
- this.canvas.onmousemove = (e) => this.onMouseMove(e);
59
- this.canvas.onmouseup = (e) => this.onMouseUp(e);
60
- }
61
-
62
- updateTheme( ) {
63
- TimeBar.BACKGROUND_COLOR = LX.getThemeColor("global-color-secondary");
64
- TimeBar.COLOR = LX.getThemeColor("global-color-quaternary");
65
- TimeBar.ACTIVE_COLOR = "#668ee4";
66
- }
67
-
68
- setDuration( duration ) {
69
- this.duration = duration;
70
- }
71
-
72
- xToTime( x ) {
73
- return ((x - this.offset) / (this.lineWidth)) * this.duration;
74
- }
75
-
76
- timeToX( time ) {
77
- return (time / this.duration) * (this.lineWidth) + this.offset;
78
- }
79
-
80
- setCurrentTime( time ) {
81
- this.currentX = this.timeToX( time );
82
- this.onSetCurrentValue( this.currentX );
83
- }
84
-
85
- setStartTime( time ) {
86
- this.startX = this.timeToX( time );
87
- this.onSetStartValue( this.startX )
88
- }
89
-
90
- setEndTime( time ) {
91
- this.endX = this.timeToX( time );
92
- this.onSetEndValue( this.endX );
93
- }
94
-
95
- onSetCurrentValue( x ) {
96
- this.update( x );
97
-
98
- const t = this.xToTime( x );
99
- if( this.onChangeCurrent ) {
100
- this.onChangeCurrent( t );
101
- }
102
- }
103
-
104
- onSetStartValue( x ) {
105
- this.update( x );
106
-
107
- const t = this.xToTime( x );
108
- if( this.onChangeStart ) {
109
- this.onChangeStart( t );
110
- }
111
- }
112
-
113
- onSetEndValue( x ) {
114
- this.update( x );
115
-
116
- const t = this.xToTime( x );
117
- if( this.onChangeEnd ) {
118
- this.onChangeEnd( t );
119
- }
120
- }
121
-
122
- _draw( ) {
123
- const ctx = this.ctx;
124
-
125
- ctx.save();
126
- ctx.fillStyle = TimeBar.BACKGROUND_COLOR;
127
- ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
128
- ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
129
-
130
- // Draw background timeline
131
- ctx.fillStyle = TimeBar.COLOR;
132
- ctx.fillRect(this.position.x, this.position.y, this.lineWidth, this.lineHeight);
133
-
134
- // Draw background trimed timeline
135
- ctx.fillStyle = TimeBar.ACTIVE_COLOR;
136
- ctx.fillRect(this.startX, this.position.y, this.endX - this.startX, this.lineHeight);
137
-
138
- ctx.restore();
139
-
140
- // Min-Max time markers
141
- this._drawTrimMarker('start', this.startX, { color: null, fillColor: TimeBar.ACTIVE_COLOR || '#5f88c9'});
142
- this._drawTrimMarker('end', this.endX, { color: null, fillColor: TimeBar.ACTIVE_COLOR || '#5f88c9'});
143
- this._drawTimeMarker('current', this.currentX, { color: '#e5e5e5', fillColor: TimeBar.ACTIVE_COLOR || '#5f88c9', width: this.markerWidth });
144
-
145
- if( this.onDraw ) {
146
- this.onDraw();
147
- }
148
- }
149
-
150
- _drawTrimMarker(name, x, options) {
151
-
152
- options = options || {};
153
-
154
- const w = this.markerWidth;
155
- const h = this.markerHeight;
156
- const y = this.canvas.height * 0.5 - h * 0.5;
157
-
158
- const ctx = this.ctx;
159
- if(this.hovering == name) {
160
- // Shadow
161
- ctx.shadowColor = "white";
162
- ctx.shadowBlur = 2;
163
- }
164
- ctx.globalAlpha = 1;
165
- ctx.fillStyle = ctx.strokeStyle = options.fillColor || '#111' // "#FFF";
166
-
167
- ctx.beginPath();
168
- ctx.roundRect(x - w * 0.5, y, w, h, 2);
169
- ctx.fill();
170
- ctx.fillStyle = ctx.strokeStyle = options.fillColor || '#111' // "#FFF";
171
-
172
- ctx.strokeStyle = "white";
173
- ctx.beginPath();
174
- ctx.lineWitdh = 2;
175
- ctx.moveTo(x, y + 4);
176
- ctx.lineTo(x, y + h - 4);
177
- ctx.stroke();
178
- ctx.shadowBlur = 0;
179
-
180
- }
181
-
182
- _drawTimeMarker(name, x, options) {
183
-
184
- options = options || {};
185
-
186
- let y = this.offset;
187
- const w = options.width ? options.width : (this.dragging == name ? 6 : 4);
188
- const h = this.canvas.height - this.offset * 2;
189
-
190
- let ctx = this.ctx;
191
-
192
- ctx.globalAlpha = 1;
193
-
194
- ctx.fillStyle = ctx.strokeStyle = options.fillColor || '#111' // "#FFF";
195
-
196
-
197
- if(this.hovering == name) {
198
- // Shadow
199
- ctx.shadowColor = "white";
200
- ctx.shadowBlur = 2;
201
- }
202
-
203
- // Current time line
204
- ctx.fillStyle = ctx.strokeStyle = "white";
205
- ctx.beginPath();
206
- ctx.moveTo(x, y);
207
- ctx.lineTo(x, y + h * 0.5);
208
- ctx.stroke();
209
- ctx.closePath();
210
- ctx.fillStyle = ctx.strokeStyle = options.fillColor || '#111' // "#FFF";
211
-
212
-
213
- y -= this.offset + 8;
214
- // Current time ball grab
215
- ctx.fillStyle = options.fillColor || '#e5e5e5';
216
- ctx.beginPath();
217
- ctx.roundRect(x - w * 0.5, y + this.offset, w, w, 5);
218
-
219
- ctx.fill();
220
- ctx.shadowBlur = 0;
221
- }
222
-
223
- update( x ) {
224
- this.currentX = Math.min(Math.max(this.startX, x), this.endX);
225
- this._draw();
226
- }
227
-
228
- onMouseDown( e ) {
229
-
230
- if( this.onMouse ) {
231
- this.onMouse( e );
232
- }
233
-
234
- e.preventDefault();
235
-
236
- if( !this.canvas || e.target != this.canvas || e.cancelBubble ) {
237
- return;
238
- }
239
- const canvas = this.canvas;
240
-
241
- // Process mouse
242
- const x = e.offsetX;
243
- const y = e.offsetY;
244
-
245
- // Check if some marker is clicked
246
- const threshold = this.markerWidth;
247
-
248
- // grab trim markers only from the bottom
249
- if( Math.abs(this.startX - x) < threshold && this.position.y < y ) {
250
- this.dragging = 'start';
251
- canvas.style.cursor = "grabbing";
252
- }
253
- else if( Math.abs(this.endX - x) < threshold && this.position.y < y ) {
254
- this.dragging = 'end';
255
- canvas.style.cursor = "grabbing";
256
- }
257
- else {
258
- this.dragging = 'current';
259
- canvas.style.cursor = "grabbing";
260
-
261
- if( x < this.startX ) {
262
- this.currentX = this.startX;
263
- }
264
- else if( x > this.endX ) {
265
- this.currentX = this.endX;
266
- }
267
- else {
268
- this.currentX = x;
269
- }
270
-
271
- this.onSetCurrentValue( this.currentX );
272
- }
273
-
274
- this._draw();
275
- }
276
-
277
- onMouseUp( e ) {
278
-
279
- if( this.onMouse ) {
280
- this.onMouse( e );
281
- }
282
-
283
- e.preventDefault();
284
-
285
- this.dragging = false;
286
- this.hovering = false;
287
-
288
- if( !this.canvas || e.cancelBubble ) {
289
- return;
290
- }
291
-
292
- const canvas = this.canvas;
293
- canvas.style.cursor = "default";
294
- }
295
-
296
- onMouseMove( e ) {
297
-
298
- if( this.onMouse ) {
299
- this.onMouse( e );
300
- }
301
-
302
- if( !this.canvas || e.cancelBubble ) {
303
- return;
304
- }
305
-
306
- e.preventDefault();
307
- const canvas = this.canvas;
308
-
309
- // Process mouse
310
- const x = e.target == canvas ? e.offsetX : e.clientX - canvas.offsetLeft;
311
- const y = e.target == canvas ? e.offsetY : e.clientY - canvas.offsetTop;
312
-
313
- if( this.dragging ) {
314
- switch( this.dragging ) {
315
- case 'start':
316
- this.startX = Math.max(this.position.x, Math.min(this.endX, x));
317
- this.currentX = this.startX;
318
- this.onSetStartValue(this.startX);
319
- break;
320
- case 'end':
321
- this.endX = Math.max( this.startX, Math.min(this.position.x + this.lineWidth, x) );
322
- this.currentX = this.endX;
323
-
324
- this.onSetEndValue( this.endX );
325
- break;
326
- default:
327
- this.currentX = Math.max( this.startX, Math.min(this.endX, x) );
328
- break;
329
- }
330
-
331
- this.onSetCurrentValue( this.currentX );
332
- }
333
- else {
334
- const threshold = this.markerWidth * 0.5;
335
-
336
- if( Math.abs(this.startX - x) < threshold ) {
337
- this.hovering = 'start';
338
- canvas.style.cursor = "grab";
339
- }
340
- else if( Math.abs(this.endX - x) < threshold ) {
341
- this.hovering = 'end';
342
- canvas.style.cursor = "grab";
343
- }
344
- else if( Math.abs(this.currentX - x) < threshold ) {
345
- this.hovering = 'current';
346
- canvas.style.cursor = "grab";
347
- }
348
- else {
349
- this.hovering = false;
350
- canvas.style.cursor = "default";
351
- }
352
- }
353
- this._draw();
354
- }
355
-
356
- resize( size ) {
357
- this.canvas.width = size[0];
358
- this.canvas.height = size[1];
359
-
360
- let newWidth = size[0] - this.offset * 2;
361
- newWidth = newWidth < 0.00001 ? 0.00001 : newWidth; // actual width of the line = canvas.width - offsetleft - offsetRight
362
- const startRatio = (this.startX - this.offset) / this.lineWidth;
363
- const currentRatio = (this.currentX - this.offset) / this.lineWidth;
364
- const endRatio = (this.endX - this.offset) / this.lineWidth;
365
-
366
- this.lineWidth = newWidth;
367
- this.startX = Math.min( Math.max(newWidth * startRatio, 0), newWidth ) + this.offset;
368
- this.currentX = Math.min(Math.max(newWidth * currentRatio, 0), newWidth) + this.offset;
369
- this.endX = Math.min( Math.max(newWidth * endRatio, 0 ), newWidth) + this.offset;
370
-
371
- this._draw();
372
- }
373
- }
374
-
375
- LX.TimeBar = TimeBar;
376
-
377
-
378
- /**
379
- * @class VideoEditor
380
- */
381
-
382
- class VideoEditor {
383
-
384
- constructor( area, options = {} ) {
385
-
386
- this.playing = false;
387
- this.requestId = null;
388
- this.videoReady = false;
389
- this.currentTime = this.startTime = 0;
390
- this.startTimeString = "0:0";
391
- this.endTimeString = "0:0";
392
- this._updateTime = true;
393
- this.speed = options.speed || 1;
394
- this.mainArea = area;
395
-
396
- let videoArea = null;
397
- let controlsArea = null;
398
- if( options.controlsArea ) {
399
- videoArea = area;
400
- controlsArea = options.controlsArea;
401
- }
402
- else {
403
- [videoArea, controlsArea] = area.split({ type: 'vertical', sizes: ["85%", null], minimizable: false, resize: false });
404
- }
405
- controlsArea.root.classList.add('lexconstrolsarea');
406
-
407
- this.cropArea = document.createElement("div");
408
- this.cropArea.id = "cropArea";
409
- this.cropArea.className = "resize-area hidden"
410
-
411
- this.brCrop = document.createElement("div");
412
- this.brCrop.className = " resize-handle br"; // bottom right
413
- this.cropArea.append(this.brCrop);
414
-
415
- this.crop = options.crop;
416
- this.dragOffsetX = 0;
417
- this.dragOffsetY = 0;
418
- // Create video element and load it
419
- let video = this.video = options.video ?? document.createElement( 'video' );
420
- this.loop = options.loop ?? false;
421
-
422
- if( options.src ) {
423
- this.video.src = options.src;
424
- this.loadVideo( options );
425
- }
426
-
427
- if( options.videoArea ) {
428
- options.videoArea.root.classList.add("lexvideoeditor");
429
- options.videoArea.attach(this.cropArea);
430
- videoArea.attach(options.videoArea);
431
- }
432
- else {
433
- videoArea.attach(video);
434
- videoArea.attach(this.cropArea);
435
- videoArea.root.classList.add("lexvideoeditor");
436
- }
437
-
438
- this.controlsArea = controlsArea;
439
- // Create playing timeline area and attach panels
440
- let [topArea, bottomArea] = controlsArea.split({ type: 'vertical', sizes:["50%", null], minimizable: false, resize: false });
441
- bottomArea.setSize([bottomArea.size[0], 40]);
442
- let [leftArea, controlsRight] = bottomArea.split({ type: 'horizontal', sizes:["92%", null], minimizable: false, resize: false });
443
- let [controlsLeft, timeBarArea] = leftArea.split({ type: 'horizontal', sizes:["10%", null], minimizable: false, resize: false });
444
-
445
- topArea.root.classList.add('lexbar');
446
- bottomArea.root.classList.add('lexbar');
447
- this.controlsCurrentPanel = new LX.Panel({className: 'lexcontrolspanel lextime'});
448
- this.controlsCurrentPanel.refresh = () => {
449
- this.controlsCurrentPanel.clear();
450
- this.controlsCurrentPanel.addLabel(this.currentTimeString, {float: "center"});
451
- }
452
- topArea.root.classList.add('lexflexarea')
453
- topArea.attach(this.controlsCurrentPanel);
454
- this.controlsCurrentPanel.refresh();
455
-
456
- const style = getComputedStyle(bottomArea.root);
457
- let padding = Number(style.getPropertyValue('padding').replace("px",""));
458
- this.timebar = new TimeBar(timeBarArea, TimeBar.TIMEBAR_TRIM, {offset: padding});
459
-
460
- // Create controls panel (play/pause button and start time)
461
- this.controlsPanelLeft = new LX.Panel({className: 'lexcontrolspanel'});
462
- this.controlsPanelLeft.refresh = () => {
463
- this.controlsPanelLeft.clear();
464
- this.controlsPanelLeft.sameLine();
465
- this.controlsPanelLeft.addButton('', "", (v) => {
466
- this.playing = !this.playing;
467
- if(this.playing) {
468
- if( this.video.currentTime + 0.000001 >= this.endTime) {
469
- this.video.currentTime = this.startTime;
470
- }
471
- this.video.play()
472
- }
473
- else {
474
- this.video.pause();
475
- }
476
- this.controlsPanelLeft.refresh();
477
- }, { width: '40px', icon: (this.playing ? 'Pause@solid' : 'Play@solid'), className: "justify-center"});
478
-
479
- if(this.speedDialog) {
480
- this.speedDialog.close();
481
- }
482
- this.speedDialog = null;
483
- const btn = this.controlsPanelLeft.addButton('', '', (v, e) => {
484
-
485
- // if(this.speedDialog) {
486
- // this.speedDialog.close();
487
- // this.speedDialog = null;
488
- // return;
489
- // }
490
-
491
- const panel = new LX.Panel();
492
- panel.addNumber("Speed", this.speed, (v) => {
493
- this.speed = v;
494
- this.video.playbackRate = v;
495
- if( this.onChangeSpeed ) {
496
- this.onChangeSpeed( v );
497
- }
498
- }, {min: 0, max: 2.5, step: 0.01, nameWidth: "50px"})
499
-
500
- new LX.Popover( e.target, [ panel ], { align: "start", side: "top", sideOffset: 12 } );
501
-
502
- }, { width: '40px', title: 'speed', icon: "Timer@solid", className: "justify-center" } );
503
-
504
- this.controlsPanelLeft.addButton('', 'Loop', (v) => {
505
- this.loop = !this.loop;
506
-
507
- this.controlsPanelLeft.refresh();
508
- }, { width: '40px', title: 'loop', icon: ( 'Repeat@solid'), className: `justify-center`, buttonClass: `${(this.loop ? 'bg-accent' : '')}`});
509
-
510
- this.controlsPanelLeft.addLabel(this.startTimeString, {width: "100px"});
511
- this.controlsPanelLeft.endLine();
512
-
513
- let availableWidth = leftArea.root.clientWidth - controlsLeft.root.clientWidth;
514
- this.timebar.resize([availableWidth, timeBarArea.root.clientHeight]);
515
- }
516
-
517
- this.controlsPanelLeft.refresh();
518
- controlsLeft.root.style.minWidth = 'fit-content';
519
- // controlsLeft.root.classList.add();
520
- controlsLeft.attach(this.controlsPanelLeft);
521
-
522
- // Create right controls panel (ens time)
523
- this.controlsPanelRight = new LX.Panel({className: 'lexcontrolspanel'});
524
- this.controlsPanelRight.refresh = () => {
525
- this.controlsPanelRight.clear();
526
- this.controlsPanelRight.addLabel(this.endTimeString, {width: 100});
527
- }
528
- this.controlsPanelRight.refresh();
529
- controlsRight.root.style.minWidth = 'fit-content';
530
- controlsRight.attach(this.controlsPanelRight);
531
-
532
- this.timebar.onChangeCurrent = this._setCurrentTime.bind(this);
533
- this.timebar.onChangeStart = this._setStartTime.bind(this);
534
- this.timebar.onChangeEnd = this._setEndTime.bind(this);
535
-
536
- window.addEventListener('resize', (v) => {
537
- if(this.onResize) {
538
- this.onResize([videoArea.root.clientWidth, videoArea.root.clientHeight]);
539
- }
540
- bottomArea.setSize([this.controlsArea.root.clientWidth, 40]);
541
- let availableWidth = this.controlsArea.root.clientWidth - controlsLeft.root.clientWidth - controlsRight.root.clientWidth;
542
- this.timebar.resize([availableWidth, timeBarArea.root.clientHeight]);
543
- this.dragCropArea( { clientX: -1, clientY: -1 } );
544
- this.resizeCropArea( { clientX: window.screen.width, clientY: window.screen.height } );
545
-
546
- })
547
-
548
- this.onKeyUp = (event) => {
549
- if( this.controls && event.key == " " ) {
550
- event.preventDefault();
551
- event.stopPropagation();
552
-
553
- this.playing = !this.playing;
554
- if( this.playing ) {
555
- if( this.video.currentTime + 0.000001 >= this.endTime) {
556
- this.video.currentTime = this.startTime;
557
- }
558
- this.video.play();
559
- }
560
- else {
561
- this.video.pause();
562
- }
563
- this.controlsPanelLeft.refresh();
564
- }
565
- }
566
-
567
- window.addEventListener( "keyup", this.onKeyUp);
568
-
569
- videoArea.onresize = (v) => {
570
- if( bottomArea.parentArea ) {
571
- bottomArea.setSize([bottomArea.parentArea.root.clientWidth, 40]);
572
- }
573
-
574
- const ratio = this.video.clientHeight / this.video.videoHeight;
575
- this.cropArea.style.height = this.video.clientHeight + "px";
576
- this.cropArea.style.width = this.video.videoWidth * ratio + "px";
577
- }
578
-
579
-
580
- timeBarArea.onresize = (v) => {
581
- let availableWidth = this.controlsArea.root.clientWidth - controlsLeft.root.clientWidth - controlsRight.root.clientWidth - 20;
582
- this.timebar.resize([availableWidth, v.height]);
583
- }
584
-
585
- const parent = controlsArea.parentElement ? controlsArea.parentElement : controlsArea.root.parentElement;
586
-
587
- // Add canvas event listeneres
588
- parent.addEventListener( "mousedown", (event) => {
589
- // if(this.controls) {
590
- // this.timebar.onMouseDown(event);
591
- // }
592
- });
593
- parent.addEventListener( "mouseup", (event) => {
594
- // if(this.controls) {
595
- // this.timebar.onMouseUp(event);
596
- // }
597
-
598
- if( ( this.isDragging || this.isResizing ) && this.onCropArea ) {
599
- if( this.onCropArea ) {
600
- this.onCropArea( this.getCroppedArea() );
601
- }
602
- }
603
- this.isDragging = false;
604
- this.isResizing = false;
605
-
606
- });
607
- parent.addEventListener( "mousemove", (event) => {
608
- // if(this.controls) {
609
- // this.timebar.onMouseMove(event);
610
- // }
611
-
612
- if ( this.isResizing ) {
613
- this.resizeCropArea( event );
614
- }
615
-
616
- if( this.isDragging ) {
617
- this.dragCropArea( event );
618
- }
619
- });
620
-
621
- this.cropArea.addEventListener( "mousedown", (event) => {
622
-
623
-
624
- if ( event.target === this.cropArea ) {
625
- const rect = this.cropArea.getBoundingClientRect();
626
- this.isDragging = true;
627
-
628
- this.dragOffsetX = event.clientX - rect.left;
629
- this.dragOffsetY = event.clientY - rect.top;
630
- }
631
- });
632
-
633
- document.querySelectorAll(".resize-handle" ).forEach( handle => {
634
-
635
- handle.addEventListener("mousedown", ( e ) => {
636
-
637
- e.stopPropagation();
638
- if (handle.classList[1] === 'br') {
639
- this.isResizing = true;
640
- }
641
- });
642
- });
643
-
644
- this.onChangeStart = null;
645
- this.onChangeEnd = null;
646
- }
647
-
648
- resizeCropArea( event ) {
649
-
650
- const mouseX = event.clientX;
651
- const mouseY = event.clientY;
652
-
653
- const isCropHidden = this.cropArea.classList.contains("hidden");
654
- const nodes = this.cropArea.parentElement.childNodes;
655
-
656
- const rectCrop = this.cropArea.getBoundingClientRect();
657
- const rectVideo = this.video.getBoundingClientRect();
658
- let width = Math.max( 0, Math.min( mouseX - rectCrop.left, rectVideo.width ) );
659
- let height = Math.max( 0, Math.min( mouseY - rectCrop.top, rectVideo.height ) );
660
-
661
- if ( (rectCrop.left + width) > rectVideo.right ){
662
- width = Math.min( rectVideo.width, rectVideo.right - rectCrop.left);
663
- }
664
- if ( (rectCrop.top + height) > rectVideo.bottom ){
665
- height = Math.min( rectVideo.height, rectVideo.bottom - rectCrop.top);
666
- }
667
-
668
- if ( !isCropHidden ){
669
- for( let i = 0; i < nodes.length; i++ ) {
670
- if( nodes[i] != this.cropArea ) {
671
- const rectEl = nodes[i].getBoundingClientRect();
672
- nodes[i].style.webkitMask = `linear-gradient(#000 0 0) ${rectCrop.x - rectEl.left}px ${ rectCrop.y - rectEl.top }px / ${width}px ${height}px, linear-gradient(rgba(0, 0, 0, 0.3) 0 0)`;
673
- nodes[i].style.webkitMaskRepeat = 'no-repeat';
674
- }
675
- }
676
- }
677
-
678
- this.cropArea.style.width = width + "px";
679
- this.cropArea.style.height = height + "px";
680
- }
681
-
682
- dragCropArea( event ) {
683
- const rectVideo = this.video.getBoundingClientRect();
684
- const rectCrop = this.cropArea.getBoundingClientRect();
685
-
686
- let x = event.clientX - this.dragOffsetX;
687
- let y = event.clientY - this.dragOffsetY;
688
-
689
- if( x < rectVideo.left ) {
690
- x = rectVideo.left;
691
- }
692
-
693
- if( x + rectCrop.width > rectVideo.right ) {
694
- x = Math.max( rectVideo.left, rectVideo.right - rectCrop.width);
695
- }
696
-
697
- if( y < rectVideo.top ) {
698
- y = rectVideo.top;
699
- }
700
-
701
- if( y + rectCrop.height > rectVideo.bottom ) {
702
- y = Math.max( rectVideo.top, rectVideo.bottom - rectCrop.height );
703
- }
704
-
705
- if ( !this.cropArea.classList.contains("hidden") ){
706
- const nodes = this.cropArea.parentElement.childNodes;
707
- for( let i = 0; i < nodes.length; i++ ) {
708
- if( nodes[i] != this.cropArea ) {
709
- const rectEl = nodes[i].getBoundingClientRect();
710
- nodes[i].style.webkitMask = `linear-gradient(#000 0 0) ${x - rectEl.left}px ${y - rectEl.top}px / ${rectCrop.width}px ${rectCrop.height}px, linear-gradient(rgba(0, 0, 0, 0.3) 0 0)`;
711
- nodes[i].style.webkitMaskRepeat = 'no-repeat';
712
- }
713
- }
714
- }
715
-
716
- const parentRect = this.cropArea.parentElement.getBoundingClientRect();
717
- this.cropArea.style.left = x - parentRect.left + "px";
718
- this.cropArea.style.top = y - parentRect.top + "px";
719
-
720
- }
721
-
722
- async loadVideo( options = {} ) {
723
- this.videoReady = false;
724
- while( this.video.duration === Infinity || isNaN(this.video.duration) || !this.timebar ) {
725
- await new Promise(r => setTimeout(r, 1000));
726
- this.video.currentTime = 10000000 * Math.random();
727
- }
728
- this.video.currentTime = 0.01; // BUG: some videos will not play unless this line is present
729
-
730
- // Duration can change if the video is dynamic (stream). This function is to ensure to load all buffer data
731
- const forceLoadChunks = () => {
732
- const state = this.videoReady;
733
- if( this.video.readyState > 3 ) {
734
- this.videoReady = true;
735
- }
736
- if( !state ) {
737
- this.video.currentTime = this.video.duration;
738
- }
739
- }
740
-
741
- this.video.addEventListener( "canplaythrough", forceLoadChunks, { passive: true } );
742
-
743
- this.video.ondurationchange = ( v ) => {
744
- if( this.video.duration != this.endTime ) {
745
-
746
- this.video.currentTime = this.startTime;
747
- console.log("duration changed from", this.endTime, " to ", this.video.duration);
748
- this.endTime = this.video.duration;
749
- this.timebar.setDuration( this.endTime );
750
- this.timebar.setEndTime( this.endTime );
751
- }
752
- this.video.currentTime = this.startTime;
753
- this.timebar.setCurrentTime( this.video.currentTime );
754
-
755
- }
756
-
757
- this.timebar.startX = this.timebar.position.x;
758
- this.timebar.endX = this.timebar.position.x + this.timebar.lineWidth;
759
- this.startTime = 0;
760
- this.endTime = this.video.duration;
761
- this.timebar.setDuration( this.endTime );
762
- this.timebar.setEndTime( this.video.duration );
763
- this.timebar.setStartTime( this.startTime );
764
- this.timebar.setCurrentTime( this.startTime );
765
- //this.timebar.setStartValue(this.timebar.startX);
766
- //this.timebar.currentX = this._timeToX(this.video.currentTime);
767
- //this.timebar.setCurrentValue(this.timebar.currentX);
768
- //this.timebar.update( this.timebar.currentX );
769
-
770
- if ( !this.requestId ){ // only have one update on flight
771
- this._update();
772
- }
773
- this.controls = options.controls ?? true;
774
-
775
- if ( !this.controls ) {
776
- this.hideControls();
777
- }
778
-
779
- this.cropArea.style.height = this.video.clientHeight + "px";
780
- this.cropArea.style.width = this.video.clientWidth + "px";
781
- this.resizeCropArea( { clientX: window.screen.width, clientY: window.screen.height } );
782
- this.dragCropArea( { clientX: -1, clientY: -1 } );
783
-
784
- if( this.crop ) {
785
- this.showCropArea();
786
- }
787
- else {
788
- this.hideCropArea();
789
- }
790
-
791
- window.addEventListener( "keyup", this.onKeyUp);
792
-
793
- if( this.onVideoLoaded ) {
794
- this.onVideoLoaded( this.video );
795
- }
796
- }
797
-
798
- _update () {
799
-
800
- // if( this.onDraw ) {
801
- // this.onDraw();
802
- // }
803
- if( this.playing ) {
804
- if( this.video.currentTime + 0.000001 >= this.endTime ) {
805
- this.video.pause();
806
- if( !this.loop ) {
807
- this.playing = false;
808
- this.controlsPanelLeft.refresh();
809
- }
810
- else {
811
- this.video.currentTime = this.startTime;
812
- this.video.play();
813
- }
814
- }
815
- this._updateTime = false;
816
- this.timebar.setCurrentTime( this.video.currentTime );
817
- this._updateTime = true;
818
- }
819
-
820
- this.requestId = requestAnimationFrame( this._update.bind(this) );
821
- }
822
-
823
- timeToString( t ) {
824
- let mzminutes = Math.floor(t / 60);
825
- let mzseconds = Math.floor(t - (mzminutes * 60));
826
- let mzmiliseconds = Math.floor((t - mzseconds)*100);
827
-
828
- mzmiliseconds = mzmiliseconds < 10 ? ('0' + mzmiliseconds) : mzmiliseconds;
829
- mzseconds = mzseconds < 10 ? ('0' + mzseconds) : mzseconds;
830
- mzminutes = mzminutes < 10 ? ('0' + mzminutes) : mzminutes;
831
- return mzminutes + ':' + mzseconds + '.' + mzmiliseconds;
832
- }
833
-
834
- _setCurrentTime( t ) {
835
-
836
- if( this.video.currentTime != t && this._updateTime ) {
837
- this.video.currentTime = t;
838
- }
839
-
840
- this.currentTimeString = this.timeToString( t );
841
- this.controlsCurrentPanel.refresh();
842
-
843
- if( this.onSetTime ) {
844
- this.onSetTime(t);
845
- }
846
- if( this.onChangeCurrent ) {
847
- this.onChangeCurrent( t );
848
- }
849
- }
850
-
851
- _setStartTime( t ) {
852
- this.startTime = this.video.currentTime = t;
853
-
854
- this.startTimeString = this.timeToString( t );
855
- this.controlsPanelLeft.refresh();
856
-
857
- if( this.onSetTime ) {
858
- this.onSetTime( t );
859
- }
860
- if( this.onChangeStart ) {
861
- this.onChangeStart ( t );
862
- }
863
- }
864
-
865
- _setEndTime( t ) {
866
- this.endTime = this.video.currentTime = t;
867
-
868
- this.endTimeString = this.timeToString( t)
869
- this.controlsPanelRight.refresh();
870
-
871
- if( this.onSetTime ) {
872
- this.onSetTime(t);
873
- }
874
- if( this.onChangeEnd ) {
875
- this.onChangeEnd( t );
876
- }
877
- }
878
-
879
- getStartTime ( ) {
880
- return this.startTime;
881
- }
882
-
883
- getEndTime ( ) {
884
- return this.endTime;
885
- }
886
-
887
- getTrimedTimes ( ) {
888
- return {start: this.startTime, end: this.endTime};
889
- }
890
-
891
- getCroppedArea ( ) {
892
- return this.cropArea.getBoundingClientRect();
893
- }
894
-
895
- showCropArea ( ) {
896
- this.cropArea.classList.remove("hidden");
897
-
898
- const nodes = this.cropArea.parentElement.childNodes;
899
- const rect = this.cropArea.getBoundingClientRect();
900
- for( let i = 0; i < nodes.length; i++ ) {
901
- if( nodes[i] != this.cropArea ) {
902
- const rectEl = nodes[i].getBoundingClientRect();
903
- nodes[i].style.webkitMask = `linear-gradient(#000 0 0) ${rect.left - rectEl.left}px ${rect.top - rectEl.top}px / ${rect.width}px ${rect.height}px, linear-gradient(rgba(0, 0, 0, 0.3) 0 0)`;
904
- nodes[i].style.webkitMaskRepeat = 'no-repeat';
905
- }
906
- }
907
- }
908
-
909
- hideCropArea ( ) {
910
- this.cropArea.classList.add("hidden");
911
-
912
- const nodes = this.cropArea.parentElement.childNodes;
913
- for( let i = 0; i < nodes.length; i++ ) {
914
- if( nodes[i] != this.cropArea ) {
915
- nodes[i].style.webkitMask = "";
916
- nodes[i].style.webkitMaskRepeat = 'no-repeat';
917
- }
918
- }
919
- }
920
-
921
- showControls ( ) {
922
- this.controls = true;
923
- this.controlsArea.show();
924
- }
925
-
926
- hideControls ( ) {
927
- this.controls = false;
928
- this.controlsArea.hide();
929
- }
930
-
931
- stopUpdates(){
932
-
933
- if(this.requestId) {
934
- cancelAnimationFrame(this.requestId);
935
- this.requestId = null;
936
- }
937
- }
938
-
939
- unbind ( ) {
940
- this.stopUpdates();
941
-
942
- this.video.pause();
943
- this.playing = false;
944
- this.controlsPanelLeft.refresh();
945
- this.video.src = "";
946
-
947
- window.removeEventListener("keyup", this.onKeyUp);
948
- }
949
- }
950
-
951
- LX.VideoEditor = VideoEditor;
952
-
953
- export { VideoEditor, TimeBar }