|
1
|
(function(F/*window.fossil object*/){ |
|
2
|
"use strict"; |
|
3
|
const D = F.dom, P = F.pikchr = {}; |
|
4
|
|
|
5
|
/** |
|
6
|
Initializes pikchr-rendered elements with the ability to |
|
7
|
toggle between their SVG and source code. |
|
8
|
|
|
9
|
The first argument may be any of: |
|
10
|
|
|
11
|
- A single SVG.pikchr element. |
|
12
|
|
|
13
|
- A collection (with a forEach method) of such elements. |
|
14
|
|
|
15
|
- A CSS selector string for one or more such elements. |
|
16
|
|
|
17
|
- An array of such strings. |
|
18
|
|
|
19
|
Passing no value is equivalent to passing 'svg.pikchr'. |
|
20
|
|
|
21
|
For each SVG in the resulting set, this function sets up event |
|
22
|
handlers which allow the user to toggle the SVG between image and |
|
23
|
source code modes. The image will switch modes in response to |
|
24
|
cltr-click and, if its *parent* element has the "toggle" CSS |
|
25
|
class, it will also switch modes in response to single-click. |
|
26
|
|
|
27
|
If the parent element has the "source" CSS class, the image |
|
28
|
starts off with its source code visible and the image hidden, |
|
29
|
instead of the default of the other way around. |
|
30
|
|
|
31
|
Returns this object. |
|
32
|
|
|
33
|
Each element will only be processed once by this routine, even if |
|
34
|
it is passed to this function multiple times. Each processed |
|
35
|
element gets a "data" attribute set to it to indicate that it was |
|
36
|
already dealt with. |
|
37
|
|
|
38
|
This code expects the following structure around the SVGs, and |
|
39
|
will not process any which don't match this: |
|
40
|
|
|
41
|
<DIV.pikchr-wrapper> |
|
42
|
<DIV.pikchr-svg><SVG.pikchr></SVG></DIV> |
|
43
|
<DIV.pikchr-src> |
|
44
|
<PRE>pikchr source code</PRE> |
|
45
|
<SPAN class='hidden'><A>link to open pikchr in /pikchrshow</A></SPAN> |
|
46
|
</DIV> |
|
47
|
</DIV> |
|
48
|
*/ |
|
49
|
P.addSrcView = function f(svg){ |
|
50
|
if(!f.hasOwnProperty('parentClick')){ |
|
51
|
f.parentClick = function(ev){ |
|
52
|
if(ev.altKey || ev.metaKey || ev.ctrlKey |
|
53
|
/* Every combination of special key (alt, shift, ctrl, |
|
54
|
meta) is handled differently everywhere. Shift is used |
|
55
|
by the browser, Ctrl doesn't work on an iMac, and Alt is |
|
56
|
intercepted by most Linux window managers to control |
|
57
|
window movement! So... we just listen for *any* of them |
|
58
|
(except Shift) and the user will need to find one which |
|
59
|
works on their environment. */ |
|
60
|
|| this.classList.contains('toggle')){ |
|
61
|
this.classList.toggle('source'); |
|
62
|
ev.stopPropagation(); |
|
63
|
ev.preventDefault(); |
|
64
|
} |
|
65
|
}; |
|
66
|
/** |
|
67
|
Event handler for the "open in pikchrshow" links: store the |
|
68
|
source code for the link's pikchr in |
|
69
|
window.sessionStorage['pikchr-xfer'] then open |
|
70
|
/pikchrshow?fromSession to trigger loading of that pikchr. |
|
71
|
*/ |
|
72
|
f.clickPikchrShow = function(ev){ |
|
73
|
const pId = this.dataset['pikchrid'] /* ID of the associated pikchr source code element */; |
|
74
|
if(!pId) return; |
|
75
|
const ePikchr = this.parentNode.parentNode.querySelector('#'+pId); |
|
76
|
if(!ePikchr) return; |
|
77
|
ev.stopPropagation() /* keep pikchr source view from toggling */; |
|
78
|
window.sessionStorage.setItem('pikchr-xfer', ePikchr.innerText); |
|
79
|
/* |
|
80
|
After returning from this function the link element will |
|
81
|
open [/pikchrshow?fromSession], and pikchrshow will extract |
|
82
|
the pikchr source code from sessionStorage['pikchr-xfer']. |
|
83
|
|
|
84
|
Quirks of this ^^^ design: |
|
85
|
|
|
86
|
We use only a single slot in sessionStorage. We could |
|
87
|
alternately use a key like pikchr-$pId and pass that key on |
|
88
|
to /pikchrshow via fromSession=pikchr-$pId, but that would |
|
89
|
eventually lead to stale session entries if loading of |
|
90
|
pikchrshow were interrupted at an untimely point. The |
|
91
|
down-side of _not_ doing that is that some user (or |
|
92
|
automation) options multiple "open in pikchrshow" links |
|
93
|
rapidly enough, the will open the same pikchr (the one which |
|
94
|
was stored in the session's slot most recently). The |
|
95
|
current approach should be fine for normal human interaction |
|
96
|
speeds, but if it proves to be a problem we can instead use |
|
97
|
the above-described approach of storing each pikchr in its |
|
98
|
own session slot and simply accept that there may be stale |
|
99
|
entries at some point. |
|
100
|
*/ |
|
101
|
}; |
|
102
|
}; |
|
103
|
if(!svg) svg = 'svg.pikchr'; |
|
104
|
if('string' === typeof svg){ |
|
105
|
document.querySelectorAll(svg).forEach((e)=>f.call(this, e)); |
|
106
|
return this; |
|
107
|
}else if(svg.forEach){ |
|
108
|
svg.forEach((e)=>f.call(this, e)); |
|
109
|
return this; |
|
110
|
} |
|
111
|
if(svg.dataset.pikchrProcessed){ |
|
112
|
return this; |
|
113
|
} |
|
114
|
svg.dataset.pikchrProcessed = 1; |
|
115
|
const parent = svg.parentNode.parentNode /* outermost DIV.pikchr-wrapper */; |
|
116
|
const srcView = parent ? svg.parentNode.nextElementSibling /* DIV.pikchr-src */ : undefined; |
|
117
|
if(srcView && srcView.classList.contains('pikchr-src')){ |
|
118
|
/* Without this element, there's nothing for us to do here. */ |
|
119
|
parent.addEventListener('click', f.parentClick, false); |
|
120
|
const eSpan = window.sessionStorage |
|
121
|
? srcView.querySelector('span') /* "open in..." link wrapper */ |
|
122
|
: undefined; |
|
123
|
if(eSpan){ |
|
124
|
const openLink = eSpan.querySelector('a'); |
|
125
|
if(openLink){ |
|
126
|
openLink.addEventListener('click', f.clickPikchrShow, false); |
|
127
|
eSpan.classList.remove('hidden'); |
|
128
|
} |
|
129
|
} |
|
130
|
} |
|
131
|
return this; |
|
132
|
}; |
|
133
|
})(window.fossil); |
|
134
|
|