-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinput-type=picker_instructions.html
More file actions
1037 lines (981 loc) · 62.5 KB
/
input-type=picker_instructions.html
File metadata and controls
1037 lines (981 loc) · 62.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta name='description' content='SoftMoon-WebWare’s JavaScript™ powered input type="picker" HTML extension.'>
<meta name='author' content='Joe Golembieski, SoftMoon-WebWare'>
<meta name='copyright' content='Copyright © 2010, 2012, 2013, 2014, 2015, 2022, 2024 Joe Golembieski, SoftMoon-WebWare'>
<meta name='last-updated' content='July 21, 2024'>
<title><input type="picker"> from SoftMoon-WebWare</title>
<link rel="icon" type="image/x-icon" href="images/SoftMoonWebWare.gif">
<script>const SoftMoon={}; Object.defineProperty(SoftMoon, 'WebWare', {value: {}, enumerable: true});</script>
<script type='text/javascript' src='JS_toolbucket/+++JS/+++.js'></script>
<script type='text/javascript' src='JS_toolbucket/SoftMoon-WebWare/UniDOM-2022.js'></script>
<script type='text/javascript' src='JS_toolbucket/SoftMoon-WebWare/Picker.js'></script>
<style type="text/css">
body, header, main, section, footer, figure, div, h1, h2, h4, h5, img, ul, ol, dl, menu, li {
margin: 0;
padding: 0;
scroll-behavior: smooth; }
body {
min-width: 62.618em;
font-family: serif;
color: #000000;
background-color: #FFDEAD;
background-image: /* SteelBlue */
linear-gradient(to right, #4682B4FF, 304px, #4682B400 800px),
linear-gradient(to bottom, #4682B4FF, 266px, #4682B400 700px),
linear-gradient(to top, #4682B4FF, 38px, #4682B400 100px);
}
mark {
color: inherit;
background-color: inherit; }
header h1 {
font-size: 1.618em;
font-weight: bold;
clear: both;
text-align: center; }
header h1:first-child {
font-family: sans-serif;
padding: .618em 1em 0 1em;
text-align: left; }
header h1 span {
padding: 0 0 0 2.618em;
font-size: .764em } /* ≈ Φ + ((1-Φ) - (1-Φ)*Φ) */
header figure {
margin-left: .382rem;
float: left; }
#logo {
opacity: .78;
font-size: 1.618em; /*for alt text*/
font-weight: bold;
line-height: 150%; }
header figcaption {
font-size: 1.382em;
font-weight: bold;
text-align: center;
width: 27em; }
header h1 span,
header figcaption span {
display: block;
font-style: oblique; }
main {
text-align: center; }
main > p {
text-align: justify;
text-indent: -.618em;
font-size: 1.236em;
width: 27em;
margin: 0 auto 0 auto;
padding: 0 1em 1em 1em; }
main > p * {
text-indent: 0; }
main > p:first-letter {
font-size: 1.618em;
font-weight: bold;
color: #000040; }
main samp, code {
white-space: nowrap;
text-align: left;
font-size: .854076em;
font-family: "Consolas"; }
p code {
display: inline-block; }
p code.block {
white-space: pre; }
main > code, main > samp {
display: block;
white-space: pre;
background-color: white;
width: 98%;
margin: 0 auto;
padding: .382em;
overflow: auto; }
.filename {
color: #0066FF;
white-space: nowrap; }
h3 {
margin: .618em 0 0 0;
padding: 0; }
label {
display: inline-block;
position: relative;
margin-bottom: 4em; }
label .pickerPanel {
position: absolute;
top: 1.618em;
right: -10.618em; }
.pickerPanel,
#songs .picker {display: none;}
.activePicker,
#songs .activePicker {display: table;}
#songs {
padding: 2em 0 .618em 0;
margin: .618em 12.618em;
position: relative; }
#songPickerControls.activePicker {
position: absolute;
top: 1em;
left: 0;
display: block;
width: 100%;
text-align: center;
background-color: yellow; }
@-moz-document url-prefix() {
#songPickerControls.activePicker {
top: 0.25em; /* value for Firefox - they need to fix this issue - going on 10+ years! */
}
}
#songs label {
margin: 0 .618em; }
#songPickerHTML {
background-color: yellow;
border: 2px solid blue; }
#songPickerHTML td {
padding: 0 .618em;
border: 1px solid;
white-space: nowrap; }
#songPickerHTML td:last-child {
font-style: oblique; }
dl {
max-width: 34em;
text-align: left;
margin: 0 auto 1em;
}
footer p {
margin: 0 1.618em;
font-size: .78em;
font-weight: bold; }
footer note {
margin-right: 2.618em; }
footer note:last-child {
margin-right: 0; }
abbr[tm] {
font-size: .763924em;
vertical-align: .236076em;
line-height: 100%; }
kbd span {
display: inline-block;
position: static;
border: 1px solid #8080FF;
border-bottom: 2px solid #404080;
border-right: 2px solid #404080;
border-radius: 1.618em / 2em;
color: white;
background-color: #202040;
text-align: center;
margin: 0 .382em;
padding: 0 .618em; }
nobr {
white-space: nowrap; }
main a::before {
content: '⟴'; } /* ⇨ ⇨ ⟴ ⟴ */
main a[href*="#"]::before {
content: '⇓'; } /* ⇕ ⇕ ⇓ ⇓ */
main a[href*="#"][up]::before {
content: '⇑'; } /* ⇕ ⇕ ⇑ ⇑ */
main a[target]::before {
content: '►'; }
blockcaption {
display: block;
margin: 0.618em 1.618em 0 1.618em;
padding: 0;
font-size: 1.168em;
}
</style>
</head>
<body>
<header>
<h1>Custom Web Software Development</h1>
<figure>
<img id='logo' src="images/SoftMoon-WebWare.gif" alt="SoftMoon-WebWare">
<figcaption>JavaScript<abbr tm>™</abbr>, <abbr>HTML 5</abbr>, <abbr>CSS 4</abbr>, & <abbr>PHP 8</abbr>:
<span>Innovative Enterprise level Scripting for interactive sites, <acronym title='software as a service'>SaaS</acronym>, & cross-platform desktop apps</span></figcaption>
</figure>
<?php include "SoftMoon-WebWare_header_menu.php"; ?>
<h1>JavaScript<abbr tm>™</abbr> powered <code><input type="picker"></code></h1>
</header>
<main id='content'>
<p><abbr>HTML</abbr> forms are an essential part of interacting with web-page viewers,
and the <code><input></code> tag is the most common interface tool used.
However, it gives little help to users that need to enter complex and/or specific data.
<abbr>HTML5</abbr> introduces the <code><datalist></code> element which can turn an
<code><input></code> tag into a hybrid with a <code><select></code> tag,
giving the user a pull-down menu to choose from without limiting the input data to only the menu items.
But sometimes that’s not enough…you want to present to your users
a complex table of data to choose from, or maybe some other source needs to generate the data on-the-fly,
such as a color-picker (like the MasterColorPicker project, for which this project was developed).
So here I present the <code><input type="picker"></code> project.
If an <code><input type="text"></code> tag is like a car,
and a <code><textarea></code> tag is like a pickup truck,
and a <code><select></code> tag is like a bus,
and an <code><input type="text"></code> with a <code><datalist></code> is like a train,
then an <code><input></code> with a <code>Picker</code> interface is like a multi-city intergalactic spacecraft.</p>
<p>When the <code>Picker</code> interface code was first developed,
Microsoft’s Internet Exploder 6 was king and <abbr>HTML</abbr> was still in its adolescence.
This concept of a “picker” was fairly new back then;
these days the concept is fully embraced and supported by native <abbr>HTML</abbr> logistics.
The <code>Picker</code> class now also supports this newer technology and easily integrates with it.
Specifically, <abbr title='Accessible Rich Internet Applications'>ARIA</abbr>
<a href='https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/combobox_role' target="Mozilla">“combobox”</a> and
<a href='https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-haspopup' target="Mozilla">“popup”</a>
specifications are incorporated into the <code>Picker</code>’s framework.
For picker pop-ups of role-type “dialog,” <a href='#tab_key'>integrated <kbd><span>Tab</span></kbd> key</a> support is also incorporated.
You can download the JavaScript<mark class='macronym'>™</mark> package from our <a href='OpenSource.php'>download page</a>
or Git it from GitHub: <a href='https://github.com/SoftMoonWebWare/input_type_Picker' target='github'>input_type_Picker</a>.</p>
</p>
<p>Using the <code>Picker</code> Class with an <code><input type="text"></code> is simple.
Just as you would do using the <code><datalist></code> tag, the “picker” <abbr>HTML</abbr>
is separate from the <code><input></code> tag itself.
However, <code><datalist></code>s are simple “lists”, and you may only associate one <code><datalist></code>
with each <code><input></code>.
With the <code>Picker</code> Class you may associate any number of “panels” (<abbr>HTML</abbr> elements),
and each panel may have any number of “pickers” along with any other <abbr>HTML</abbr> markup
and auxiliary displays and data, as well as <a href='#interface_controls'>“interface controls”</a> – form controls
(such as <code><input></code>s and <code><select></code>s)
that modify the “pickers” themselves and/or their behaviors.
Furthermore, not only may you associate multiple “panels” and “pickers” with an <code><input></code>,
you may associate multiple <code><input></code>s with the same “panel”(s)/“picker”(s).
For instance, the <a href="MasterColorPicker_instructions.php">MasterColorPicker</a> project uses
9 “panels” and 19 “pickers” by default (at this time of writing),
and the <a href="RainbowMaker%202.0/">Rainbow-Maker 2</a> project
associates the MasterColorPicker with many different <code><input></code>s.</p>
<p>“Panels” and their “pickers” receive JavaScript<abbr tm>™</abbr>-applied <abbr>CSS</abbr> class-names when “active”,
allowing the developer to pop-up and pop-out the picker <abbr>HTML</abbr> if desired
(as noted <abbr>ARIA</abbr>-support is included for pop-ups also),
or style it any way needed for complete flexibility.
“Panels” may be styled any way you choose using your custom <abbr>CSS</abbr>,
and if you have more than one panel, they may visually overlap on the display;
the <code>Picker</code> Class manages an additional set of class-names which allows you to manage the
Z-index level of each panel, so the last one clicked on may be “brought to the top.”
The target <code><input></code> (or <code><textarea></code>, etc., but for simplicity we will just refer to inputs)
has its <abbr>ARIA</abbr> attributes modified in real-time to reflect this “pop-up” state of the picker’s panels.
The target <code><input></code> also needs to be kept in focus when you click on the Picker Interface “panel(s).”
Additionally, “panels” and “pickers” may be assigned user-defined event-handlers which are triggered
when they become “active” or “inactive” (<code>onPickerStateChange</code> and <code>onInterfaceStateChange</code>).
All this is managed by the Picker Class by a complex interaction − a dance − of event-handlers.
This is its primary function in life.
It would be easy to have a JavaScript function that
simply adds/replaces an <code><input></code>’s <code>value</code> with anything when you click on whatever…
And it’s easy to keep up with which <code><input></code> is the one of many
of which to replace the old value with a new value.
The trick is keeping up with when an <code><input></code> is “active”, especially when Picker-interfaces
need to scroll or if they need to use another <abbr>HTML</abbr> element that requires keyboard “focus.”
Generally, when an <code><input></code> is associated with a Picker-interface,
the <code><input></code> and Picker are considered “active” when the <code><input></code> has “focus.”
If your interface itself needs to have an <abbr>HTML</abbr> element that requires keyboard “focus”,
including other <code><input type='text'></code>s,
<code><input type='file'></code>s, <code><input type='number'></code>s,
<code><textarea></code>s, <code><select></code>s, etc.,
then they need to be “registered.”
The <code>Picker</code> Class gives you all you need to manage all the event-handlers required to maintain the “focus”
of the target <code><input></code> associated with a Picker-interface when necessary, and to keep track of
when the Picker-interface is “active”, and when it is not.
The comments in the code file explain all this.
</p>
<p>But wait!
You don’t even need to use an <code><input type='text'></code> with the <code>Picker</code> Class
as the <code>dataTarget</code> (the object that receives the data picked by the end-user).
<abbr>HTML</abbr> <code><textarea></code>s, <code><select></code>s,
<code><input type='text' list='<var>datalistID</var>'></code>s,
and even document text-nodes are automatically recognized by the <code><var>Picker_instance</var>.pick()</code> method.
By default, the <code><var>Picker_instance</var>.dataTarget</code> receives the data-value picked by the end user.
If no <code>dataTarget</code> is set then the <code><var>Picker_instance</var>.masterTarget</code> is used, if any.
The <code>dataTarget</code> and <code>masterTarget</code> each may be any
JavaScript<abbr tm>™</abbr> Object.
Or they may be <code><input type='hidden'></code> if you don't care to show the user the picked value,
but want to send the value back to the server with a form-submittal.
To supplement this functionality, add a JavaScript<abbr tm>™</abbr> function to the
Array of <code><var>Picker_instance</var>.pickFilters</code>.
To replace this functionality, replace the <code><var>Picker_instance</var>.pick</code> method with your own function.</p>
<p>There is a plethora of comments embedded in the <code>Picker</code> JavaScript<abbr tm>™</abbr> code
which you should read if you want to use this package.
Beyond that, as usual a simple example does best to explain the usage of the <code>Picker</code> Class Object.
Below you will see the code for a simple <abbr>HTML</abbr> <code>Picker</code> example and the working demo
of this example code.
<!-- If you open your browser’s JavaScript<abbr tm>™</abbr> console,
you can see the “flow” of events as they happen. -->
The MasterColorPicker<span class='footmark'>™</span> project is a more complex example of
using the <code>Picker</code> package.
If you study the <span class='filename'>MasterColorPicker2.js</span> file, you will see the
<code>Color_Picker</code> Class (found near the beginning of that file)
acts as an intermediary between the color-pickers and the <code>Picker</code> Class.
At the end of each color-picker’s code section you will see the event-handlers attached to each color-picker,
and how they interact with the <code>Color_Picker</code> class to “pick” colors;
and the YinYang NíHóng<span class='footmark'>™</span> Color Picker code shows an advanced example of
using the <code>onPickerStateChange</code> event.
At the end of the <span class='filename'>MasterColorPicker2.js</span> file you will see how the
<code>MasterColorPicker</code> Object (which is used by the <code>Color_Picker</code> Class)
is created from an instance of the <code>Picker</code> Class,
an example of how the <code>pickFilter</code> works, and another example of using the <code>onPickerStateChange</code> event.</p>
<h3>Implementing the <code>Picker</code> class</h3>
<p>The <code>Picker</code> class code resides in the <code>SoftMoon.WebWare</code> namespace
to avoid conflict with other software packages.
Back in the day, the code file could create this namespace on-the-fly if need be;
then along came the <code>const</code> type of “JavaScript<footnote>™</footnote> variable”.
The code file can not check for the existance of the namespace and then create it
if <code>SoftMoon</code> is a constant, and in the SoftMoon-WebWare world’s codebase, it now is.
So <strong>you</strong> must first create this namespace:
<code class='block'><script>
const SoftMoon=Object.defineProperty({}, "WebWare", {value:{}, enumerable:true});
<script></code>
and then load the files:
<code><script scr='JS_toolbucket/+++JS/+++.js'></script></code>
<code><script scr='JS_toolbucket/SoftMoon-WebWare/UniDOM-2022.js'></script></code>
<code><script scr='JS_toolbucket/SoftMoon-WebWare/Picker.js'></script></code></p>
<blockcaption>All these examples below use the following simple <abbr>CSS</abbr>:</blockcaption>
<code>
<style>
label {
display: inline-block;
position: relative;
margin-bottom: 4em; }
label .pickerPanel {
position: absolute;
top: 1.1618em;
right: -10.618em; }
.pickerPanel {display: none;}
.activePicker {display: table;}
</style>
</code>
<blockcaption>A <em>very</em> simple example:</blockcaption>
<code>
<label>Pick a color <input type='text' id='getColor'>
<table id='colorPicker' role='dialog'>
<caption>Colors</caption>
<tr>
<td style='background-color:red'>red</td>
<td style='background-color:orange'>orange</td>
<td style='background-color:yellow'>yellow</td>
<td style='background-color:green'>green</td>
<td style='background-color:cyan'>cyan</td>
<td style='background-color:blue'>blue</td>
<td style='background-color:violet'>violet</td>
<td style='background-color:indigo'>indigo</td>
</tr>
</table></label>
<script type='text/javascript'>
// note how you can use the required/included “UniDOM” package to add event handlers, or add them any other way.
UniDOM.addEventHandler(window, 'onload', function() {
const
itybitHTML=document.getElementById('colorPicker'),
itybitColorPicker=new SoftMoon.WebWare.Picker(itybitHTML), // no options needed
itybitColorInput=document.getElementById('getColor');
function selector(event) {
if (event.target.tagName==='TD') itybitColorPicker.pick(event.target.firstChild.data); }
UniDOM.addEventHandler(itybitHTML, 'onclick', selector);
itybitColorPicker.registerTargetElement(itybitColorInput); });
</script>
</code>
<label>Pick a color <input type='text' id='getColor'>
<table id='colorPicker' class='picker' role='dialog'>
<caption>Colors</caption>
<tr>
<td style='background-color:red'>red</td>
<td style='background-color:orange'>orange</td>
<td style='background-color:yellow'>yellow</td>
<td style='background-color:green'>green</td>
<td style='background-color:cyan'>cyan</td>
<td style='background-color:blue'>blue</td>
<td style='background-color:violet'>violet</td>
<td style='background-color:indigo'>indigo</td>
</tr>
</table></label>
<script type='text/javascript'>
UniDOM.addEventHandler(window, 'onload', function() {
const
itybitHTML=document.getElementById('colorPicker'),
itybitColorPicker=new SoftMoon.WebWare.Picker(itybitHTML), // no options needed
itybitColorInput=document.getElementById('getColor');
function selector(event) {
if (event.target.tagName==='TD') itybitColorPicker.pick(event.target.firstChild.data); }
UniDOM.addEventHandler(itybitHTML, 'onclick', selector);
itybitColorPicker.registerTargetElement(itybitColorInput); });
</script>
<br>
<blockcaption>Basically the same, but with two different target-inputs, and a relocating Picker.
Note the difference in the way the <code>dataTarget <input></code>s are registered:</blockcaption>
<code>
<div id='relocatableColorPicker'>
<label>Foreground Color:<input type='text'></label>
<label>Background Color:<input type='text'></label>
</div>
<script type='text/javascript'>
UniDOM.addEventHandler(window, 'onload', function() {
const
itybitHTML=document.getElementById('colorPicker').cloneNode(true), // we might instead use “removeNode” but we keep the original because it is used in the previous example.
itybitColorPicker=new SoftMoon.WebWare.Picker(itybitHTML), // no options needed
cInputs=document.querySelectorAll('#relocatableColorPicker input');
function selector(event) {
if (event.target.tagName==='TD') itybitColorPicker.pick(event.target.firstChild.data); }
itybitHTML.id+="2"; //we should make the id unique since we cloned it for HTML syntax, but really it doesn’t affect anything
UniDOM.addEventHandler(itybitHTML, 'onclick', selector);
for (const inp of cInputs) { // ↓↓ note we pass in the <label> for each input as the containing element for the picker.
itybitColorPicker.registerTargetElement(inp, inp.parentNode); } });
</script>
</code>
<div id='relocatableColorPicker'>
<label>Foreground Color:<input type='text'></label>
<label>Background Color:<input type='text'></label>
</div>
<script type='text/javascript'>
UniDOM.addEventHandler(window, 'onload', function() {
const
itybitHTML=document.getElementById('colorPicker').cloneNode(true), // we might instead use “removeNode” but we keep the original because it is used in the previous example.
itybitColorPicker=new SoftMoon.WebWare.Picker(itybitHTML), // no options needed
cInputs=document.querySelectorAll('#relocatableColorPicker input');
function selector(event) {
if (event.target.tagName==='TD') itybitColorPicker.pick(event.target.firstChild.data); }
itybitHTML.id+="2"; //we should make the id unique since we cloned it for HTML syntax, but really it doesn’t affect anything
UniDOM.addEventHandler(itybitHTML, 'onclick', selector);
for (const inp of cInputs) { // ↓↓ note we pass in the <label> for each input as the containing element for the picker.
itybitColorPicker.registerTargetElement(inp, inp.parentNode); } });
</script>
<h3>Picker “types”</h3>
<p>With the newer <abbr>HTML5</abbr> spec and the <abbr>ARIA</abbr> support specs that go along,
there are 5 “types” of picker-interfaces:
<nobr><code>menu</code>,</nobr> <nobr><code>list</code>,</nobr> <nobr><code>tree</code>,</nobr> <nobr><code>grid</code>,</nobr> and <code>dialog</code>.
The “dialog” type indicates a complex interface, which the <code>Picker</code> class was designed for,
including <a href='#multipanel'>multiple panels</a>, <a href='#interface_controls'>interface controls</a>,
and <a href='#tab_key'><kbd><span>Tab</span></kbd> key control</a>.
But <code>Picker</code>s may also embrace the other “types” which by definition
do not typically embrace multiple panels or interface controls,
and therefore also have no need for tab-key-control.
<a href='https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-haspopup' target="Mozilla"><abbr>ARIA</abbr> “popup” specs</a>
indicate that these other picker-types use a different set of keyboard keys
to navigate the “pop-up options” (the arrow keys
<nobr>(<kbd><span>↑</span></kbd></nobr> <kbd><span>↓</span></kbd> <kbd><span>←</span></kbd> <nobr><kbd><span>→</span></kbd>)</nobr> and others),
and specifically <strong><em>not</em></strong> the <kbd><span>Tab</span></kbd> key that the <code>Picker</code> class uses
to navigate though <a href='#interface_controls'>“interface controls”</a>.
If you implement a picker of type <nobr><code>menu</code>,</nobr> <nobr><code>list</code>,</nobr> <nobr><code>tree</code>,</nobr> or <code>grid</code>
it will by definition typically not have interface controls and therefore no integrated tab-key-control.
With these other picker-types, you must develop your own keyboard control within
to manage the “required” <abbr>ARIA</abbr> keyboard support.
Every interface design will be unique, and the <code>Picker</code> class can not simply adapt,
nor do we want to place limits on your interface design.
But the <code>Picker</code> can still manage the “pop-up” and “pop-down” aspects of that for you.
These color-picker examples above would be better suited to being type “list” (instead of the type “dialog” that they are)
and as such there should be JavaScript<abbr tm>™</abbr> included that
allows the end-user to navigate through the list of colors using the arrow keys; but this is just a simple example. </p>
<p>As mentioned above, the <code>Picker</code> Class manages more complex situations
where the Picker <abbr>HTML</abbr> Interface needs to scroll (using a scroll-bar).
Older versions of the <code>Picker</code> for older browsers required any Elements of your picker’s <abbr>HTML</abbr>
that needed to scroll should be contained within a panel; the panels themselves could not scroll.
I believe this problem is remedied with newer browsers, but it is still unchecked…
The idea of the <code>Picker</code> Class is that when you click anywhere on the Picker <abbr>HTML</abbr> Interface,
“focus” is retained by the “data target” <code><input></code>, even if nothing is “picked.”
Click on the caption of the itybitColorPicker above (where it says “Colors”),
or anywhere on the yellow background of the song-picker below, as a demonstration of this principle.
The <code>Picker</code> was designed to be intrinsically
<a href='https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-modal' target='Mozilla'>non-modal</a>
and when your “data target” looses focus by the end-user
clicking anywhere outside the Picker’s panels or outside of the “data target”
or otherwise tabs-out (using the <kbd><span>Tab</span></kbd> key) of the “data target”,
the picker “pops down” (or “pops out”, or “ disappears ”, or otherwise simply becomes “inactive”,
depending on <em>your</em> <abbr>CSS</abbr>).</p>
<p>This was the trick in older browsers, because of limitations in the
<acronym>DOM</acronym>—JavaScript<abbr tm>™</abbr> bindings,
and the <nobr><code>Picker</code>’s</nobr> code-base was much larger and complex.
I pulled hair for a while when the original prototypes for the <code>Picker</code> code would work and then wouldn’t,
so I would modify the way the handlers interacted, and that would work and then not…
With newer <abbr>HTML5</abbr> browsers,
there are newer native features that allow JavaScript<abbr tm>™</abbr> to “know” more about what is going on.
The latest version of the Picker Class works only with these newer browsers.</p>
<h3 id='data_targets'>Data Targets</h3>
<p>As mentioned, the “data target” is the JavaScript<abbr tm>™</abbr> Object
that receives the value that the end-user “picks”
(typically a textual value, but there is no global limitation).
The Picker keeps track of the current Data Target using the <code><var>pickerInstance</var>.dataTarget</code> property.
Typically, the “data target” will be an <abbr>HTML</abbr> form element,
and specifically <code><textarea></code>s, <code><select></code>s, and
<code><input></code>s with or without the <code>list='<var>datalistID</var>'</code> attribute are supported;
but as mentioned, <abbr>HTML</abbr> text-nodes are also supported natively.
<code><input></code>s may be <code>type='text'</code> or other types typically including
<code>color</code>, <code>email</code>, <code>number</code>, <code>password</code>, <code>search</code>, <code>tel</code>, & <code>url</code>.
<code><input></code> types not listed above could also be used, but browsers typically supply a native widget,
or they don’t typically interface with humans in a “picker” sort of way;
note file-inputs will <strong>not</strong> allow a picker to change or access the complete file-path
in any way, so you can’t use the <code>Picker</code> class with them.
Most “data targets” get their <code>.value</code> property set with the picked-data;
<code><select></code>s and <code><input list='<var>datalistID</var>'></code>
have their “options-list” changed.
Data targets may be “additive” or not.
Additive data targets have the “picked data” added to the existing value or options-list;
whereas non-additive data targets have their values or an option replaced by the “picked data”.
Textual additive data targets may have the “picked data” added where the cursor is placed,
or if text is “selected” (highlighted) that section of text will be replaced with the “picked data”.
However, if the additive nature is specified as <code>end</code>, the “picked data” will
<strong>always</strong> be added to the end of the existing textual value.
If there is an “options list” with an “additive” element,
another “option” will be added to the end of the list (always at the end);
if the element is not “additive” the <em>selected</em> option’s old value will be replaced with the new “picked data”.
You can specify that a “data target” is additive (or not — the default anyway) when you register it:
<code><var>pickerInstance</var>.registerTargetElement(<var>input</var>, {additive:'end'});</code>
or by specifying such with an attribute:
<code><input type='text' additive></code>
noting that the <code>additive</code> attribute is <a href='#Boolean'>Boolean¹</a> in nature,
and indicates <code>true</code> when present without a value;
but could also be <code>='end'</code> or <code>='false'</code>.
Attribute values override the specs passed in when registering a target,
so you would only need <code>='false'</code> if you were "bulk processing"
a bunch of “data targets” and most of them were supposed to be additive.
</p>
<h3 id='interface_controls'>Interface Controls</h3>
<p>Your picker(s) themselves may need to adapt to the end-user’s demands,
and they do such using “interface controls” – form controls
(such as <code><input></code>s and <code><select></code>s)
that modify the “pickers” themselves and/or their behaviors.
The Picker keeps track of the current Interface Control using the <code><var>pickerInstance</var>.activeInterface</code> property.
Your interface controls may be in (on?) one of your panels or not
– that’s up to you and the design specs that work best for you.
If they are in a panel, you need not worry about them,
the <code>Picker</code> class takes care of all the details.
If they are free-range (somewhere else in your web-page),
you need to register them so the <code>Picker</code> class
knows to keep your picker active while the end-user interacts
with these “interface controls”
(assuming you <em>want</em> to keep it active – that is not strictly required, but usually nice for the end-user).
To do that, use:
<code><var>pickerInstance</var>.registerInterfaceControl(<var>input</var>)</code>
noting that <code><var>input</var></code> may be any element that accepts keyboard focus
(typically, <code><textarea></code>, <code><select></code>, <code><button></code>, as well as <code><input></code>,
but also note you may customize any element to receive focus using the <code>tabindex</code> attribute).
</p>
<h3 id='interface_targets'>Interface Targets</h3>
<p>“Interface Targets” are special “interface controls” of your Picker
that can receive a value “picked” from another part of your Picker,
where normally a user “picks” something for the “data Target”.
The Picker keeps track of the current Interface Target using the <code><var>pickerInstance</var>.interfaceTarget</code> property.
(Note that the <code><var>pickerInstance</var>.activeInterface</code> property is <em>also</em> set to this element.)
For instance, the <a href='https://softmoon-webware.com/MasterColorPicker_instructions.php'>MasterColorPicker</a>
Version 2 project has a “color filter” that filters the color a user “picks” from one of the palettes,
modifying the “picked” color by mixing in the “filter-color”.
The user must first choose this filter-color, by picking a color from one of the palettes.
So the color-filter panel has an “interface Target” to receive the filter-color picked.
As usual, the Picker Class makes sure keyboard focus is directed to the proper Element at all times,
as well as properly managing the “top panel” under these shifting conditions.
An “interface control” in your panel may be designated as being an “interface target”
by giving it the <abbr>HTML</abbr> attribute <code>interfaceTarget='true'</code>
(note this is a “<a href='#Boolean'>Boolean¹</a>” attribute, and its presence without a value equates to "true");
or you can register it using <code><var>pickerInstance</var>.registerInterfaceControl(<var>input</var>, {interfaceTarget:true});</code>
(if it is on a picker-panel, register it before you register the panel).
Or when you register a picker-panel, you may specify that <strong>all</strong>
interface-controls within are interface-targets using
<code><var>pickerInstance</var>.registerInterfacePanel(<var>panel</var>, {interfaceTarget:true});</code>
and in this case, individual <nobr><code><input></code>s</nobr> (etc.) may have the attribute
<code>interfaceTarget='false'</code>.</p>
<h3><code>.currentTarget</code> & <code>.currentInterface</code></h3>
<p>These properties of a <code><var>pickerInstance</var></code> are “convenience” properties.
The <code>.currentTarget</code> will return either the <code>.interfaceTarget</code> or the <code>.dataTarget</code>,
whichever is currently active.
The <code>.currentInterface</code> will return either the <code>.activeInterface</code> or the <code>.dataTarget</code>,
whichever is currently active.
Note that <code>.currentTarget</code> & <code>.currentInterface</code> are “getters” only;
you can not “set” thier values, as you can the
<code>.dataTarget</code>, <code>.activeInterface</code>, & <code>.interfaceTarget</code> properties.</p>
<h3 id='multipanel'>Multiple “panels” & “pickers” with “dialog” type pickers</h3>
<p>If you want to use more that one “panel”
(<abbr>i.e.</abbr> more than the <code>mainPanel</code>,
which is passed in as the first (required) argument when creating a new <code>Picker</code> instance),
you need to “register” the additional panels,
the same as you need to “register” interface control-elements
(see above, and also the comments in the <code>Picker</code> code file).
Registering an additional panel (besides the <code>mainPanel</code>) also automatically
registers any “interface control-elements” within that panel.
If you want to use more than one “picker,” the <code>Picker</code> Class can manage that also,
provided all the “pickers” reside within these “registered”
<code><var>PickerInstance</var>.panels</code> (including the <code>mainPanel</code>).
The <code>options</code> Object that can be passed in as a
second argument when creating a new <code>Picker</code> instance
may hold a <code>pickerSelect</code> property that references either a <code><select></code> tag
or an array of <code><input type='checkbox‖radio'></code> buttons that can control which “Pickers”
are considered “active.”
You should also probably “register” your <code>pickerSelect</code>
if it is a <code><select></code> tag and not within a registered “panel”,
assuming you want to keep the “focus” of the target <code><input></code> while selecting different pickers. </p>
<h3 id='implementing_ARIA'><abbr title='Accessible Rich Internet Applications'>ARIA</abbr> support with multiple “panels”</h3>
<p>If you do choose to implement more than one picker-panel,
you need to inform your <code>Picker</code> instance
on how to handle the <abbr>ARIA</abbr>-support for pop-ups
(unless for some reason you don’t want <abbr>ARIA</abbr> supported).
When your picker is active, the “target input” is told what element “pops-up”
and this can then be made properly available to a screen reader, etc.
Typically, it is nice to have all the panels nested within this one containing “pop-up” element,
and this element should contain nothing else;
then when you create a new <code>Picker</code> instance,
pass in that containing element as the value for <code>opts.aria_popUp</code>.
The <a href="MasterColorPicker_instructions.php">MasterColorPicker</a> project uses that approach.
This is not always possible based on the <abbr>HTML</abbr> layout you need to implement.
When multiple picker-panels can not be nested within a single container,
the specified “aria-pop-up container” should use the <abbr>HTML</abbr> attribute <code>aria-owns</code>
to point to the free-range picker-panel(s), so the screen-reader can properly find and acknowledge them.
Alternatively, your “main-panel” may use <code>aria-owns</code> to point to the other “auxiliary” panels,
as does the example below.
If you only have the one single main-panel and you don’t pass in a value for <code>opts.aria_popUp</code>
then the main-panel automatically becomes the “popup dialog element”, as in the mini-color-picker example above.
If you don’t want the <code>Picker</code> to support <abbr>ARIA</abbr>,
simply pass in <code>opts.aria_popUp=null</code> when you create your <code>Picker</code> instance.
At any time after you create a picker, you can also modify <code><var>pickerInstance</var>.aria_popUp</code>
to whatever value you desire.
See <a href='#ARIA_logistics'>more about <abbr>ARIA</abbr></a> below.</p>
One final example (using the same <abbr>CSS</abbr> as those above, but with the addition shown below):
<code>
<style>
#songs .picker {
display: none; }
#songs .activePicker {
display: table; }
#songs {
padding: 2em 0 .618em 0;
margin: .618em 12.618em;
position: relative; }
#songPickerControls.activePicker {
position: absolute;
top: 1.1618em;
left: 0;
display: block;
width: 100%;
text-align: center;
background-color: yellow; }
#songs label {
margin: 0 .618em; }
#songPickerHTML {
background-color: yellow;
border: 2px solid blue; }
</style>
<fieldset id='songs'><legend>Pick 3 songs</legend>
<label>1. <input type='text' role='combobox' aria-haspopup='dialog'></label>
<label>2. <input type='text' role='combobox' aria-haspopup='dialog'></label>
<label>3. <input type='text' role='combobox' aria-haspopup='dialog'></label>
<div id='songPickerHTML' role='dialog' aria-owns='songPickerControls'><!-- this is the picker’s mainPanel -->
<table id='PinkFloyd' class='picker rock'><caption>Pink Floyd</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Set the Controls for the Heart of the Sun</td><td>A Saucerful Of Secrets</td></tr>
<tr><td>Main Theme</td><td>More</td></tr>
<tr><td>Echoes</td><td>Meddle</td></tr>
</table>
<table id='GratefulDead' class='picker rock'><caption>Grateful Dead</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Bertha</td><td>Grateful Dead</td></tr>
<tr><td>China Cat Sunflower</td><td>Aoxomoxoa</td></tr>
<tr><td>Eyes Of The World</td><td>Wake Of The Flood</td></tr>
</table>
<table id='LedZeppelin' class='picker rock'><caption>Led Zeppelin</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Whole Lotta Love</td><td>Led Zeppelin II</td></tr>
<tr><td>Stairway to Heaven</td><td>Led Zeppelin IV</td></tr>
<tr><td>In The Light</td><td>Physical Graffiti</td></tr>
</table>
<table id='MilesDavis' class='picker jazz'><caption>Miles Davis</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Flamenco Sketches</td><td>Kind Of Blue</td></tr>
<tr><td>“Shhh”/“Peaceful”</td><td>In A Silent Way</td></tr>
<tr><td>Pharaoh’s Dance</td><td>Bitches Brew</td></tr>
</table>
<table id='TheloniusMonk' class='picker jazz'><caption>Thelonius Monk</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Bright Mississippi</td><td>Monk’s Dream</td></tr>
<tr><td>I’m Confessin’ (That I Love You)</td><td>Solo Monk</td></tr>
<tr><td>Ugly Beauty</td><td>Underground</td></tr>
</table>
<table id='TedeschiTrucksBand' class='picker blues'><caption>Tedeschi Trucks Band</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Don’t Let Me Slide</td><td>Revelator</td></tr>
<tr><td>Love Has Something Else To Say</td><td>Revelator</td></tr>
<tr><td>Learn How To Love</td><td>Everybody’s Talkin’</td></tr>
</table>
<table id='PinkAnderson' class='picker blues'><caption>Pink Anderson</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Papa’s About To Get Mad / Gonna Tip Out Tonight</td><td>(single)</td></tr>
<tr><td>Every Day In The Week Blues / C.C And O. Blues</td><td>(single)</td></tr>
<tr><td>Greasy Greens</td><td>American Street Songs</td></tr>
</table>
<table id='FloydCouncil' class='picker blues'><caption>Floyd Council</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>I Don’t Want No Hungry Woman</td><td>Carolina Blues</td></tr>
<tr><td>Lookin’ For My Baby</td><td>Carolina Blues</td></tr>
<tr><td>Workin Man Blues</td><td>Carolina Blues</td></tr>
</table>
</div><!-- close songPickerHTML -->
<div id='songPickerControls' role='dialog'>
<label>Style: <select id='musicStyle'>
<option selected='selected'>rock</option>
<option>jazz</option>
<option>blues</option>
</select></label>
<label>Artist: <select id='artists'>
<option class='rock' selected='selected'>Pink Floyd</option>
<option class='rock'>Grateful Dead</option>
<option class='rock'>Led Zeppelin</option>
<option class='jazz'>Miles Davis</option>
<option class='jazz'>Thelonius Monk</option>
<option class='blues'>Tedeschi Trucks Band</option>
<option class='blues'>Pink Anderson</option>
<option class='blues'>Floyd Council</option>
</select></label>
</div>
</fieldset>
<script type='text/javascript'>
UniDOM.addEventHandler(window, 'onload', function() {
var songsHTML=document.getElementById('songPickerHTML'),
musicStyle=document.getElementById('musicStyle'),
artistSelect=document.getElementById('artists'),
sInputs=document.getElementById('songs').getElementsByTagName('input'),
songs=songsHTML.getElementsByTagName('tr'),
SongPicker=new SoftMoon.WebWare.Picker(songsHTML, {picker_select: artistSelect),
selector=function() {SongPicker.pick(this.firstChild.data);},
setStyle=function(onBoot) {
for (var i=0, flag=false; i<artistSelect.options.length; i++) {
artistSelect.options[i].disabled=!(this.value===artistSelect.options[i].className);
if (!flag) artistSelect.options[i].selected=(flag=(this.value===artistSelect.options[i].className)); }
if (onBoot!==true) SongPicker.choosePicker(); };
for (var i=0; i<songs.length; i++) { //attach event handlers to each song in every table
if (songs[i].firstChild.nodeName==='TD') UniDOM.addEventHandler(songs[i].firstChild, 'onclick', selector); }
for (i=0; i<sInputs.length; i++) { // ↓↓note we pass in the <label> for each input as the containing element for the picker.
SongPicker.registerTargetElement(sInputs[i], sInputs[i].parentNode); }
SongPicker.registerInterfacePanel(document.getElementById('songPickerControls'));
UniDOM.addEventHandler(musicStyle, 'onchange', setStyle);
setStyle.call(musicStyle, true); });
</script>
</code>
<fieldset id='songs'><legend>Pick 3 songs</legend>
<label>1. <input type='text' role='combobox' aria-hasopup='dialog'></label>
<label>2. <input type='text' role='combobox' aria-hasopup='dialog'></label>
<label>3. <input type='text' role='combobox' aria-hasopup='dialog'></label>
<div id='songPickerHTML' role='dialog' aria-owns='songPickerControls'>
<table id='PinkFloyd' class='picker rock'><caption>Pink Floyd</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Set the Controls for the Heart of the Sun</td><td>A Saucerful Of Secrets</td></tr>
<tr><td>Main Theme</td><td>More</td></tr>
<tr><td>Echoes</td><td>Meddle</td></tr>
</table>
<table id='GratefulDead' class='picker rock'><caption>Grateful Dead</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Bertha</td><td>Grateful Dead</td></tr>
<tr><td>China Cat Sunflower</td><td>Aoxomoxoa</td></tr>
<tr><td>Eyes Of The World</td><td>Wake Of The Flood</td></tr>
</table>
<table id='LedZeppelin' class='picker rock'><caption>Led Zeppelin</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Whole Lotta Love</td><td>Led Zeppelin II</td></tr>
<tr><td>Stairway to Heaven</td><td>Led Zeppelin IV</td></tr>
<tr><td>In The Light</td><td>Physical Graffiti</td></tr>
</table>
<table id='MilesDavis' class='picker jazz'><caption>Miles Davis</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Flamenco Sketches</td><td>Kind Of Blue</td></tr>
<tr><td>“Shhh”/“Peaceful”</td><td>In A Silent Way</td></tr>
<tr><td>Pharaoh’s Dance</td><td>Bitches Brew</td></tr>
</table>
<table id='TheloniusMonk' class='picker jazz'><caption>Thelonius Monk</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Bright Mississippi</td><td>Monk’s Dream</td></tr>
<tr><td>I’m Confessin’ (That I Love You)</td><td>Solo Monk</td></tr>
<tr><td>Ugly Beauty</td><td>Underground</td></tr>
</table>
<table id='TedeschiTrucksBand' class='picker blues'><caption>Tedeschi Trucks Band</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Don’t Let Me Slide</td><td>Revelator</td></tr>
<tr><td>Love Has Something Else To Say</td><td>Revelator</td></tr>
<tr><td>Learn How To Love</td><td>Everybody’s Talkin’</td></tr>
</table>
<table id='PinkAnderson' class='picker blues'><caption>Pink Anderson</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>Papa’s About To Get Mad / Gonna Tip Out Tonight</td><td>(single)</td></tr>
<tr><td>Every Day In The Week Blues / C.C And O. Blues</td><td>(single)</td></tr>
<tr><td>Greasy Greens</td><td>American Street Songs</td></tr>
</table>
<table id='FloydCouncil' class='picker blues'><caption>Floyd Council</caption>
<tr><th>song</th><th>from album</th></tr>
<tr><td>I Don’t Want No Hungry Woman</td><td>Carolina Blues</td></tr>
<tr><td>Lookin’ For My Baby</td><td>Carolina Blues</td></tr>
<tr><td>Workin Man Blues</td><td>Carolina Blues</td></tr>
</table>
</div>
<div id='songPickerControls' role='dialog'>
<label>Style: <select id='musicStyle'>
<option selected='selected'>rock</option>
<option>jazz</option>
<option>blues</option>
</select></label>
<label>Artist: <select id='artists'>
<option class='rock' selected='selected'>Pink Floyd</option>
<option class='rock'>Grateful Dead</option>
<option class='rock'>Led Zeppelin</option>
<option class='jazz'>Miles Davis</option>
<option class='jazz'>Thelonius Monk</option>
<option class='blues'>Tedeschi Trucks Band</option>
<option class='blues'>Pink Anderson</option>
<option class='blues'>Floyd Council</option>
</select></label>
</div>
</fieldset>
<script type='text/javascript'>
UniDOM.addEventHandler(window, 'onload', function() {
const
songsHTML=document.getElementById('songPickerHTML'),
musicStyle=document.getElementById('musicStyle'),
artistSelect=document.getElementById('artists'),
sInputs=document.getElementById('songs').getElementsByTagName('input'),
songs=songsHTML.getElementsByTagName('tr'),
SongPicker=new SoftMoon.WebWare.Picker(songsHTML, {picker_select: artistSelect}),
selector=function() {SongPicker.pick(this.firstChild.data);},
setStyle=function(onBoot) {
for (var i=0, flag=false; i<artistSelect.options.length; i++) {
artistSelect.options[i].disabled=!(this.value===artistSelect.options[i].className);
if (!flag) artistSelect.options[i].selected=(flag=(this.value===artistSelect.options[i].className)); }
if (onBoot!==true) SongPicker.choosePicker(); };
for (var i=0; i<songs.length; i++) { //attach event handlers to each song in every table
if (songs[i].firstChild.nodeName==='TD') UniDOM.addEventHandler(songs[i].firstChild, 'onclick', selector); }
for (i=0; i<sInputs.length; i++) { // ↓↓note we pass in the <label> for each input as the containing element for the picker.
SongPicker.registerTargetElement(sInputs[i], sInputs[i].parentNode); }
SongPicker.registerInterfacePanel(document.getElementById('songPickerControls'));
UniDOM.addEventHandler(musicStyle, 'onchange', setStyle);
setStyle.call(musicStyle, true); });
</script>
<p>The above example need not use two panels, and realistically complicates a simple interface.
But if you were to include items such as dates, publishers, playtimes, etc. into the tables in this example,
you may want to filter the possible song selections using any number of factors.
In a real-world shopping-cart project, having these filters in a separate panel from the “pickers,”
maybe even one that is always shown to the end-user,
can be an optimal way of organizing your <abbr>HTML</abbr> page.</p>
<h3 id='ARIA_logistics'><abbr title='Accessible Rich Internet Applications'>ARIA</abbr> support logistics</h3>
<p>Your picker-panel(s) do not need to
<a href='https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-haspopup' target="Mozilla">“pop-up” and “pop-down”</a>.
It/they may always be displayed to the user, if that is what you choose or need.
In that case, <a href='#implementing_ARIA' up>as mentioned above</a>,
pass in <code>opts.aria_popUp=null</code> to the <code>Picker</code> constructor,
or set <code><var>pickerInstance</var>.aria_popUp=null</code> after you construct your picker.
If you <strong>do</strong> want you picker-panel(s) to “pop-up”
when the associated <code><input></code> (etc.) is active (has keyboard focus),
as <a href='#implementing_ARIA' up>described above</a>, you need to tell your <code>Picker</code> instance
what the <abbr>HTML</abbr> pop-up element is by setting the
<nobr><code>Picker</code>’s</nobr> <code>.aria_popUp</code> value.
From there, the <code>Picker</code> class takes care of the dirty details.
The <code>Picker</code> supports basic rules of the <abbr>HTML</abbr> element
<a href='https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/combobox_role' target="Mozilla">“combobox-role”</a>.
Your <code><var>pickerInstance</var>.aria_popUp</code> element
(remember, which may automatically be your <code>mainPanel</code> if it is the only one)
should by definition have a <code>role</code> attribute that describes what <strong><em>kind</em></strong> of
<a href='https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-haspopup' target="Mozilla">“pop-up”</a>
it is: menu, list, tree, grid, or dialog.
The picker recognizes this when your <code>Picker</code> is active,
and sets the <nobr><code>dataTarget <input></code>’s</nobr> <code>aria-haspopup</code> attribute
properly according to the <code>role</code> attribute of your “pop-up”
(note: remember, other <abbr>HTML</abbr> <code>dataTarget</code> elements are also supported besides <code><input></code>).
It also properly sets the <code>dataTarget</code>’s <code>aria-controls</code> and <code>aria-expanded</code> attributes
when your <code>Picker</code> is active;
as such, your <code><var>pickerInstance</var>.aria_popUp</code> element
should (by definition) have an <code>id</code> attribute.
When your <code>Picker</code> becomes inactive, it removes these attributes from the <code>dataTarget</code>,
allowing another <code><input></code> (etc.) to become the <code>dataTarget</code> for your <code>Picker</code>.
It is up to you to supply any JavaScript<abbr tm>™</abbr> code to make the internals of
your picker-panels work according to <abbr>ARIA</abbr> rules for menus, lists, trees, or grids.
For <code>Picker</code>s with the <code>dialog</code> role,
this class supports the <kbd><span>Tab</span></kbd> key natively
for your “interface controls” that reside on the picker-panel(s),
and which integrates with the native “multi-panel” support by necessity:
</p>
<h3 id='tab_key'>The <kbd><span>Tab</span></kbd> key with “dialog” type Pickers</h3>
<p>With version 2+ of the Picker Class, the non-standard <abbr>HTML</abbr> attributes
“tabToTarget” (same as Version 1 release) and
“backTabToTarget” may have values of <code>"true"</code> or <code>"false"</code>;
these are “<a href='#Boolean'>Boolean¹</a>” attributes, and as such their simple inclusion without a value indicates a “true” value.
When registering an “interface panel” the first and last “interface controls” found on the panel
by default are registered to backtab-to-target and tab-to-target, respectively.
You can override this with the <abbr>HTML</abbr> attributes markup,
or with JavaScript<abbr tm>™</abbr> by “registering” them separately
with the desired options before registering the panel they are on (see the comments in the code-file).</p>
<p>For more complex tab-control needs, <abbr>HTML</abbr> of course gives you the <code>tabIndex</code>
attribute, but this can be confusing at best sometimes. If you are creating a plugin that should work within
any web-page, how can you be sure that your tab-indexes are not going to conflict with what the developer wants,
or with any other plugin?
Since registering an “interface control” interferes with the tab-key control by necessity,
I thought it best to fully integrate tab-key direction within this project for Version 2+, and enhanced that in Versions 5+.
This allows so much more flexibility and dynamicity in developing your Picker,
with multiple panels that may be added/removed and active/inactive according to needs (perhaps loaded via Ajax on demand).
So two more additional <abbr>HTML</abbr> attributes are now recognized by the Picker Class
for “registered interface controls”: “tabTo” and “backTabTo”.
They may have one of three value types.
Their values may be the <abbr>HTML</abbr> “id” of the Element to tab to, by preceding the “id” with a <code>#</code> symbol similar to <abbr>CSS</abbr> specs.
For example: <code><input tabTo='#myWebPageElement'></code>.
The <code>@</code> symbol may be used to signify one of several “special cases” that can not be simply pointed to or scripted.
These are <code>@next-panel</code>, <code>@previous-panel</code>, <code>@adjacent-panel</code>, and <code>@target</code>.
(see below for more about tabbing from one panel to another)
For example: <code><input tabTo='@next-panel'></code>.
The <code>@adjacent-panel</code> value relies on the <kbd><span>Shift</span></kbd> key being pressed to determine if the panel is the “next” or “previous” one.
If you only have two (2) panels, use this value.
The <code>@target</code> value works like the <code>tabToTarget</code> and <code>backTabToTarget</code> attributes noted above,
but it can not signify a <nobr>“<code>false</code>”</nobr> value like they can.
The value of a <nobr>“tabTo”</nobr> or <nobr>“backtabTo”</nobr> attribute may also be a
JavaScript<abbr tm>™</abbr> <em>expression</em> that evaluates to the Element to tab to.
The value of <nobr>“<code>this</code>”</nobr> within the expression is the “interface control” element that has keyboard focus;
you also have access within your expression to the values of the keydown <nobr>“<code>event</code>”</nobr>, the <nobr>“<code>PickerInstance</code>”</nobr>,
and the <nobr>“<code>actions</code>”</nobr> Object that you passed in when you <a href='#interface_controls' up>registered the interface control</a>.
For example: <code><input tabTo='this.closest(".panel").querySelector(<br>
(event.shiftKey || actions.forceShiftKey) ^ PickerInstance.toggleShiftKeyAction ? ".coldButton" : ".hotButton")'></code></p>
<p>Users may also want to manually tab from one panel to another.
By default they could now do this using <kbd><span>CTRL</span>+<span>Tab</span></kbd>
and <kbd><span>SHIFT</span>+<span>CTRL</span>+<span>Tab</span></kbd>
except for that most browsers block these key-combos from JavaScript<abbr tm>™</abbr>.
So by default the Picker class <em>also</em> recognizes <kbd><span>CTRL</span>+<span><</span></kbd>
and <kbd><span>CTRL</span>+<span>></span></kbd>
(on <acronym>QUERTY</acronym> keyboards this would actually be
<kbd><span>SHIFT</span>+<span>CTRL</span>+<span>,<</span></kbd> and
<kbd><span>SHIFT</span>+<span>CTRL</span>+<span>.></span></kbd>)
to serve this need.
On non-<acronym>QUERTY</acronym> keyboards, the <kbd><span><</span></kbd> and <kbd><span>></span></kbd> keys
may normally generate characters when pressed in combo with the <kbd><span>CTRL</span></kbd> key,
and the Picker’s default panel-tab control may then conflict (and not work!).
You may alter the values found in:
<code><var>pickerInstance</var>.panelTabKey</code> and <code><var>pickerInstance</var>.panelBacktabKey</code>
which are <code>UniDOM.KeySniffer</code> objects.
A value of <code>true</code> or <code>false</code> for a modifier-key means that key
<em>must</em> be in the indicated state.
A value of <code>undefined</code> for a modifier-key means the KeySniffer doesn’t care.</p>
<p>You may want your “interface control” to remain focused when it has focus
when the user presses the <kbd><span>↵Enter</span></kbd> key;
or maybe you want keyboard focus to return to your data-target (<code><input></code>).
The Boolean value found at <code><var>pickerInstance</var>.doKeepInterfaceFocus</code>
controls that default action;
but your “interface control” itself may have an attribute <code>keep-focus</code>
and this may have a <a href='#Boolean'>Boolean¹</a> value that overrides the <code><var>pickerInstance</var></code> default.
For <abbr>HTML</abbr> <code><button></code> “interface controls”
the Picker also adds a customized <code>buttonpress</code> event
when the <code><button></code> has keyboard focus
and the user presses the <kbd><span>↵Enter</span></kbd> key.</p>