/** * JavaScript functions to handle standard behaviors of a QuickForm advmultiselect element * * @category HTML * @package HTML_QuickForm_advmultiselect * @author Laurent Laville * @copyright 2007-2009 Laurent Laville * @license http://www.opensource.org/licenses/bsd-license.php New BSD License * @version CVS: $Id: qfamsHandler.js,v 1.13 2009/02/06 09:49:22 farell Exp $ * @since File available since Release 1.3.0 */ if (typeof QFAMS === "undefined" || !QFAMS) { /** * The QFAMS global namespace object. If QFAMS is already defined, the * existing QFAMS object will not be overwritten so that defined * namespaces are preserved. * @class QFAMS * @static * @public * @since 1.5.0 */ var QFAMS = {}; } /** * QFAMS.env is used to keep track of end-user preferences * for persistant values. * * @class QFAMS.env * @static */ QFAMS.env = QFAMS.env || { /** * Keeps the persistant selection preference when items are selected or unselected * * @property persistantSelection * @type Boolean */ persistantSelection: false, /** * Keeps the persistant selection preference when items are moved up or down * * @property persistantMove * @type Boolean */ persistantMove: true }; /** * Uses QFAMS.updateCounter as a * text tools to replace all childs of 'c' element by a new text node of 'v' value * * @param dom element c html element; is best use in most case * @param string v new counter value * * @method updateCounter * @static * @return void * @public * @since 1.5.0 */ QFAMS.updateCounter = function (c, v) { var i; var nodeText = null; if (c !== null) { // remove all previous child nodes of 'c' element if (c.childNodes) { for (i = 0; i < c.childNodes.length; i++) { c.removeChild(c.childNodes[i]); } } // add new text value 'v' nodeText = document.createTextNode(v); c.appendChild(nodeText); } }; /** * Uses QFAMS.updateLiveCounter as a * standard onclick event handler to dynamic change value of counter * that display current selection * * @method updateLiveCounter * @static * @return void * @private * @since 1.5.0 */ QFAMS.updateLiveCounter = function () { var lbl = this.parentNode; var selectedCount = 0; // Find all the checkboxes... var div = lbl.parentNode; var inputs = div.getElementsByTagName('input'); for (var i = 0; i < inputs.length; i++) { if (inputs[i].checked == 1) { selectedCount++; } } var e = div.id; var qfamsName = e.substring(e.indexOf('_', 0) + 1, e.length); // updates item count var span = document.getElementById(qfamsName + '_selected'); QFAMS.updateCounter(span, selectedCount + '/' + inputs.length); }; /** * Uses QFAMS.editSelection * in single select box mode, to edit current selection and update live counter * * @param string qfamsName QuickForm advmultiselect element name * @param integer selectMode Selection mode (0 = uncheck, 1 = check, 2 = toggle) * * @method editSelection * @static * @return void * @public * @since 1.5.0 */ QFAMS.editSelection = function (qfamsName, selectMode) { if (selectMode !== 0 && selectMode !== 1 && selectMode !== 2) { return; } var selectedCount = 0; // Find all the checkboxes... var ams = document.getElementById('qfams_' + qfamsName); var inputs = ams.getElementsByTagName('input'); // Loop through all checkboxes (input element) for (var i = 0; i < inputs.length; i++) { if (selectMode === 2) { if (inputs[i].checked == 0) { inputs[i].checked = 1; } else if (inputs[i].checked == 1) { inputs[i].checked = 0; } } else { inputs[i].checked = selectMode; } if (inputs[i].checked == 1) { selectedCount++; } } // updates selected item count var span = document.getElementById(qfamsName + '_selected'); QFAMS.updateCounter(span, selectedCount + '/' + inputs.length); }; /** * Uses QFAMS.moveSelection * in double select box mode, to move current selection and update live counter * * @param string qfamsName QuickForm advmultiselect element name * @param dom element selectLeft Data source list * @param dom element selectRight Target data list * @param dom element selectHidden Full data source (selected, unselected) * private usage * @param string action Action name (add, remove, all, none, toggle) * @param string arrange Sort option (none, asc, desc) * * @method moveSelection * @static * @return void * @public * @since 1.5.0 */ QFAMS.moveSelection = function (qfamsName, selectLeft, selectRight, selectHidden, action, arrange) { var isIE = /*@cc_on!@*/false; //IE detector var source = null; var target = null; var option; var c = null; var s = null; var i; var maxFrom, maxTo; if (action === 'add' || action === 'all' || action === 'toggle') { source = selectLeft; target = selectRight; } else { source = selectRight; target = selectLeft; } // Don't do anything if nothing selected. Otherwise we throw javascript errors. if (source.selectedIndex === -1 && (action === 'add' || action === 'remove')) { return; } maxFrom = source.options.length; maxTo = target.options.length; // check if target list is empty and remove fake empty option (tip to be XHTML compliant) if (maxTo > 0 && target.options[0].value === "") { target.removeAttribute("disabled"); target.options[0] = null; } // Add items to the 'TO' list. for (i = (maxFrom - 1); i >= 0; i--) { if (action === 'all' || action === 'none' || action === 'toggle' || source.options[i].selected === true) { if (source.options[i].disabled === false) { if (isIE) { option = source.options[i].removeNode(true); option.selected = QFAMS.env.persistantSelection; target.appendChild(option); } else { option = source.options[i].cloneNode(true); option.selected = QFAMS.env.persistantSelection; target.options[target.options.length] = option; } } } } // Remove items from the 'FROM' list. if (!isIE) { for (i = (maxFrom - 1); i >= 0; i--) { if (action === 'all' || action === 'none' || action === 'toggle' || source.options[i].selected === true) { if (source.options[i].disabled === false) { source.options[i] = null; } } } } // Add items to the 'FROM' list for toggle function if (action === 'toggle') { for (i = (maxTo - 1); i >= 0; i--) { if (target.options[i].disabled === false) { if (isIE) { option = target.options[i].removeNode(true); option.selected = QFAMS.env.persistantSelection; source.appendChild(option); } else { option = target.options[i].cloneNode(true); option.selected = QFAMS.env.persistantSelection; source.options[source.options.length] = option; } } } if (!isIE) { for (i = (maxTo - 1); i >= 0; i--) { if (target.options[i].disabled === false) { target.options[i] = null; } } } } // updates unselected item count c = document.getElementById(qfamsName + '_unselected'); s = document.getElementById(qfamsName + '-f'); QFAMS.updateCounter(c, s.length); // updates selected item count c = document.getElementById(qfamsName + '_selected'); s = document.getElementById(qfamsName + '-t'); QFAMS.updateCounter(c, s.length); // Sort list if required if (arrange !== 'none') { QFAMS.sortList(target, QFAMS.compareText, arrange); } // Set the appropriate items as 'selected in the hidden select. // These are the values that will actually be posted with the form. QFAMS.updateHidden(selectHidden, selectRight); }; /** * Uses QFAMS.sortList to * sort selection list if option is given in HTML_QuickForm_advmultiselect class constructor * * @param dom element list Selection data list * @param prototype compareFunction to sort each element of a list * @param string arrange Sort option (none, asc, desc) * * @method sortList * @static * @return void * @private * @since 1.5.0 */ QFAMS.sortList = function (list, compareFunction, arrange) { var i; var options = new Array(list.options.length); for (i = 0; i < options.length; i++) { options[i] = new Option(list.options[i].text, list.options[i].value, list.options[i].defaultSelected, list.options[i].selected); } options.sort(compareFunction); if (arrange === 'desc') { options.reverse(); } list.options.length = 0; for (i = 0; i < options.length; i++) { list.options[i] = options[i]; } }; /** * QFAMS.compareText * is a callback function to sort each element of two lists A and B * * @param string option1 single element of list A * @param string option2 single element of list B * * @method compareText * @static * @return integer -1 if option1 is less than option2, * 0 if option1 is equal to option2 * 1 if option1 is greater than option2 * @private * @since 1.5.0 */ QFAMS.compareText = function (option1, option2) { if (option1.text === option2.text) { return 0; } return option1.text < option2.text ? -1 : 1; }; /** * QFAMS.updateHidden * updates the private list that handle selection of all elements (selected and unselected) * * @param dom element h hidden list (contains all elements) * @param dom element r selection list (contains only elements selected) * * @method updateHidden * @static * @return void * @private * @since 1.5.0 */ QFAMS.updateHidden = function (h, r) { var i; for (i = 0; i < h.length; i++) { h.options[i].selected = false; } for (i = 0; i < r.length; i++) { h.options[h.length] = new Option(r.options[i].text, r.options[i].value); h.options[h.length - 1].selected = true; } }; /** * With QFAMS.moveUp * end-user may arrange and element up to the selection list * * @param dom element l selection list (contains only elements selected) * @param dom element h hidden list (contains all elements) * * @method moveUp * @static * @return void * @public * @since 1.5.0 */ QFAMS.moveUp = function (l, h) { var indice = l.selectedIndex; if (indice < 0) { return; } if (indice > 0) { QFAMS.moveSwap(l, indice, indice - 1); QFAMS.updateHidden(h, l); } }; /** * With QFAMS.moveDown * end-user may arrange and element down to the selection list * * @param dom element l selection list (contains only elements selected) * @param dom element h hidden list (contains all elements) * * @method moveDown * @static * @return void * @public * @since 1.5.0 */ QFAMS.moveDown = function (l, h) { var indice = l.selectedIndex; if (indice < 0) { return; } if (indice < l.options.length - 1) { QFAMS.moveSwap(l, indice, indice + 1); QFAMS.updateHidden(h, l); } }; /** * With QFAMS.moveTop * end-user may arrange and element up to the top of selection list * * @param dom element l selection list (contains only elements selected) * @param dom element h hidden list (contains all elements) * * @method moveTop * @static * @return void * @public * @since 1.5.0 */ QFAMS.moveTop = function (l, h) { var indice = l.selectedIndex; if (indice < 0) { return; } while (indice > 0) { QFAMS.moveSwap(l, indice, indice - 1); QFAMS.updateHidden(h, l); indice--; } }; /** * With QFAMS.moveBottom * end-user may arrange and element down to the bottom of selection list * * @param dom element l selection list (contains only elements selected) * @param dom element h hidden list (contains all elements) * * @method moveBottom * @static * @return void * @public * @since 1.5.0 */ QFAMS.moveBottom = function (l, h) { var indice = l.selectedIndex; if (indice < 0) { return; } while (indice < l.options.length - 1) { QFAMS.moveSwap(l, indice, indice + 1); QFAMS.updateHidden(h, l); indice++; } }; /** * With QFAMS.moveSwap * end-user may invert two elements position in the selection list * * @param dom element l selection list (contains only elements selected) * @param integer i element source indice * @param integer j element target indice * * @method moveSwap * @static * @return void * @public * @since 1.5.0 */ QFAMS.moveSwap = function (l, i, j) { var node; node = l.replaceChild(l.options[i], l.options[j]); if (i > j) { l.insertBefore(node, l.options[j].nextSibling); } else { l.insertBefore(node, l.options[i]); } if (QFAMS.env.persistantMove) { l.selectedIndex = j; } else { l.selectedIndex = -1; } }; /** * Uses QFAMS.init to * initialize onclick event handler for all checkbox element * of a QuickForm advmultiselect element with single select box. * * @method init * @static * @return void * @public * @since 1.5.0 */ QFAMS.init = function (elm) { var e, i; for (e = 0; e < elm.length; e++) { var div = document.getElementById('qfams_' + elm[e]); if (div !== null) { var inputs = div.getElementsByTagName('input'); if (inputs !== null) { for (i = 0; i < inputs.length; i++) { inputs[i].onclick = QFAMS.updateLiveCounter; } } } } };