summaryrefslogtreecommitdiff
path: root/httemplate/elements/auto-table.html
diff options
context:
space:
mode:
Diffstat (limited to 'httemplate/elements/auto-table.html')
-rw-r--r--httemplate/elements/auto-table.html311
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>