-
Notifications
You must be signed in to change notification settings - Fork 1
/
namedboolarray.go
149 lines (133 loc) · 3.87 KB
/
namedboolarray.go
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
package jaws
import (
"html/template"
"strings"
"github.com/linkdata/deadlock"
)
// NamedBoolArray stores the data required to support HTML 'select' elements
// and sets of HTML radio buttons. It it safe to use from multiple goroutines
// concurrently.
type NamedBoolArray struct {
Multi bool // allow multiple NamedBools to be true
mu deadlock.RWMutex // protects following
data []*NamedBool
}
var _ SelectHandler = (*NamedBoolArray)(nil)
// NewNamedBoolArray creates a new object to track a related set of named booleans.
//
// The JaWS ID string 'jid' is used as the ID for <select> elements and the
// value for the 'name' attribute for radio buttons. If left empty, MakeID() will
// be used to assign a unique ID.
func NewNamedBoolArray() *NamedBoolArray {
return &NamedBoolArray{}
}
// ReadLocked calls the given function with the NamedBoolArray locked for reading.
func (nba *NamedBoolArray) ReadLocked(fn func(nbl []*NamedBool)) {
nba.mu.RLock()
defer nba.mu.RUnlock()
fn(nba.data)
}
// WriteLocked calls the given function with the NamedBoolArray locked for writing and
// replaces the internal []*NamedBool slice with the return value.
func (nba *NamedBoolArray) WriteLocked(fn func(nbl []*NamedBool) []*NamedBool) {
nba.mu.Lock()
defer nba.mu.Unlock()
nba.data = fn(nba.data)
}
func (nba *NamedBoolArray) JawsContains(e *Element) (contents []UI) {
nba.mu.RLock()
for _, nb := range nba.data {
contents = append(contents, UiOption{nb})
}
nba.mu.RUnlock()
return
}
// Add adds a NamedBool with the given name and the given text.
// Returns itself.
//
// Note that while it's legal to have multiple NamedBool with the same name
// since it's allowed in HTML, it's probably not a good idea.
func (nba *NamedBoolArray) Add(name string, text template.HTML) *NamedBoolArray {
nba.mu.Lock()
nba.data = append(nba.data, NewNamedBool(nba, name, text, false))
nba.mu.Unlock()
return nba
}
// Set sets the Checked state for the NamedBool(s) with the given name.
func (nba *NamedBoolArray) Set(name string, state bool) (changed bool) {
nba.mu.RLock()
defer nba.mu.RUnlock()
for _, nb := range nba.data {
if nb.Name() == name {
changed = nb.Set(state) || changed
}
}
if state && !nba.Multi {
for _, nb := range nba.data {
if nb.Name() != name {
changed = nb.Set(false) || changed
}
}
}
return
}
// Get returns the name of first NamedBool in the group that
// has it's Checked value set to true. Returns an empty string
// if none are true.
//
// In case you can have more than one selected or you need to
// distinguish between a blank name and the fact that none are
// set to true, use ReadLocked() to inspect the data directly.
func (nba *NamedBoolArray) Get() (name string) {
nba.mu.RLock()
for _, nb := range nba.data {
if nb.Checked() {
name = nb.Name()
break
}
}
nba.mu.RUnlock()
return
}
func (nba *NamedBoolArray) isCheckedLocked(name string) bool {
for _, nb := range nba.data {
if nb.Checked() && nb.Name() == name {
return true
}
}
return false
}
// IsChecked returns true if any of the NamedBool in the set that have the
// given name are Checked. Returns false if the name is not found.
func (nba *NamedBoolArray) IsChecked(name string) (state bool) {
nba.mu.RLock()
state = nba.isCheckedLocked(name)
nba.mu.RUnlock()
return
}
// String returns a string representation of the NamedBoolArray suitable for debugging.
func (nba *NamedBoolArray) String() string {
var sb strings.Builder
sb.WriteString("&NamedBoolArray{[")
nba.mu.RLock()
for i, nb := range nba.data {
if i > 0 {
sb.WriteByte(',')
}
sb.WriteString(nb.String())
}
nba.mu.RUnlock()
sb.WriteString("]}")
return sb.String()
}
func (nba *NamedBoolArray) JawsGet(e *Element) string {
return nba.Get()
}
func (nba *NamedBoolArray) JawsSet(e *Element, name string) (err error) {
if nba.Set(name, true) {
e.Dirty(nba)
} else {
err = ErrValueUnchanged
}
return
}