|
7fcb462…
|
stephan
|
1 |
/* |
|
7fcb462…
|
stephan
|
2 |
2022-05-20 |
|
7fcb462…
|
stephan
|
3 |
|
|
7fcb462…
|
stephan
|
4 |
The author disclaims copyright to this source code. In place of a |
|
7fcb462…
|
stephan
|
5 |
legal notice, here is a blessing: |
|
7fcb462…
|
stephan
|
6 |
|
|
7fcb462…
|
stephan
|
7 |
* May you do good and not evil. |
|
7fcb462…
|
stephan
|
8 |
* May you find forgiveness for yourself and forgive others. |
|
7fcb462…
|
stephan
|
9 |
* May you share freely, never taking more than you give. |
|
7fcb462…
|
stephan
|
10 |
|
|
7fcb462…
|
stephan
|
11 |
*********************************************************************** |
|
7fcb462…
|
stephan
|
12 |
|
|
7fcb462…
|
stephan
|
13 |
This is the main entry point for the WASM rendition of fossil's |
|
7fcb462…
|
stephan
|
14 |
/pikchrshow app. It sets up the various UI bits, loads a Worker for |
|
7fcb462…
|
stephan
|
15 |
the pikchr process, and manages the communication between the UI and |
|
7fcb462…
|
stephan
|
16 |
worker. |
|
7fcb462…
|
stephan
|
17 |
|
|
7fcb462…
|
stephan
|
18 |
API dependencies: fossil.dom, fossil.copybutton, fossil.storage |
|
7fcb462…
|
stephan
|
19 |
*/ |
|
7fcb462…
|
stephan
|
20 |
(function(F/*fossil object*/){ |
|
7fcb462…
|
stephan
|
21 |
'use strict'; |
|
7fcb462…
|
stephan
|
22 |
|
|
7fcb462…
|
stephan
|
23 |
/* Recall that the 'self' symbol, except where locally |
|
7fcb462…
|
stephan
|
24 |
overwritten, refers to the global window or worker object. */ |
|
7fcb462…
|
stephan
|
25 |
|
|
7fcb462…
|
stephan
|
26 |
const D = F.dom; |
|
7fcb462…
|
stephan
|
27 |
/** Name of the stored copy of this app's config. */ |
|
7fcb462…
|
stephan
|
28 |
const configStorageKey = 'pikchrshow-config'; |
|
7fcb462…
|
stephan
|
29 |
|
|
7fcb462…
|
stephan
|
30 |
/* querySelectorAll() proxy */ |
|
7fcb462…
|
stephan
|
31 |
const EAll = function(/*[element=document,] cssSelector*/){ |
|
7fcb462…
|
stephan
|
32 |
return (arguments.length>1 ? arguments[0] : document) |
|
7fcb462…
|
stephan
|
33 |
.querySelectorAll(arguments[arguments.length-1]); |
|
7fcb462…
|
stephan
|
34 |
}; |
|
7fcb462…
|
stephan
|
35 |
/* querySelector() proxy */ |
|
7fcb462…
|
stephan
|
36 |
const E = function(/*[element=document,] cssSelector*/){ |
|
7fcb462…
|
stephan
|
37 |
return (arguments.length>1 ? arguments[0] : document) |
|
7fcb462…
|
stephan
|
38 |
.querySelector(arguments[arguments.length-1]); |
|
7fcb462…
|
stephan
|
39 |
}; |
|
7fcb462…
|
stephan
|
40 |
|
|
7fcb462…
|
stephan
|
41 |
/** The main application object. */ |
|
7fcb462…
|
stephan
|
42 |
const PS = { |
|
7fcb462…
|
stephan
|
43 |
/* Config options. */ |
|
7fcb462…
|
stephan
|
44 |
config: { |
|
7fcb462…
|
stephan
|
45 |
/* If true, display input/output areas side-by-side, else stack |
|
7fcb462…
|
stephan
|
46 |
them vertically. */ |
|
7fcb462…
|
stephan
|
47 |
sideBySide: true, |
|
7fcb462…
|
stephan
|
48 |
/* If true, swap positions of the input/output areas. */ |
|
7fcb462…
|
stephan
|
49 |
swapInOut: false, |
|
7fcb462…
|
stephan
|
50 |
/* If true, the SVG is allowed to resize to fit the parent |
|
7fcb462…
|
stephan
|
51 |
content area, else the parent is resized to fit the rendered |
|
7fcb462…
|
stephan
|
52 |
SVG (as sized by pikchr). */ |
|
7fcb462…
|
stephan
|
53 |
renderAutofit: false, |
|
7fcb462…
|
stephan
|
54 |
/* If true, automatically render while the user is typing. */ |
|
7fcb462…
|
stephan
|
55 |
renderWhileTyping: false |
|
7fcb462…
|
stephan
|
56 |
}, |
|
7fcb462…
|
stephan
|
57 |
/* Various DOM elements. */ |
|
7fcb462…
|
stephan
|
58 |
e: { |
|
7fcb462…
|
stephan
|
59 |
previewCopyButton: E('#preview-copy-button'), |
|
7fcb462…
|
stephan
|
60 |
previewModeLabel: E('label[for=preview-copy-button]'), |
|
7fcb462…
|
stephan
|
61 |
zoneInputButtons: E('.zone-wrapper.input > legend > .button-bar'), |
|
7fcb462…
|
stephan
|
62 |
zoneOutputButtons: E('.zone-wrapper.output > legend > .button-bar'), |
|
7fcb462…
|
stephan
|
63 |
outText: E('#pikchr-output-text'), |
|
7fcb462…
|
stephan
|
64 |
pikOutWrapper: E('#pikchr-output-wrapper'), |
|
7fcb462…
|
stephan
|
65 |
pikOut: E('#pikchr-output'), |
|
ff1c48a…
|
stephan
|
66 |
btnRender: E('#btn-render') |
|
7fcb462…
|
stephan
|
67 |
}, |
|
7fcb462…
|
stephan
|
68 |
renderModes: ['svg'/*SVG must be at index 0*/,'markdown', 'wiki', 'text'], |
|
7fcb462…
|
stephan
|
69 |
renderModeLabels: { |
|
7fcb462…
|
stephan
|
70 |
svg: 'SVG', markdown: 'Markdown', wiki: 'Fossil Wiki', text: 'Text' |
|
7fcb462…
|
stephan
|
71 |
}, |
|
7fcb462…
|
stephan
|
72 |
_msgMap: {}, |
|
7fcb462…
|
stephan
|
73 |
/** Adds a worker message handler for messages of the given |
|
7fcb462…
|
stephan
|
74 |
type. */ |
|
7fcb462…
|
stephan
|
75 |
addMsgHandler: function f(type,callback){ |
|
7fcb462…
|
stephan
|
76 |
if(Array.isArray(type)){ |
|
7fcb462…
|
stephan
|
77 |
type.forEach((t)=>this.addMsgHandler(t, callback)); |
|
7fcb462…
|
stephan
|
78 |
return this; |
|
7fcb462…
|
stephan
|
79 |
} |
|
7fcb462…
|
stephan
|
80 |
(this._msgMap.hasOwnProperty(type) |
|
7fcb462…
|
stephan
|
81 |
? this._msgMap[type] |
|
7fcb462…
|
stephan
|
82 |
: (this._msgMap[type] = [])).push(callback); |
|
7fcb462…
|
stephan
|
83 |
return this; |
|
7fcb462…
|
stephan
|
84 |
}, |
|
7fcb462…
|
stephan
|
85 |
/** Given a worker message, runs all handlers for msg.type. */ |
|
7fcb462…
|
stephan
|
86 |
runMsgHandlers: function(msg){ |
|
7fcb462…
|
stephan
|
87 |
const list = (this._msgMap.hasOwnProperty(msg.type) |
|
7fcb462…
|
stephan
|
88 |
? this._msgMap[msg.type] : false); |
|
7fcb462…
|
stephan
|
89 |
if(!list){ |
|
7fcb462…
|
stephan
|
90 |
console.warn("No handlers found for message type:",msg); |
|
7fcb462…
|
stephan
|
91 |
return false; |
|
7fcb462…
|
stephan
|
92 |
} |
|
7fcb462…
|
stephan
|
93 |
list.forEach((f)=>f(msg)); |
|
7fcb462…
|
stephan
|
94 |
return true; |
|
7fcb462…
|
stephan
|
95 |
}, |
|
7fcb462…
|
stephan
|
96 |
/** Removes all message handlers for the given message type. */ |
|
7fcb462…
|
stephan
|
97 |
clearMsgHandlers: function(type){ |
|
7fcb462…
|
stephan
|
98 |
delete this._msgMap[type]; |
|
7fcb462…
|
stephan
|
99 |
return this; |
|
7fcb462…
|
stephan
|
100 |
}, |
|
7fcb462…
|
stephan
|
101 |
/* Posts a message in the form {type, data} to the db worker. Returns this. */ |
|
7fcb462…
|
stephan
|
102 |
wMsg: function(type,data){ |
|
7fcb462…
|
stephan
|
103 |
this.worker.postMessage({type, data}); |
|
7fcb462…
|
stephan
|
104 |
return this; |
|
7fcb462…
|
stephan
|
105 |
}, |
|
7fcb462…
|
stephan
|
106 |
/** Stores this object's config in the browser's storage. */ |
|
7fcb462…
|
stephan
|
107 |
storeConfig: function(){ |
|
7fcb462…
|
stephan
|
108 |
F.storage.setJSON(configStorageKey,this.config); |
|
7fcb462…
|
stephan
|
109 |
} |
|
7fcb462…
|
stephan
|
110 |
}; |
|
7fcb462…
|
stephan
|
111 |
PS.renderModes.selectedIndex = 0; |
|
7fcb462…
|
stephan
|
112 |
PS._config = F.storage.getJSON(configStorageKey); |
|
7fcb462…
|
stephan
|
113 |
if(PS._config){ |
|
7fcb462…
|
stephan
|
114 |
/* Copy all properties to PS.config which are currently in |
|
7fcb462…
|
stephan
|
115 |
PS._config. We don't bother copying any other properties: those |
|
7fcb462…
|
stephan
|
116 |
would be stale/removed config entries. */ |
|
7fcb462…
|
stephan
|
117 |
Object.keys(PS.config).forEach(function(k){ |
|
7fcb462…
|
stephan
|
118 |
if(PS._config.hasOwnProperty(k)){ |
|
7fcb462…
|
stephan
|
119 |
PS.config[k] = PS._config[k]; |
|
7fcb462…
|
stephan
|
120 |
} |
|
7fcb462…
|
stephan
|
121 |
}); |
|
7fcb462…
|
stephan
|
122 |
delete PS._config; |
|
7fcb462…
|
stephan
|
123 |
} |
|
7fcb462…
|
stephan
|
124 |
|
|
dd20f34…
|
drh
|
125 |
/* Randomize the name of the worker script so that it is never cached. |
|
dd20f34…
|
drh
|
126 |
** The Fossil /builtin method will automatically remove the "-v000000000" |
|
dd20f34…
|
drh
|
127 |
** part of the filename, resolving it to just "pikchr-worker.js". */ |
|
dd20f34…
|
drh
|
128 |
PS.worker = new Worker('builtin/extsrc/pikchr-worker-v'+ |
|
dd20f34…
|
drh
|
129 |
(Math.floor(Math.random()*10000000000) + 1000000000)+ |
|
dd20f34…
|
drh
|
130 |
'.js'); |
|
7fcb462…
|
stephan
|
131 |
PS.worker.onmessage = (ev)=>PS.runMsgHandlers(ev.data); |
|
7fcb462…
|
stephan
|
132 |
PS.addMsgHandler('stdout', console.log.bind(console)); |
|
7fcb462…
|
stephan
|
133 |
PS.addMsgHandler('stderr', console.error.bind(console)); |
|
7fcb462…
|
stephan
|
134 |
|
|
7fcb462…
|
stephan
|
135 |
/** Handles status updates from the Module object. */ |
|
7fcb462…
|
stephan
|
136 |
PS.addMsgHandler('module', function f(ev){ |
|
7fcb462…
|
stephan
|
137 |
ev = ev.data; |
|
7fcb462…
|
stephan
|
138 |
if('status'!==ev.type){ |
|
7fcb462…
|
stephan
|
139 |
console.warn("Unexpected module-type message:",ev); |
|
7fcb462…
|
stephan
|
140 |
return; |
|
7fcb462…
|
stephan
|
141 |
} |
|
7fcb462…
|
stephan
|
142 |
if(!f.ui){ |
|
7fcb462…
|
stephan
|
143 |
f.ui = { |
|
7fcb462…
|
stephan
|
144 |
status: E('#module-status'), |
|
7fcb462…
|
stephan
|
145 |
progress: E('#module-progress'), |
|
7fcb462…
|
stephan
|
146 |
spinner: E('#module-spinner') |
|
7fcb462…
|
stephan
|
147 |
}; |
|
7fcb462…
|
stephan
|
148 |
} |
|
7fcb462…
|
stephan
|
149 |
const msg = ev.data; |
|
7fcb462…
|
stephan
|
150 |
if(f.ui.progres){ |
|
7fcb462…
|
stephan
|
151 |
progress.value = msg.step; |
|
7fcb462…
|
stephan
|
152 |
progress.max = msg.step + 1/*we don't know how many steps to expect*/; |
|
7fcb462…
|
stephan
|
153 |
} |
|
7fcb462…
|
stephan
|
154 |
if(1==msg.step){ |
|
7fcb462…
|
stephan
|
155 |
f.ui.progress.classList.remove('hidden'); |
|
7fcb462…
|
stephan
|
156 |
f.ui.spinner.classList.remove('hidden'); |
|
7fcb462…
|
stephan
|
157 |
} |
|
7fcb462…
|
stephan
|
158 |
if(msg.text){ |
|
7fcb462…
|
stephan
|
159 |
f.ui.status.classList.remove('hidden'); |
|
7fcb462…
|
stephan
|
160 |
f.ui.status.innerText = msg.text; |
|
7fcb462…
|
stephan
|
161 |
}else{ |
|
7fcb462…
|
stephan
|
162 |
if(f.ui.progress){ |
|
7fcb462…
|
stephan
|
163 |
f.ui.progress.remove(); |
|
7fcb462…
|
stephan
|
164 |
f.ui.spinner.remove(); |
|
7fcb462…
|
stephan
|
165 |
delete f.ui.progress; |
|
7fcb462…
|
stephan
|
166 |
delete f.ui.spinner; |
|
7fcb462…
|
stephan
|
167 |
} |
|
7fcb462…
|
stephan
|
168 |
f.ui.status.classList.add('hidden'); |
|
7fcb462…
|
stephan
|
169 |
/* The module can post messages about fatal problems, |
|
7fcb462…
|
stephan
|
170 |
e.g. an exit() being triggered or assertion failure, |
|
7fcb462…
|
stephan
|
171 |
after the last "load" message has arrived, so |
|
7fcb462…
|
stephan
|
172 |
leave f.ui.status and message listener intact. */ |
|
7fcb462…
|
stephan
|
173 |
} |
|
7fcb462…
|
stephan
|
174 |
}); |
|
7fcb462…
|
stephan
|
175 |
|
|
7fcb462…
|
stephan
|
176 |
PS.e.previewModeLabel.innerText = |
|
7fcb462…
|
stephan
|
177 |
PS.renderModeLabels[PS.renderModes[PS.renderModes.selectedIndex]]; |
|
7fcb462…
|
stephan
|
178 |
|
|
7fcb462…
|
stephan
|
179 |
/** |
|
44cd975…
|
stephan
|
180 |
The 'pikchr-ready' event is fired (with no payload) when the |
|
7fcb462…
|
stephan
|
181 |
wasm module has finished loading. */ |
|
dd20f34…
|
drh
|
182 |
PS.addMsgHandler('pikchr-ready', function(event){ |
|
44cd975…
|
stephan
|
183 |
PS.clearMsgHandlers('pikchr-ready'); |
|
dd20f34…
|
drh
|
184 |
F.page.onPikchrshowLoaded(event.data); |
|
7fcb462…
|
stephan
|
185 |
}); |
|
7fcb462…
|
stephan
|
186 |
|
|
7fcb462…
|
stephan
|
187 |
/** |
|
7fcb462…
|
stephan
|
188 |
Performs all app initialization which must wait until after the |
|
7fcb462…
|
stephan
|
189 |
worker module is loaded. This function removes itself when it's |
|
7fcb462…
|
stephan
|
190 |
called. |
|
7fcb462…
|
stephan
|
191 |
*/ |
|
dd20f34…
|
drh
|
192 |
F.page.onPikchrshowLoaded = function(pikchrVersion){ |
|
7fcb462…
|
stephan
|
193 |
delete this.onPikchrshowLoaded; |
|
7fcb462…
|
stephan
|
194 |
// Unhide all elements which start out hidden |
|
7fcb462…
|
stephan
|
195 |
EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden')); |
|
7fcb462…
|
stephan
|
196 |
const taInput = E('#input'); |
|
7fcb462…
|
stephan
|
197 |
const btnClearIn = E('#btn-clear'); |
|
7fcb462…
|
stephan
|
198 |
btnClearIn.addEventListener('click',function(){ |
|
7fcb462…
|
stephan
|
199 |
taInput.value = ''; |
|
7fcb462…
|
stephan
|
200 |
},false); |
|
7fcb462…
|
stephan
|
201 |
const getCurrentText = function(){ |
|
7fcb462…
|
stephan
|
202 |
let text; |
|
7fcb462…
|
stephan
|
203 |
if(taInput.selectionStart<taInput.selectionEnd){ |
|
7fcb462…
|
stephan
|
204 |
text = taInput.value.substring(taInput.selectionStart,taInput.selectionEnd).trim(); |
|
7fcb462…
|
stephan
|
205 |
}else{ |
|
7fcb462…
|
stephan
|
206 |
text = taInput.value.trim(); |
|
7fcb462…
|
stephan
|
207 |
} |
|
7fcb462…
|
stephan
|
208 |
return text;; |
|
7fcb462…
|
stephan
|
209 |
}; |
|
7fcb462…
|
stephan
|
210 |
const renderCurrentText = function(){ |
|
7fcb462…
|
stephan
|
211 |
const text = getCurrentText(); |
|
7fcb462…
|
stephan
|
212 |
if(text) PS.render(text); |
|
7fcb462…
|
stephan
|
213 |
}; |
|
7fcb462…
|
stephan
|
214 |
const setCurrentText = function(txt){ |
|
7fcb462…
|
stephan
|
215 |
taInput.value = txt; |
|
521da5c…
|
drh
|
216 |
renderCurrentText(); |
|
7fcb462…
|
stephan
|
217 |
}; |
|
7fcb462…
|
stephan
|
218 |
PS.e.btnRender.addEventListener('click',function(ev){ |
|
7fcb462…
|
stephan
|
219 |
ev.preventDefault(); |
|
7fcb462…
|
stephan
|
220 |
renderCurrentText(); |
|
7fcb462…
|
stephan
|
221 |
},false); |
|
7fcb462…
|
stephan
|
222 |
|
|
7fcb462…
|
stephan
|
223 |
/** To be called immediately before work is sent to the |
|
7fcb462…
|
stephan
|
224 |
worker. Updates some UI elements. The 'working'/'end' |
|
7fcb462…
|
stephan
|
225 |
event will apply the inverse, undoing the bits this |
|
7fcb462…
|
stephan
|
226 |
function does. This impl is not in the 'working'/'start' |
|
7fcb462…
|
stephan
|
227 |
event handler because that event is given to us |
|
7fcb462…
|
stephan
|
228 |
asynchronously _after_ we need to have performed this |
|
7fcb462…
|
stephan
|
229 |
work. |
|
7fcb462…
|
stephan
|
230 |
*/ |
|
7fcb462…
|
stephan
|
231 |
const preStartWork = function f(){ |
|
7fcb462…
|
stephan
|
232 |
if(!f._){ |
|
7fcb462…
|
stephan
|
233 |
const title = E('title'); |
|
7fcb462…
|
stephan
|
234 |
f._ = { |
|
7fcb462…
|
stephan
|
235 |
pageTitle: title, |
|
7fcb462…
|
stephan
|
236 |
pageTitleOrig: title.innerText |
|
7fcb462…
|
stephan
|
237 |
}; |
|
7fcb462…
|
stephan
|
238 |
} |
|
7fcb462…
|
stephan
|
239 |
//f._.pageTitle.innerText = "[working...] "+f._.pageTitleOrig; |
|
7fcb462…
|
stephan
|
240 |
PS.e.btnRender.setAttribute('disabled','disabled'); |
|
7fcb462…
|
stephan
|
241 |
}; |
|
7fcb462…
|
stephan
|
242 |
|
|
7fcb462…
|
stephan
|
243 |
/** |
|
7fcb462…
|
stephan
|
244 |
Submits the current input text to pikchr and renders the |
|
7fcb462…
|
stephan
|
245 |
result. */ |
|
7fcb462…
|
stephan
|
246 |
PS.render = function f(txt){ |
|
7fcb462…
|
stephan
|
247 |
preStartWork(); |
|
7fcb462…
|
stephan
|
248 |
this.wMsg('pikchr',{ |
|
7fcb462…
|
stephan
|
249 |
pikchr: txt, |
|
7fcb462…
|
stephan
|
250 |
darkMode: !!window.fossil.config.skin.isDark |
|
7fcb462…
|
stephan
|
251 |
}); |
|
7fcb462…
|
stephan
|
252 |
}; |
|
7fcb462…
|
stephan
|
253 |
|
|
7fcb462…
|
stephan
|
254 |
/** |
|
7fcb462…
|
stephan
|
255 |
Event handler for 'pikchr' messages from the Worker thread. |
|
7fcb462…
|
stephan
|
256 |
*/ |
|
7fcb462…
|
stephan
|
257 |
PS.addMsgHandler('pikchr', function(ev){ |
|
7fcb462…
|
stephan
|
258 |
const m = ev.data, pikOut = this.e.pikOut; |
|
7fcb462…
|
stephan
|
259 |
pikOut.classList[m.isError ? 'add' : 'remove']('error'); |
|
7fcb462…
|
stephan
|
260 |
pikOut.dataset.pikchr = m.pikchr; |
|
7fcb462…
|
stephan
|
261 |
const mode = this.renderModes[this.renderModes.selectedIndex]; |
|
7fcb462…
|
stephan
|
262 |
switch(mode){ |
|
7fcb462…
|
stephan
|
263 |
case 'text': case 'markdown': case 'wiki': { |
|
7fcb462…
|
stephan
|
264 |
let body; |
|
7fcb462…
|
stephan
|
265 |
switch(mode){ |
|
7fcb462…
|
stephan
|
266 |
case 'markdown': |
|
7fcb462…
|
stephan
|
267 |
body = ['```pikchr', m.pikchr, '```'].join('\n'); |
|
7fcb462…
|
stephan
|
268 |
break; |
|
7fcb462…
|
stephan
|
269 |
case 'wiki': |
|
7fcb462…
|
stephan
|
270 |
body = ['<verbatim type="pikchr">', m.pikchr, '</verbatim>'].join(''); |
|
7fcb462…
|
stephan
|
271 |
break; |
|
7fcb462…
|
stephan
|
272 |
default: |
|
7fcb462…
|
stephan
|
273 |
body = m.result; |
|
7fcb462…
|
stephan
|
274 |
} |
|
7fcb462…
|
stephan
|
275 |
this.e.outText.value = body; |
|
7fcb462…
|
stephan
|
276 |
this.e.outText.classList.remove('hidden'); |
|
7fcb462…
|
stephan
|
277 |
pikOut.classList.add('hidden'); |
|
7fcb462…
|
stephan
|
278 |
this.e.pikOutWrapper.classList.add('text'); |
|
7fcb462…
|
stephan
|
279 |
break; |
|
7fcb462…
|
stephan
|
280 |
} |
|
7fcb462…
|
stephan
|
281 |
case 'svg': |
|
7fcb462…
|
stephan
|
282 |
this.e.outText.classList.add('hidden'); |
|
7fcb462…
|
stephan
|
283 |
pikOut.classList.remove('hidden'); |
|
7fcb462…
|
stephan
|
284 |
this.e.pikOutWrapper.classList.remove('text'); |
|
7fcb462…
|
stephan
|
285 |
pikOut.innerHTML = m.result; |
|
7fcb462…
|
stephan
|
286 |
this.e.outText.value = m.result/*for clipboard copy*/; |
|
7fcb462…
|
stephan
|
287 |
break; |
|
7fcb462…
|
stephan
|
288 |
default: throw new Error("Unhandled render mode: "+mode); |
|
7fcb462…
|
stephan
|
289 |
} |
|
7fcb462…
|
stephan
|
290 |
let vw = null, vh = null; |
|
7fcb462…
|
stephan
|
291 |
if('svg'===mode){ |
|
7fcb462…
|
stephan
|
292 |
if(m.isError){ |
|
7fcb462…
|
stephan
|
293 |
vw = vh = '100%'; |
|
7fcb462…
|
stephan
|
294 |
}else if(this.config.renderAutofit){ |
|
7fcb462…
|
stephan
|
295 |
/* FIXME: current behavior doesn't work as desired when width>height |
|
7fcb462…
|
stephan
|
296 |
(e.g. non-side-by-side mode).*/ |
|
7fcb462…
|
stephan
|
297 |
vw = vh = '98%'; |
|
7fcb462…
|
stephan
|
298 |
}else{ |
|
7fcb462…
|
stephan
|
299 |
vw = m.width+1+'px'; vh = m.height+1+'px'; |
|
7fcb462…
|
stephan
|
300 |
/* +1 is b/c the SVG uses floating point sizes but pikchr() |
|
7fcb462…
|
stephan
|
301 |
returns truncated integers. */ |
|
7fcb462…
|
stephan
|
302 |
} |
|
7fcb462…
|
stephan
|
303 |
pikOut.style.width = vw; |
|
7fcb462…
|
stephan
|
304 |
pikOut.style.height = vh; |
|
7fcb462…
|
stephan
|
305 |
} |
|
7fcb462…
|
stephan
|
306 |
}.bind(PS))/*'pikchr' msg handler*/; |
|
7fcb462…
|
stephan
|
307 |
|
|
7fcb462…
|
stephan
|
308 |
E('#btn-render-mode').addEventListener('click',function(){ |
|
7fcb462…
|
stephan
|
309 |
const modes = this.renderModes; |
|
7fcb462…
|
stephan
|
310 |
modes.selectedIndex = (modes.selectedIndex + 1) % modes.length; |
|
7fcb462…
|
stephan
|
311 |
this.e.previewModeLabel.innerText = this.renderModeLabels[modes[modes.selectedIndex]]; |
|
7fcb462…
|
stephan
|
312 |
if(this.e.pikOut.dataset.pikchr){ |
|
7fcb462…
|
stephan
|
313 |
this.render(this.e.pikOut.dataset.pikchr); |
|
7fcb462…
|
stephan
|
314 |
} |
|
7fcb462…
|
stephan
|
315 |
}.bind(PS)); |
|
7fcb462…
|
stephan
|
316 |
F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText}); |
|
7fcb462…
|
stephan
|
317 |
|
|
7fcb462…
|
stephan
|
318 |
PS.addMsgHandler('working',function f(ev){ |
|
7fcb462…
|
stephan
|
319 |
switch(ev.data){ |
|
7fcb462…
|
stephan
|
320 |
case 'start': /* See notes in preStartWork(). */; return; |
|
7fcb462…
|
stephan
|
321 |
case 'end': |
|
7fcb462…
|
stephan
|
322 |
//preStartWork._.pageTitle.innerText = preStartWork._.pageTitleOrig; |
|
7fcb462…
|
stephan
|
323 |
this.e.btnRender.removeAttribute('disabled'); |
|
7fcb462…
|
stephan
|
324 |
this.e.pikOutWrapper.classList[this.config.renderAutofit ? 'add' : 'remove']('autofit'); |
|
7fcb462…
|
stephan
|
325 |
return; |
|
7fcb462…
|
stephan
|
326 |
} |
|
7fcb462…
|
stephan
|
327 |
console.warn("Unhandled 'working' event:",ev.data); |
|
7fcb462…
|
stephan
|
328 |
}.bind(PS)); |
|
7fcb462…
|
stephan
|
329 |
|
|
7fcb462…
|
stephan
|
330 |
/* For each checkbox with data-csstgt, set up a handler which |
|
7fcb462…
|
stephan
|
331 |
toggles the given CSS class on the element matching |
|
7fcb462…
|
stephan
|
332 |
E(data-csstgt). */ |
|
7fcb462…
|
stephan
|
333 |
EAll('input[type=checkbox][data-csstgt]') |
|
7fcb462…
|
stephan
|
334 |
.forEach(function(e){ |
|
7fcb462…
|
stephan
|
335 |
const tgt = E(e.dataset.csstgt); |
|
7fcb462…
|
stephan
|
336 |
const cssClass = e.dataset.cssclass || 'error'; |
|
7fcb462…
|
stephan
|
337 |
e.checked = tgt.classList.contains(cssClass); |
|
7fcb462…
|
stephan
|
338 |
e.addEventListener('change', function(){ |
|
7fcb462…
|
stephan
|
339 |
tgt.classList[ |
|
7fcb462…
|
stephan
|
340 |
this.checked ? 'add' : 'remove' |
|
7fcb462…
|
stephan
|
341 |
](cssClass) |
|
7fcb462…
|
stephan
|
342 |
}, false); |
|
7fcb462…
|
stephan
|
343 |
}); |
|
7fcb462…
|
stephan
|
344 |
/* For each checkbox with data-config=X, set up a binding to |
|
7fcb462…
|
stephan
|
345 |
PS.config[X]. These must be set up AFTER data-csstgt |
|
7fcb462…
|
stephan
|
346 |
checkboxes so that those two states can be synced properly. */ |
|
7fcb462…
|
stephan
|
347 |
EAll('input[type=checkbox][data-config]') |
|
7fcb462…
|
stephan
|
348 |
.forEach(function(e){ |
|
7fcb462…
|
stephan
|
349 |
const confVal = !!PS.config[e.dataset.config]; |
|
7fcb462…
|
stephan
|
350 |
if(e.checked !== confVal){ |
|
7fcb462…
|
stephan
|
351 |
/* Ensure that data-csstgt mappings (if any) get |
|
7fcb462…
|
stephan
|
352 |
synced properly. */ |
|
7fcb462…
|
stephan
|
353 |
e.checked = confVal; |
|
7fcb462…
|
stephan
|
354 |
e.dispatchEvent(new Event('change')); |
|
7fcb462…
|
stephan
|
355 |
} |
|
7fcb462…
|
stephan
|
356 |
e.addEventListener('change', function(){ |
|
7fcb462…
|
stephan
|
357 |
PS.config[this.dataset.config] = this.checked; |
|
7fcb462…
|
stephan
|
358 |
PS.storeConfig(); |
|
7fcb462…
|
stephan
|
359 |
}, false); |
|
7fcb462…
|
stephan
|
360 |
}); |
|
7fcb462…
|
stephan
|
361 |
E('#opt-cb-autofit').addEventListener('change',function(){ |
|
7fcb462…
|
stephan
|
362 |
/* PS.config.renderAutofit was set by the data-config |
|
7fcb462…
|
stephan
|
363 |
event handler. */ |
|
7fcb462…
|
stephan
|
364 |
if(0==PS.renderModes.selectedIndex && PS.e.pikOut.dataset.pikchr){ |
|
7fcb462…
|
stephan
|
365 |
PS.render(PS.e.pikOut.dataset.pikchr); |
|
7fcb462…
|
stephan
|
366 |
} |
|
7fcb462…
|
stephan
|
367 |
}); |
|
7fcb462…
|
stephan
|
368 |
/* For each button with data-cmd=X, map a click handler which |
|
7fcb462…
|
stephan
|
369 |
calls PS.render(X). */ |
|
7fcb462…
|
stephan
|
370 |
const cmdClick = function(){PS.render(this.dataset.cmd);}; |
|
7fcb462…
|
stephan
|
371 |
EAll('button[data-cmd]').forEach( |
|
7fcb462…
|
stephan
|
372 |
e => e.addEventListener('click', cmdClick, false) |
|
7fcb462…
|
stephan
|
373 |
); |
|
7fcb462…
|
stephan
|
374 |
|
|
7fcb462…
|
stephan
|
375 |
|
|
7fcb462…
|
stephan
|
376 |
//////////////////////////////////////////////////////////// |
|
7fcb462…
|
stephan
|
377 |
// Set up selection list of predefined scripts... |
|
7fcb462…
|
stephan
|
378 |
if(true){ |
|
7fcb462…
|
stephan
|
379 |
const selectScript = PS.e.selectScript = D.select(); |
|
7fcb462…
|
stephan
|
380 |
D.append(PS.e.zoneInputButtons, selectScript); |
|
7fcb462…
|
stephan
|
381 |
PS.predefinedPiks.forEach(function(script,ndx){ |
|
7fcb462…
|
stephan
|
382 |
const opt = D.option(script.code ? script.code.trim() :'', script.name); |
|
7fcb462…
|
stephan
|
383 |
D.append(selectScript, opt); |
|
7fcb462…
|
stephan
|
384 |
if(!ndx) selectScript.selectedIndex = 0 /*timing/ordering workaround*/; |
|
7fcb462…
|
stephan
|
385 |
if(ndx && !script.code){ |
|
7fcb462…
|
stephan
|
386 |
/* Treat entries w/ no code as separators EXCEPT for the |
|
7fcb462…
|
stephan
|
387 |
first one, which we want to keep selectable solely for |
|
7fcb462…
|
stephan
|
388 |
cosmetic reasons. */ |
|
7fcb462…
|
stephan
|
389 |
D.disable(opt); |
|
7fcb462…
|
stephan
|
390 |
} |
|
7fcb462…
|
stephan
|
391 |
}); |
|
7fcb462…
|
stephan
|
392 |
delete PS.predefinedPiks; |
|
7fcb462…
|
stephan
|
393 |
selectScript.addEventListener('change', function(ev){ |
|
7fcb462…
|
stephan
|
394 |
const val = ev.target.value; |
|
7fcb462…
|
stephan
|
395 |
if(!val) return; |
|
7fcb462…
|
stephan
|
396 |
setCurrentText(val); |
|
7fcb462…
|
stephan
|
397 |
}, false); |
|
7fcb462…
|
stephan
|
398 |
}/*Examples*/ |
|
7fcb462…
|
stephan
|
399 |
|
|
7fcb462…
|
stephan
|
400 |
/** |
|
7b4bbc8…
|
stephan
|
401 |
TODO? Handle load/import of an external pikchr file. |
|
7fcb462…
|
stephan
|
402 |
*/ |
|
7fcb462…
|
stephan
|
403 |
if(0) E('#load-pikchr').addEventListener('change',function(){ |
|
7fcb462…
|
stephan
|
404 |
const f = this.files[0]; |
|
7fcb462…
|
stephan
|
405 |
const r = new FileReader(); |
|
7fcb462…
|
stephan
|
406 |
const status = {loaded: 0, total: 0}; |
|
7fcb462…
|
stephan
|
407 |
this.setAttribute('disabled','disabled'); |
|
7fcb462…
|
stephan
|
408 |
const that = this; |
|
7fcb462…
|
stephan
|
409 |
r.addEventListener('load', function(){ |
|
7fcb462…
|
stephan
|
410 |
that.removeAttribute('disabled'); |
|
7fcb462…
|
stephan
|
411 |
stdout("Loaded",f.name+". Opening pikchr..."); |
|
7fcb462…
|
stephan
|
412 |
PS.wMsg('open',{ |
|
7fcb462…
|
stephan
|
413 |
filename: f.name, |
|
7fcb462…
|
stephan
|
414 |
buffer: this.result |
|
7fcb462…
|
stephan
|
415 |
}); |
|
7fcb462…
|
stephan
|
416 |
}); |
|
7fcb462…
|
stephan
|
417 |
r.addEventListener('error',function(){ |
|
7fcb462…
|
stephan
|
418 |
that.removeAttribute('disabled'); |
|
7fcb462…
|
stephan
|
419 |
stderr("Loading",f.name,"failed for unknown reasons."); |
|
7fcb462…
|
stephan
|
420 |
}); |
|
7fcb462…
|
stephan
|
421 |
r.addEventListener('abort',function(){ |
|
7fcb462…
|
stephan
|
422 |
that.removeAttribute('disabled'); |
|
7fcb462…
|
stephan
|
423 |
stdout("Cancelled loading of",f.name+"."); |
|
7fcb462…
|
stephan
|
424 |
}); |
|
7fcb462…
|
stephan
|
425 |
r.readAsArrayBuffer(f); |
|
7fcb462…
|
stephan
|
426 |
}); |
|
7fcb462…
|
stephan
|
427 |
|
|
7fcb462…
|
stephan
|
428 |
EAll('fieldset.collapsible').forEach(function(fs){ |
|
864ed8d…
|
stephan
|
429 |
const btnToggle = E(fs,'legend > .fieldset-toggle'), |
|
7fcb462…
|
stephan
|
430 |
content = EAll(fs,':scope > div'); |
|
7fcb462…
|
stephan
|
431 |
btnToggle.addEventListener('click', function(){ |
|
7fcb462…
|
stephan
|
432 |
fs.classList.toggle('collapsed'); |
|
7fcb462…
|
stephan
|
433 |
content.forEach((d)=>d.classList.toggle('hidden')); |
|
7fcb462…
|
stephan
|
434 |
}, false); |
|
7fcb462…
|
stephan
|
435 |
}); |
|
7fcb462…
|
stephan
|
436 |
|
|
521da5c…
|
drh
|
437 |
if(window.sessionStorage){ |
|
521da5c…
|
drh
|
438 |
/* If sessionStorage['pikchr-xfer'] exists and the "fromSession" |
|
521da5c…
|
drh
|
439 |
URL argument was passed to this page, load the pikchr source |
|
521da5c…
|
drh
|
440 |
from the session. This is used by the "open in pikchrshow" |
|
521da5c…
|
drh
|
441 |
link in the forum. */ |
|
521da5c…
|
drh
|
442 |
const src = window.sessionStorage.getItem('pikchr-xfer'); |
|
521da5c…
|
drh
|
443 |
if( src && (new URL(self.location.href).searchParams).has('fromSession') ){ |
|
521da5c…
|
drh
|
444 |
taInput.value = src; |
|
521da5c…
|
drh
|
445 |
window.sessionStorage.removeItem('pikchr-xfer'); |
|
521da5c…
|
drh
|
446 |
} |
|
521da5c…
|
drh
|
447 |
} |
|
dd20f34…
|
drh
|
448 |
D.append(E('fieldset.options > div'), |
|
dd20f34…
|
drh
|
449 |
D.append(D.addClass(D.span(), 'labeled-input'), |
|
dd20f34…
|
drh
|
450 |
'pikchr v. '+pikchrVersion)); |
|
521da5c…
|
drh
|
451 |
|
|
7fcb462…
|
stephan
|
452 |
PS.e.btnRender.click(); |
|
521da5c…
|
drh
|
453 |
|
|
7fcb462…
|
stephan
|
454 |
/** Debounce handler for auto-rendering while typing. */ |
|
7fcb462…
|
stephan
|
455 |
const debounceAutoRender = F.debounce(function f(){ |
|
7fcb462…
|
stephan
|
456 |
if(!PS._isDirty) return; |
|
7fcb462…
|
stephan
|
457 |
const text = getCurrentText(); |
|
7fcb462…
|
stephan
|
458 |
if(f._ === text){ |
|
7fcb462…
|
stephan
|
459 |
PS._isDirty = false; |
|
7fcb462…
|
stephan
|
460 |
return; |
|
7fcb462…
|
stephan
|
461 |
} |
|
7fcb462…
|
stephan
|
462 |
f._ = text; |
|
7fcb462…
|
stephan
|
463 |
PS._isDirty = false; |
|
7fcb462…
|
stephan
|
464 |
PS.render(text || ''); |
|
7fcb462…
|
stephan
|
465 |
}, 800, false); |
|
7fcb462…
|
stephan
|
466 |
|
|
7fcb462…
|
stephan
|
467 |
taInput.addEventListener('keydown',function f(ev){ |
|
7fcb462…
|
stephan
|
468 |
if((ev.ctrlKey || ev.shiftKey) && 13 === ev.keyCode){ |
|
7fcb462…
|
stephan
|
469 |
// Ctrl-enter and shift-enter both run the current input |
|
7fcb462…
|
stephan
|
470 |
PS._isDirty = false/*prevent a pending debounce from re-rendering*/; |
|
7fcb462…
|
stephan
|
471 |
ev.preventDefault(); |
|
7fcb462…
|
stephan
|
472 |
ev.stopPropagation(); |
|
7fcb462…
|
stephan
|
473 |
renderCurrentText(); |
|
7fcb462…
|
stephan
|
474 |
return; |
|
7fcb462…
|
stephan
|
475 |
} |
|
7fcb462…
|
stephan
|
476 |
if(!PS.config.renderWhileTyping) return; |
|
7fcb462…
|
stephan
|
477 |
/* Auto-render while typing... */ |
|
7fcb462…
|
stephan
|
478 |
switch(ev.keyCode){ |
|
7fcb462…
|
stephan
|
479 |
case (ev.keyCode<32): /*any ctrl char*/ |
|
7fcb462…
|
stephan
|
480 |
/* ^^^ w/o that, simply tapping ctrl is enough to |
|
7fcb462…
|
stephan
|
481 |
force a re-render. Similarly, TAB-ing focus away |
|
7fcb462…
|
stephan
|
482 |
should not re-render. */ |
|
7fcb462…
|
stephan
|
483 |
case 33: case 34: /* page up/down */ |
|
7fcb462…
|
stephan
|
484 |
case 35: case 36: /* home/end */ |
|
7fcb462…
|
stephan
|
485 |
case 37: case 38: case 39: case 40: /* arrows */ |
|
7fcb462…
|
stephan
|
486 |
return; |
|
7fcb462…
|
stephan
|
487 |
} |
|
7fcb462…
|
stephan
|
488 |
PS._isDirty = true; |
|
7fcb462…
|
stephan
|
489 |
debounceAutoRender(); |
|
7fcb462…
|
stephan
|
490 |
}, false); |
|
7fcb462…
|
stephan
|
491 |
|
|
7fcb462…
|
stephan
|
492 |
const ForceResizeKludge = (function(){ |
|
7fcb462…
|
stephan
|
493 |
/* Workaround for Safari mayhem regarding use of vh CSS |
|
7fcb462…
|
stephan
|
494 |
units.... We cannot use vh units to set the main view |
|
7fcb462…
|
stephan
|
495 |
size because Safari chokes on that, so we calculate |
|
7fcb462…
|
stephan
|
496 |
that height here. Larger than ~95% is too big for |
|
7fcb462…
|
stephan
|
497 |
Firefox on Android, causing the input area to move |
|
7fcb462…
|
stephan
|
498 |
off-screen. */ |
|
7fcb462…
|
stephan
|
499 |
const appViews = EAll('.app-view'); |
|
7fcb462…
|
stephan
|
500 |
const elemsToCount = [ |
|
7fcb462…
|
stephan
|
501 |
/* Elements which we need to always count in the |
|
7fcb462…
|
stephan
|
502 |
visible body size. */ |
|
7b4bbc8…
|
stephan
|
503 |
E('body > header'), |
|
7b4bbc8…
|
stephan
|
504 |
E('body > nav.mainmenu'), |
|
7b4bbc8…
|
stephan
|
505 |
E('body > footer') |
|
7fcb462…
|
stephan
|
506 |
]; |
|
7fcb462…
|
stephan
|
507 |
const resized = function f(){ |
|
7fcb462…
|
stephan
|
508 |
if(f.$disabled) return; |
|
7fcb462…
|
stephan
|
509 |
const wh = window.innerHeight; |
|
7fcb462…
|
stephan
|
510 |
var ht; |
|
7fcb462…
|
stephan
|
511 |
var extra = 0; |
|
7fcb462…
|
stephan
|
512 |
elemsToCount.forEach((e)=>e ? extra += F.dom.effectiveHeight(e) : false); |
|
7fcb462…
|
stephan
|
513 |
ht = wh - extra; |
|
7fcb462…
|
stephan
|
514 |
appViews.forEach(function(e){ |
|
7fcb462…
|
stephan
|
515 |
e.style.height = |
|
7fcb462…
|
stephan
|
516 |
e.style.maxHeight = [ |
|
7fcb462…
|
stephan
|
517 |
"calc(", (ht>=100 ? ht : 100), "px", |
|
7fcb462…
|
stephan
|
518 |
" - 2em"/*fudge value*/,")" |
|
7fcb462…
|
stephan
|
519 |
/* ^^^^ hypothetically not needed, but both |
|
7fcb462…
|
stephan
|
520 |
Chrome/FF on Linux will force scrollbars on the |
|
7fcb462…
|
stephan
|
521 |
body if this value is too small. */ |
|
7fcb462…
|
stephan
|
522 |
].join(''); |
|
7fcb462…
|
stephan
|
523 |
}); |
|
7fcb462…
|
stephan
|
524 |
}; |
|
7fcb462…
|
stephan
|
525 |
resized.$disabled = true/*gets deleted when setup is finished*/; |
|
7fcb462…
|
stephan
|
526 |
window.addEventListener('resize', F.debounce(resized, 250), false); |
|
7fcb462…
|
stephan
|
527 |
return resized; |
|
7fcb462…
|
stephan
|
528 |
})()/*ForceResizeKludge*/; |
|
7fcb462…
|
stephan
|
529 |
|
|
7fcb462…
|
stephan
|
530 |
delete ForceResizeKludge.$disabled; |
|
7fcb462…
|
stephan
|
531 |
ForceResizeKludge(); |
|
7fcb462…
|
stephan
|
532 |
}/*onPikchrshowLoaded()*/; |
|
7fcb462…
|
stephan
|
533 |
|
|
7fcb462…
|
stephan
|
534 |
|
|
7fcb462…
|
stephan
|
535 |
/** |
|
44e1c41…
|
stephan
|
536 |
Predefined example pikchr scripts. Each entry is an object: |
|
7fcb462…
|
stephan
|
537 |
|
|
7fcb462…
|
stephan
|
538 |
{ |
|
7fcb462…
|
stephan
|
539 |
name: required string, |
|
7fcb462…
|
stephan
|
540 |
code: optional code string. An entry with a falsy code is treated |
|
7fcb462…
|
stephan
|
541 |
like a separator in the resulting SELECT element (a |
|
7fcb462…
|
stephan
|
542 |
disabled OPTION). |
|
7fcb462…
|
stephan
|
543 |
} |
|
7fcb462…
|
stephan
|
544 |
*/ |
|
7fcb462…
|
stephan
|
545 |
PS.predefinedPiks = [ |
|
7fcb462…
|
stephan
|
546 |
{name: "-- Example Scripts --", code: false}, |
|
7fcb462…
|
stephan
|
547 |
/* |
|
7fcb462…
|
stephan
|
548 |
The following were imported from the pikchr test scripts: |
|
7fcb462…
|
stephan
|
549 |
|
|
7fcb462…
|
stephan
|
550 |
https://fossil-scm.org/pikchr/dir/examples |
|
7fcb462…
|
stephan
|
551 |
*/ |
|
7fcb462…
|
stephan
|
552 |
{name:"Cardinal headings",code:` linerad = 5px |
|
7fcb462…
|
stephan
|
553 |
C: circle "Center" rad 150% |
|
7fcb462…
|
stephan
|
554 |
circle "N" at 1.0 n of C; arrow from C to last chop -> |
|
7fcb462…
|
stephan
|
555 |
circle "NE" at 1.0 ne of C; arrow from C to last chop <- |
|
7fcb462…
|
stephan
|
556 |
circle "E" at 1.0 e of C; arrow from C to last chop <-> |
|
7fcb462…
|
stephan
|
557 |
circle "SE" at 1.0 se of C; arrow from C to last chop -> |
|
7fcb462…
|
stephan
|
558 |
circle "S" at 1.0 s of C; arrow from C to last chop <- |
|
7fcb462…
|
stephan
|
559 |
circle "SW" at 1.0 sw of C; arrow from C to last chop <-> |
|
7fcb462…
|
stephan
|
560 |
circle "W" at 1.0 w of C; arrow from C to last chop -> |
|
7fcb462…
|
stephan
|
561 |
circle "NW" at 1.0 nw of C; arrow from C to last chop <- |
|
7fcb462…
|
stephan
|
562 |
arrow from 2nd circle to 3rd circle chop |
|
7fcb462…
|
stephan
|
563 |
arrow from 4th circle to 3rd circle chop |
|
7fcb462…
|
stephan
|
564 |
arrow from SW to S chop <-> |
|
7fcb462…
|
stephan
|
565 |
circle "ESE" at 2.0 heading 112.5 from Center \ |
|
7fcb462…
|
stephan
|
566 |
thickness 150% fill lightblue radius 75% |
|
7fcb462…
|
stephan
|
567 |
arrow from Center to ESE thickness 150% <-> chop |
|
7fcb462…
|
stephan
|
568 |
arrow from ESE up 1.35 then to NE chop |
|
7fcb462…
|
stephan
|
569 |
line dashed <- from E.e to (ESE.x,E.y) |
|
7fcb462…
|
stephan
|
570 |
line dotted <-> thickness 50% from N to NW chop |
|
7fcb462…
|
stephan
|
571 |
`},{name:"Core object types",code:`AllObjects: [ |
|
7fcb462…
|
stephan
|
572 |
|
|
7fcb462…
|
stephan
|
573 |
# First row of objects |
|
7fcb462…
|
stephan
|
574 |
box "box" |
|
7fcb462…
|
stephan
|
575 |
box rad 10px "box (with" "rounded" "corners)" at 1in right of previous |
|
7fcb462…
|
stephan
|
576 |
circle "circle" at 1in right of previous |
|
7fcb462…
|
stephan
|
577 |
ellipse "ellipse" at 1in right of previous |
|
7fcb462…
|
stephan
|
578 |
|
|
7fcb462…
|
stephan
|
579 |
# second row of objects |
|
7fcb462…
|
stephan
|
580 |
OVAL1: oval "oval" at 1in below first box |
|
7fcb462…
|
stephan
|
581 |
oval "(tall &" "thin)" "oval" width OVAL1.height height OVAL1.width \ |
|
7fcb462…
|
stephan
|
582 |
at 1in right of previous |
|
7fcb462…
|
stephan
|
583 |
cylinder "cylinder" at 1in right of previous |
|
7fcb462…
|
stephan
|
584 |
file "file" at 1in right of previous |
|
7fcb462…
|
stephan
|
585 |
|
|
7fcb462…
|
stephan
|
586 |
# third row shows line-type objects |
|
7fcb462…
|
stephan
|
587 |
dot "dot" above at 1in below first oval |
|
7fcb462…
|
stephan
|
588 |
line right from 1.8cm right of previous "lines" above |
|
7fcb462…
|
stephan
|
589 |
arrow right from 1.8cm right of previous "arrows" above |
|
7fcb462…
|
stephan
|
590 |
spline from 1.8cm right of previous \ |
|
7fcb462…
|
stephan
|
591 |
go right .15 then .3 heading 30 then .5 heading 160 then .4 heading 20 \ |
|
7fcb462…
|
stephan
|
592 |
then right .15 |
|
7fcb462…
|
stephan
|
593 |
"splines" at 3rd vertex of previous |
|
7fcb462…
|
stephan
|
594 |
|
|
7fcb462…
|
stephan
|
595 |
# The third vertex of the spline is not actually on the drawn |
|
7fcb462…
|
stephan
|
596 |
# curve. The third vertex is a control point. To see its actual |
|
7fcb462…
|
stephan
|
597 |
# position, uncomment the following line: |
|
7fcb462…
|
stephan
|
598 |
#dot color red at 3rd vertex of previous spline |
|
7fcb462…
|
stephan
|
599 |
|
|
7fcb462…
|
stephan
|
600 |
# Draw various lines below the first line |
|
7fcb462…
|
stephan
|
601 |
line dashed right from 0.3cm below start of previous line |
|
7fcb462…
|
stephan
|
602 |
line dotted right from 0.3cm below start of previous |
|
7fcb462…
|
stephan
|
603 |
line thin right from 0.3cm below start of previous |
|
7fcb462…
|
stephan
|
604 |
line thick right from 0.3cm below start of previous |
|
7fcb462…
|
stephan
|
605 |
|
|
7fcb462…
|
stephan
|
606 |
|
|
7fcb462…
|
stephan
|
607 |
# Draw arrows with different arrowhead configurations below |
|
7fcb462…
|
stephan
|
608 |
# the first arrow |
|
7fcb462…
|
stephan
|
609 |
arrow <- right from 0.4cm below start of previous arrow |
|
7fcb462…
|
stephan
|
610 |
arrow <-> right from 0.4cm below start of previous |
|
7fcb462…
|
stephan
|
611 |
|
|
7fcb462…
|
stephan
|
612 |
# Draw splines with different arrowhead configurations below |
|
7fcb462…
|
stephan
|
613 |
# the first spline |
|
7fcb462…
|
stephan
|
614 |
spline same from .4cm below start of first spline -> |
|
7fcb462…
|
stephan
|
615 |
spline same from .4cm below start of previous <- |
|
7fcb462…
|
stephan
|
616 |
spline same from .4cm below start of previous <-> |
|
7fcb462…
|
stephan
|
617 |
|
|
7fcb462…
|
stephan
|
618 |
] # end of AllObjects |
|
7fcb462…
|
stephan
|
619 |
|
|
7fcb462…
|
stephan
|
620 |
# Label the whole diagram |
|
7fcb462…
|
stephan
|
621 |
text "Examples Of Pikchr Objects" big bold at .8cm above north of AllObjects |
|
7fcb462…
|
stephan
|
622 |
`},{name:"Swimlanes",code:` $laneh = 0.75 |
|
7fcb462…
|
stephan
|
623 |
|
|
7fcb462…
|
stephan
|
624 |
# Draw the lanes |
|
7fcb462…
|
stephan
|
625 |
down |
|
7fcb462…
|
stephan
|
626 |
box width 3.5in height $laneh fill 0xacc9e3 |
|
7fcb462…
|
stephan
|
627 |
box same fill 0xc5d8ef |
|
7fcb462…
|
stephan
|
628 |
box same as first box |
|
7fcb462…
|
stephan
|
629 |
box same as 2nd box |
|
7fcb462…
|
stephan
|
630 |
line from 1st box.sw+(0.2,0) up until even with 1st box.n \ |
|
7fcb462…
|
stephan
|
631 |
"Alan" above aligned |
|
7fcb462…
|
stephan
|
632 |
line from 2nd box.sw+(0.2,0) up until even with 2nd box.n \ |
|
7fcb462…
|
stephan
|
633 |
"Betty" above aligned |
|
7fcb462…
|
stephan
|
634 |
line from 3rd box.sw+(0.2,0) up until even with 3rd box.n \ |
|
7fcb462…
|
stephan
|
635 |
"Charlie" above aligned |
|
7fcb462…
|
stephan
|
636 |
line from 4th box.sw+(0.2,0) up until even with 4th box.n \ |
|
7fcb462…
|
stephan
|
637 |
"Darlene" above aligned |
|
7fcb462…
|
stephan
|
638 |
|
|
7fcb462…
|
stephan
|
639 |
# fill in content for the Alice lane |
|
7fcb462…
|
stephan
|
640 |
right |
|
7fcb462…
|
stephan
|
641 |
A1: circle rad 0.1in at end of first line + (0.2,-0.2) \ |
|
521da5c…
|
drh
|
642 |
fill white thickness 1.5px "1" |
|
7fcb462…
|
stephan
|
643 |
arrow right 50% |
|
7fcb462…
|
stephan
|
644 |
circle same "2" |
|
7fcb462…
|
stephan
|
645 |
arrow right until even with first box.e - (0.65,0.0) |
|
7fcb462…
|
stephan
|
646 |
ellipse "future" fit fill white height 0.2 width 0.5 thickness 1.5px |
|
7fcb462…
|
stephan
|
647 |
A3: circle same at A1+(0.8,-0.3) "3" fill 0xc0c0c0 |
|
7fcb462…
|
stephan
|
648 |
arrow from A1 to last circle chop "fork!" below aligned |
|
7fcb462…
|
stephan
|
649 |
|
|
7fcb462…
|
stephan
|
650 |
# content for the Betty lane |
|
7fcb462…
|
stephan
|
651 |
B1: circle same as A1 at A1-(0,$laneh) "1" |
|
7fcb462…
|
stephan
|
652 |
arrow right 50% |
|
7fcb462…
|
stephan
|
653 |
circle same "2" |
|
7fcb462…
|
stephan
|
654 |
arrow right until even with first ellipse.w |
|
7fcb462…
|
stephan
|
655 |
ellipse same "future" |
|
7fcb462…
|
stephan
|
656 |
B3: circle same at A3-(0,$laneh) "3" |
|
7fcb462…
|
stephan
|
657 |
arrow right 50% |
|
7fcb462…
|
stephan
|
658 |
circle same as A3 "4" |
|
7fcb462…
|
stephan
|
659 |
arrow from B1 to 2nd last circle chop |
|
7fcb462…
|
stephan
|
660 |
|
|
7fcb462…
|
stephan
|
661 |
# content for the Charlie lane |
|
7fcb462…
|
stephan
|
662 |
C1: circle same as A1 at B1-(0,$laneh) "1" |
|
7fcb462…
|
stephan
|
663 |
arrow 50% |
|
7fcb462…
|
stephan
|
664 |
circle same "2" |
|
7fcb462…
|
stephan
|
665 |
arrow right 0.8in "goes" "offline" |
|
7fcb462…
|
stephan
|
666 |
C5: circle same as A3 "5" |
|
7fcb462…
|
stephan
|
667 |
arrow right until even with first ellipse.w \ |
|
7fcb462…
|
stephan
|
668 |
"back online" above "pushes 5" below "pulls 3 & 4" below |
|
7fcb462…
|
stephan
|
669 |
ellipse same "future" |
|
7fcb462…
|
stephan
|
670 |
|
|
7fcb462…
|
stephan
|
671 |
# content for the Darlene lane |
|
7fcb462…
|
stephan
|
672 |
D1: circle same as A1 at C1-(0,$laneh) "1" |
|
7fcb462…
|
stephan
|
673 |
arrow 50% |
|
7fcb462…
|
stephan
|
674 |
circle same "2" |
|
7fcb462…
|
stephan
|
675 |
arrow right until even with C5.w |
|
7fcb462…
|
stephan
|
676 |
circle same "5" |
|
7fcb462…
|
stephan
|
677 |
arrow 50% |
|
7fcb462…
|
stephan
|
678 |
circle same as A3 "6" |
|
7fcb462…
|
stephan
|
679 |
arrow right until even with first ellipse.w |
|
7fcb462…
|
stephan
|
680 |
ellipse same "future" |
|
7fcb462…
|
stephan
|
681 |
D3: circle same as B3 at B3-(0,2*$laneh) "3" |
|
7fcb462…
|
stephan
|
682 |
arrow 50% |
|
7fcb462…
|
stephan
|
683 |
circle same "4" |
|
7fcb462…
|
stephan
|
684 |
arrow from D1 to D3 chop |
|
7fcb462…
|
stephan
|
685 |
`},{ |
|
7fcb462…
|
stephan
|
686 |
name: "The Stuff of Dreams", |
|
7fcb462…
|
stephan
|
687 |
code:` |
|
7fcb462…
|
stephan
|
688 |
O: text "DREAMS" color grey |
|
7fcb462…
|
stephan
|
689 |
circle rad 0.9 at 0.6 above O thick color red |
|
7fcb462…
|
stephan
|
690 |
text "INEXPENSIVE" big bold at 0.9 above O color red |
|
7fcb462…
|
stephan
|
691 |
|
|
7fcb462…
|
stephan
|
692 |
circle rad 0.9 at 0.6 heading 120 from O thick color green |
|
7fcb462…
|
stephan
|
693 |
text "FAST" big bold at 0.9 heading 120 from O color green |
|
7fcb462…
|
stephan
|
694 |
|
|
7fcb462…
|
stephan
|
695 |
circle rad 0.9 at 0.6 heading -120 from O thick color blue |
|
7fcb462…
|
stephan
|
696 |
text "HIGH" big bold "QUALITY" big bold at 0.9 heading -120 from O color blue |
|
7fcb462…
|
stephan
|
697 |
|
|
7fcb462…
|
stephan
|
698 |
text "EXPENSIVE" at 0.55 below O color cyan |
|
7fcb462…
|
stephan
|
699 |
text "SLOW" at 0.55 heading -60 from O color magenta |
|
7fcb462…
|
stephan
|
700 |
text "POOR" "QUALITY" at 0.55 heading 60 from O color gold |
|
ff1c48a…
|
stephan
|
701 |
`},{name:"Precision Arrows",code:` |
|
ff1c48a…
|
stephan
|
702 |
# Source: https://pikchr.org/home/forumpost/7f2f9a03eb |
|
ff1c48a…
|
stephan
|
703 |
define quiver { |
|
ff1c48a…
|
stephan
|
704 |
dot invis at 0.5 < $1.ne , $1.e > |
|
ff1c48a…
|
stephan
|
705 |
dot invis at 0.5 < $1.nw , $1.w > |
|
ff1c48a…
|
stephan
|
706 |
dot invis at 0.5 < $1.se , $1.e > |
|
ff1c48a…
|
stephan
|
707 |
dot invis at 0.5 < $1.sw , $1.w > |
|
ff1c48a…
|
stephan
|
708 |
|
|
ff1c48a…
|
stephan
|
709 |
dot at $2 right of 4th previous dot |
|
ff1c48a…
|
stephan
|
710 |
dot at $3 right of 4th previous dot |
|
ff1c48a…
|
stephan
|
711 |
dot at $4 right of 4th previous dot |
|
ff1c48a…
|
stephan
|
712 |
dot at $5 right of 4th previous dot |
|
ff1c48a…
|
stephan
|
713 |
arrow <- from previous dot to 2nd previous dot |
|
ff1c48a…
|
stephan
|
714 |
arrow -> from 3rd previous dot to 4th previous dot |
|
ff1c48a…
|
stephan
|
715 |
} |
|
ff1c48a…
|
stephan
|
716 |
|
|
ff1c48a…
|
stephan
|
717 |
define show_compass_l { |
|
ff1c48a…
|
stephan
|
718 |
dot color red at $1.e " .e" ljust |
|
ff1c48a…
|
stephan
|
719 |
dot same at $1.ne " .ne" ljust above |
|
ff1c48a…
|
stephan
|
720 |
line thick color green from previous to 2nd last dot |
|
ff1c48a…
|
stephan
|
721 |
} |
|
ff1c48a…
|
stephan
|
722 |
|
|
ff1c48a…
|
stephan
|
723 |
define show_compass_r { |
|
ff1c48a…
|
stephan
|
724 |
dot color red at $1.w " .w" ljust |
|
ff1c48a…
|
stephan
|
725 |
dot same at $1.nw " .nw" ljust above |
|
ff1c48a…
|
stephan
|
726 |
line thick color green from previous to 2nd last dot |
|
ff1c48a…
|
stephan
|
727 |
} |
|
ff1c48a…
|
stephan
|
728 |
|
|
ff1c48a…
|
stephan
|
729 |
PROGRAM: file "Program" rad 45px |
|
ff1c48a…
|
stephan
|
730 |
show_compass_l(PROGRAM) |
|
ff1c48a…
|
stephan
|
731 |
QUIVER: box invis ht 0.75 |
|
ff1c48a…
|
stephan
|
732 |
DATABASE: oval "Database" ht 0.75 wid 1.1 |
|
ff1c48a…
|
stephan
|
733 |
show_compass_r(DATABASE) |
|
ff1c48a…
|
stephan
|
734 |
|
|
ff1c48a…
|
stephan
|
735 |
quiver(QUIVER, 5px, -5px, 5px, 0px) |
|
ff1c48a…
|
stephan
|
736 |
|
|
ff1c48a…
|
stephan
|
737 |
text "Query" with .c at 0.1in above last arrow |
|
ff1c48a…
|
stephan
|
738 |
text "Records" with .c at 0.1in below 2nd last arrow |
|
7fcb462…
|
stephan
|
739 |
`} |
|
7fcb462…
|
stephan
|
740 |
]; |
|
7fcb462…
|
stephan
|
741 |
|
|
7fcb462…
|
stephan
|
742 |
|
|
7fcb462…
|
stephan
|
743 |
})(window.fossil); |