@teachinglab/omd 0.5.3 → 0.5.5
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/canvas/ui/cursor.js +9 -9
- package/canvas/ui/toolbar.js +7 -7
- package/jsvg/jsvg.js +898 -0
- package/jsvg/jsvgComponents.js +359 -0
- package/package.json +4 -2
package/canvas/ui/cursor.js
CHANGED
|
@@ -175,12 +175,12 @@ export class Cursor {
|
|
|
175
175
|
const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
176
176
|
group.setAttribute('data-shape', 'select');
|
|
177
177
|
|
|
178
|
-
//
|
|
178
|
+
// Classic arrow cursor for selection tool
|
|
179
179
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
180
|
-
path.setAttribute('d', 'M 2,2 L 2,
|
|
181
|
-
path.setAttribute('fill', '
|
|
182
|
-
path.setAttribute('stroke', '
|
|
183
|
-
path.setAttribute('stroke-width', '1');
|
|
180
|
+
path.setAttribute('d', 'M 2,2 L 2,16 L 7,11 L 10,18 L 12,17 L 9,10 L 16,10 Z');
|
|
181
|
+
path.setAttribute('fill', 'white');
|
|
182
|
+
path.setAttribute('stroke', '#000000');
|
|
183
|
+
path.setAttribute('stroke-width', '1.2');
|
|
184
184
|
path.setAttribute('stroke-linejoin', 'round');
|
|
185
185
|
|
|
186
186
|
group.appendChild(path);
|
|
@@ -198,10 +198,10 @@ export class Cursor {
|
|
|
198
198
|
|
|
199
199
|
// Standard arrow pointer cursor
|
|
200
200
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
201
|
-
path.setAttribute('d', 'M
|
|
202
|
-
path.setAttribute('fill', '
|
|
203
|
-
path.setAttribute('stroke', '
|
|
204
|
-
path.setAttribute('stroke-width', '1');
|
|
201
|
+
path.setAttribute('d', 'M 2,2 L 2,16 L 7,11 L 10,18 L 12,17 L 9,10 L 16,10 Z');
|
|
202
|
+
path.setAttribute('fill', 'white');
|
|
203
|
+
path.setAttribute('stroke', '#000000');
|
|
204
|
+
path.setAttribute('stroke-width', '1.2');
|
|
205
205
|
path.setAttribute('stroke-linejoin', 'round');
|
|
206
206
|
|
|
207
207
|
group.appendChild(path);
|
package/canvas/ui/toolbar.js
CHANGED
|
@@ -177,7 +177,7 @@ export class Toolbar {
|
|
|
177
177
|
_getToolIconSvg(toolName) {
|
|
178
178
|
const icons = {
|
|
179
179
|
'pointer': `<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
180
|
-
<path d="M2 1L2
|
|
180
|
+
<path d="M2 1L2 12L5.5 8.5L7.5 13L9 12.5L7 8L12 8L2 1Z" fill="white" stroke="black" stroke-width="1" stroke-linejoin="round"/>
|
|
181
181
|
</svg>`,
|
|
182
182
|
'pencil': `<svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
183
183
|
<path d="M13.3658 4.68008C13.7041 4.34179 13.8943 3.88294 13.8943 3.40447C13.8944 2.926 13.7044 2.4671 13.3661 2.12872C13.0278 1.79035 12.5689 1.60022 12.0905 1.60016C11.612 1.6001 11.1531 1.79011 10.8147 2.1284L2.27329 10.6718C2.12469 10.8199 2.0148 11.0023 1.95329 11.203L1.10785 13.9882C1.09131 14.0436 1.09006 14.1024 1.10423 14.1584C1.11841 14.2144 1.14748 14.2655 1.18836 14.3063C1.22924 14.3471 1.28041 14.3761 1.33643 14.3902C1.39246 14.4043 1.45125 14.403 1.50657 14.3863L4.29249 13.5415C4.49292 13.4806 4.67532 13.3713 4.82369 13.2234L13.3658 4.68008Z" stroke="black" stroke-width="1.28" stroke-linecap="round" stroke-linejoin="round"/>
|
|
@@ -188,12 +188,12 @@ export class Toolbar {
|
|
|
188
188
|
<path d="M3.07159 6.41772L8.72151 12.0676" stroke="black" stroke-width="1.28" stroke-linecap="round" stroke-linejoin="round"/>
|
|
189
189
|
</svg>`,
|
|
190
190
|
'select': `<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
191
|
-
<rect x="
|
|
192
|
-
<
|
|
193
|
-
<circle cx="
|
|
194
|
-
<circle cx="
|
|
195
|
-
<circle cx="
|
|
196
|
-
<
|
|
191
|
+
<rect x="4" y="4" width="9" height="9" fill="rgba(0,123,255,0.15)" stroke="#007bff" stroke-width="1.2" stroke-dasharray="2,1"/>
|
|
192
|
+
<circle cx="4" cy="4" r="1.2" fill="white" stroke="#007bff" stroke-width="1"/>
|
|
193
|
+
<circle cx="13" cy="4" r="1.2" fill="white" stroke="#007bff" stroke-width="1"/>
|
|
194
|
+
<circle cx="4" cy="13" r="1.2" fill="white" stroke="#007bff" stroke-width="1"/>
|
|
195
|
+
<circle cx="13" cy="13" r="1.2" fill="white" stroke="#007bff" stroke-width="1"/>
|
|
196
|
+
<path d="M1 1L1 6.5L2.5 5L4 8.5L5 8L3.5 4.5L6 4.5L1 1Z" fill="black" stroke="white" stroke-width="0.5" stroke-linejoin="round"/>
|
|
197
197
|
</svg>`
|
|
198
198
|
};
|
|
199
199
|
|
package/jsvg/jsvg.js
ADDED
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
// ================ jsvgObject ================================= //
|
|
4
|
+
|
|
5
|
+
export class jsvgObject
|
|
6
|
+
{
|
|
7
|
+
constructor()
|
|
8
|
+
{
|
|
9
|
+
this.name = "";
|
|
10
|
+
this.xpos = 0;
|
|
11
|
+
this.scale = 1.0;
|
|
12
|
+
this.rotation = 0.0;
|
|
13
|
+
this.ypos = 0;
|
|
14
|
+
this.width = 0;
|
|
15
|
+
this.height = 0;
|
|
16
|
+
this.opacity = 1.0;
|
|
17
|
+
this.visible = true;
|
|
18
|
+
this.svgObject = null;
|
|
19
|
+
this.childList = [];
|
|
20
|
+
this.parent = null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// set properties
|
|
24
|
+
|
|
25
|
+
setName( N )
|
|
26
|
+
{
|
|
27
|
+
this.name = N;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setPosition( x, y )
|
|
31
|
+
{
|
|
32
|
+
this.xpos = x;
|
|
33
|
+
this.ypos = y;
|
|
34
|
+
this.updateTransform();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
setScale( s )
|
|
38
|
+
{
|
|
39
|
+
this.scale = s;
|
|
40
|
+
this.updateTransform();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setRotation( r )
|
|
44
|
+
{
|
|
45
|
+
this.rotation = r;
|
|
46
|
+
this.updateTransform();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
updateTransform()
|
|
50
|
+
{
|
|
51
|
+
// Ensure xpos and ypos are valid numbers
|
|
52
|
+
const x = isNaN(this.xpos) ? 0 : this.xpos;
|
|
53
|
+
const y = isNaN(this.ypos) ? 0 : this.ypos;
|
|
54
|
+
|
|
55
|
+
var transformParams = " translate(" + x.toString() + "," + y.toString() + ") "
|
|
56
|
+
if ( this.scale != 1.0 )
|
|
57
|
+
transformParams += " scale(" + this.scale.toString() + ")";
|
|
58
|
+
if ( this.rotation != 0.0 )
|
|
59
|
+
transformParams += " rotate(" + this.rotation + ")";
|
|
60
|
+
this.svgObject.setAttribute("transform", transformParams );
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
setWidth( w )
|
|
64
|
+
{
|
|
65
|
+
this.width = w;
|
|
66
|
+
this.svgObject.setAttribute("width", this.width.toString() );
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setHeight( h )
|
|
70
|
+
{
|
|
71
|
+
this.height = h;
|
|
72
|
+
this.svgObject.setAttribute("height", this.height.toString() );
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
setWidthAndHeight( w,h )
|
|
76
|
+
{
|
|
77
|
+
this.setWidth( w );
|
|
78
|
+
this.setHeight( h );
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
setFillColor( color )
|
|
82
|
+
{
|
|
83
|
+
this.svgObject.setAttribute( "fill", color );
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setStrokeColor( color )
|
|
87
|
+
{
|
|
88
|
+
this.svgObject.setAttribute( "stroke", color );
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
setStrokeWidth( sW )
|
|
92
|
+
{
|
|
93
|
+
this.svgObject.setAttribute( "stroke-width", sW.toString() );
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
setOpacity( opacity )
|
|
97
|
+
{
|
|
98
|
+
this.opacity = opacity;
|
|
99
|
+
this.svgObject.style.opacity = opacity;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
setDropShadow()
|
|
103
|
+
{
|
|
104
|
+
// this is a generic dropshadow
|
|
105
|
+
this.svgObject.style.filter = "drop-shadow(0px 3px 6px rgba(0, 0, 0, 0.4))";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
show()
|
|
109
|
+
{
|
|
110
|
+
this.visible = true;
|
|
111
|
+
this.svgObject.style.display = "";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
hide()
|
|
115
|
+
{
|
|
116
|
+
this.visible = false;
|
|
117
|
+
this.svgObject.style.display = "none";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// update
|
|
121
|
+
|
|
122
|
+
update()
|
|
123
|
+
{
|
|
124
|
+
if ( ! this.visible )
|
|
125
|
+
return;
|
|
126
|
+
|
|
127
|
+
for( var C of this.childList )
|
|
128
|
+
{
|
|
129
|
+
C.update();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// child stuff
|
|
134
|
+
|
|
135
|
+
addChild( child )
|
|
136
|
+
{
|
|
137
|
+
if ( this.svgObject && child.svgObject )
|
|
138
|
+
{
|
|
139
|
+
this.childList.push( child );
|
|
140
|
+
this.svgObject.appendChild( child.svgObject );
|
|
141
|
+
child.parent = this;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getChild( index )
|
|
146
|
+
{
|
|
147
|
+
return this.childList[index];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
replaceChild( newChild, oldChild )
|
|
151
|
+
{
|
|
152
|
+
// replace the node
|
|
153
|
+
this.svgObject.replaceChild( newChild.svgObject, oldChild.svgObject );
|
|
154
|
+
|
|
155
|
+
// replace in childList
|
|
156
|
+
for ( var i=0; i<this.childList.length; i++ )
|
|
157
|
+
{
|
|
158
|
+
if ( this.childList[i] == oldChild )
|
|
159
|
+
{
|
|
160
|
+
this.childList[i] = newChild;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
oldChild.parent = null;
|
|
166
|
+
newChild.parent = this;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
getNumberOfChildren()
|
|
170
|
+
{
|
|
171
|
+
return this.childList.length;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
removeChild( C )
|
|
175
|
+
{
|
|
176
|
+
const index = this.childList.indexOf(C);
|
|
177
|
+
this.removeChildByIndex( index );
|
|
178
|
+
C.parent = null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
removeChildByIndex( index )
|
|
182
|
+
{
|
|
183
|
+
if (index >= 0)
|
|
184
|
+
{
|
|
185
|
+
var C = this.childList[index];
|
|
186
|
+
this.childList.splice(index, 1);
|
|
187
|
+
this.svgObject.removeChild( C.svgObject );
|
|
188
|
+
C.parent = null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
removeAllChildren()
|
|
193
|
+
{
|
|
194
|
+
while( this.svgObject.firstChild )
|
|
195
|
+
{
|
|
196
|
+
this.svgObject.removeChild( this.svgObject.lastChild );
|
|
197
|
+
}
|
|
198
|
+
this.childList = [];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
sortChildren( sortFunction )
|
|
202
|
+
{
|
|
203
|
+
this.childList.sort( sortFunction );
|
|
204
|
+
|
|
205
|
+
// If the child is a reference to an existing node in the document, appendChild() moves it from its current position to the new positio
|
|
206
|
+
this.childList.forEach(child => this.svgObject.appendChild( child.svgObject ));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// set up events
|
|
210
|
+
setClickCallback( callback )
|
|
211
|
+
{
|
|
212
|
+
if ( this.svgObject )
|
|
213
|
+
{
|
|
214
|
+
var outerThis = this;
|
|
215
|
+
this.svgObject.onclick = function() { callback(outerThis) };
|
|
216
|
+
this.svgObject.style.cursor = "pointer";
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
enableDragging()
|
|
221
|
+
{
|
|
222
|
+
this.svgObject.style.cursor = "pointer";
|
|
223
|
+
|
|
224
|
+
// on mouse down
|
|
225
|
+
var outerThis = this;
|
|
226
|
+
this.svgObject.onmousedown = function(E)
|
|
227
|
+
{
|
|
228
|
+
// console.log("mouse down");
|
|
229
|
+
outerThis.lastX = E.clientX;
|
|
230
|
+
outerThis.lastY = E.clientY;
|
|
231
|
+
|
|
232
|
+
// register on mouse move function (unregister on mouseup)
|
|
233
|
+
window.onmousemove = function(E)
|
|
234
|
+
{
|
|
235
|
+
// console.log("mouse move");
|
|
236
|
+
var dX = E.clientX - outerThis.lastX; // this method works if there's no scaling or rotation
|
|
237
|
+
var dY = E.clientY - outerThis.lastY;
|
|
238
|
+
outerThis.lastX = E.clientX;
|
|
239
|
+
outerThis.lastY = E.clientY;
|
|
240
|
+
outerThis.setPosition( outerThis.xpos+dX, outerThis.ypos+dY );
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// on mouse up
|
|
244
|
+
window.onmouseup = function()
|
|
245
|
+
{
|
|
246
|
+
// console.log("mouse up");
|
|
247
|
+
window.onmousemove = null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
// ================ jsvgContainer ================================= //
|
|
255
|
+
|
|
256
|
+
export class jsvgContainer extends jsvgObject
|
|
257
|
+
{
|
|
258
|
+
constructor()
|
|
259
|
+
{
|
|
260
|
+
super();
|
|
261
|
+
|
|
262
|
+
const svgNS = "http://www.w3.org/2000/svg";
|
|
263
|
+
this.svgObject = document.createElementNS(svgNS, "svg");
|
|
264
|
+
this.svgObject.setAttribute("x", "0");
|
|
265
|
+
this.svgObject.setAttribute("y", "0");
|
|
266
|
+
this.svgObject.setAttribute("width", "500");
|
|
267
|
+
this.svgObject.setAttribute("height", "500");
|
|
268
|
+
this.svgObject.setAttribute("backgroundColor", "blue");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
setViewbox( w, h )
|
|
272
|
+
{
|
|
273
|
+
this.setWidth(w);
|
|
274
|
+
this.setHeight(h);
|
|
275
|
+
var viewBoxParams = "0 0 " + w.toString() + " " + h.toString();
|
|
276
|
+
this.svgObject.setAttribute("viewBox", viewBoxParams );
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ================ jsvgGroup ================================= //
|
|
281
|
+
|
|
282
|
+
export class jsvgGroup extends jsvgObject
|
|
283
|
+
{
|
|
284
|
+
constructor()
|
|
285
|
+
{
|
|
286
|
+
super();
|
|
287
|
+
|
|
288
|
+
const svgNS = "http://www.w3.org/2000/svg";
|
|
289
|
+
this.svgObject = document.createElementNS(svgNS, "g");
|
|
290
|
+
this.svgObject.setAttribute("x", "0");
|
|
291
|
+
this.svgObject.setAttribute("y", "0");
|
|
292
|
+
this.svgObject.setAttribute("width", "500");
|
|
293
|
+
this.svgObject.setAttribute("height", "500");
|
|
294
|
+
this.svgObject.setAttribute("viewBox", "0 0 500 500");
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ================ jsvgLine ================================= //
|
|
299
|
+
|
|
300
|
+
export class jsvgLine extends jsvgObject
|
|
301
|
+
{
|
|
302
|
+
constructor()
|
|
303
|
+
{
|
|
304
|
+
super();
|
|
305
|
+
|
|
306
|
+
this.x1 = 0;
|
|
307
|
+
this.y1 = 0;
|
|
308
|
+
this.x2 = 100;
|
|
309
|
+
this.y2 = 100;
|
|
310
|
+
|
|
311
|
+
const svgNS = "http://www.w3.org/2000/svg";
|
|
312
|
+
this.svgObject = document.createElementNS(svgNS, "line");
|
|
313
|
+
this.svgObject.setAttribute("x1", "0");
|
|
314
|
+
this.svgObject.setAttribute("y1", "0");
|
|
315
|
+
this.svgObject.setAttribute("x2", "100");
|
|
316
|
+
this.svgObject.setAttribute("y2", "100");
|
|
317
|
+
|
|
318
|
+
this.setStrokeColor("black");
|
|
319
|
+
this.setStrokeWidth( 1 );
|
|
320
|
+
|
|
321
|
+
this.setEndpointA( this.x1, this.y1 );
|
|
322
|
+
this.setEndpointB( this.x2, this.y2 );
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
setEndpointA( x, y )
|
|
326
|
+
{
|
|
327
|
+
this.x1 = x;
|
|
328
|
+
this.y1 = y;
|
|
329
|
+
this.svgObject.setAttribute("x1", x.toString() );
|
|
330
|
+
this.svgObject.setAttribute("y1", y.toString() );
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
setEndpointB( x, y )
|
|
334
|
+
{
|
|
335
|
+
this.x2 = x;
|
|
336
|
+
this.y2 = y;
|
|
337
|
+
this.svgObject.setAttribute("x2", x.toString() );
|
|
338
|
+
this.svgObject.setAttribute("y2", y.toString() );
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
setEndpoints( x1,y1, x2,y2 )
|
|
342
|
+
{
|
|
343
|
+
this.setEndpointA( x1,y1 );
|
|
344
|
+
this.setEndpointB( x2,y2 );
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ================ jsvgRect ================================= //
|
|
349
|
+
|
|
350
|
+
export class jsvgRect extends jsvgObject
|
|
351
|
+
{
|
|
352
|
+
constructor()
|
|
353
|
+
{
|
|
354
|
+
super();
|
|
355
|
+
|
|
356
|
+
this.cornerRadius = 0;
|
|
357
|
+
|
|
358
|
+
const svgNS = "http://www.w3.org/2000/svg";
|
|
359
|
+
this.svgObject = document.createElementNS(svgNS, "rect");
|
|
360
|
+
this.setWidthAndHeight( 100,100 );
|
|
361
|
+
this.setFillColor( "white" );
|
|
362
|
+
this.setStrokeColor( "black" );
|
|
363
|
+
this.setStrokeWidth( 0 );
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
setCornerRadius( r )
|
|
367
|
+
{
|
|
368
|
+
this.cornerRadius = r;
|
|
369
|
+
this.svgObject.setAttribute( "rx", r.toString() );
|
|
370
|
+
this.svgObject.setAttribute( "ry", r.toString() );
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ================ jsvgEllipse ================================= //
|
|
375
|
+
|
|
376
|
+
export class jsvgEllipse extends jsvgObject
|
|
377
|
+
{
|
|
378
|
+
constructor()
|
|
379
|
+
{
|
|
380
|
+
super();
|
|
381
|
+
|
|
382
|
+
const svgNS = "http://www.w3.org/2000/svg";
|
|
383
|
+
this.svgObject = document.createElementNS(svgNS, "ellipse");
|
|
384
|
+
|
|
385
|
+
this.setPosition( 0,0 );
|
|
386
|
+
this.setWidthAndHeight( 100,100 );
|
|
387
|
+
this.setFillColor( "white" );
|
|
388
|
+
this.setStrokeColor( "black" );
|
|
389
|
+
this.setStrokeWidth( 0 );
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
setWidth( w )
|
|
393
|
+
{
|
|
394
|
+
this.width = w;
|
|
395
|
+
this.svgObject.setAttribute("rx", (this.width/2).toString() );
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
setHeight( h )
|
|
399
|
+
{
|
|
400
|
+
this.height = h;
|
|
401
|
+
this.svgObject.setAttribute("ry", (this.height/2).toString() );
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
// ================ jsvgArc ================================= //
|
|
407
|
+
|
|
408
|
+
export class jsvgArc extends jsvgObject
|
|
409
|
+
{
|
|
410
|
+
constructor()
|
|
411
|
+
{
|
|
412
|
+
super();
|
|
413
|
+
|
|
414
|
+
const svgNS = "http://www.w3.org/2000/svg";
|
|
415
|
+
this.svgObject = document.createElementNS(svgNS, "path");
|
|
416
|
+
|
|
417
|
+
this.setFillColor("white");
|
|
418
|
+
this.setStrokeColor("black");
|
|
419
|
+
this.setStrokeWidth(0);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
createPieSlice(radius, startAngle, endAngle)
|
|
423
|
+
{
|
|
424
|
+
// Convert polar coordinates to Cartesian for SVG arc
|
|
425
|
+
function polarToCartesian(radius, angleInDegrees)
|
|
426
|
+
{
|
|
427
|
+
const angleInRadians = (angleInDegrees - 90) * Math.PI / 180;
|
|
428
|
+
return {
|
|
429
|
+
x: (radius * Math.cos(angleInRadians)),
|
|
430
|
+
y: (radius * Math.sin(angleInRadians))
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const start = polarToCartesian(radius, endAngle);
|
|
435
|
+
const end = polarToCartesian(radius, startAngle);
|
|
436
|
+
|
|
437
|
+
const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
|
|
438
|
+
|
|
439
|
+
const d = [
|
|
440
|
+
`M ${0} ${0}`, // Move to center
|
|
441
|
+
`L ${start.x} ${start.y}`, // Draw line to start of arc
|
|
442
|
+
`A ${radius} ${radius} 0 ${largeArcFlag} 0 ${end.x} ${end.y}`, // Draw arc
|
|
443
|
+
`Z` // Close path to center
|
|
444
|
+
].join(" ");
|
|
445
|
+
|
|
446
|
+
this.svgObject.setAttribute("d", d);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// ================ jsvgPath ================================= //
|
|
451
|
+
|
|
452
|
+
export class jsvgPath extends jsvgObject
|
|
453
|
+
{
|
|
454
|
+
constructor()
|
|
455
|
+
{
|
|
456
|
+
super();
|
|
457
|
+
|
|
458
|
+
// Create the path element and add it to the SVG element
|
|
459
|
+
this.path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
460
|
+
this.path.setAttribute("fill", "none"); // Default fill is none
|
|
461
|
+
this.path.setAttribute("stroke", "black"); // Default stroke is black
|
|
462
|
+
this.path.setAttribute("stroke-width", "2"); // Default stroke width
|
|
463
|
+
// this.svgElement.appendChild(this.path);
|
|
464
|
+
|
|
465
|
+
this.svgObject = this.path;
|
|
466
|
+
|
|
467
|
+
// Initialize the points array
|
|
468
|
+
this.points = [];
|
|
469
|
+
|
|
470
|
+
// Update the path's 'd' attribute
|
|
471
|
+
this.updatePath();
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
addPoint(x, y)
|
|
475
|
+
{
|
|
476
|
+
if (typeof x !== "number" || typeof y !== "number")
|
|
477
|
+
{
|
|
478
|
+
throw new Error("Coordinates must be numbers.");
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
this.points.push({ x:x, y:y });
|
|
482
|
+
|
|
483
|
+
// must call updatePath to see new points
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
updatePath()
|
|
487
|
+
{
|
|
488
|
+
if (this.points.length === 0)
|
|
489
|
+
{
|
|
490
|
+
this.path.setAttribute("d", "");
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Create the 'd' attribute for the path
|
|
495
|
+
const d = this.points
|
|
496
|
+
.map((point, index) => (index === 0 ? `M ${point.x},${point.y}` : `L ${point.x},${point.y}`))
|
|
497
|
+
.join(" ");
|
|
498
|
+
|
|
499
|
+
// Close the polygon if it has at least three points
|
|
500
|
+
const pathData = d;
|
|
501
|
+
|
|
502
|
+
this.path.setAttribute("d", pathData);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
clearPoints()
|
|
506
|
+
{
|
|
507
|
+
this.points = [];
|
|
508
|
+
this.updatePath();
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
// ================ jsvgClipMask ================================= //
|
|
514
|
+
|
|
515
|
+
export class jsvgClipMask extends jsvgObject
|
|
516
|
+
{
|
|
517
|
+
constructor( width=300, height=300, cornerRadius=10 )
|
|
518
|
+
{
|
|
519
|
+
super();
|
|
520
|
+
|
|
521
|
+
// make a new svg group
|
|
522
|
+
const svgNamespace = "http://www.w3.org/2000/svg";
|
|
523
|
+
const svg = document.createElementNS(svgNamespace, "g");
|
|
524
|
+
this.svgObject = svg;
|
|
525
|
+
|
|
526
|
+
this.width = width;
|
|
527
|
+
this.height = height;
|
|
528
|
+
this.cornerRadius = cornerRadius;
|
|
529
|
+
|
|
530
|
+
this.maskID = "clipMaskID" + Math.random();
|
|
531
|
+
|
|
532
|
+
svg.setAttribute("width", this.width);
|
|
533
|
+
svg.setAttribute("height", this.height);
|
|
534
|
+
svg.setAttribute("viewBox", `0 0 ${this.width} ${this.height}`);
|
|
535
|
+
|
|
536
|
+
// Create a clipPath to apply rounded corners
|
|
537
|
+
const clipPath = document.createElementNS(svgNamespace, "clipPath");
|
|
538
|
+
clipPath.setAttribute("id", this.maskID );
|
|
539
|
+
|
|
540
|
+
this.clipRect = document.createElementNS(svgNamespace, "rect");
|
|
541
|
+
this.clipRect.setAttribute("x", 0);
|
|
542
|
+
this.clipRect.setAttribute("y", 0);
|
|
543
|
+
this.clipRect.setAttribute("width", this.width);
|
|
544
|
+
this.clipRect.setAttribute("height", this.height);
|
|
545
|
+
this.clipRect.setAttribute("rx", this.cornerRadius); // Rounded corner radius
|
|
546
|
+
this.clipRect.setAttribute("ry", this.cornerRadius);
|
|
547
|
+
|
|
548
|
+
clipPath.appendChild(this.clipRect);
|
|
549
|
+
svg.appendChild(clipPath);
|
|
550
|
+
|
|
551
|
+
// we create an inner group that gets masked
|
|
552
|
+
// all children get added this group
|
|
553
|
+
this.clipGroup = new jsvgGroup();
|
|
554
|
+
this.clipGroup.svgObject.setAttribute("clip-path", `url(#${this.maskID})`);
|
|
555
|
+
super.addChild( this.clipGroup ); // use super to avoid recursion
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
setWidth( width )
|
|
559
|
+
{
|
|
560
|
+
this.width = width;
|
|
561
|
+
|
|
562
|
+
this.svgObject.setAttribute("width", this.width);
|
|
563
|
+
this.svgObject.setAttribute("viewBox", `0 0 ${this.width} ${this.height}`);
|
|
564
|
+
this.clipRect.setAttribute("width", this.width);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
setHeight( height )
|
|
568
|
+
{
|
|
569
|
+
this.height = height;
|
|
570
|
+
|
|
571
|
+
this.svgObject.setAttribute("height", this.height);
|
|
572
|
+
this.svgObject.setAttribute("viewBox", `0 0 ${this.width} ${this.height}`);
|
|
573
|
+
this.clipRect.setAttribute("height", this.height);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// setWidthAndHeight( width, height )
|
|
577
|
+
// {
|
|
578
|
+
// this.width = width;
|
|
579
|
+
// this.height = height;
|
|
580
|
+
|
|
581
|
+
// this.svgObject.setAttribute("width", this.width);
|
|
582
|
+
// this.svgObject.setAttribute("height", this.height);
|
|
583
|
+
// this.svgObject.setAttribute("viewBox", `0 0 ${this.width} ${this.height}`);
|
|
584
|
+
|
|
585
|
+
// this.clipRect.setAttribute("width", this.width);
|
|
586
|
+
// this.clipRect.setAttribute("height", this.height);
|
|
587
|
+
// }
|
|
588
|
+
|
|
589
|
+
setCornerRadius( cornerRadius )
|
|
590
|
+
{
|
|
591
|
+
this.cornerRadius = cornerRadius;
|
|
592
|
+
this.clipRect.setAttribute("rx", this.cornerRadius); // Rounded corner radius
|
|
593
|
+
this.clipRect.setAttribute("ry", this.cornerRadius);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
addChild( obj )
|
|
597
|
+
{
|
|
598
|
+
// add to masked clip group
|
|
599
|
+
this.clipGroup.addChild( obj );
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
// ================ jsvgImage ================================= //
|
|
607
|
+
|
|
608
|
+
export class jsvgImage extends jsvgObject
|
|
609
|
+
{
|
|
610
|
+
constructor()
|
|
611
|
+
{
|
|
612
|
+
super();
|
|
613
|
+
|
|
614
|
+
this.preserveAspectRatio = true;
|
|
615
|
+
|
|
616
|
+
const svgNS = "http://www.w3.org/2000/svg";
|
|
617
|
+
this.svgObject = document.createElementNS(svgNS, "image");
|
|
618
|
+
this.setWidthAndHeight( 100,100 );
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
setImageURL( newURL )
|
|
622
|
+
{
|
|
623
|
+
this.svgObject.setAttribute("href", newURL );
|
|
624
|
+
this.imageURL = newURL;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
setAspectRatio( svgAspectRatio )
|
|
628
|
+
{
|
|
629
|
+
// to fill the whole space use "xMidYMid slice"
|
|
630
|
+
this.svgObject.setAttribute("preserveAspectRatio", svgAspectRatio );
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
applyRoundedCorners( cornerRadius, svgAspectRatio = 0 )
|
|
634
|
+
{
|
|
635
|
+
// remove the original image
|
|
636
|
+
if ( this.svgObject )
|
|
637
|
+
this.svgObject.remove();
|
|
638
|
+
|
|
639
|
+
// make a new svg image
|
|
640
|
+
const svgNamespace = "http://www.w3.org/2000/svg";
|
|
641
|
+
const svg = document.createElementNS(svgNamespace, "g");
|
|
642
|
+
this.svgObject = svg;
|
|
643
|
+
|
|
644
|
+
svg.setAttribute("width", this.width);
|
|
645
|
+
svg.setAttribute("height", this.height);
|
|
646
|
+
svg.setAttribute("viewBox", `0 0 ${this.width} ${this.height}`);
|
|
647
|
+
|
|
648
|
+
// Create a clipPath to apply rounded corners
|
|
649
|
+
const clipPath = document.createElementNS(svgNamespace, "clipPath");
|
|
650
|
+
clipPath.setAttribute("id", "rounded-corners");
|
|
651
|
+
|
|
652
|
+
const rect = document.createElementNS(svgNamespace, "rect");
|
|
653
|
+
rect.setAttribute("x", 0);
|
|
654
|
+
rect.setAttribute("y", 0);
|
|
655
|
+
rect.setAttribute("width", this.width);
|
|
656
|
+
rect.setAttribute("height", this.height);
|
|
657
|
+
rect.setAttribute("rx", cornerRadius); // Rounded corner radius
|
|
658
|
+
rect.setAttribute("ry", cornerRadius);
|
|
659
|
+
|
|
660
|
+
clipPath.appendChild(rect);
|
|
661
|
+
svg.appendChild(clipPath);
|
|
662
|
+
|
|
663
|
+
// Create the image element
|
|
664
|
+
const image = document.createElementNS(svgNamespace, "image");
|
|
665
|
+
image.setAttribute("href", this.imageURL );
|
|
666
|
+
image.setAttribute("width", this.width);
|
|
667
|
+
image.setAttribute("height", this.height);
|
|
668
|
+
image.setAttribute("clip-path", "url(#rounded-corners)");
|
|
669
|
+
if ( svgAspectRatio )
|
|
670
|
+
image.setAttribute("preserveAspectRatio", svgAspectRatio );
|
|
671
|
+
|
|
672
|
+
svg.appendChild(image);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
// ================ jsvgTextLine ================================= //
|
|
678
|
+
|
|
679
|
+
export class jsvgTextLine extends jsvgObject
|
|
680
|
+
{
|
|
681
|
+
constructor()
|
|
682
|
+
{
|
|
683
|
+
super();
|
|
684
|
+
|
|
685
|
+
this.text = "";
|
|
686
|
+
|
|
687
|
+
const svgNS = "http://www.w3.org/2000/svg";
|
|
688
|
+
this.svgObject = document.createElementNS(svgNS, "text");
|
|
689
|
+
this.svgObject.textContent = "hello world";
|
|
690
|
+
this.svgObject.setAttribute("x", "0"); // x-coordinate of the rectangle
|
|
691
|
+
this.svgObject.setAttribute("y", "0"); // y-coordinate of the rectangle
|
|
692
|
+
this.svgObject.setAttribute("fill", "black"); // fill color of the rectangle
|
|
693
|
+
this.svgObject.style.fontFamily = "Arial, Helvetica, sans-serif;";
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
setText( text ) { this.text = text; this.svgObject.textContent = text; }
|
|
697
|
+
getText() { return this.text; }
|
|
698
|
+
|
|
699
|
+
setFontSize( S ) { this.svgObject.style.fontSize = S.toString() + 'px'; }
|
|
700
|
+
|
|
701
|
+
setFontFamily( F ) { this.svgObject.style.fontFamily = F; }
|
|
702
|
+
|
|
703
|
+
setFontColor( C ) { this.svgObject.style.fill = C; }
|
|
704
|
+
|
|
705
|
+
setTextAnchor( A )
|
|
706
|
+
{
|
|
707
|
+
// left, middle or right
|
|
708
|
+
this.svgObject.setAttribute("text-anchor", A ); // fill color of the rectangle
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
setAlignment( A )
|
|
712
|
+
{
|
|
713
|
+
this.alignment = A;
|
|
714
|
+
if ( A == 'left' )
|
|
715
|
+
this.setTextAnchor( 'start' );
|
|
716
|
+
if ( A == 'center' )
|
|
717
|
+
this.setTextAnchor( 'middle' );
|
|
718
|
+
if ( A == 'right' )
|
|
719
|
+
this.setTextAnchor( 'end' );
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
setFontWeight( W ) {
|
|
723
|
+
this.svgObject.style.fontWeight = W.toString();
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
// ================ jsvgTextBox (Div) ================================= //
|
|
729
|
+
|
|
730
|
+
export class jsvgTextBox extends jsvgGroup
|
|
731
|
+
{
|
|
732
|
+
constructor()
|
|
733
|
+
{
|
|
734
|
+
super();
|
|
735
|
+
|
|
736
|
+
this.text = "";
|
|
737
|
+
|
|
738
|
+
var ns = 'http://www.w3.org/2000/svg';
|
|
739
|
+
this.foreignObject = document.createElementNS( ns, 'foreignObject');
|
|
740
|
+
|
|
741
|
+
this.foreignObject.style.backgroundColor = 'none';
|
|
742
|
+
this.svgObject.appendChild( this.foreignObject );
|
|
743
|
+
|
|
744
|
+
this.div = document.createElement('div');
|
|
745
|
+
this.foreignObject.appendChild( this.div );
|
|
746
|
+
|
|
747
|
+
this.setWidthAndHeight( 200,100 );
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
setText( T ) { this.text = T; this.div.innerHTML = T; }
|
|
751
|
+
|
|
752
|
+
getText() { return this.text; }
|
|
753
|
+
|
|
754
|
+
setStyle( S ) // for style setting with CSS
|
|
755
|
+
{
|
|
756
|
+
this.div.className = S;
|
|
757
|
+
this.div.style.position = 'fixed'; // for safari
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
setFontFamily( F ) { this.div.style.fontFamily = F; }
|
|
761
|
+
|
|
762
|
+
setFontSize( S ) { this.div.style.fontSize = S.toString() + "px"; }
|
|
763
|
+
|
|
764
|
+
setLineHeight( H ) { this.div.style.lineHeight = H + 'px'; }
|
|
765
|
+
|
|
766
|
+
setFontWeight( W ) { this.div.style.fontWeight = W; }
|
|
767
|
+
|
|
768
|
+
setFontColor( C ) { this.div.style.color = C; }
|
|
769
|
+
|
|
770
|
+
setAlignment( A ) { this.div.style.textAlign = A; }
|
|
771
|
+
|
|
772
|
+
setVerticalCentering()
|
|
773
|
+
{
|
|
774
|
+
this.div.style.display = "flex";
|
|
775
|
+
this.div.style.flexFlow = "column"
|
|
776
|
+
this.div.style.justifyContent = "center";
|
|
777
|
+
this.div.style.alignItems = "center";
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
makeEditable()
|
|
781
|
+
{
|
|
782
|
+
this.div.style.contentEditable = 'true';
|
|
783
|
+
this.div.onclick = function() { console.log("here"); this.focus(); };
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
setBorderWidthAndColor( width, color ) { this.div.style.border = width + "px solid " + color; }
|
|
787
|
+
|
|
788
|
+
setWidth( W )
|
|
789
|
+
{
|
|
790
|
+
this.width = W;
|
|
791
|
+
if ( this.div )
|
|
792
|
+
this.div.style.width = W + 'px'
|
|
793
|
+
|
|
794
|
+
if ( this.foreignObject )
|
|
795
|
+
this.foreignObject.setAttribute('width', W );
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
setHeight( H )
|
|
799
|
+
{
|
|
800
|
+
this.height = H;
|
|
801
|
+
if ( this.div )
|
|
802
|
+
this.div.style.height = H + 'px'
|
|
803
|
+
|
|
804
|
+
if ( this.foreignObject )
|
|
805
|
+
this.foreignObject.setAttribute('height', H );
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
// ================ jsvgTextInput ================================= //
|
|
812
|
+
|
|
813
|
+
export class jsvgTextInput extends jsvgTextBox
|
|
814
|
+
{
|
|
815
|
+
constructor()
|
|
816
|
+
{
|
|
817
|
+
super();
|
|
818
|
+
|
|
819
|
+
this.callback = null;
|
|
820
|
+
|
|
821
|
+
// remove the generic div before replacing it
|
|
822
|
+
this.foreignObject.removeChild( this.div );
|
|
823
|
+
|
|
824
|
+
this.div = document.createElement('input');
|
|
825
|
+
this.div.contentEditable = true;
|
|
826
|
+
|
|
827
|
+
this.div.onkeyup = this.handleInput.bind(this);
|
|
828
|
+
this.foreignObject.appendChild( this.div );
|
|
829
|
+
|
|
830
|
+
this.setWidthAndHeight( 200,30 );
|
|
831
|
+
this.setPlaceholderText("placeholder");
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
setStyle( S ) // for style setting with CSS
|
|
835
|
+
{
|
|
836
|
+
this.div.className = S;
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
setPlaceholderText( T ) { this.div.placeholder = T }
|
|
840
|
+
|
|
841
|
+
setText( T ) { this.div.value = T; }
|
|
842
|
+
|
|
843
|
+
getText() { return this.div.value; }
|
|
844
|
+
|
|
845
|
+
setInputCallback( callback ) { this.callback = callback; } // the callback is a function which receives text
|
|
846
|
+
|
|
847
|
+
handleInput( event )
|
|
848
|
+
{
|
|
849
|
+
if ( event.key == 'Enter' || event.keyCode==13 )
|
|
850
|
+
{
|
|
851
|
+
if ( this.callback )
|
|
852
|
+
this.callback( this.div.value )
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
// ================ jsvgTextArea ================================= //
|
|
859
|
+
|
|
860
|
+
export class jsvgTextArea extends jsvgTextBox
|
|
861
|
+
{
|
|
862
|
+
constructor()
|
|
863
|
+
{
|
|
864
|
+
super();
|
|
865
|
+
|
|
866
|
+
this.callback = null;
|
|
867
|
+
|
|
868
|
+
// remove the generic div before replacing it
|
|
869
|
+
this.foreignObject.removeChild( this.div );
|
|
870
|
+
|
|
871
|
+
this.div = document.createElement('textarea');
|
|
872
|
+
|
|
873
|
+
this.div.onkeyup = this.handleInput.bind(this);
|
|
874
|
+
this.foreignObject.appendChild( this.div );
|
|
875
|
+
|
|
876
|
+
this.setWidthAndHeight( 200,30 );
|
|
877
|
+
this.setPlaceholderText("placeholder");
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
setText( T ) { this.div.value = T; }
|
|
881
|
+
getText() { return this.div.value; }
|
|
882
|
+
|
|
883
|
+
setStyle( S ) // for style setting with CSS
|
|
884
|
+
{
|
|
885
|
+
this.div.className = S;
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
handleInput( event )
|
|
889
|
+
{
|
|
890
|
+
if ( this.callback )
|
|
891
|
+
this.callback( this );
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
setPlaceholderText( T ) { this.div.placeholder = T }
|
|
895
|
+
|
|
896
|
+
setInputCallback( callback ) { this.callback = callback; } // the callback is a function which receives text
|
|
897
|
+
|
|
898
|
+
}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// ================ jsvgButton ================================= //
|
|
5
|
+
|
|
6
|
+
export class jsvgButton extends jsvgGroup
|
|
7
|
+
{
|
|
8
|
+
constructor()
|
|
9
|
+
{
|
|
10
|
+
super();
|
|
11
|
+
|
|
12
|
+
this.callback = null;
|
|
13
|
+
this.fontSize = 12;
|
|
14
|
+
this.text = 'button';
|
|
15
|
+
|
|
16
|
+
// add rect
|
|
17
|
+
this.backRect = new jsvgRect();
|
|
18
|
+
this.backRect.setWidthAndHeight( 100,30 );
|
|
19
|
+
this.backRect.setFillColor( "white" );
|
|
20
|
+
this.backRect.setStrokeWidth(0);
|
|
21
|
+
this.backRect.setCornerRadius( 15 );
|
|
22
|
+
this.backRect.svgObject.style.cursor = "pointer";
|
|
23
|
+
this.addChild( this.backRect );
|
|
24
|
+
|
|
25
|
+
// add text
|
|
26
|
+
this.buttonText = new jsvgTextLine();
|
|
27
|
+
this.buttonText.setPosition(50,20);
|
|
28
|
+
this.buttonText.setFontFamily( "Arial" );
|
|
29
|
+
this.buttonText.setFontSize( 12 );
|
|
30
|
+
this.buttonText.setText("button");
|
|
31
|
+
this.buttonText.setTextAnchor("middle");
|
|
32
|
+
this.buttonText.svgObject.style.cursor = "pointer";
|
|
33
|
+
|
|
34
|
+
this.addChild( this.buttonText );
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
setText( T ) { this.text = T; this.buttonText.setText(T) }
|
|
38
|
+
setFontFamily( F ) { this.buttonText.setFontFamily(F) }
|
|
39
|
+
setFontSize( S ) { this.fontSize=S; this.buttonText.setFontSize(S) }
|
|
40
|
+
setFontColor( C ) { this.buttonText.setFontColor(C) }
|
|
41
|
+
setFillColor( C ) { this.backRect.setFillColor( C ) };
|
|
42
|
+
setStrokeColor( C ) { this.backRect.setStrokeColor( C ) };
|
|
43
|
+
setStrokeWidth( C ) { this.backRect.setStrokeWidth( C ) };
|
|
44
|
+
setCornerRadius( R ) { this.backRect.setCornerRadius( R ) };
|
|
45
|
+
|
|
46
|
+
addImage( imageURL, imageWidth=30, imageHeight=30 )
|
|
47
|
+
{
|
|
48
|
+
this.image = new jsvgImage();
|
|
49
|
+
this.image.setImageURL( imageURL );
|
|
50
|
+
this.image.setWidthAndHeight( imageWidth, imageHeight );
|
|
51
|
+
this.image.setPosition( this.backRect.width/2-imageWidth/2, this.backRect.height/2-imageHeight/2 );
|
|
52
|
+
this.image.svgObject.style.cursor = "pointer";
|
|
53
|
+
this.addChild( this.image );
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
setWidthAndHeight( W, H )
|
|
57
|
+
{
|
|
58
|
+
this.width = W;
|
|
59
|
+
this.height = H;
|
|
60
|
+
this.backRect.setWidthAndHeight( W, H );
|
|
61
|
+
this.buttonText.setPosition( W/2, H*0.50+2 + this.fontSize*0.25 );
|
|
62
|
+
if ( this.image )
|
|
63
|
+
{
|
|
64
|
+
this.image.setPosition( this.backRect.width/2-this.image.width/2,
|
|
65
|
+
this.backRect.height/2-this.image.height/2 );
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
// ================ jsvgLayoutGroup ================================= //
|
|
72
|
+
|
|
73
|
+
export class jsvgLayoutGroup extends jsvgGroup
|
|
74
|
+
{
|
|
75
|
+
constructor()
|
|
76
|
+
{
|
|
77
|
+
super();
|
|
78
|
+
|
|
79
|
+
this.spacer = 10;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
setSpacer( S )
|
|
83
|
+
{
|
|
84
|
+
this.spacer = S;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
updateLayout()
|
|
88
|
+
{
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
showAll()
|
|
92
|
+
{
|
|
93
|
+
var N = this.getNumberOfChildren();
|
|
94
|
+
for ( var i=0; i<N; i++)
|
|
95
|
+
{
|
|
96
|
+
var C = this.getChild(i);
|
|
97
|
+
C.show();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
showOnly( indexToShow )
|
|
102
|
+
{
|
|
103
|
+
var N = this.getNumberOfChildren();
|
|
104
|
+
for ( var i=0; i<N; i++)
|
|
105
|
+
{
|
|
106
|
+
var C = this.getChild(i);
|
|
107
|
+
if ( i == indexToShow )
|
|
108
|
+
C.show();
|
|
109
|
+
else
|
|
110
|
+
C.hide();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
doVerticalLayout()
|
|
115
|
+
{
|
|
116
|
+
var N = this.getNumberOfChildren();
|
|
117
|
+
|
|
118
|
+
// layout children from top to bottom
|
|
119
|
+
var pX = 0;
|
|
120
|
+
var pY = 0;
|
|
121
|
+
var minW = 0;
|
|
122
|
+
for ( var i=0; i<N; i++)
|
|
123
|
+
{
|
|
124
|
+
var C = this.getChild(i);
|
|
125
|
+
if ( ! C.visible )
|
|
126
|
+
continue;
|
|
127
|
+
C.setPosition( pX,pY );
|
|
128
|
+
pY += C.height;
|
|
129
|
+
pY += this.spacer;
|
|
130
|
+
|
|
131
|
+
if ( C.width > minW )
|
|
132
|
+
minW = C.width;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// set width and height
|
|
136
|
+
this.height = pY - this.spacer;
|
|
137
|
+
this.width = minW;
|
|
138
|
+
|
|
139
|
+
// horizontal center all items
|
|
140
|
+
for ( var i=0; i<N; i++)
|
|
141
|
+
{
|
|
142
|
+
var C = this.getChild(i);
|
|
143
|
+
var pX = this.width/2-C.width/2;
|
|
144
|
+
C.setPosition( pX, C.ypos );
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
doHorizontalLayout()
|
|
149
|
+
{
|
|
150
|
+
var N = this.getNumberOfChildren();
|
|
151
|
+
|
|
152
|
+
// layout children from left to right
|
|
153
|
+
var pX = 0;
|
|
154
|
+
var pY = 0;
|
|
155
|
+
var minH = 0;
|
|
156
|
+
for ( var i=0; i<N; i++)
|
|
157
|
+
{
|
|
158
|
+
var C = this.getChild(i);
|
|
159
|
+
if ( ! C.visible )
|
|
160
|
+
continue;
|
|
161
|
+
C.setPosition( pX,pY );
|
|
162
|
+
pX += C.width;
|
|
163
|
+
pX += this.spacer;
|
|
164
|
+
|
|
165
|
+
if ( C.height > minH )
|
|
166
|
+
minH = C.height;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// set width and height
|
|
170
|
+
this.width = pX - this.spacer;
|
|
171
|
+
this.height = minH;
|
|
172
|
+
|
|
173
|
+
// vertically center all items
|
|
174
|
+
for ( var i=0; i<N; i++)
|
|
175
|
+
{
|
|
176
|
+
var C = this.getChild(i);
|
|
177
|
+
var pY = this.height/2-C.height/2;
|
|
178
|
+
C.setPosition( C.xpos, pY );
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
// ================ jsvgScrollbox ================================= //
|
|
185
|
+
|
|
186
|
+
export class jsvgScrollbox extends jsvgGroup
|
|
187
|
+
{
|
|
188
|
+
constructor()
|
|
189
|
+
{
|
|
190
|
+
super();
|
|
191
|
+
|
|
192
|
+
this.scrollPercent = 0;
|
|
193
|
+
this.scrollOffset = 0;
|
|
194
|
+
this.contentHeight = 0;
|
|
195
|
+
|
|
196
|
+
this.oldMouseX = 0;
|
|
197
|
+
this.oldMouseY = 0;
|
|
198
|
+
|
|
199
|
+
// make clip mask
|
|
200
|
+
this.clipMask = new jsvgClipMask( 200,400, 10 );
|
|
201
|
+
super.addChild( this.clipMask ); // use super to avoid addChild recursion
|
|
202
|
+
|
|
203
|
+
// make background rect
|
|
204
|
+
this.backRect = new jsvgRect();
|
|
205
|
+
this.backRect.setFillColor("white");
|
|
206
|
+
this.clipMask.addChild( this.backRect );
|
|
207
|
+
|
|
208
|
+
// make scroll group
|
|
209
|
+
this.scrollGroup = new jsvgGroup();
|
|
210
|
+
this.clipMask.addChild( this.scrollGroup );
|
|
211
|
+
|
|
212
|
+
// make scroll bar
|
|
213
|
+
this.scrollBar = new jsvgRect();
|
|
214
|
+
this.scrollBar.setWidthAndHeight( 10,50 );
|
|
215
|
+
this.scrollBar.setCornerRadius( 5 );
|
|
216
|
+
this.scrollBar.setFillColor( "darkgray" );
|
|
217
|
+
this.scrollBar.setPosition( 180, 10 );
|
|
218
|
+
this.scrollBar.svgObject.onmousedown = this.handleMouseDown.bind(this);
|
|
219
|
+
this.scrollBar.svgObject.style.cursor = "pointer";
|
|
220
|
+
this.clipMask.addChild( this.scrollBar );
|
|
221
|
+
|
|
222
|
+
this.setWidthAndHeight( 200,500 );
|
|
223
|
+
|
|
224
|
+
this.updateLayout();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
setWidth( W )
|
|
228
|
+
{
|
|
229
|
+
super.setWidth( W );
|
|
230
|
+
this.clipMask.setWidth( W );
|
|
231
|
+
this.backRect.setWidth( W );
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
setHeight( H )
|
|
235
|
+
{
|
|
236
|
+
super.setWidth( H );
|
|
237
|
+
this.clipMask.setHeight( H );
|
|
238
|
+
this.backRect.setHeight( H );
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
setCornerRadius( R )
|
|
242
|
+
{
|
|
243
|
+
this.clipMask.setCornerRadius( R );
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
addChild( C )
|
|
247
|
+
{
|
|
248
|
+
this.scrollGroup.addChild( C );
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
handleMouseDown( event )
|
|
252
|
+
{
|
|
253
|
+
if ( ! this.visible )
|
|
254
|
+
return;
|
|
255
|
+
|
|
256
|
+
// handle dragging
|
|
257
|
+
function handleMouseMove(event)
|
|
258
|
+
{
|
|
259
|
+
event.preventDefault();
|
|
260
|
+
// console.log("handleMouseMove");
|
|
261
|
+
|
|
262
|
+
// get mouseX and mouseY
|
|
263
|
+
var mouseX, mouseY;
|
|
264
|
+
if ( event.touches && event.touches.length >= 1 )
|
|
265
|
+
{
|
|
266
|
+
mouseX = event.touches[0].clientX;
|
|
267
|
+
mouseY = event.touches[0].clientY;
|
|
268
|
+
}
|
|
269
|
+
else
|
|
270
|
+
{
|
|
271
|
+
mouseX = event.clientX;
|
|
272
|
+
mouseY = event.clientY;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// calculate dX and dY
|
|
276
|
+
if ( this.oldMouseX && this.oldMouseY )
|
|
277
|
+
{
|
|
278
|
+
var dX = mouseX - this.oldMouseX;
|
|
279
|
+
var dY = mouseY - this.oldMouseY;
|
|
280
|
+
|
|
281
|
+
var pY = this.scrollBar.ypos + dY;
|
|
282
|
+
|
|
283
|
+
// enforce slider bounds
|
|
284
|
+
var minY = 10;
|
|
285
|
+
var maxY = this.backRect.height - this.scrollBar.height - 10;
|
|
286
|
+
if ( pY < minY )
|
|
287
|
+
pY = minY;
|
|
288
|
+
if ( pY > maxY )
|
|
289
|
+
pY = maxY;
|
|
290
|
+
|
|
291
|
+
// move the circle
|
|
292
|
+
this.scrollBar.setPosition( this.scrollBar.xpos, pY );
|
|
293
|
+
|
|
294
|
+
this.scrollPercent = (pY-minY) / (maxY-minY);
|
|
295
|
+
// console.log( this.scrollPercent.toFixed(2) );
|
|
296
|
+
|
|
297
|
+
// update layout
|
|
298
|
+
this.updateLayout();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// set oldMouseX and oldMouseY
|
|
302
|
+
this.oldMouseX = mouseX;
|
|
303
|
+
this.oldMouseY = mouseY;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// on mouse up
|
|
307
|
+
function handleMouseUp(event)
|
|
308
|
+
{
|
|
309
|
+
// console.log("handleMouseUp");
|
|
310
|
+
this.activeBubble = null;
|
|
311
|
+
window.onmousemove = null;
|
|
312
|
+
window.ontouchmove = null;
|
|
313
|
+
window.onmouseup = null;
|
|
314
|
+
window.ontouchend = null;
|
|
315
|
+
this.oldMouseX = 0;
|
|
316
|
+
this.oldMouseY = 0;
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// set up events
|
|
321
|
+
window.onmousemove = handleMouseMove.bind(this);
|
|
322
|
+
window.ontouchmove = handleMouseMove.bind(this);;
|
|
323
|
+
window.onmouseup = handleMouseUp.bind(this);
|
|
324
|
+
window.ontouchend = handleMouseUp.bind(this);;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
computeContentBounds()
|
|
328
|
+
{
|
|
329
|
+
var maxY = 0;
|
|
330
|
+
var N = this.scrollGroup.getNumberOfChildren();
|
|
331
|
+
|
|
332
|
+
// find bottom most point of all child objects
|
|
333
|
+
for ( var i=0; i<N; i++ )
|
|
334
|
+
{
|
|
335
|
+
var C = this.scrollGroup.getChild(i);
|
|
336
|
+
var bottomY = C.ypos + C.height;
|
|
337
|
+
if ( bottomY > maxY )
|
|
338
|
+
maxY = bottomY;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
this.contentHeight = maxY;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
updateLayout()
|
|
345
|
+
{
|
|
346
|
+
var W = this.backRect.width;
|
|
347
|
+
var H = this.backRect.height;
|
|
348
|
+
|
|
349
|
+
this.computeContentBounds();
|
|
350
|
+
|
|
351
|
+
if ( this.contentHeight > this.backRect.height )
|
|
352
|
+
{
|
|
353
|
+
var maxShift = this.contentHeight - this.backRect.height;
|
|
354
|
+
var scrollOffset = -1.0 * this.scrollPercent * maxShift;
|
|
355
|
+
this.scrollGroup.setPosition( 0, scrollOffset );
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teachinglab/omd",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"description": "omd",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"src/",
|
|
15
15
|
"omd/",
|
|
16
16
|
"docs/",
|
|
17
|
-
"canvas/"
|
|
17
|
+
"canvas/",
|
|
18
|
+
"jsvg/"
|
|
18
19
|
],
|
|
19
20
|
"keywords": [
|
|
20
21
|
"math",
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
"license": "MIT",
|
|
30
31
|
"dependencies": {
|
|
31
32
|
"@teachinglab/jsvg": "^0.1.1",
|
|
33
|
+
"@teachinglab/omd": "^0.5.4",
|
|
32
34
|
"mathjs": "^14.5.2"
|
|
33
35
|
},
|
|
34
36
|
"scripts": {
|