/* * data_grid.js * javascript helper functions for PHP DataGrid class */ var DG = window.DG || {}; // {{{ DG.apply_filters /** * assembles filter criteria to a URL parameter and loads the * document to apply the filter * @param object frm form containing filter controls * @param boolean apply true=apply filters, false=clear filters * @param string fp name of 'fp' parameter. if present in url, * it is reset to 1 **/ DG.apply_filters = function(frm, apply, fp) { var args = DG.parse_query(location.search); var f = frm.elements[frm._fparam.value]; if (f.nodeName) f = [f]; if (apply) { var temp = []; for (i = 0; i < f.length; i++) { temp.push(f[i].value); } args[frm._fparam.value] = temp.join("\x01"); } else { delete args[frm._fparam.value]; } if (args[fp]) args[fp] = 1; location.search = DG.make_query(args); } // }}} // {{{ DG.apply_sorting /** * assembles sorting criteria to a URL parameter and loads the * document to apply or reset the sorting * @param integer frm form containing sorting controls * @param boolean apply true=apply sorting, false=clear sorting **/ DG.apply_sorting = function(frm, apply) { var args = DG.parse_query(location.search); var s = frm.elements[frm._sparam.value]; if (s.nodeName) s = [s]; var d = frm.elements[frm._dparam.value]; if (d.nodeName) d = [d]; if (apply) { var stemp = []; var dtemp = []; for (i = 0; i < s.length; i++) { if (s[i].value != '') { stemp.push(s[i].value); dtemp.push(d[i].value); } } args[frm._sparam.value] = stemp.join("\x01"); args[frm._dparam.value] = dtemp.join("\x01"); } else { delete args[frm._sparam.value]; delete args[frm._dparam.value]; } location.search = DG.make_query(args); } // }}} // {{{ DG.clear_insert_row /** * clears an insert row by resetting all of it's fields to their default * values. * @param object row TR element **/ DG.clear_insert_row = function(row) { var inputs = row.getElementsByTagName('INPUT'); for (var i = 0; i < inputs.length; i++) { var el = inputs[i]; if (el.name && el.name.match(/^_e\[/)) { if (typeof(el.defaultValue) != 'undefined') el.value = el.defaultValue; if (typeof(el.defaultChecked) != 'undefined') el.checked = el.defaultChecked; } } var inputs = row.getElementsByTagName('SELECT'); for (var i = 0; i < inputs.length; i++) { var el = inputs[i]; if (el.name && el.name.match(/^_e\[/)) { var options = el.options; for (j = 0; j < options.length; j++) { options[j].selected = options[j].defaultSelected; } } } } // }}} // {{{ DG.copy_row /** * copies fields from row target for event to last insert row * @param object el Element in row to copy **/ DG.copy_row = function(el) { frow = DG.enclosing_element(el, 'TR'); if (!frow) return; // find the last insert row var trow = frow.nextSibling; while (!DG.in_last_insert_row(trow)) trow = trow.nextSibling; if (!trow) return; // copy INPUT elements between the rows var finputs = frow.getElementsByTagName('INPUT'); var tinputs = trow.getElementsByTagName('INPUT'); for (i = 0; i < finputs.length; i++) { var ef = finputs[i]; if (ef.name && ef.name.match(/_e\[/)) { for (j = 0; j < tinputs.length; j++) { var et = tinputs[j]; if (et.name == ef.name) { // copy the data et.value = ef.value; if (ef.checked) et.checked = ef.checked; } } } } // copy SELECT elements between the rows var finputs = frow.getElementsByTagName('SELECT'); var tinputs = trow.getElementsByTagName('SELECT'); for (i = 0; i < finputs.length; i++) { var ef = finputs[i]; if (ef.name && ef.name.match(/_e\[/)) { for (j = 0; j < tinputs.length; j++) { var et = tinputs[j]; if (et.name == ef.name) { // copy the data et.options.length = 0; for (var k = 0; k < ef.options.length; k++) { var o = new Option( ef.options[k].text, ef.options[k].value, ef.options[k].defaultSelected, ef.options[k].selected ); et.options[et.options.length] = o; } } } } } // after copying, make sure we have an open row DG.open_insert_row(trow); } // }}} // {{{ DG.delete_row /** * prepares form submission to delete row for for a given grid instance * @param object form edit form element * @param string col Column title (for confirmation box) * @param string key Key value to delete **/ DG.delete_row = function(form, col, key) { if (!confirm('Διαγραφή ' + col + ' \'' + key + '\'?')) return; form._action.value = 'delete'; form._key.value = key; form.submit(); } // }}} // {{{ DG.delete_rows /** * deletes all selected rows * @param object form edit form element **/ DG.delete_rows = function(form) { ids = DG.selected_row_ids(form); if (!ids.length) { alert('Επιλέξτε μία ή περισσοτερες εγγραφές'); return; } if (!confirm('Διαγραφή ' + ids.length + ' row' + (ids.length> 1 ? 's' : '') + '?')) return; form._action.value = 'delete'; form._key.value = ids.join("\x01"); form.submit(); } // }}} // {{{ DG.enclosing_element /** * returns the closet element with nodeName of 'name' that encloses the given * element, or null if no such element found * @param object el DOM element * @param string name node name to search for **/ DG.enclosing_element = function(el, name) { while (el && el.nodeName != name) el = el.parentNode; return el; } // }}} // {{{ DG.export_data /** * handles export of data grid using helper form * @param integer instance DataGrid instance * @param string format Export format (csv, pdf, xhtml) **/ DG.export_data = function(instance, format) { // get the helper form var form = document.forms['DGHelperForm' + instance]; // dispatch the action form._action.value = format; form._exp_from.value = document.getElementById('exp_from' + instance).value; form._exp_count.value = document.getElementById('exp_count' + instance).value; if (format == 'xhtml') { form.target = '_blank'; form._pagesize.value = document.getElementById('pagesize' + instance).value; } form.submit(); form.target = '_self'; } // }}} // {{{ DG.export_form /** * handles export of form data using helper form * @param integer instance DataGrid instance * @param string format Export format (pdf, xhtml) * @param string key Primary key of row to export **/ DG.export_form = function(instance, format, key) { // get the helper form var form = document.forms['DGHelperForm' + instance]; // dispatch the action form._action.value = 'form_' + format; form._key.value = key; if (format == 'xhtml') form.target = '_blank'; form.submit(); form.target = '_self'; } // }}} // {{{ DG.find_node /** * searches through a node and its children returning first node * for which func returns true * @param object el starting element to search * @param object func function to apply to each node **/ DG.find_node = function(el, func) { if (func(el)) return el; el = el.firstChild; while (el) { var node = DG.find_node(el, func); if (node) return node; el = el.nextSibling; } return null; } // }}} // {{{ DG.in_insert_row /** * returns true if element is contained within an insert row * @param object el DOM element **/ DG.in_insert_row = function(el) { var row = DG.enclosing_element(el, 'TR'); if (!row) return false; inputs = row.getElementsByTagName('INPUT'); for (i = 0; i < inputs.length; i++) { if (inputs[i].name == '_i[]') return inputs[i].value ? true : false; } return false; } // }}} // {{{ DG.in_last_insert_row /** * returns true if element is contained within the last insert row * @param object el DOM element **/ DG.in_last_insert_row = function(el) { row = DG.enclosing_element(el, 'TR'); return (row && DG.in_insert_row(row) && (!row.nextSibling || !DG.in_insert_row(row.nextSibling))); } // }}} // {{{ DG.make_query /** * combines arguments into a query string **/ DG.make_query = function(args) { var query = ''; for (var name in args) { var value = encodeURIComponent(args[name]); if (query.length) query += '&'; query += name + '=' + value; } if (query.length) query = '?' + query; return query; } // }}} // {{{ DG.open_insert_row /** * opens an empty insert row at the bottom of the table if last * insert row is not empty (or there is no last insert row) * @param integer el Element within the table body **/ DG.open_insert_row = function(el) { // find last insert row var tbody = DG.enclosing_element(el, 'TBODY'); if (!tbody) return; var row = tbody.lastChild; while (row && !DG.in_last_insert_row(row)) row = row.previousSibling; if (!row) return; // check the row to see if its completely blank, in which case we dont need // to add a new row. we do this before cloning, because the selectedIndex // property doesnt seem to survive the cloning operation. var allblank = true; var inputs = row.getElementsByTagName('INPUT'); for (i = 0; i < inputs.length; i++) { el = inputs[i]; if (el.name && el.name.match(/^_e/)) { if (typeof(el.defaultValue) != 'undefined' && el.value != el.defaultValue) allblank = false; if (typeof(el.defaultChecked) != 'undefined' && el.checked != el.defaultChecked) allblank = false; } } var inputs = row.getElementsByTagName('SELECT'); for (i = 0; i < inputs.length; i++) { el = inputs[i]; if (el.name && el.name.match(/^_e/)) { var options = el.options; for (j = 0; j < options.length; j++) { //if ((options[j].selected != options[j].defaultSelected)) { if(j>0){ if(options[j].selected){ allblank = false; } } } } } if (allblank) return; // clone the row and prepare to add it to the table var newrow = row.cloneNode(true); // clear all the input values DG.clear_insert_row(newrow); // add the new row to the table if we had to create a blank row row.parentNode.insertBefore(newrow, row.nextSibling) } // }}} // {{{ DG.parse_query /** * splits the query arguments from the URL into an object * if parameter appears multiple times, it is combined into * a string using "\x01" character **/ DG.parse_query = function(query) { var args = new Object(); if (query.length) { var pairs = query.substring(1).split('&'); for (var i = 0; i < pairs.length; i++) { var pos = pairs[i].indexOf('='); if (pos == -1) continue; var name = pairs[i].substring(0, pos); var value = pairs[i].substring(pos + 1); if (args[name]) args[name] = args[name] + "\x01" + unescape(value); else args[name] = unescape(value); } } return args; } // }}} // {{{ DG.remove_insert_row /** * removes insert row after confirmation * @param object el DOM element in row to remove **/ DG.remove_insert_row = function(el) { var row = DG.enclosing_element(el, 'TR'); if (!row) return; // confirm removal if (!confirm('Να διαγραφεί η εγγραφή;')) return; // if this is the last insert row, we just blank it instead of // removing it if (DG.in_last_insert_row(row)) { DG.clear_insert_row(row); return; } // remove the row and make sure there is an open insert row tbody = row.parentNode; tbody.removeChild(row); DG.open_insert_row(tbody) } // }}} // {{{ DG.reset_rows /** * resets form data, removing any inserted rows * @param object form edit form element **/ DG.reset_rows = function(form) { location.href = location.href; } // }}} // {{{ DG.row_clicked /** * event handler to capture click on a row to toggle that row's * selection checkbox * @param object evt DOM Event object **/ DG.row_clicked = function(evt) { // get event object and target element. we only care about clicks // on the table cell (TD) elements var e = window.event ? window.event : evt; var el = e.srcElement ? e.srcElement : e.target; if (el.tagName != 'TD') return; // get the parent (TR) node var tr = el.parentNode; // find the _s checkbox (if any) and toggle it for (var i = 0; i < tr.childNodes.length; i++) { var td = tr.childNodes[i]; for (var j = 0; j < td.childNodes.length; j++) { var el = td.childNodes[i]; if (el.name == '_s') { el.checked = !el.checked; return; } } } } // }}} // {{{ DG.save_form /** * validates form for saving/inserting single row and then submits the form * @param object form edit form element **/ DG.save_form = function(form) { if (DG.validate_rows(form, true)) { form._action.value = 'save'; form.submit(); } } // }}} // {{{ DG.save_rows /** * validates form for saving/inserting rows and then submits the form * @param object form edit form element **/ DG.save_rows = function(form) { DG.open_insert_row(form.elements['_i[]'][0]); if (DG.validate_rows(form, false)) { form._action.value = 'save'; form.submit(); } } // }}} // {{{ DG.select_rows /** * passes row id's of selected rows to a function * @param object form edit form element **/ DG.select_rows = function(form) { ids = DG.selected_row_ids(form); if (!ids.length) { alert('Επιλέξτε μία ή περισσοτερες εγγραφές'); return; } alert('TODO: Pass row IDs (' + ids.join(',') + ') to some function?'); } // }}} // {{{ DG.selected_row_ids /** * returns an array of row ids that are currently selected * @param object form edit form element **/ DG.selected_row_ids = function(form) { var s = form._s; var ids = new Array(); for (i = 0; i < s.length; i++) { if (s[i].checked) ids.push(s[i].value); } return ids; } // }}} // {{{ DG.toggle_rows /** * inverts the row selection checkboxes for the given form * @param object form edit form element **/ DG.toggle_rows = function(form) { var s = form._s; for (i = 0; i < s.length; i++) s[i].click(); form._toggle.checked = false; } // }}} // {{{ DG.update_picklist /** * inverts the row selection checkboxes for the given form * @param object el Constraining SELECT element * @param string fld Field Name being updated **/ DG.update_picklist = function(el, fld, instance) { var row = DG.enclosing_element(el, 'TR'); var frm = el.form; var instance = frm.name.match(/\d+/); // find the column number of the target field to update var _f = frm.elements['_f[]']; for (var c = 0; c < _f.length; c++) { if (_f[c].value == fld) break; } // find the corresponding SELECT element var upd; var inputs = row.getElementsByTagName('SELECT'); for (i = 0; i < inputs.length; i++) { if (inputs[i].name == '_e[' + c + '][]') { upd = inputs[i]; break; } } if (!upd) return; // make sure prototype library is present if (!window.Ajax || !window.Ajax.Request) { alert('prototype.js must be included!'); return; } // get the DGHelperForm, which has parameters we need for the // Ajax request var fh = document.forms['DGHelperForm' + instance]; // build and execute the request new Ajax.Request( fh.action, { method: 'post', postBody: '_action=update_picklist&_dg=' + encodeURIComponent(fh._dg.value) + '&_field=' + encodeURIComponent(fld) + '&_fk=' + encodeURIComponent(el.value) + '&_value=' + encodeURIComponent(upd.value), onSuccess: function(req) { var opts; try { opts = eval(req.responseText) } catch(e) { return; } upd.options.length = 0; for (var i = 0; i < opts.length; i++) { var o = new Option( opts[i][1], opts[i][0], false, opts[i][2] ); upd.options[upd.options.length] = o; } } } ); } // }}} // {{{ DG.validate_rows /** * validates form for saving/inserting rows. the last insert row * is not validated, because it is always blank * @param object form edit form element * @param boolean all_rows if true, all rows are validated. otherwise (form), * the last insert row is not validated (grid) **/ DG.validate_rows = function(form, all_rows) { // use _i[] element to iterate over each row var _iarr = form.elements['_i[]']; if (_iarr.nodeName) _iarr = [_iarr]; for (var r = 0; r < _iarr.length; r++) { // _i is flag for insert row var _i = _iarr[r].value - 0; // skip validation on last insert row if (!all_rows && _i && r == _iarr.length - 1) continue; // use _t[] element to interate over each column var _tarr = form.elements['_t[]']; if (_tarr.nodeName) _tarr = [_tarr]; for (var c = 0; c < _tarr.length; c++) { // get controls for this column var _t = _tarr[c].value; var _marr = form.elements['_m[]']; if (_marr.nodeName) _marr = [_marr]; var _m = _marr[c].value - 0; var _earr = form.elements['_e[' + c + '][]']; if (_earr.nodeName) _earr = [_earr]; var _e = _earr[r]; var _varr = form.elements['_v[' + c + '][]']; if (_varr.nodeName) _varr = [_varr]; var _v = _varr[r]; // check for errors var err; if (_m && _e.value == '') err = 'Απαιτείται κάποια τιμή'; else if (_t == 'date' && _e.value != '' && !ValidDate(_e.value)) err = 'Λάθος ημερομηνία'; else if (_t == 'datetime' && _e.value != '' && !ValidDatetime(_e.value)) err = 'Λάθος ημερομηνία/ώρα'; else if (_t == 'integer' && _e.value != '' && !ValidInt(_e.value)) err = 'Λάθος ακέραιος'; //else if (_t == 'real' && _e.value != '' && !ValidReal(_e)) // err = 'Λάθος αριθμός'; else if (_t == 'textarea' && !ValidTextarea(_e)) err = 'Η τιμή που δώσατε ξεπερνάει τους ' + _e.attributes['maxlength'].value + ' χαρακτήρες'; if (err) { alert(err); _e.focus(); try { _e.select(); } catch(e) { } return false; } } } return true; } // }}} // vim: fdm=marker: