@annotorious/annotorious 3.0.0-rc.8 → 3.0.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 (158) hide show
  1. package/dist/Annotorious.d.ts +7 -8
  2. package/dist/AnnotoriousOpts.d.ts +5 -7
  3. package/dist/annotation/SVGAnnotationLayer.svelte.d.ts +1 -1
  4. package/dist/annotation/SVGAnnotationLayerPointerEvent.d.ts +3 -3
  5. package/dist/annotation/Transform.d.ts +0 -1
  6. package/dist/annotation/editors/Editor.svelte.d.ts +1 -1
  7. package/dist/annotation/editors/EditorMount.svelte.d.ts +1 -1
  8. package/dist/annotation/editors/Handle.svelte.d.ts +1 -0
  9. package/dist/annotation/editors/editorsRegistry.d.ts +3 -4
  10. package/dist/annotation/editors/index.d.ts +1 -2
  11. package/dist/annotation/editors/polygon/PolygonEditor.svelte.d.ts +1 -1
  12. package/dist/annotation/editors/polygon/index.d.ts +0 -1
  13. package/dist/annotation/editors/rectangle/RectangleEditor.svelte.d.ts +1 -1
  14. package/dist/annotation/editors/rectangle/index.d.ts +0 -1
  15. package/dist/annotation/index.d.ts +0 -1
  16. package/dist/annotation/shapes/Ellipse.svelte.d.ts +1 -1
  17. package/dist/annotation/shapes/Polygon.svelte.d.ts +1 -1
  18. package/dist/annotation/shapes/Rectangle.svelte.d.ts +1 -1
  19. package/dist/annotation/shapes/index.d.ts +0 -1
  20. package/dist/annotation/tools/DrawingToolConfig.d.ts +1 -2
  21. package/dist/annotation/tools/ToolMount.svelte.d.ts +1 -1
  22. package/dist/annotation/tools/drawingToolsRegistry.d.ts +4 -5
  23. package/dist/annotation/tools/index.d.ts +0 -1
  24. package/dist/annotation/tools/polygon/RubberbandPolygon.svelte.d.ts +1 -1
  25. package/dist/annotation/tools/polygon/index.d.ts +0 -1
  26. package/dist/annotation/tools/rectangle/RubberbandRectangle.svelte.d.ts +1 -1
  27. package/dist/annotation/tools/rectangle/index.d.ts +0 -1
  28. package/dist/annotation/utils/index.d.ts +0 -1
  29. package/dist/annotation/utils/math.d.ts +0 -1
  30. package/dist/annotation/utils/responsive.d.ts +1 -2
  31. package/dist/annotation/utils/styling.d.ts +3 -4
  32. package/dist/annotation/utils/touch.d.ts +0 -1
  33. package/dist/annotorious.css +1 -1
  34. package/dist/annotorious.es.js +2489 -2025
  35. package/dist/annotorious.es.js.map +1 -1
  36. package/dist/annotorious.js +1 -1
  37. package/dist/annotorious.js.map +1 -1
  38. package/dist/index.d.ts +1 -2
  39. package/dist/keyboardCommands.d.ts +2 -2
  40. package/dist/model/core/ImageAnnotation.d.ts +2 -3
  41. package/dist/model/core/Shape.d.ts +1 -2
  42. package/dist/model/core/ellipse/Ellipse.d.ts +1 -2
  43. package/dist/model/core/ellipse/ellipseUtils.d.ts +0 -1
  44. package/dist/model/core/ellipse/index.d.ts +0 -1
  45. package/dist/model/core/index.d.ts +0 -1
  46. package/dist/model/core/polygon/Polygon.d.ts +1 -2
  47. package/dist/model/core/polygon/index.d.ts +0 -1
  48. package/dist/model/core/polygon/polygonUtils.d.ts +0 -1
  49. package/dist/model/core/rectangle/Rectangle.d.ts +1 -2
  50. package/dist/model/core/rectangle/index.d.ts +0 -1
  51. package/dist/model/core/rectangle/rectangleUtils.d.ts +2 -3
  52. package/dist/model/core/shapeUtils.d.ts +2 -6
  53. package/dist/model/index.d.ts +0 -1
  54. package/dist/model/w3c/W3CImageAnnotation.d.ts +10 -0
  55. package/dist/model/w3c/W3CImageFormatAdapter.d.ts +11 -7
  56. package/dist/model/w3c/fragment/FragmentSelector.d.ts +2 -4
  57. package/dist/model/w3c/fragment/index.d.ts +0 -1
  58. package/dist/model/w3c/index.d.ts +1 -1
  59. package/dist/model/w3c/svg/SVG.d.ts +0 -1
  60. package/dist/model/w3c/svg/SVGSelector.d.ts +2 -4
  61. package/dist/model/w3c/svg/index.d.ts +0 -1
  62. package/dist/state/ImageAnnotationStore.d.ts +2 -3
  63. package/dist/state/ImageAnnotatorState.d.ts +4 -5
  64. package/dist/state/index.d.ts +0 -1
  65. package/dist/state/spatialTree.d.ts +2 -3
  66. package/dist/themes/index.d.ts +0 -1
  67. package/dist/themes/smart/index.d.ts +0 -1
  68. package/dist/themes/smart/setTheme.d.ts +1 -2
  69. package/package.json +16 -19
  70. package/src/Annotorious.css +13 -5
  71. package/src/Annotorious.ts +33 -17
  72. package/src/AnnotoriousOpts.ts +12 -16
  73. package/src/annotation/SVGAnnotationLayer.svelte +68 -35
  74. package/src/annotation/SVGAnnotationLayerPointerEvent.ts +2 -3
  75. package/src/annotation/Transform.ts +1 -1
  76. package/src/annotation/editors/Editor.svelte +10 -11
  77. package/src/annotation/editors/EditorMount.svelte +3 -3
  78. package/src/annotation/editors/Handle.svelte +66 -0
  79. package/src/annotation/editors/editorsRegistry.ts +2 -2
  80. package/src/annotation/editors/index.ts +2 -2
  81. package/src/annotation/editors/polygon/PolygonEditor.svelte +16 -16
  82. package/src/annotation/editors/rectangle/RectangleEditor.svelte +46 -43
  83. package/src/annotation/shapes/Ellipse.svelte +4 -4
  84. package/src/annotation/shapes/Polygon.svelte +4 -4
  85. package/src/annotation/shapes/Rectangle.svelte +4 -4
  86. package/src/annotation/tools/ToolMount.svelte +3 -3
  87. package/src/annotation/tools/drawingToolsRegistry.ts +2 -1
  88. package/src/annotation/tools/polygon/RubberbandPolygon.svelte +36 -11
  89. package/src/annotation/tools/rectangle/RubberbandRectangle.svelte +27 -11
  90. package/src/annotation/utils/responsive.ts +1 -1
  91. package/src/annotation/utils/styling.ts +18 -9
  92. package/src/annotation/utils/touch.ts +9 -1
  93. package/src/index.ts +1 -3
  94. package/src/keyboardCommands.ts +13 -5
  95. package/src/model/w3c/W3CImageAnnotation.ts +17 -0
  96. package/src/model/w3c/W3CImageFormatAdapter.ts +79 -32
  97. package/src/model/w3c/fragment/FragmentSelector.ts +5 -6
  98. package/src/model/w3c/index.ts +1 -0
  99. package/src/model/w3c/svg/SVG.ts +1 -2
  100. package/src/model/w3c/svg/SVGSelector.ts +11 -13
  101. package/src/state/ImageAnnotatorState.ts +5 -5
  102. package/src/state/spatialTree.ts +13 -6
  103. package/src/themes/dark/index.css +3 -3
  104. package/src/themes/light/index.css +2 -2
  105. package/src/themes/smart/setTheme.ts +2 -2
  106. package/dist/Annotorious.d.ts.map +0 -1
  107. package/dist/AnnotoriousOpts.d.ts.map +0 -1
  108. package/dist/annotation/SVGAnnotationLayerPointerEvent.d.ts.map +0 -1
  109. package/dist/annotation/Transform.d.ts.map +0 -1
  110. package/dist/annotation/editors/Handle.d.ts +0 -14
  111. package/dist/annotation/editors/Handle.d.ts.map +0 -1
  112. package/dist/annotation/editors/editorsRegistry.d.ts.map +0 -1
  113. package/dist/annotation/editors/index.d.ts.map +0 -1
  114. package/dist/annotation/editors/polygon/index.d.ts.map +0 -1
  115. package/dist/annotation/editors/rectangle/index.d.ts.map +0 -1
  116. package/dist/annotation/index.d.ts.map +0 -1
  117. package/dist/annotation/shapes/index.d.ts.map +0 -1
  118. package/dist/annotation/tools/DrawingToolConfig.d.ts.map +0 -1
  119. package/dist/annotation/tools/drawingToolsRegistry.d.ts.map +0 -1
  120. package/dist/annotation/tools/index.d.ts.map +0 -1
  121. package/dist/annotation/tools/polygon/index.d.ts.map +0 -1
  122. package/dist/annotation/tools/rectangle/index.d.ts.map +0 -1
  123. package/dist/annotation/utils/index.d.ts.map +0 -1
  124. package/dist/annotation/utils/math.d.ts.map +0 -1
  125. package/dist/annotation/utils/responsive.d.ts.map +0 -1
  126. package/dist/annotation/utils/styling.d.ts.map +0 -1
  127. package/dist/annotation/utils/touch.d.ts.map +0 -1
  128. package/dist/index.d.ts.map +0 -1
  129. package/dist/keyboardCommands.d.ts.map +0 -1
  130. package/dist/model/core/ImageAnnotation.d.ts.map +0 -1
  131. package/dist/model/core/Shape.d.ts.map +0 -1
  132. package/dist/model/core/ellipse/Ellipse.d.ts.map +0 -1
  133. package/dist/model/core/ellipse/ellipseUtils.d.ts.map +0 -1
  134. package/dist/model/core/ellipse/index.d.ts.map +0 -1
  135. package/dist/model/core/index.d.ts.map +0 -1
  136. package/dist/model/core/polygon/Polygon.d.ts.map +0 -1
  137. package/dist/model/core/polygon/index.d.ts.map +0 -1
  138. package/dist/model/core/polygon/polygonUtils.d.ts.map +0 -1
  139. package/dist/model/core/rectangle/Rectangle.d.ts.map +0 -1
  140. package/dist/model/core/rectangle/index.d.ts.map +0 -1
  141. package/dist/model/core/rectangle/rectangleUtils.d.ts.map +0 -1
  142. package/dist/model/core/shapeUtils.d.ts.map +0 -1
  143. package/dist/model/index.d.ts.map +0 -1
  144. package/dist/model/w3c/W3CImageFormatAdapter.d.ts.map +0 -1
  145. package/dist/model/w3c/fragment/FragmentSelector.d.ts.map +0 -1
  146. package/dist/model/w3c/fragment/index.d.ts.map +0 -1
  147. package/dist/model/w3c/index.d.ts.map +0 -1
  148. package/dist/model/w3c/svg/SVG.d.ts.map +0 -1
  149. package/dist/model/w3c/svg/SVGSelector.d.ts.map +0 -1
  150. package/dist/model/w3c/svg/index.d.ts.map +0 -1
  151. package/dist/state/ImageAnnotationStore.d.ts.map +0 -1
  152. package/dist/state/ImageAnnotatorState.d.ts.map +0 -1
  153. package/dist/state/index.d.ts.map +0 -1
  154. package/dist/state/spatialTree.d.ts.map +0 -1
  155. package/dist/themes/index.d.ts.map +0 -1
  156. package/dist/themes/smart/index.d.ts.map +0 -1
  157. package/dist/themes/smart/setTheme.d.ts.map +0 -1
  158. package/src/annotation/editors/Handle.ts +0 -21
@@ -1,19 +1,19 @@
1
- <script type="ts">
2
- import type { DrawingStyle } from '@annotorious/core';
1
+ <script lang="ts">
2
+ import type { DrawingStyleExpression } from '@annotorious/core';
3
3
  import type { Geometry, ImageAnnotation, RectangleGeometry } from '../../model';
4
4
  import { computeStyle } from '../utils/styling';
5
5
 
6
6
  /** Props **/
7
7
  export let annotation: ImageAnnotation;
8
8
  export let geom: Geometry;
9
- export let style: DrawingStyle | ((annotation: ImageAnnotation) => DrawingStyle) = undefined;
9
+ export let style: DrawingStyleExpression<ImageAnnotation> | undefined;
10
10
 
11
11
  $: computedStyle = computeStyle(annotation, style);
12
12
 
13
13
  $: ({ x, y, w, h } = geom as RectangleGeometry);
14
14
  </script>
15
15
 
16
- <g data-id={annotation.id}>
16
+ <g class="a9s-annotation" data-id={annotation.id}>
17
17
  <rect
18
18
  class="a9s-outer"
19
19
  style={computedStyle ? 'display:none;' : undefined}
@@ -23,9 +23,9 @@
23
23
 
24
24
  const cleanup: Function[] = [];
25
25
 
26
- const addEventListener = (name: string, handler: (evt: PointerEvent) => void, capture?: boolean) => {
27
- svg.addEventListener(name, handler, capture);
28
- cleanup.push(() => svg.removeEventListener(name, handler, capture));
26
+ const addEventListener = (name: keyof SVGSVGElementEventMap, handler: EventListenerOrEventListenerObject, capture?: boolean) => {
27
+ svg?.addEventListener(name, handler, capture);
28
+ cleanup.push(() => svg?.removeEventListener(name, handler, capture));
29
29
  }
30
30
 
31
31
  toolComponent = new tool({
@@ -9,10 +9,11 @@ export type DrawingToolOpts = {
9
9
 
10
10
  drawingMode?: DrawingMode;
11
11
 
12
- [key: string]: string;
12
+ [key: string]: any;
13
13
 
14
14
  }
15
15
 
16
+ // @ts-ignore
16
17
  const REGISTERED = new Map<DrawingTool, { tool: typeof SvelteComponent, opts?: DrawingToolOpts }>([
17
18
  ['rectangle', { tool: RubberbandRectangle }],
18
19
  ['polygon', { tool: RubberbandPolygon }]
@@ -1,4 +1,4 @@
1
- <script type="ts">
1
+ <script lang="ts">
2
2
  import { onMount, createEventDispatcher } from 'svelte';
3
3
  import type { DrawingMode } from '../../../AnnotoriousOpts';
4
4
  import { boundsFromPoints, computeArea, ShapeType, type Polygon } from '../../../model';
@@ -17,15 +17,24 @@
17
17
 
18
18
  let points: [number, number][] = [];
19
19
 
20
- let cursor: [number, number] = null;
20
+ let cursor: [number, number] | undefined;
21
+
22
+ // Keep track of the user keeping the finger
23
+ // in place. Long pauses will be interpreted like a
24
+ // double click and close the shape.
25
+ let touchPauseTimer: number | undefined;
21
26
 
22
27
  let isClosable: boolean = false;
23
28
 
24
29
  const CLOSE_DISTANCE = 20;
25
30
 
31
+ const TOUCH_PAUSE_LIMIT = 1500;
32
+
26
33
  $: handleSize = 10 / viewportScale;
27
34
 
28
- const onPointerDown = (evt: PointerEvent) => {
35
+ const onPointerDown = (event: Event) => {
36
+ const evt = event as PointerEvent;
37
+
29
38
  // Note that the event itself is ephemeral!
30
39
  const { timeStamp, offsetX, offsetY } = evt;
31
40
  lastPointerDown = { timeStamp, offsetX, offsetY };
@@ -40,7 +49,11 @@
40
49
  }
41
50
  }
42
51
 
43
- const onPointerMove = (evt: PointerEvent) => {
52
+ const onPointerMove = (event: Event) => {
53
+ const evt = event as PointerEvent;
54
+
55
+ if (touchPauseTimer) clearTimeout(touchPauseTimer);
56
+
44
57
  if (points.length > 0) {
45
58
  cursor = transform.elementToImage(evt.offsetX, evt.offsetY);
46
59
 
@@ -48,10 +61,20 @@
48
61
  const d = distance(cursor, points[0]) * viewportScale;
49
62
  isClosable = d < CLOSE_DISTANCE;
50
63
  }
64
+
65
+ if (evt.pointerType === 'touch') {
66
+ touchPauseTimer = setTimeout(() => {
67
+ onDblClick();
68
+ }, TOUCH_PAUSE_LIMIT);
69
+ }
51
70
  }
52
71
  }
53
72
 
54
- const onPointerUp = (evt: PointerEvent) => {
73
+ const onPointerUp = (event: Event) => {
74
+ const evt = event as PointerEvent;
75
+
76
+ if (touchPauseTimer) clearTimeout(touchPauseTimer);
77
+
55
78
  if (drawingMode === 'click') {
56
79
  const timeDifference = evt.timeStamp - lastPointerDown.timeStamp;
57
80
 
@@ -71,17 +94,17 @@
71
94
 
72
95
  cursor = point;
73
96
  } else {
74
- points.push(cursor);
97
+ points.push(cursor!);
75
98
  }
76
99
  } else {
77
100
  // Require minimum drag of 4px
78
101
  if (points.length === 1) {
79
- const dist = distance(points[0], cursor);
102
+ const dist = distance(points[0], cursor!);
80
103
 
81
104
  if (dist <= 4) {
82
105
  // Cancel
83
106
  points = [];
84
- cursor = null;
107
+ cursor = undefined;
85
108
 
86
109
  return;
87
110
  }
@@ -93,12 +116,14 @@
93
116
  if (isClosable) {
94
117
  stopDrawing();
95
118
  } else {
96
- points.push(cursor);
119
+ points.push(cursor!);
97
120
  }
98
121
  }
99
122
  }
100
123
 
101
124
  const onDblClick = () => {
125
+ if (!cursor) return;
126
+
102
127
  // Require min 3 points (incl. cursor) and minimum
103
128
  // polygon area
104
129
  const p = [...points, cursor];
@@ -114,7 +139,7 @@
114
139
  const area = computeArea(shape);
115
140
  if (area > 4) {
116
141
  points = [];
117
- cursor = null;
142
+ cursor = undefined;
118
143
 
119
144
  dispatch('create', shape);
120
145
  }
@@ -130,7 +155,7 @@
130
155
  }
131
156
 
132
157
  points = [];
133
- cursor = null;
158
+ cursor = undefined;
134
159
 
135
160
  dispatch('create', shape);
136
161
  }
@@ -1,4 +1,4 @@
1
- <script type="ts">
1
+ <script lang="ts">
2
2
  import { createEventDispatcher, onMount } from 'svelte';
3
3
  import type { DrawingMode } from '../../../AnnotoriousOpts';
4
4
  import { ShapeType, type Rectangle } from '../../../model';
@@ -13,13 +13,15 @@
13
13
 
14
14
  let lastPointerDown: number;
15
15
 
16
- let origin: [x: number, y: number];
16
+ let origin: [x: number, y: number] | undefined;
17
17
 
18
- let anchor: [number, number];
18
+ let anchor: [number, number] | undefined;
19
19
 
20
20
  let x: number, y: number, w: number, h: number;
21
21
 
22
- const onPointerDown = (evt: PointerEvent) => {
22
+ const onPointerDown = (event: Event) => {
23
+ const evt = event as PointerEvent;
24
+
23
25
  lastPointerDown = performance.now();
24
26
 
25
27
  if (drawingMode === 'drag') {
@@ -33,7 +35,9 @@
33
35
  }
34
36
  }
35
37
 
36
- const onPointerMove = (evt: PointerEvent) => {
38
+ const onPointerMove = (event: Event) => {
39
+ const evt = event as PointerEvent;
40
+
37
41
  if (origin) {
38
42
  anchor = transform.elementToImage(evt.offsetX, evt.offsetY);
39
43
 
@@ -44,7 +48,9 @@
44
48
  }
45
49
  }
46
50
 
47
- const onPointerUp = (evt: PointerEvent) => {
51
+ const onPointerUp = (event: Event) => {
52
+ const evt = event as PointerEvent;
53
+
48
54
  const timeDifference = performance.now() - lastPointerDown;
49
55
 
50
56
  if (drawingMode === 'click') {
@@ -52,7 +58,17 @@
52
58
  if (timeDifference > 300)
53
59
  return;
54
60
 
55
- evt.stopPropagation();
61
+ // This statement caused a weird bug on OSD: when starting
62
+ // to draw with a quick drag (<300ms), OSD got stuck in mouseNav
63
+ // mode. The image still moved with the mouse cursor, even though
64
+ // button was no longer pressed.
65
+ // I'm commenting out this statement as a fix. But there must have
66
+ // been a reason I put it here in the first place. Keep an eye ou
67
+ // for regressions.
68
+ //
69
+ // And if you are ever tempted to un-comment the statement: beware!
70
+
71
+ // evt.stopPropagation();
56
72
 
57
73
  if (origin) {
58
74
  stopDrawing();
@@ -71,8 +87,8 @@
71
87
  evt.stopPropagation();
72
88
  stopDrawing();
73
89
  } else {
74
- origin = null;
75
- anchor = null;
90
+ origin = undefined;
91
+ anchor = undefined;
76
92
  }
77
93
  }
78
94
  }
@@ -96,8 +112,8 @@
96
112
  dispatch('create', shape);
97
113
  }
98
114
 
99
- origin = null;
100
- anchor = null;
115
+ origin = undefined;
116
+ anchor = undefined;
101
117
  }
102
118
 
103
119
  onMount(() => {
@@ -42,7 +42,7 @@ export const enableResponsive = (image: HTMLImageElement | HTMLCanvasElement, sv
42
42
  set(scale);
43
43
  });
44
44
 
45
- resizeObserver.observe(svg.parentElement);
45
+ resizeObserver.observe(svg.parentElement!);
46
46
  }
47
47
 
48
48
  const destroy = () => {
@@ -1,19 +1,28 @@
1
- import type { DrawingStyle } from '@annotorious/core';
1
+ import type { DrawingStyleExpression } from '@annotorious/core';
2
2
  import type { ImageAnnotation } from '../../model';
3
3
 
4
- export const computeStyle = (annotation: ImageAnnotation, style?: DrawingStyle | ((a: ImageAnnotation) => DrawingStyle)) => {
4
+ export const computeStyle = (
5
+ annotation: ImageAnnotation,
6
+ style?: DrawingStyleExpression<ImageAnnotation>
7
+ ) => {
5
8
  const computed = typeof style === 'function' ? style(annotation) : style;
6
9
 
7
10
  if (computed) {
8
- const { fill, fillOpacity } = computed;
11
+ const { fill, fillOpacity, stroke, strokeWidth, strokeOpacity } = computed;
9
12
 
10
13
  let css = '';
11
-
12
- if (fill)
13
- css += `fill:${fill};stroke:${fill};`;
14
-
15
- css += `fill-opacity:${fillOpacity || '0.25'};`;
14
+
15
+ if (fill) {
16
+ css += `fill:${fill};`;
17
+ css += `fill-opacity:${fillOpacity || '0.25'};`;
18
+ }
19
+
20
+ if (stroke) {
21
+ css += `stroke:${stroke};`;
22
+ css += `stroke-width:${strokeWidth || '1'};`;
23
+ css += `stroke-opacity:${strokeOpacity || '1'};`;
24
+ }
16
25
 
17
26
  return css;
18
27
  }
19
- }
28
+ };
@@ -1 +1,9 @@
1
- export const isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
1
+ export const isTouch = (() => {
2
+ if (typeof window === 'undefined' || typeof navigator === 'undefined')
3
+ return false;
4
+
5
+ return 'ontouchstart' in window ||
6
+ navigator.maxTouchPoints > 0 ||
7
+ // @ts-ignore
8
+ navigator.msMaxTouchPoints > 0;
9
+ })();
package/src/index.ts CHANGED
@@ -24,7 +24,6 @@ export type {
24
24
  LifecycleEvents,
25
25
  ParseResult,
26
26
  PresentUser,
27
- Purpose,
28
27
  Selection,
29
28
  SelectionState,
30
29
  Store,
@@ -33,6 +32,5 @@ export type {
33
32
  User,
34
33
  W3CAnnotation,
35
34
  W3CAnnotationBody,
36
- W3CAnnotationTarget,
37
- W3CSelector
35
+ W3CAnnotationTarget
38
36
  } from '@annotorious/core';
@@ -1,6 +1,10 @@
1
1
  import type { Annotation, UndoStack } from '@annotorious/core';
2
2
 
3
- const isMac = navigator.userAgent.indexOf('Mac OS X') !== -1;
3
+ export const isMac = (() => {
4
+ if (typeof navigator === 'undefined') return false;
5
+
6
+ return navigator.userAgent.indexOf('Mac OS X') !== -1;
7
+ })();
4
8
 
5
9
  export const initKeyboardCommands = <T extends Annotation>(
6
10
  undoStack: UndoStack<T>,
@@ -9,15 +13,19 @@ export const initKeyboardCommands = <T extends Annotation>(
9
13
 
10
14
  const el = container || document;
11
15
 
12
- const onWinKeyDown = (event: KeyboardEvent) => {
13
- if (event.key === 'Z' && event.ctrlKey) {
16
+ const onWinKeyDown = (evt: Event) => {
17
+ const event = evt as KeyboardEvent;
18
+
19
+ if (event.key === 'z' && event.ctrlKey) {
14
20
  undoStack.undo();
15
- } else if (event.key === 'Y' && event.ctrlKey) {
21
+ } else if (event.key === 'y' && event.ctrlKey) {
16
22
  undoStack.redo()
17
23
  }
18
24
  };
19
25
 
20
- const onMacKeyDown = (event: KeyboardEvent) => {
26
+ const onMacKeyDown = (evt: Event) => {
27
+ const event = evt as KeyboardEvent;
28
+
21
29
  if (event.key === 'z' && event.metaKey) {
22
30
  if (event.shiftKey) {
23
31
  undoStack.redo()
@@ -0,0 +1,17 @@
1
+ import type { W3CAnnotation, W3CAnnotationTarget } from '@annotorious/core';
2
+ import type { FragmentSelector } from './fragment';
3
+ import type { SVGSelector } from './svg';
4
+
5
+ export interface W3CImageAnnotation extends W3CAnnotation {
6
+
7
+ target: W3CImageAnnotationTarget | W3CImageAnnotationTarget[];
8
+
9
+ }
10
+
11
+ export interface W3CImageAnnotationTarget extends W3CAnnotationTarget {
12
+
13
+ selector: W3CImageSelector | W3CImageSelector[];
14
+
15
+ }
16
+
17
+ export type W3CImageSelector = FragmentSelector | SVGSelector;
@@ -1,86 +1,133 @@
1
1
  import { v4 as uuidv4 } from 'uuid';
2
- import { parseW3CBodies, serializeW3CBodies } from '@annotorious/core';
2
+ import { parseW3CUser, parseW3CBodies, serializeW3CBodies } from '@annotorious/core';
3
3
  import type { FormatAdapter, ParseResult, W3CAnnotation } from '@annotorious/core';
4
4
  import { ShapeType } from '../core';
5
5
  import type { ImageAnnotation, RectangleGeometry } from '../core';
6
- import type { FragmentSelector } from './fragment';
6
+ import type {FragmentSelector } from './fragment';
7
7
  import { parseFragmentSelector, serializeFragmentSelector } from './fragment';
8
8
  import type { SVGSelector } from './svg';
9
9
  import { parseSVGSelector, serializeSVGSelector } from './svg';
10
+ import type { W3CImageAnnotation } from './W3CImageAnnotation';
10
11
 
11
- export type W3CImageFormatAdapter = FormatAdapter<ImageAnnotation, W3CAnnotation>;
12
+ export type W3CImageFormatAdapter = FormatAdapter<ImageAnnotation, W3CImageAnnotation>;
13
+
14
+ export interface W3CImageFormatAdapterOpts {
15
+
16
+ strict: boolean;
17
+
18
+ invertY: boolean;
19
+
20
+ }
12
21
 
13
22
  export const W3CImageFormat = (
14
23
  source: string,
15
- invertY: boolean = false
24
+ opts: W3CImageFormatAdapterOpts = { strict: true, invertY: false }
16
25
  ): W3CImageFormatAdapter => {
17
26
 
18
27
  const parse = (serialized: W3CAnnotation) =>
19
- parseW3CImageAnnotation(serialized, invertY);
28
+ parseW3CImageAnnotation(serialized, opts);
20
29
 
21
30
  const serialize = (annotation: ImageAnnotation) =>
22
- serializeW3CImageAnnotation(annotation, source);
31
+ serializeW3CImageAnnotation(annotation, source, opts);
23
32
 
24
33
  return { parse, serialize }
25
34
  }
26
35
 
27
36
  export const parseW3CImageAnnotation = (
28
37
  annotation: W3CAnnotation,
29
- invertY: boolean = false
38
+ opts: W3CImageFormatAdapterOpts = { strict: true, invertY: false }
30
39
  ): ParseResult<ImageAnnotation> => {
31
40
  const annotationId = annotation.id || uuidv4();
32
41
 
33
- const { body, ...rest } = annotation;
42
+ const {
43
+ creator,
44
+ created,
45
+ modified,
46
+ body,
47
+ ...rest
48
+ } = annotation;
34
49
 
35
- const bodies = parseW3CBodies(body, annotationId);
50
+ const bodies = parseW3CBodies(body || [], annotationId);
36
51
 
37
- const target = Array.isArray(annotation.target) ? annotation.target[0] : annotation.target;
52
+ const w3cTarget = Array.isArray(annotation.target)
53
+ ? annotation.target[0] : annotation.target;
38
54
 
39
- const w3cSelector = Array.isArray(target.selector) ? target.selector[0] : target.selector;
55
+ const w3cSelector = Array.isArray(w3cTarget.selector)
56
+ ? w3cTarget.selector[0] : w3cTarget.selector;
40
57
 
41
58
  const selector =
42
- w3cSelector.type === 'FragmentSelector' ?
43
- parseFragmentSelector(w3cSelector as FragmentSelector, invertY) :
44
- w3cSelector.type === 'SvgSelector' ?
59
+ w3cSelector?.type === 'FragmentSelector' ?
60
+ parseFragmentSelector(w3cSelector as FragmentSelector, opts.invertY) :
61
+ w3cSelector?.type === 'SvgSelector' ?
45
62
  parseSVGSelector(w3cSelector as SVGSelector) : undefined;
46
63
 
47
- return selector ? {
64
+ return (selector || !opts.strict) ? {
48
65
  parsed: {
49
66
  ...rest,
50
67
  id: annotationId,
51
68
  bodies,
52
69
  target: {
53
- ...rest.target,
70
+ created: created ? new Date(created) : undefined,
71
+ creator: parseW3CUser(creator),
72
+ updated: modified ? new Date(modified) : undefined,
73
+ ...(Array.isArray(rest.target) ? rest.target[0] : rest.target),
54
74
  annotation: annotationId,
55
- selector
75
+ selector: selector || w3cSelector
56
76
  }
57
77
  }
58
78
  } : {
59
- error: Error(`Unknown selector type: ${w3cSelector.type}`)
79
+ error: Error(`Invalid selector: ${JSON.stringify(w3cSelector)}`)
60
80
  };
61
-
62
81
  }
63
82
 
64
83
  export const serializeW3CImageAnnotation = (
65
84
  annotation: ImageAnnotation,
66
- source: string
67
- ): W3CAnnotation => {
68
- const shape = annotation.target.selector;
69
-
70
- const selector =
71
- shape.type == ShapeType.RECTANGLE ?
72
- serializeFragmentSelector(shape.geometry as RectangleGeometry) :
73
- serializeSVGSelector(shape);
74
-
75
- return {
85
+ source: string,
86
+ opts: W3CImageFormatAdapterOpts = { strict: true, invertY: false }
87
+ ): W3CImageAnnotation => {
88
+ const {
89
+ selector,
90
+ creator,
91
+ created,
92
+ updated,
93
+ updatedBy, // Excluded from serialization
94
+ ...rest
95
+ } = annotation.target;
96
+
97
+ let w3cSelector: FragmentSelector | SVGSelector | unknown;
98
+
99
+ try {
100
+ w3cSelector = selector.type == ShapeType.RECTANGLE ?
101
+ serializeFragmentSelector(selector.geometry as RectangleGeometry) :
102
+ serializeSVGSelector(selector);
103
+ } catch (error) {
104
+ if (opts.strict)
105
+ throw error;
106
+ else
107
+ w3cSelector = selector;
108
+ }
109
+
110
+ const serialized = {
76
111
  ...annotation,
77
112
  '@context': 'http://www.w3.org/ns/anno.jsonld',
78
113
  id: annotation.id,
79
114
  type: 'Annotation',
80
115
  body: serializeW3CBodies(annotation.bodies),
116
+ created: created?.toISOString(),
117
+ creator,
118
+ modified: updated?.toISOString(),
81
119
  target: {
120
+ ...rest,
82
121
  source,
83
- selector
122
+ selector: w3cSelector
84
123
  }
85
- };
86
- };
124
+ } as W3CImageAnnotation;
125
+
126
+ // Remove core properties that should not appear in the W3C annotation
127
+ delete serialized.bodies;
128
+
129
+ if ('annotation' in serialized.target)
130
+ delete serialized.target.annotation;
131
+
132
+ return serialized;
133
+ }
@@ -1,15 +1,14 @@
1
- import type { W3CSelector } from '@annotorious/core';
2
- import { ShapeType } from '../../core';
3
1
  import type { Rectangle, RectangleGeometry } from '../../core';
2
+ import { ShapeType } from '../../core';
4
3
 
5
- export interface FragmentSelector extends W3CSelector {
4
+ export interface FragmentSelector {
6
5
 
7
6
  type: 'FragmentSelector';
8
7
 
9
8
  conformsTo: 'http://www.w3.org/TR/media-frags/',
10
9
 
11
10
  value: string;
12
-
11
+
13
12
  }
14
13
 
15
14
  export const parseFragmentSelector = (
@@ -46,7 +45,7 @@ export const parseFragmentSelector = (
46
45
  }
47
46
  }
48
47
  }
49
- };
48
+ }
50
49
 
51
50
  export const serializeFragmentSelector = (geometry: RectangleGeometry): FragmentSelector => {
52
51
  const { x, y, w, h } = geometry;
@@ -56,4 +55,4 @@ export const serializeFragmentSelector = (geometry: RectangleGeometry): Fragment
56
55
  conformsTo: 'http://www.w3.org/TR/media-frags/',
57
56
  value: `xywh=pixel:${x},${y},${w},${h}`
58
57
  };
59
- };
58
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './fragment';
2
2
  export * from './svg';
3
+ export * from './W3CImageAnnotation';
3
4
  export * from './W3CImageFormatAdapter';
@@ -13,8 +13,7 @@ export const sanitize = (doc: Element | Document) => {
13
13
  // Remove script tags
14
14
  const scripts = doc.getElementsByTagName('script');
15
15
 
16
- Array.from(scripts).reverse().forEach(el =>
17
- el.parentNode.removeChild(el));
16
+ Array.from(scripts).reverse().forEach(el => el.parentNode!.removeChild(el));
18
17
 
19
18
  Array.from(doc.querySelectorAll('*')).forEach(cleanEl);
20
19
 
@@ -1,14 +1,13 @@
1
- import type { W3CSelector } from '@annotorious/core';
2
- import { boundsFromPoints, ShapeType } from '../../core';
3
1
  import type { Ellipse, EllipseGeometry, Polygon, PolygonGeometry, Shape } from '../../core';
4
- import { SVG_NAMESPACE, insertSVGNamespace, sanitize } from './SVG';
2
+ import { boundsFromPoints, ShapeType } from '../../core';
3
+ import { insertSVGNamespace, sanitize, SVG_NAMESPACE } from './SVG';
5
4
 
6
- export interface SVGSelector extends W3CSelector {
5
+ export interface SVGSelector {
7
6
 
8
7
  type: 'SvgSelector';
9
8
 
10
9
  value: string;
11
-
10
+
12
11
  }
13
12
 
14
13
  const parseSVGXML = (value: string): Element => {
@@ -29,9 +28,6 @@ const parseSVGXML = (value: string): Element => {
29
28
 
30
29
  const parseSVGPolygon = (value: string): Polygon => {
31
30
  const [a, b, str] = value.match(/(<polygon points=["|'])([^("|')]*)/) || [];
32
-
33
- if (!str) return;
34
-
35
31
  const points = str.split(' ').map((p) => p.split(',').map(parseFloat));
36
32
 
37
33
  return {
@@ -46,10 +42,10 @@ const parseSVGPolygon = (value: string): Polygon => {
46
42
  const parseSVGEllipse = (value: string): Ellipse => {
47
43
  const doc = parseSVGXML(value);
48
44
 
49
- const cx = parseFloat(doc.getAttribute('cx'));
50
- const cy = parseFloat(doc.getAttribute('cy'));
51
- const rx = parseFloat(doc.getAttribute('rx'));
52
- const ry = parseFloat(doc.getAttribute('ry'));
45
+ const cx = parseFloat(doc.getAttribute('cx')!);
46
+ const cy = parseFloat(doc.getAttribute('cy')!);
47
+ const rx = parseFloat(doc.getAttribute('rx')!);
48
+ const ry = parseFloat(doc.getAttribute('ry')!);
53
49
 
54
50
  const bounds = {
55
51
  minX: cx - rx,
@@ -77,10 +73,12 @@ export const parseSVGSelector = <T extends Shape>(valueOrSelector: SVGSelector |
77
73
  return parseSVGPolygon(value) as unknown as T;
78
74
  else if (value.includes('<ellipse '))
79
75
  return parseSVGEllipse(value) as unknown as T;
76
+ else
77
+ throw 'Unsupported SVG shape: ' + value;
80
78
  }
81
79
 
82
80
  export const serializeSVGSelector = (shape: Shape): SVGSelector => {
83
- let value: string;
81
+ let value: string | undefined;
84
82
 
85
83
  if (shape.type === ShapeType.POLYGON) {
86
84
  const geom = shape.geometry as PolygonGeometry;