Twizy: Tuning Profile Editor

This plugin has been added to the Twizy code. It’s used here as a more complex example of what can be done by plugins.
It’s a full featured SEVCON tuning profile editor including dialogs to load & save profiles from the OVMS configuration and to create & read base64 profile codes.
See Twizy documentation for details on tuning profiles and capabilities.
Install: not necessary if vehicle Twizy is configured (see “Twizy” menu). If
you’d like to test this for another vehicle, add as a page
plugin e.g. /test/profed
.
profile-editor.htm
(hint: right click, save as)
1<!--
2 Twizy page plugin: Tuning Profile Editor
3 Note: included in firmware v3.2
4-->
5
6<style>
7.form-inline .form-control.slider-value {
8 width: 70px;
9}
10.radio-list {
11 height: 313px;
12 overflow-y: auto;
13 overflow-x: hidden;
14 padding-right: 15px;
15}
16.radio-list .radio {
17 overflow: hidden;
18}
19.radio-list .key {
20 min-width: 20px;
21 display: inline-block;
22 text-align: center;
23 margin: 0 10px;
24}
25.radio-list kbd {
26 min-width: 60px;
27 display: inline-block;
28 text-align: center;
29 margin: 0 20px 0 10px;
30}
31.radio-list .radio label {
32 width: 100%;
33 text-align: left;
34 padding: 8px 30px;
35}
36.radio-list .radio label.active {
37 background-color: #337ab7;
38 color: #fff;
39 outline: none;
40}
41.radio-list .radio label.active input {
42 outline: none;
43}
44.night .radio-list .radio label:hover {
45 color: #fff;
46}
47</style>
48
49<div class="panel panel-primary">
50 <div class="panel-heading">Tuning Profile <span id="headkey">Editor</span></div>
51 <div class="panel-body">
52 <form action="#">
53
54 <div class="form-group">
55 <div class="flex-group">
56 <button type="button" class="btn btn-default action-new">New</button>
57 <button type="button" class="btn btn-default action-load">Load…</button>
58 <input type="hidden" id="input-key" name="key" value="">
59 <input type="hidden" id="input-base64-reset" name="base64-reset" value="">
60 <input type="text" class="form-control font-monospace" placeholder="Base64 profile code"
61 name="base64" id="input-base64" value="" autocapitalize="none" autocorrect="off"
62 autocomplete="off" spellcheck="false">
63 </div>
64 </div>
65
66 <ul class="nav nav-tabs">
67 <li class="active"><a data-toggle="tab" href="#tab-drive" aria-expanded="true">Drive</a></li>
68 <li class=""><a data-toggle="tab" href="#tab-recup" aria-expanded="false">Recup</a></li>
69 <li class=""><a data-toggle="tab" href="#tab-ramps" aria-expanded="false">Ramps</a></li>
70 </ul>
71 <div class="tab-content">
72
73 <div id="tab-drive" class="tab-pane section-drive active in">
74
75 <fieldset id="part-speed">
76 <legend>Speed</legend>
77 <div class="form-horizontal">
78 <div class="form-group">
79 <label class="control-label col-sm-2" for="input-speed">Max speed:</label>
80 <div class="col-sm-10"><div class="form-control slider" id="speed" /></div>
81 </div>
82 <div class="form-group">
83 <label class="control-label col-sm-2" for="input-warn">Warn speed:</label>
84 <div class="col-sm-10"><div class="form-control slider" id="warn" /></div>
85 </div>
86 </div>
87 </fieldset>
88
89 <fieldset id="part-power">
90 <legend>Power</legend>
91 <div class="form-horizontal">
92 <div class="form-group">
93 <label class="control-label col-sm-2" for="input-current">Current:</label>
94 <div class="col-sm-10"><div class="form-control slider" id="current" /></div>
95 </div>
96 <div class="form-group">
97 <label class="control-label col-sm-2" for="input-torque">Torque:</label>
98 <div class="col-sm-10"><div class="form-control slider" id="torque" /></div>
99 </div>
100 <div class="form-group">
101 <label class="control-label col-sm-2" for="input-power_low">Power low:</label>
102 <div class="col-sm-10"><div class="form-control slider" id="power_low" /></div>
103 </div>
104 <div class="form-group">
105 <label class="control-label col-sm-2" for="input-power_high">Power high:</label>
106 <div class="col-sm-10"><div class="form-control slider" id="power_high" /></div>
107 </div>
108 </div>
109 </fieldset>
110
111 <fieldset id="part-drive">
112 <legend>Drive</legend>
113 <div class="form-horizontal">
114 <div class="form-group">
115 <label class="control-label col-sm-2" for="input-drive">Drive level:</label>
116 <div class="col-sm-10"><div class="form-control slider" id="drive" /></div>
117 </div>
118 <div class="form-group">
119 <label class="control-label col-sm-2" for="input-autodrive_ref">Auto 100% ref:</label>
120 <div class="col-sm-10"><div class="form-control slider" id="autodrive_ref" /></div>
121 </div>
122 <div class="form-group">
123 <label class="control-label col-sm-2" for="input-autodrive_minprc">Auto min level:</label>
124 <div class="col-sm-10"><div class="form-control slider" id="autodrive_minprc" /></div>
125 </div>
126 </div>
127 </fieldset>
128
129 <fieldset id="part-tsmapd">
130 <legend>Torque/Speed-Map: Drive</legend>
131 <div class="form-horizontal">
132 <div class="form-group">
133 <label class="control-label col-sm-2" for="input-tsd_prc1">Trq level 1:</label>
134 <div class="col-sm-10"><div class="form-control slider" id="tsd_prc1" /></div>
135 </div>
136 <div class="form-group">
137 <label class="control-label col-sm-2" for="input-tsd_spd1">…at speed:</label>
138 <div class="col-sm-10"><div class="form-control slider" id="tsd_spd1" /></div>
139 </div>
140 <div class="form-group">
141 <label class="control-label col-sm-2" for="input-tsd_prc2">Trq level 2:</label>
142 <div class="col-sm-10"><div class="form-control slider" id="tsd_prc2" /></div>
143 </div>
144 <div class="form-group">
145 <label class="control-label col-sm-2" for="input-tsd_spd2">…at speed:</label>
146 <div class="col-sm-10"><div class="form-control slider" id="tsd_spd2" /></div>
147 </div>
148 <div class="form-group">
149 <label class="control-label col-sm-2" for="input-tsd_prc3">Trq level 3:</label>
150 <div class="col-sm-10"><div class="form-control slider" id="tsd_prc3" /></div>
151 </div>
152 <div class="form-group">
153 <label class="control-label col-sm-2" for="input-tsd_spd3">…at speed:</label>
154 <div class="col-sm-10"><div class="form-control slider" id="tsd_spd3" /></div>
155 </div>
156 <div class="form-group">
157 <label class="control-label col-sm-2" for="input-tsd_prc4">Trq level 4:</label>
158 <div class="col-sm-10"><div class="form-control slider" id="tsd_prc4" /></div>
159 </div>
160 <div class="form-group">
161 <label class="control-label col-sm-2" for="input-tsd_spd4">…at speed:</label>
162 <div class="col-sm-10"><div class="form-control slider" id="tsd_spd4" /></div>
163 </div>
164 </div>
165 </fieldset>
166
167 </div>
168
169 <div id="tab-recup" class="tab-pane section-recup">
170
171 <fieldset id="part-recup">
172 <legend>Recup</legend>
173 <div class="form-horizontal">
174 <div class="form-group">
175 <label class="control-label col-sm-2" for="input-neutral">Neutral level:</label>
176 <div class="col-sm-10"><div class="form-control slider" id="neutral" /></div>
177 </div>
178 <div class="form-group">
179 <label class="control-label col-sm-2" for="input-brake">Brake level:</label>
180 <div class="col-sm-10"><div class="form-control slider" id="brake" /></div>
181 </div>
182 <div class="form-group">
183 <label class="control-label col-sm-2" for="input-autorecup_ref">Auto 100% ref:</label>
184 <div class="col-sm-10"><div class="form-control slider" id="autorecup_ref" /></div>
185 </div>
186 <div class="form-group">
187 <label class="control-label col-sm-2" for="input-autorecup_minprc">Auto min level:</label>
188 <div class="col-sm-10"><div class="form-control slider" id="autorecup_minprc" /></div>
189 </div>
190 </div>
191 </fieldset>
192
193 <fieldset id="part-tsmapn">
194 <legend>Torque/Speed-Map: Neutral</legend>
195 <div class="form-horizontal">
196 <div class="form-group">
197 <label class="control-label col-sm-2" for="input-tsn_prc1">Trq level 1:</label>
198 <div class="col-sm-10"><div class="form-control slider" id="tsn_prc1" /></div>
199 </div>
200 <div class="form-group">
201 <label class="control-label col-sm-2" for="input-tsn_spd1">…at speed:</label>
202 <div class="col-sm-10"><div class="form-control slider" id="tsn_spd1" /></div>
203 </div>
204 <div class="form-group">
205 <label class="control-label col-sm-2" for="input-tsn_prc2">Trq level 2:</label>
206 <div class="col-sm-10"><div class="form-control slider" id="tsn_prc2" /></div>
207 </div>
208 <div class="form-group">
209 <label class="control-label col-sm-2" for="input-tsn_spd2">…at speed:</label>
210 <div class="col-sm-10"><div class="form-control slider" id="tsn_spd2" /></div>
211 </div>
212 <div class="form-group">
213 <label class="control-label col-sm-2" for="input-tsn_prc3">Trq level 3:</label>
214 <div class="col-sm-10"><div class="form-control slider" id="tsn_prc3" /></div>
215 </div>
216 <div class="form-group">
217 <label class="control-label col-sm-2" for="input-tsn_spd3">…at speed:</label>
218 <div class="col-sm-10"><div class="form-control slider" id="tsn_spd3" /></div>
219 </div>
220 <div class="form-group">
221 <label class="control-label col-sm-2" for="input-tsn_prc4">Trq level 4:</label>
222 <div class="col-sm-10"><div class="form-control slider" id="tsn_prc4" /></div>
223 </div>
224 <div class="form-group">
225 <label class="control-label col-sm-2" for="input-tsn_spd4">…at speed:</label>
226 <div class="col-sm-10"><div class="form-control slider" id="tsn_spd4" /></div>
227 </div>
228 </div>
229 </fieldset>
230
231 <fieldset id="part-tsmapb">
232 <legend>Torque/Speed-Map: Brake</legend>
233 <div class="form-horizontal">
234 <div class="form-group">
235 <label class="control-label col-sm-2" for="input-tsb_prc1">Trq level 1:</label>
236 <div class="col-sm-10"><div class="form-control slider" id="tsb_prc1" /></div>
237 </div>
238 <div class="form-group">
239 <label class="control-label col-sm-2" for="input-tsb_spd1">…at speed:</label>
240 <div class="col-sm-10"><div class="form-control slider" id="tsb_spd1" /></div>
241 </div>
242 <div class="form-group">
243 <label class="control-label col-sm-2" for="input-tsb_prc2">Trq level 2:</label>
244 <div class="col-sm-10"><div class="form-control slider" id="tsb_prc2" /></div>
245 </div>
246 <div class="form-group">
247 <label class="control-label col-sm-2" for="input-tsb_spd2">…at speed:</label>
248 <div class="col-sm-10"><div class="form-control slider" id="tsb_spd2" /></div>
249 </div>
250 <div class="form-group">
251 <label class="control-label col-sm-2" for="input-tsb_prc3">Trq level 3:</label>
252 <div class="col-sm-10"><div class="form-control slider" id="tsb_prc3" /></div>
253 </div>
254 <div class="form-group">
255 <label class="control-label col-sm-2" for="input-tsb_spd3">…at speed:</label>
256 <div class="col-sm-10"><div class="form-control slider" id="tsb_spd3" /></div>
257 </div>
258 <div class="form-group">
259 <label class="control-label col-sm-2" for="input-tsb_prc4">Trq level 4:</label>
260 <div class="col-sm-10"><div class="form-control slider" id="tsb_prc4" /></div>
261 </div>
262 <div class="form-group">
263 <label class="control-label col-sm-2" for="input-tsb_spd4">…at speed:</label>
264 <div class="col-sm-10"><div class="form-control slider" id="tsb_spd4" /></div>
265 </div>
266 </div>
267 </fieldset>
268
269 </div>
270
271 <div id="tab-ramps" class="tab-pane section-ramps">
272
273 <fieldset id="part-rampsglobal">
274 <legend>General</legend>
275 <div class="form-horizontal">
276 <div class="form-group">
277 <label class="control-label col-sm-2" for="input-ramplimit_accel">Limit up:</label>
278 <div class="col-sm-10"><div class="form-control slider" id="ramplimit_accel" /></div>
279 </div>
280 <div class="form-group">
281 <label class="control-label col-sm-2" for="input-ramplimit_decel">Limit down:</label>
282 <div class="col-sm-10"><div class="form-control slider" id="ramplimit_decel" /></div>
283 </div>
284 <div class="form-group">
285 <label class="control-label col-sm-2" for="input-smooth">Smoothing:</label>
286 <div class="col-sm-10"><div class="form-control slider" id="smooth" /></div>
287 </div>
288 </div>
289 </fieldset>
290
291 <fieldset id="part-ramps">
292 <legend>Ramp Speeds</legend>
293 <div class="form-horizontal">
294 <div class="form-group">
295 <label class="control-label col-sm-2" for="input-ramp_start">Start/reverse:</label>
296 <div class="col-sm-10"><div class="form-control slider" id="ramp_start" /></div>
297 </div>
298 <div class="form-group">
299 <label class="control-label col-sm-2" for="input-ramp_accel">Accelerate:</label>
300 <div class="col-sm-10"><div class="form-control slider" id="ramp_accel" /></div>
301 </div>
302 <div class="form-group">
303 <label class="control-label col-sm-2" for="input-ramp_decel">Decelerate:</label>
304 <div class="col-sm-10"><div class="form-control slider" id="ramp_decel" /></div>
305 </div>
306 <div class="form-group">
307 <label class="control-label col-sm-2" for="input-ramp_neutral">Neutral:</label>
308 <div class="col-sm-10"><div class="form-control slider" id="ramp_neutral" /></div>
309 </div>
310 <div class="form-group">
311 <label class="control-label col-sm-2" for="input-ramp_brake">Brake:</label>
312 <div class="col-sm-10"><div class="form-control slider" id="ramp_brake" /></div>
313 </div>
314 </div>
315 </fieldset>
316
317 </div>
318
319 </div>
320
321 <br>
322 <div class="form-horizontal">
323 <div class="form-group">
324 <label class="control-label col-sm-2" for="input-label">Label:</label>
325 <div class="col-sm-10">
326 <input type="text" class="form-control" placeholder="Short button label"
327 name="label" id="input-label" value="" autocapitalize="none" autocorrect="off"
328 autocomplete="off" spellcheck="false">
329 </div>
330 </div>
331 <div class="form-group">
332 <label class="control-label col-sm-2" for="input-title">Title:</label>
333 <div class="col-sm-10">
334 <input type="text" class="form-control" placeholder="optional title/name"
335 name="title" id="input-title" value="" autocapitalize="none" autocorrect="off"
336 autocomplete="off" spellcheck="false">
337 </div>
338 </div>
339 </div>
340
341 <br>
342 <div class="text-center">
343 <button type="button" class="btn btn-default action-reset">Reset</button>
344 <button type="button" class="btn btn-default action-saveas">Save as…</button>
345 <button type="button" class="btn btn-primary action-save">Save</button>
346 </div>
347
348 </form>
349 </div>
350</div>
351
352<div id="key-dialog" />
353
354<script>
355(function(){
356
357 // init sliders:
358
359 $('#speed').slider({ unit:'kph', min:6, max:120, default:80, value:80, checked:false });
360 $('#warn').slider({ unit:'kph', min:6, max:120, default:89, value:89, checked:false });
361
362 $('#current').slider({ unit:'%', min:10, max:123, default:100, value:100, checked:false });
363 $('#torque').slider({ unit:'%', min:10, max:130, default:100, value:100, checked:false });
364 $('#power_low').slider({ unit:'%', min:10, max:139, default:100, value:100, checked:false });
365 $('#power_high').slider({ unit:'%', min:10, max:130, default:100, value:100, checked:false });
366
367 $('#drive').slider({ unit:'%', min:10, max:100, default:100, value:100, checked:false });
368 $('#autodrive_ref').slider({ unit:'kW', min:0, max:25, step:0.1, default:0, value:0, checked:false });
369 $('#autodrive_minprc').slider({ unit:'%', min:0, max:100, default:0, value:0, checked:false });
370
371 $('#tsd_prc1').slider({ unit:'%', min:0, max:100, default:100, value:100, checked:false });
372 $('#tsd_spd1').slider({ unit:'kph', min:0, max:120, default:33, value:33, checked:false });
373 $('#tsd_prc2').slider({ unit:'%', min:0, max:100, default:100, value:100, checked:false });
374 $('#tsd_spd2').slider({ unit:'kph', min:0, max:120, default:39, value:39, checked:false });
375 $('#tsd_prc3').slider({ unit:'%', min:0, max:100, default:100, value:100, checked:false });
376 $('#tsd_spd3').slider({ unit:'kph', min:0, max:120, default:50, value:50, checked:false });
377 $('#tsd_prc4').slider({ unit:'%', min:0, max:100, default:100, value:100, checked:false });
378 $('#tsd_spd4').slider({ unit:'kph', min:0, max:120, default:66, value:66, checked:false });
379
380 $('#neutral').slider({ unit:'%', min:0, max:100, default:18, value:18, checked:false });
381 $('#brake').slider({ unit:'%', min:0, max:100, default:18, value:18, checked:false });
382 $('#autorecup_ref').slider({ unit:'kW', min:0, max:25, step:0.1, default:0, value:0, checked:false });
383 $('#autorecup_minprc').slider({ unit:'%', min:0, max:100, default:0, value:0, checked:false });
384
385 $('#tsn_prc1').slider({ unit:'%', min:0, max:100, default:100, value:100, checked:false });
386 $('#tsn_spd1').slider({ unit:'kph', min:0, max:120, default:33, value:33, checked:false });
387 $('#tsn_prc2').slider({ unit:'%', min:0, max:100, default:80, value:80, checked:false });
388 $('#tsn_spd2').slider({ unit:'kph', min:0, max:120, default:39, value:39, checked:false });
389 $('#tsn_prc3').slider({ unit:'%', min:0, max:100, default:50, value:50, checked:false });
390 $('#tsn_spd3').slider({ unit:'kph', min:0, max:120, default:50, value:50, checked:false });
391 $('#tsn_prc4').slider({ unit:'%', min:0, max:100, default:20, value:20, checked:false });
392 $('#tsn_spd4').slider({ unit:'kph', min:0, max:120, default:66, value:66, checked:false });
393
394 $('#tsb_prc1').slider({ unit:'%', min:0, max:100, default:100, value:100, checked:false });
395 $('#tsb_spd1').slider({ unit:'kph', min:0, max:120, default:33, value:33, checked:false });
396 $('#tsb_prc2').slider({ unit:'%', min:0, max:100, default:80, value:80, checked:false });
397 $('#tsb_spd2').slider({ unit:'kph', min:0, max:120, default:39, value:39, checked:false });
398 $('#tsb_prc3').slider({ unit:'%', min:0, max:100, default:50, value:50, checked:false });
399 $('#tsb_spd3').slider({ unit:'kph', min:0, max:120, default:50, value:50, checked:false });
400 $('#tsb_prc4').slider({ unit:'%', min:0, max:100, default:20, value:20, checked:false });
401 $('#tsb_spd4').slider({ unit:'kph', min:0, max:120, default:66, value:66, checked:false });
402
403
404 $('#ramplimit_accel').slider({ unit:'%', min:1, max:100, default:30, value:30, checked:false });
405 $('#ramplimit_decel').slider({ unit:'%', min:0, max:100, default:30, value:30, checked:false });
406 $('#smooth').slider({ unit:'%', min:0, max:100, default:70, value:70, checked:false });
407
408 $('#ramp_start').slider({ unit:'‰', min:1, max:250, default:40, value:40, checked:false });
409 $('#ramp_accel').slider({ unit:'%', min:1, max:100, default:25, value:25, checked:false });
410 $('#ramp_decel').slider({ unit:'%', min:0, max:100, default:20, value:20, checked:false });
411 $('#ramp_neutral').slider({ unit:'%', min:0, max:100, default:40, value:40, checked:false });
412 $('#ramp_brake').slider({ unit:'%', min:0, max:100, default:40, value:40, checked:false });
413
414 // profile handling:
415
416 const keys = [
417 "checksum",
418 "speed",
419 "warn",
420 "torque",
421 "power_low",
422 "power_high",
423 "drive",
424 "neutral",
425 "brake",
426 "tsd_spd1",
427 "tsd_spd2",
428 "tsd_spd3",
429 "tsd_spd4",
430 "tsd_prc1",
431 "tsd_prc2",
432 "tsd_prc3",
433 "tsd_prc4",
434 "tsn_spd1",
435 "tsn_spd2",
436 "tsn_spd3",
437 "tsn_spd4",
438 "tsn_prc1",
439 "tsn_prc2",
440 "tsn_prc3",
441 "tsn_prc4",
442 "tsb_spd1",
443 "tsb_spd2",
444 "tsb_spd3",
445 "tsb_spd4",
446 "tsb_prc1",
447 "tsb_prc2",
448 "tsb_prc3",
449 "tsb_prc4",
450 "ramp_start",
451 "ramp_accel",
452 "ramp_decel",
453 "ramp_neutral",
454 "ramp_brake",
455 "smooth",
456 "brakelight_on",
457 "brakelight_off",
458 "ramplimit_accel",
459 "ramplimit_decel",
460 "autorecup_minprc",
461 "autorecup_ref",
462 "autodrive_minprc",
463 "autodrive_ref",
464 "current",
465 ];
466
467 const scale = {
468 "autodrive_ref": 0.1,
469 "autorecup_ref": 0.1,
470 };
471
472 var profile = {};
473
474 var plist = [
475 { label: "–", title: "Working Set" },
476 { label: "PWR", title: "Power" },
477 { label: "ECO", title: "Economy" },
478 { label: "ICE", title: "Winter" },
479 ];
480
481 // load profile list:
482 var plistloader = loadcmd('config list xrt').then(function(data) {
483 var lines = data.split('\n'), line, i, key;
484 for (i = 0; i < lines.length; i++) {
485 line = lines[i].match(/profile([0-9]{2})\.?([^:]*): (.*)/);
486 if (line && line.length == 4) {
487 key = Number(line[1]);
488 if (key < 1 || key > 99) continue;
489 if (!plist[key]) plist[key] = {};
490 plist[key][line[2]||"profile"] = line[3];
491 }
492 }
493 });
494
495 // current control → power ranges:
496 function currentControl(on, trigger) {
497 if (on) {
498 $("#input-torque, #input-power_low, #input-power_high").slider({ max: 254 });
499 } else {
500 $("#input-torque, #input-power_high").slider({ max: 130 });
501 $("#input-power_low").slider({ max: 139 });
502 }
503 }
504 $('#input-current').on('change', function(ev) {
505 currentControl(this.checked);
506 $("#input-torque, #input-power_low, #input-power_high").trigger('change');
507 });
508
509 // load profile into sliders:
510 function loadProfile() {
511 currentControl(profile["current"] >= 0);
512 $.map(profile, function(val, key) {
513 $('#input-'+key).slider({ value: (val >= 0) ? (val * (scale[key]||1)) : null, checked: (val >= 0) });
514 });
515 }
516
517 // calculate profile checksum:
518 function calcChecksum() {
519 var checksum, i;
520 checksum = 0x0101;
521 for (i=1; i<keys.length; i++)
522 checksum += profile[keys[i]] + 1;
523 if ((checksum & 0x0ff) == 0)
524 checksum >>= 8;
525 return (checksum & 0x0ff) - 1;
526 }
527
528 // load a base64 string into profile:
529 function loadBase64(base64) {
530 var bin = atob(base64);
531 $.map(keys, function(key, i) { profile[key] = (bin.charCodeAt(i)||0) - 1; });
532 if (profile["checksum"] == calcChecksum()) {
533 loadProfile();
534 return true;
535 } else {
536 confirmdialog("Profile Error", "Invalid base64 code (checksum mismatch)", ["Close"]);
537 return false;
538 }
539 }
540
541 // make a base64 string from profile:
542 function makeBase64() {
543 profile["checksum"] = calcChecksum();
544 var u8 = new Uint8Array(keys.length);
545 $.map(keys, function(key, i) { u8[i] = profile[key] + 1; });
546 return btoa(String.fromCharCode.apply(null, u8));
547 }
548
549 // load a profile:
550 function loadKey(key) {
551 if (key < 0 || key > 99) return;
552 plistloader.then(function() {
553 $('#input-label').val(plist[key] && plist[key]["label"] || "");
554 $('#input-title').val(plist[key] && plist[key]["title"] || "");
555 loadcmd('xrt cfg get ' + key).fail(function(request, textStatus, errorThrown) {
556 confirmdialog("Load Profile", xhrErrorInfo(request, textStatus, errorThrown), ["Close"]);
557 }).done(function(res) {
558 var base64;
559 if (res.match(/error/i))
560 base64 = "AQ";
561 else
562 base64 = res.substr(res.lastIndexOf(' ')+1);
563 if (loadBase64(base64)) {
564 $('#input-key').val(key);
565 $('#input-base64, #input-base64-reset').val(base64);
566 $('#headkey').text('#' + key);
567 }
568 });
569 });
570 }
571
572 // save profile:
573 function saveKey(key) {
574 if (key < 0 || key > 99) return;
575 var base64 = $('#input-base64').val(),
576 label = $('#input-label').val().replace(/"/g, '\\"'),
577 title = $('#input-title').val().replace(/"/g, '\\"'),
578 ckey = 'profile' + ((key<10)?'0':'') + key;
579 var cmd = 'xrt cfg set ' + key + ' ' + base64 + ' "' + label + '" "' + title + '"';
580 loadcmd(cmd).fail(function(request, textStatus, errorThrown) {
581 confirmdialog("Save Profile", xhrErrorInfo(request, textStatus, errorThrown), ["Close"]);
582 }).done(function(res) {
583 confirmdialog("Save Profile", res, ["Close"]);
584 if (!res.match(/error/i)) {
585 if (key > 0) {
586 if (!plist[key]) plist[key] = {};
587 plist[key]["label"] = label;
588 plist[key]["title"] = title;
589 }
590 $('#input-key').val(key);
591 $('#input-base64-reset').val(base64);
592 $('#headkey').text('#' + key);
593 }
594 });
595 }
596
597 // base64 input:
598 $('#input-base64').on('change', function(ev) {
599 var base64 = $(this).val() || "AQ";
600 if (loadBase64(base64))
601 $('#input-base64-reset').val(base64);
602 });
603
604 // update profile & base64 on slider changes:
605 $('.slider-value').on('change', function(ev) {
606 var key = this.name;
607 var val = Math.max(this.min, Math.min(this.max, 1*this.value));
608 profile[key] = this.checked ? (val / (scale[key]||1)) : -1;
609 var base64 = makeBase64();
610 $('#input-base64').val(base64);
611 });
612
613 // prep key dialog:
614 $('#key-dialog').dialog({
615 show: false,
616 onShow: function(input) {
617 var $this = $(this);
618 $this.addClass("loading");
619 plistloader.then(function(data) {
620 var curkey = $('#input-key').val() || 0, i, label, title;
621 $plist = $('<div class="radio-list" data-toggle="buttons" />');
622 for (i = 0; i <= Math.min(99, plist.length + 2); i++) {
623 if (plist[i] && (i==0 || plist[i].profile)) {
624 label = plist[i].label || "–";
625 title = plist[i].title || (plist[i].profile.substr(0, 10) + "…");
626 } else {
627 label = "–";
628 title = "–new–";
629 }
630 $plist.append('<div class="radio"><label class="btn">' +
631 '<input type="radio" name="key" value="' + i + '"><span class="key">' +
632 ((i==0) ? "WS" : ("#" + ((i<10)?'0':'') + i)) + '</span> <kbd>' +
633 encode_html(label) + '</kbd> ' + encode_html(title) + '</label></div>');
634 }
635 $this.find('.modal-body').append($plist).find('input[value="'+curkey+'"]')
636 .prop("checked", true).parent().addClass("active");
637 $plist
638 .on('dblclick', 'label.btn', function(ev) { $this.dialog('triggerButton', 1); })
639 .on('keypress', function(ev) { if (ev.which==13) $this.dialog('triggerButton', 1); });
640 $this.removeClass("loading");
641 });
642 },
643 onShown: function(input) {
644 $(this).find('.btn.active').focus();
645 },
646 onHide: function(input) {
647 var $this = $(this), dlg = $this.data("dialog");
648 var key = $this.find('input[name="key"]:checked').val();
649 if (key !== undefined && input.button && input.button.index)
650 dlg.options.onAction.call(this, key);
651 },
652 });
653
654 // buttons:
655 $('.action-new').on('click', function(ev) {
656 $('.slider').slider({ value: null });
657 $('#headkey').text('Editor');
658 $('#input-base64, #input-label, #input-title').val('').trigger('change');
659 });
660 $('.action-reset').on('click', function(ev) {
661 $('#input-base64').val($('#input-base64-reset').val()).trigger('change');
662 });
663 $('.action-load').on('click', function(ev) {
664 $('#key-dialog').dialog({
665 show: true, title: 'Load Profile', body: '',
666 buttons: [{ label: 'Cancel', btnClass: 'default' },{ label: 'Load', btnClass: 'primary' }],
667 onAction: function(key) { loadKey(key) },
668 });
669 });
670 $('.action-saveas').on('click', function(ev) {
671 $('#key-dialog').dialog({
672 show: true, title: 'Save Profile', body: '',
673 buttons: [{ label: 'Cancel', btnClass: 'default' },{ label: 'Save', btnClass: 'primary' }],
674 onAction: function(key) { saveKey(key) },
675 });
676 });
677 $('.action-save').on('click', function(ev) {
678 var key = $('#input-key').val();
679 if (key === "")
680 $('.action-saveas').trigger('click');
681 else
682 saveKey(key);
683 });
684
685 // start: load profile / open load dialog:
686 if (page.params["key"] !== undefined)
687 loadKey(page.params["key"]);
688 else
689 $('.action-load').trigger('click');
690
691})();
692</script>