tracky-mouse 2.5.0 → 2.7.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 (73) hide show
  1. package/README.md +2 -1
  2. package/audio/click-press.wav +0 -0
  3. package/audio/click-release.wav +0 -0
  4. package/audio/middle-click-press.wav +0 -0
  5. package/audio/middle-click-release.wav +0 -0
  6. package/audio/pause.wav +0 -0
  7. package/audio/unpause.wav +0 -0
  8. package/audio.js +145 -0
  9. package/images/head-not-found.svg +135 -0
  10. package/images/manual-takeback.svg +127 -0
  11. package/locales/ar/translation.json +199 -202
  12. package/locales/ar-EG/translation.json +199 -202
  13. package/locales/bg/translation.json +199 -202
  14. package/locales/bn/translation.json +199 -202
  15. package/locales/ca/translation.json +199 -202
  16. package/locales/ce/translation.json +199 -202
  17. package/locales/ceb/translation.json +199 -202
  18. package/locales/cs/translation.json +199 -202
  19. package/locales/da/translation.json +199 -202
  20. package/locales/de/translation.json +199 -202
  21. package/locales/el/translation.json +199 -202
  22. package/locales/emoji/translation.json +199 -202
  23. package/locales/en/translation.json +199 -202
  24. package/locales/eo/translation.json +199 -202
  25. package/locales/es/translation.json +199 -202
  26. package/locales/eu/translation.json +199 -202
  27. package/locales/fa/translation.json +199 -202
  28. package/locales/fi/translation.json +199 -202
  29. package/locales/fr/translation.json +199 -202
  30. package/locales/gu/translation.json +199 -202
  31. package/locales/ha/translation.json +199 -202
  32. package/locales/he/translation.json +199 -202
  33. package/locales/hi/translation.json +199 -202
  34. package/locales/hr/translation.json +199 -202
  35. package/locales/hu/translation.json +199 -202
  36. package/locales/hy/translation.json +199 -202
  37. package/locales/id/translation.json +199 -202
  38. package/locales/it/translation.json +199 -202
  39. package/locales/ja/translation.json +199 -202
  40. package/locales/jv/translation.json +199 -202
  41. package/locales/ko/translation.json +199 -202
  42. package/locales/mr/translation.json +199 -202
  43. package/locales/ms/translation.json +199 -202
  44. package/locales/nan/translation.json +199 -202
  45. package/locales/nb/translation.json +199 -202
  46. package/locales/nl/translation.json +199 -202
  47. package/locales/pa/translation.json +199 -202
  48. package/locales/pl/translation.json +199 -202
  49. package/locales/pt/translation.json +199 -202
  50. package/locales/pt-BR/translation.json +199 -202
  51. package/locales/ro/translation.json +199 -202
  52. package/locales/ru/translation.json +199 -202
  53. package/locales/sk/translation.json +199 -202
  54. package/locales/sl/translation.json +199 -202
  55. package/locales/sr/translation.json +199 -202
  56. package/locales/sv/translation.json +199 -202
  57. package/locales/sw/translation.json +199 -202
  58. package/locales/ta/translation.json +199 -202
  59. package/locales/te/translation.json +199 -202
  60. package/locales/th/translation.json +199 -202
  61. package/locales/tl/translation.json +199 -202
  62. package/locales/tr/translation.json +199 -202
  63. package/locales/tt/translation.json +199 -202
  64. package/locales/uk/translation.json +199 -202
  65. package/locales/ur/translation.json +199 -202
  66. package/locales/uz/translation.json +199 -202
  67. package/locales/vi/translation.json +199 -202
  68. package/locales/war/translation.json +199 -202
  69. package/locales/zh/translation.json +199 -202
  70. package/locales/zh-simplified/translation.json +200 -203
  71. package/package.json +4 -1
  72. package/tracky-mouse.css +73 -7
  73. package/tracky-mouse.js +590 -343
package/README.md CHANGED
@@ -147,7 +147,7 @@ This is the callback that you need to define to simulate pointer movement.
147
147
  This starts up the dwell clicker.
148
148
 
149
149
  Arguments:
150
- - `config.targets` (required): a CSS selector for the elements to click. Anything else will be ignored.
150
+ - `config.targets` (required): a CSS selector for the elements to click. Anything else will be ignored (except as an occluder).
151
151
  - `config.shouldDrag(el)` (optional): a function that returns true if the element should be dragged rather than simply clicked.
152
152
  - `config.noCenter(el)` (optional): a function that returns true if the element should be clicked anywhere on the element, rather than always at the center.
153
153
  - `config.retarget` (optional): an array of `{ from, to, withinMargin }` objects, which define rules for dynamically changing what is hovered/clicked when the mouse is over a different element.
@@ -156,6 +156,7 @@ Arguments:
156
156
  - `withinMargin` (optional): a number of pixels within which to consider the mouse over the `to` element. Default to infinity.
157
157
  - `config.isEquivalentTarget(el1, el2)` (optional): a function that returns true if two elements should be considered part of the same control, i.e. if clicking either should do the same thing. Elements that are equal are always considered equivalent even if you return false. This option is used for preventing the system from detecting occluding elements as separate controls, and rejecting the click. (When an occlusion is detected, it flashes a red box.)
158
158
  - `config.dwellClickEvenIfPaused(el)` (optional): a function that returns true if the element should be clicked even while dwell clicking is otherwise paused. Use this for a dwell clicking toggle button, so it's possible to resume dwell clicking. With dwell clicking it's important to let users take a break, since otherwise you have to constantly move the cursor in order to not click on things!
159
+ - `config.shouldClickThrough(el)` (optional): a function that returns true if the element should be totally ignored, allowing clicking on content behind it. Prefer `pointer-events: none` when possible, which will work for all input methods. Use this only if you need to differentiate input methods. Default: `(el) => el.matches(".tracky-mouse-click-through, .tracky-mouse-click-through *")`
159
160
  - `config.click({x, y, target})` (required): a function to trigger a click on the given target element.
160
161
  - `config.beforeDispatch()` (optional): a function to call before a pointer event is dispatched. For detecting un-trusted user gestures, outside of an event handler.
161
162
  - `config.afterDispatch()` (optional): a function to call after a pointer event is dispatched. For detecting un-trusted user gestures, outside of an event handler.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/audio.js ADDED
@@ -0,0 +1,145 @@
1
+
2
+ /** @type {AudioContext | null} */
3
+ let actx = null;
4
+
5
+ const audioPath = new URL("./audio", import.meta.url).href;
6
+ const audioFiles = {
7
+ // https://opengameart.org/content/51-ui-sound-effects-buttons-switches-and-clicks (CC0)
8
+ clickPress: `${audioPath}/click-press.wav`,
9
+ clickRelease: `${audioPath}/click-release.wav`,
10
+ // https://opengameart.org/content/middle-mouse-click (original recordings for this project, released under CC0)
11
+ middleClickPress: `${audioPath}/middle-click-press.wav`,
12
+ middleClickRelease: `${audioPath}/middle-click-release.wav`,
13
+ // https://opengameart.org/content/9-sci-fi-computer-sounds-and-beeps (CC-BY 3.0)
14
+ pause: `${audioPath}/pause.wav`,
15
+ unpause: `${audioPath}/unpause.wav`,
16
+ };
17
+ const audioBuffers = {};
18
+
19
+ // Sound effects are disabled by default because the dwell clicker can be initialized without the UI,
20
+ // in which case there's no UI to disable the sound effects from.
21
+ // The actual default in the app is separate.
22
+ export let audioEnabled = false;
23
+
24
+ export function setAudioEnabled(enabled) {
25
+ audioEnabled = enabled;
26
+ };
27
+
28
+ export function initAudio() {
29
+ if (actx === null) {
30
+ actx = new AudioContext();
31
+
32
+ // "User gesture" requirements cripple this accessibility feature,
33
+ // but we have to at least try to work around it.
34
+ const unsuspend = (event) => {
35
+ if (actx.state === "suspended") {
36
+ console.log("Starting suspended audio context via", event.type);
37
+ actx.resume();
38
+ }
39
+ };
40
+ addEventListener("keydown", unsuspend);
41
+ addEventListener("pointerdown", unsuspend);
42
+
43
+ // Load audio files
44
+ for (const [key, url] of Object.entries(audioFiles)) {
45
+ fetch(url)
46
+ .then((response) => response.arrayBuffer())
47
+ .then((arrayBuffer) => actx.decodeAudioData(arrayBuffer))
48
+ .then((audioBuffer) => {
49
+ audioBuffers[key] = audioBuffer;
50
+ })
51
+ .catch((error) => {
52
+ console.error("Error loading audio file:", url, error);
53
+ });
54
+ }
55
+ }
56
+ }
57
+
58
+ export function playSound(soundId, { delay = 0, playbackRate = 1, volume = 1 } = {}) {
59
+ if (audioEnabled && actx && actx.state === "running" && audioBuffers[soundId]) {
60
+ const gain = actx.createGain();
61
+ const source = actx.createBufferSource();
62
+ source.buffer = audioBuffers[soundId];
63
+ source.connect(gain).connect(actx.destination);
64
+ gain.gain.value = volume;
65
+ source.playbackRate.value = playbackRate;
66
+ source.start(actx.currentTime + delay);
67
+ }
68
+ }
69
+
70
+ export class SleepSweep {
71
+ constructor(ctx = actx) {
72
+ this.ctx = ctx;
73
+
74
+ this.osc = this.ctx.createOscillator();
75
+ this.osc.type = "sine";
76
+
77
+ this.gain = this.ctx.createGain();
78
+ this.gain.gain.value = 0;
79
+
80
+ // Not sure how much this filter is actually doing
81
+ this.filter = this.ctx.createBiquadFilter();
82
+ this.filter.type = "lowpass";
83
+ this.filter.frequency.value = 800;
84
+
85
+ this.osc.connect(this.filter);
86
+ this.filter.connect(this.gain);
87
+ this.gain.connect(this.ctx.destination);
88
+
89
+ this.osc.start();
90
+
91
+ this.enabled = false;
92
+ this.active = false;
93
+ this.timeOfLastGestureTrigger = 0; // audio context time
94
+ this.maxEffectDurationAfterGestureTrigger = 2.0; // seconds
95
+ }
96
+
97
+ update(gestureProgress) {
98
+ const now = this.ctx.currentTime;
99
+ if (!this.enabled || !audioEnabled) {
100
+ this.gain.gain.setTargetAtTime(0, now, 0.05);
101
+ return;
102
+ }
103
+
104
+ const effectStartFraction = 0.5;
105
+
106
+ if (gestureProgress < effectStartFraction) {
107
+ if (this.timeOfLastGestureTrigger + this.maxEffectDurationAfterGestureTrigger < now) {
108
+ this.gain.gain.setTargetAtTime(0, now, 0.05);
109
+ }
110
+ return;
111
+ }
112
+
113
+ const effectProgress = (gestureProgress - effectStartFraction) / (1 - effectStartFraction);
114
+
115
+ const volume = effectProgress * effectProgress * 0.4;
116
+
117
+ const baseFreq = 120;
118
+ const freq = baseFreq + effectProgress * 40;
119
+
120
+ this.gain.gain.setTargetAtTime(volume, now, 0.05);
121
+ this.osc.frequency.setTargetAtTime(freq, now, 0.05);
122
+
123
+ this.filter.frequency.setTargetAtTime(800 + effectProgress * 1200, now, 0.05);
124
+ }
125
+
126
+ sleepModeWasToggled(nowInSleepMode) {
127
+ const now = this.ctx.currentTime;
128
+ const currentFreq = this.osc.frequency.value;
129
+ const targetFreq = currentFreq * (nowInSleepMode ? 0.5 : 2.0);
130
+
131
+ this.osc.frequency.cancelScheduledValues(now);
132
+ this.osc.frequency.setValueAtTime(currentFreq, now);
133
+ this.osc.frequency.exponentialRampToValueAtTime(targetFreq, now + (nowInSleepMode ? 1 : 0.15));
134
+
135
+ this.gain.gain.setTargetAtTime(0, now + 0.05, nowInSleepMode ? 0.2 : 0.1); // should be <= this.maxEffectDurationAfterGestureTrigger
136
+
137
+ playSound(nowInSleepMode ? "pause" : "unpause");
138
+
139
+ this.timeOfLastGestureTrigger = now;
140
+ }
141
+
142
+ setEnabled(enabled) {
143
+ this.enabled = enabled;
144
+ }
145
+ }
@@ -0,0 +1,135 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ width="64.0px"
6
+ height="64.0px"
7
+ viewBox="0 0 64.0 64.0"
8
+ version="1.1"
9
+ id="SVGRoot"
10
+ sodipodi:docname="head-not-found.svg"
11
+ inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
12
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ xmlns:svg="http://www.w3.org/2000/svg">
16
+ <sodipodi:namedview
17
+ id="namedview1054"
18
+ pagecolor="#505050"
19
+ bordercolor="#ffffff"
20
+ borderopacity="1"
21
+ inkscape:showpageshadow="0"
22
+ inkscape:pageopacity="0"
23
+ inkscape:pagecheckerboard="1"
24
+ inkscape:deskcolor="#505050"
25
+ inkscape:document-units="px"
26
+ showgrid="true"
27
+ inkscape:zoom="5.828192"
28
+ inkscape:cx="18.787988"
29
+ inkscape:cy="18.616408"
30
+ inkscape:current-layer="layer1"
31
+ inkscape:window-width="1920"
32
+ inkscape:window-height="1009"
33
+ inkscape:window-x="-8"
34
+ inkscape:window-y="-8"
35
+ inkscape:window-maximized="1">
36
+ <inkscape:grid
37
+ type="xygrid"
38
+ id="grid1540"
39
+ empspacing="8"
40
+ originx="0"
41
+ originy="0"
42
+ spacingy="1"
43
+ spacingx="1"
44
+ units="px"
45
+ visible="true" />
46
+ </sodipodi:namedview>
47
+ <defs
48
+ id="defs1049" />
49
+ <g
50
+ inkscape:label="Layer 1"
51
+ inkscape:groupmode="layer"
52
+ id="layer1">
53
+ <g
54
+ id="g3699"
55
+ transform="translate(0.44861459,3.114008)"
56
+ inkscape:label="head/face">
57
+ <path
58
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000;stroke-dasharray:1,3;stroke-dashoffset:0"
59
+ d="m 30.141868,12.978181 c 14.494394,-1.150235 14.279704,11.10915 12.398736,17.726188 -1.811897,6.374057 -1.014423,8.596418 -3.989219,11.681623 C 35.57659,45.471196 28.068583,46.316195 24.159832,42.123198 21.531192,39.303401 21.197535,35.865302 19.751164,31.11657 17.067937,22.306985 19.888772,13.791838 30.141868,12.978181 Z"
60
+ id="path1660"
61
+ sodipodi:nodetypes="ssssss"
62
+ inkscape:label="head outline" />
63
+ <path
64
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
65
+ d="m 34.049842,27.080668 c 0.01551,1.045547 0.30038,1.817774 0.538426,2.018566 2.96671,2.50248 -1.324581,6.211798 -3.858385,3.634784"
66
+ id="path3273"
67
+ sodipodi:nodetypes="csc"
68
+ inkscape:label="nose" />
69
+ <path
70
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000;stroke-dasharray:1,3;stroke-dashoffset:0"
71
+ d="m 37.164979,36.569026 c -1.967047,3.259348 -6.756567,4.047549 -9.731686,0.636254"
72
+ id="path3273-3"
73
+ sodipodi:nodetypes="cc"
74
+ inkscape:label="mouth" />
75
+ <path
76
+ d="m 29.303443,26.583633 c -0.286386,-3.512331 -5.95152,-3.14935 -6.125787,0.0025 1.177451,1.011134 4.143639,1.403238 6.125787,-0.0025 z"
77
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
78
+ id="path3547"
79
+ sodipodi:nodetypes="ccc"
80
+ inkscape:label="eye" />
81
+ <path
82
+ d="m 40.199316,26.313676 c -0.388768,-3.372612 -6.017232,-3.133673 -6.152333,0.105298 1.343476,1.302451 4.337462,1.588922 6.152333,-0.105298 z"
83
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
84
+ id="path3547-3"
85
+ sodipodi:nodetypes="ccc"
86
+ inkscape:label="eye" />
87
+ <circle
88
+ style="fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
89
+ id="path3815"
90
+ cx="26.390192"
91
+ cy="26.663704"
92
+ r="0.75630671"
93
+ inkscape:label="pupil" />
94
+ <circle
95
+ style="fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
96
+ id="path3815-2"
97
+ cx="37.200237"
98
+ cy="26.602921"
99
+ r="0.75630671"
100
+ inkscape:label="pupil" />
101
+ <path
102
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
103
+ d="m 23.975227,24.831052 c 2.776779,-1.22921 3.865192,-0.537986 5.217286,-0.146685"
104
+ id="path3942"
105
+ sodipodi:nodetypes="cc"
106
+ inkscape:label="eye brow"
107
+ transform="translate(-0.44861459,-3.114008)" />
108
+ <path
109
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
110
+ d="m 34.747591,24.816768 c 2.776779,-1.22921 3.865192,-0.537986 5.217286,-0.146685"
111
+ id="path3942-6"
112
+ sodipodi:nodetypes="cc"
113
+ inkscape:label="eye brow"
114
+ transform="translate(-0.44861459,-3.114008)" />
115
+ </g>
116
+ <g
117
+ id="g7"
118
+ inkscape:label="magnifying glass"
119
+ transform="rotate(-35.29423,47.928286,36.354572)">
120
+ <circle
121
+ style="fill:none;stroke:#f9f9f9;stroke-linecap:round;-inkscape-stroke:none"
122
+ id="path4"
123
+ cx="43.5"
124
+ cy="36.5"
125
+ r="9"
126
+ inkscape:label="lens" />
127
+ <path
128
+ style="fill:none;stroke:#f9f9f9;stroke-linecap:round;-inkscape-stroke:none"
129
+ d="m 43,46 v 10 c 0,0 0,1 1,1 1,0 1,-1 1,-1 V 46"
130
+ id="path7"
131
+ sodipodi:nodetypes="ccscc"
132
+ inkscape:label="handle" />
133
+ </g>
134
+ </g>
135
+ </svg>
@@ -0,0 +1,127 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ width="64.0px"
6
+ height="64.0px"
7
+ viewBox="0 0 64.0 64.0"
8
+ version="1.1"
9
+ id="SVGRoot"
10
+ sodipodi:docname="manual-takeback.svg"
11
+ inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
12
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ xmlns:svg="http://www.w3.org/2000/svg">
16
+ <sodipodi:namedview
17
+ id="namedview1054"
18
+ pagecolor="#505050"
19
+ bordercolor="#ffffff"
20
+ borderopacity="1"
21
+ inkscape:showpageshadow="true"
22
+ inkscape:pageopacity="0"
23
+ inkscape:pagecheckerboard="true"
24
+ inkscape:deskcolor="#505050"
25
+ inkscape:document-units="px"
26
+ showgrid="false"
27
+ inkscape:zoom="8.0000001"
28
+ inkscape:cx="20.8125"
29
+ inkscape:cy="35.8125"
30
+ inkscape:current-layer="layer1"
31
+ inkscape:window-width="1920"
32
+ inkscape:window-height="1009"
33
+ inkscape:window-x="-8"
34
+ inkscape:window-y="-8"
35
+ inkscape:window-maximized="1"
36
+ inkscape:clip-to-page="false">
37
+ <inkscape:grid
38
+ type="xygrid"
39
+ id="grid1540"
40
+ empspacing="8"
41
+ originx="0"
42
+ originy="0"
43
+ spacingy="1"
44
+ spacingx="1"
45
+ units="px"
46
+ visible="false"
47
+ empcolor="#0099e5"
48
+ empopacity="0.55686275"
49
+ color="#0099e5"
50
+ opacity="0.29803922" />
51
+ </sodipodi:namedview>
52
+ <defs
53
+ id="defs1049" />
54
+ <g
55
+ inkscape:label="Layer 1"
56
+ inkscape:groupmode="layer"
57
+ id="layer1">
58
+ <path
59
+ id="path4104"
60
+ style="fill:none;stroke:#ffffff;stroke-linecap:square;stop-color:#000000"
61
+ inkscape:label="mouse"
62
+ d="M 31.999999 16 L 31.999999 19 " />
63
+ <path
64
+ id="path30"
65
+ style="fill:none;stroke:#ffffff;stroke-linecap:round;stop-color:#000000"
66
+ inkscape:label="mouse"
67
+ d="M 20.749999 30 L 27.999999 30 " />
68
+ <path
69
+ id="path29"
70
+ style="fill:none;stroke:#ffffff;stroke-linecap:square;stop-color:#000000"
71
+ inkscape:label="mouse"
72
+ d="M 30.13762 25.321063 C 29.999999 24.639123 29.999999 23.81956 29.999999 23 C 29.999999 21.000002 30.000001 19 31.999999 19 C 33.899211 19 33.99492 20.803509 33.999743 22.697898 " />
73
+ <path
74
+ id="path26"
75
+ style="fill:none;stroke:#ffffff;stroke-linecap:round;stop-color:#000000"
76
+ inkscape:label="mouse"
77
+ d="M 23.926371 45.094776 C 20.801453 42.003936 20 36.894562 20 32 C 20 24.268022 22.000009 16 32 16 C 38.458981 16 41.580495 19.449298 42.981274 23.975549 " />
78
+ <path
79
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
80
+ d="M 7.0111673,32 H 18.999999"
81
+ id="path3594"
82
+ sodipodi:nodetypes="cc"
83
+ inkscape:label="stem" />
84
+ <path
85
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
86
+ d="M 11.210169,35.866575 6.3738751,31.919639 11.166217,27.998078"
87
+ id="path3596"
88
+ sodipodi:nodetypes="ccc"
89
+ inkscape:label="head" />
90
+ <path
91
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
92
+ d="M 59.988831,32 H 53"
93
+ id="path20"
94
+ sodipodi:nodetypes="cc"
95
+ inkscape:label="stem" />
96
+ <path
97
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
98
+ d="m 55.789829,35.866575 4.836294,-3.946936 -4.792342,-3.921561"
99
+ id="path21"
100
+ sodipodi:nodetypes="ccc"
101
+ inkscape:label="head" />
102
+ <path
103
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
104
+ d="M 31.999999,4.005584 V 15.994416"
105
+ id="path18"
106
+ sodipodi:nodetypes="cc"
107
+ inkscape:label="stem" />
108
+ <path
109
+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:square;stop-color:#000000"
110
+ d="M 35.866574,8.2045861 31.919638,3.3682918 27.998077,8.1606341"
111
+ id="path19"
112
+ sodipodi:nodetypes="ccc"
113
+ inkscape:label="head" />
114
+ <path
115
+ id="path9"
116
+ style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;-inkscape-stroke:none"
117
+ d="m 46,28 c 0,0 2.095179,2.919938 2.53125,4 C 48.912909,32.945295 50,40 50,40 M 39,26 c 0,0 4.556424,5.014257 5,6 0.582452,1.294361 1,8 1,8 M 32,26 c 0,0 6.464592,7 7,8 0.648718,1.211633 1,6 1,6"
118
+ inkscape:label="finger overlap lines"
119
+ sodipodi:nodetypes="csccsccsc" />
120
+ <path
121
+ style="display:inline;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;-inkscape-stroke:none"
122
+ d="M 32,63 C 32,63 25.133038,51.99557 25,50 L 23.472906,43 C 23.461073,42.035965 23.889387,41.358925 24,40 c 0.118866,-1.460319 -0.07643,-3.615892 0,-5 0,0 5,0 5,3 v 6 c 0,2 3,4 4,4 1,0 2,0 2,-1 V 41 C 35,40.644095 34.23999,36.654849 33.215871,35.483079 31.362482,33.362482 28.644095,30.644095 28,30 27,29 27.022097,27.116117 28.0221,26.116117 29.022097,25.116117 30.828641,25 32,26 c -0.149262,-1.298311 0,-2.37565 1,-3 1.199602,-0.748971 3,0 4,1 l 2,2 c 0,0 -0.425206,-1.5 0.574794,-2.5 1,-1 2.576678,-0.631371 3.425206,0.5 l 3,4 c 0,0 -0.151205,-1.424398 1,-2 2,-1 4.817482,4.62588 5,5 0.487858,1 2.035456,6.236811 3,9.40625 C 55.345413,42.274422 55,43 55,46 v 17 z"
123
+ id="path6"
124
+ inkscape:label="hand"
125
+ sodipodi:nodetypes="cscscsssssssscsccsscsssscc" />
126
+ </g>
127
+ </svg>