summaryrefslogtreecommitdiff
path: root/httemplate/elements/checkbox-tristate.html
blob: 90966a5099260bc0da76ae666cb0c417d8a5e1a9 (plain)
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
<%doc>
A tristate checkbox (with three values: true, false, and null).
Internally, this creates a checkbox, coupled via javascript to a hidden
field that actually contains the value.  For now, the only values these
can have are 1, -1, and empty.  Clicking the checkbox cycles between them.

For compatibility with regular checkboxes, empty is the false state and 
-1 is the indeterminate state.

Displaying these is a problem.  "indeterminate" is a standard HTML5 attribute
but some browsers display it in unhelpful ways (e.g. Firefox slightly grays
the checkbox, approximately #dddddd), and checkboxes ignore nearly all CSS 
styling.
</%doc>
<%shared>
my $init = 0;
</%shared>
% if ( !$init ) {
%   $init = 1;
<STYLE>
input.partial {
  position: absolute;
  opacity: 0;
  z-index: 1;
}
input.partial + label::before {
  position: relative;
  content: "\2610";
}
input.partial:checked + label::before {
  content: "\2611";
}
input.partial:indeterminate + label::before {
  content: "\2612";
}
</STYLE>
<SCRIPT TYPE="text/javascript">
function tristate_onclick() {
  var checkbox = this;
  var input = checkbox.input;
  if ( input.value == "" ) { // false -> true
    input.value = "1";
    checkbox.checked = true;
    checkbox.indeterminate = false;
  } else if ( input.value == "1" ) { // true -> indeterminate
    input.value = "-1";
    checkbox.checked = false;
    checkbox.indeterminate = true;
  } else if ( input.value == "-1" ) { // indeterminate -> false
    input.value = "";
    checkbox.checked = false;
    checkbox.indeterminate = false;
  }
}

var tristates = [];
var tristate_boxes = [];
window.onload = function() { // don't do this until all of the checkboxes exist
%#  tristates = document.getElementsByClassName('tristate'); # curse you, IE8
  var all_inputs = document.getElementsByTagName('input');
  for (var i=0; i < all_inputs.length; i++) {
    if ( all_inputs[i].className == 'tristate' ) {
      tristates.push(all_inputs[i]);
    }
  }
  for (var i=0; i < tristates.length; i++) {
    tristate_boxes[i] =
      document.getElementById('checkbox_' + tristates[i].name);
    // make sure they can find each other
    tristate_boxes[i].input = tristates[i];
    tristates[i].checkbox = tristate_boxes[i];
    // set event handler
    tristate_boxes[i].onclick = tristate_onclick;
    // set initial value
    if ( tristates[i].value == "-1" ) {
      tristate_boxes[i].indeterminate = true
    }
    if ( tristates[i].value == "1" ) {
      tristate_boxes[i].checked = true;
    }
  }
};
</SCRIPT>
% } # end of $init
<INPUT TYPE="hidden" NAME="<% $opt{field} %>"
                     ID="<% $opt{id} %>"
                     VALUE="<% $curr_value %>"
                     CLASS="tristate">
<INPUT TYPE="checkbox" ID="checkbox_<%$opt{field}%>" CLASS="partial">
<LABEL />
<%init>

my %opt = @_;

# might be useful but I'm not implementing it yet
#my $onchange = $opt{'onchange'}
#                 ? 'onChange="'. $opt{'onchange'}. '(this)"'
#                 : '';

$opt{'id'} ||= 'hidden_'.$opt{'field'};
my $curr_value = '-1';
if (exists $opt{curr_value}) {
  $curr_value = $opt{curr_value};
  $curr_value = '' unless $curr_value eq '-1' or $curr_value eq '1';
}

</%init>