diff options
Diffstat (limited to 'httemplate/elements/auto-table.html')
-rw-r--r-- | httemplate/elements/auto-table.html | 311 |
1 files changed, 163 insertions, 148 deletions
diff --git a/httemplate/elements/auto-table.html b/httemplate/elements/auto-table.html index 49222745a..ed011097e 100644 --- a/httemplate/elements/auto-table.html +++ b/httemplate/elements/auto-table.html @@ -1,166 +1,181 @@ <%doc> - -Example: -<% include('/elements/auto-table.html', - - ### - # required - ### - - 'header' => [ '#', 'Item', 'Amount' ], - 'fields' => [ 'id', 'name', 'amount' ], - - ### - # highly recommended - ### - - 'size' => [ 4, 12, 8 ], - 'maxl' => [ 4, 12, 8 ], - 'align' => [ 'right', 'left', 'right' ], - - ### - # optional - ### - - 'data' => [ [ 1, 'Widget', 25 ], - [ 12, 'Super Widget, 7 ] ], - #or - 'records' => [ qsearch('item', { } ) ], - # or any other array of FS::Record objects - - 'select' => [ '', - [ 1 => 'option 1', - 2 => 'option 2', ... - ], # options for second field - '' ], - - 'prefix' => 'mytable_', -) %> - -Values will be passed through as "mytable_id1", etc. +(within a form) +<table> +<tr> + <th>Field 1</th> + <th>Field 2</th> +</tr> +<tr id="mytemplate"> + <td><input type="text" name="field1"></td> + <td><select name="field2">...</td> + ... +</tr> +</table> +<& /elements/auto-table.html, + table => 'mytable', + template_row = 'mytemplate', + rows => [ + { field1 => 'foo', field2 => 'CA', ... }, + { field1 => 'bar', field2 => 'TX', ... }, ... + ], +&> + + or if you prefer: +... + fieldorder => [ 'field1', 'field2', ... ], + rows => [ + [ 'foo', 'CA' ], + [ 'bar', 'TX' ], + ], + +In the process/ handler, something like: +my @rows; +my %vars = $cgi->Vars; +for my $k ( keys %vars ) { + $k =~ /^${pre}magic(\d+)$/ or next; + my $rownum = $1; + # find all submitted names ending in this rownum + my %thisrow = + map { $_ => $vars{$_} } + grep /^(.*[\d])$rownum$/, keys %vars; + $thisrow->{num} = delete $thisrow{"${pre}magic$rownum"}; + push @rows, $thisrow; +} </%doc> - -<TABLE ID="<% $prefix %>AutoTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0> - <TR> -% foreach (@header) { - <TH><% $_ %></TH> -% } - </TR> -% my $row = 0; -% for ( $row = 0; $row < scalar @data; $row++ ) { - <TR> -% my $col = 0; -% for ( $col = 0; $col < scalar @fields; $col++ ) { -% my $id = $prefix . $fields[$col]; -% # don't suffix rownum in the final, blank row -% $id .= $row if $row < (scalar @data) - 1; - <TD> -% my @o = @{ $select[$col] }; -% if( @o ) { - <SELECT NAME="<% $id %>" ID="<% $id %>"> -% while(@o) { -% my $val = shift @o; - <OPTION VALUE=<% $val %><% -$val eq $data[$row][$col] ? ' SELECTED' : ''%>><% shift @o %></OPTION> -% } - </SELECT> -% } -% else { - <INPUT TYPE = "text" - NAME = "<% $id %>" - ID = "<% $id %>" - SIZE = <% $size[$col] %> - MAXLENGTH = <% $maxl[$col] %> - STYLE = "text-align:<% $align[$col] %>" - VALUE = "<% $data[$row][$col] %>" -% if( $opt{'autoadd'} ) { - onchange = "possiblyAddRow(this);" -% } - > - </TD> -% } -% } - <TD> - <IMG SRC = "<% "${p}images/cross.png" %>" - ALT = "X" - onclick = "deleteRow(this);" - > - </TD> - </TR> -% } -</TABLE> -% if( !$opt{'autoadd'} ) { -<INPUT TYPE="button" VALUE="Add" onclick="<% $prefix %>addRow();"><BR> -% } - -<SCRIPT TYPE="text/javascript"> - var <% $prefix %>rownum = <% $row %>; - var <% $prefix %>table = document.getElementById('<% $prefix %>AutoTable'); - // last row is initially blank, clone it and remove it - var <% $prefix %>_blank = - <% $prefix %>table.rows[<% $prefix %>table.rows.length-1].cloneNode(true); -% if( !$opt{'autoadd'} ) { - <% $prefix %>table.deleteRow(<% $prefix %>table.rows.length-1); -% } - - - - function rownum_of(obj) { - return (obj.parentNode.parentNode.sectionRowIndex); +<tbody id="<%$pre%>autotable"></tbody> +<script type="text/javascript"> +var <%$pre%>template; +var <%$pre%>tbody; +var <%$pre%>next_rownum; +var <%$pre%>set_rownum; +var <%$pre%>addRow; +var <%$pre%>deleteRow; +var <%$pre%>fieldorder = <% to_json($fieldorder) %>; + +function <%$pre%>possiblyAddRow_factory(obj) { + var callback = obj.onchange; + return function() { + if ( obj.rownum == <%$pre%>tbody.lastChild.rownum ) { + // then this is the last row, and it's being changed, so spawn a new row + <%$pre%>addRow(); + } + if ( callback ) { + callback.apply(obj); + } } +} - function <% $prefix %>possiblyAddRow(obj) { - if ( <% $prefix %>rownum == rownum_of(obj) ) { - <% $prefix %>addRow(); +function <%$pre%>set_rownum(obj, rownum) { + obj.rownum = rownum; + if ( obj.id ) { + obj.id = obj.id + rownum; + } + if ( obj.name ) { + obj.name = obj.name + rownum; + // also, in this case it's a form field that will be part of the record + // so set up an onchange handler + obj.onchange = <%$pre%>possiblyAddRow_factory(obj); + } + for (var i = 0; i < obj.children.length; i++) { + if ( obj.children[i] instanceof Node ) { + <%$pre%>set_rownum(obj.children[i], rownum); } } +} - function <% $prefix %>addRow() { - var row = <% $prefix %>table.insertRow(-1); - var cells = <% $prefix %>_blank.cells; - for (i=0; i<cells.length; i++) { - var node = row.appendChild(cells[i].cloneNode(true)); - var input = node.children[0]; - input.id = input.id + row.sectionRowIndex; - input.name = input.name + row.sectionRowIndex; +function <%$pre%>addRow(data) { + // duplicate the node + // warning: cloneNode doesn't clone event handlers that were set through + // the DOM + // if 'data' is an object, prepopulate the row's fields with the object's + // elements + // returns the rownum of the new row + var row = <%$pre%>template.cloneNode(true); + <%$pre%>tbody.appendChild(row); + var this_rownum = <%$pre%>next_rownum; + <%$pre%>set_rownum(row, this_rownum); + if(data instanceof Array) { + for (i = 0; i < data.length && i < <%$pre%>fieldorder.length; i++) { + var el = document.getElementsByName(<%$pre%>fieldorder[i] + this_rownum)[0]; + if (el) { + el.value = data[i]; + } + } + } else if (data instanceof Object) { + for (var field in data) { + var el = document.getElementsByName(field + this_rownum)[0]; + if (el) { + el.value = data[field]; +% # doesn't work for checkbox + } } - <% $prefix %>rownum++; + } // else nothing + <%$pre%>next_rownum++; + return this_rownum; +} + +function <%$pre%>deleteRow(rownum) { + if ( rownum == <%$pre%>tbody.lastChild.rownum ) { + // if this is the last row, spawn another one after it + <%$pre%>addRow(); } + var r = document.getElementById('<%$pre%>row' + rownum); + <%$pre%>tbody.removeChild(r); +} - function deleteRow(obj) { - if(<% $prefix %>rownum == rownum_of(obj)) { - <% $prefix %>addRow(); - } - <% $prefix %>table.deleteRow(rownum_of(obj)); - <% $prefix %>rownum--; - return(false); +function <%$pre%>init() { + <%$pre%>template = document.getElementById(<% $template_row |js_string%>); + <%$pre%>tbody = document.getElementById('<%$pre%>autotable'); + <%$pre%>next_rownum = <%$pre%>template.sectionRowIndex; + // detach the template row + var table = <%$pre%>template.parentNode; + table.removeChild(<%$pre%>template); + // give it an id + <%$pre%>template.id = <%$pre |js_string%> + 'row'; + // and a magic identifier so we know it's been submitted + var magic = document.createElement('INPUT'); + magic.setAttribute('type', 'hidden'); + magic.setAttribute('name', '<%$pre%>magic'); + magic.value = '1'; + // and a delete button +%# should this be enclosed in an actual <button> for aesthetics? + var delete_button = document.createElement('IMG'); + delete_button.id = 'delete_button'; + delete_button.src = '<%$fsurl%>images/cross.png'; + delete_button.alt = 'X'; + // use an inline string for this so that it will be cloned properly + delete_button.setAttribute('onclick', "<%$pre%>deleteRow(this.rownum);"); + var delete_cell = document.createElement('TD'); + delete_cell.appendChild(delete_button); + delete_cell.appendChild(magic); // it has to go somewhere + <%$pre%>template.appendChild(delete_cell); + + // preload rows + var rows = <% to_json(\@rows) %>; + for (var i = 0; i < rows.length; i++) { + <%$pre%>addRow(rows[i]); } -</SCRIPT> + <%$pre%>addRow(); +} +<%$pre%>init(); +</script> <%init> my %opt = @_; - -my @header = @{ $opt{'header'} }; -my @fields = @{ $opt{'fields'} }; -my @data = (); -if($opt{'data'}) { - @data = @{ $opt{'data'} }; -} -elsif($opt{'records'}) { - foreach my $rec (@{ $opt{'records'} }) { - push @data, [ map { $rec->getfield($_) } @fields ]; +my $pre = ''; +$pre = $opt{'table'} . '_' if $opt{'table'}; +my $template_row = $opt{'template_row'} + or die "auto-table requires template_row\n"; # a DOM id + +my %vars = $cgi->Vars; +# rows that we will preload, as hashrefs of name => value +my @rows = @{ $opt{'data'} || [] }; +foreach (@rows) { + # allow an array of FS::Record objects to be passed + if ( blessed($_) and $_->isa('FS::Record') ) { + $_ = $_->hashref; } } -# else @data = (); -push @data, [ map {''} @fields ]; # make a blank row - -my $prefix = $opt{'prefix'}; -my @size = $opt{'size'} ? @{ $opt{'size'} } : (map {16} @fields); -my @maxl = $opt{'maxl'} ? @{ $opt{'maxl'} } : @size; -my @align = $opt{'align'} ? @{ $opt{'align'} } : (map {'right'} @fields); -my @select = @{ $opt{'select'} || [] }; -foreach (0..scalar(@fields)-1) { - $select[$_] ||= []; -} +my $fieldorder = $opt{'fieldorder'} || []; </%init> |