/** * eGroupWare eTemplate2 - JS Nextmatch object * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package etemplate * @subpackage api * @link http://www.egroupware.org * @author Andreas Stöckel * @copyright Stylite 2011 * @version $Id$ */ "use strict"; /*egw:uses jquery.jquery; et2_widget; et2_core_interfaces; et2_core_DOMWidget; et2_widget_template; et2_widget_grid; et2_widget_selectbox; et2_extension_nextmatch_dynheight; et2_dataview_view_gridContainer; et2_dataview_model_dataProvider; */ /** * Interface all special nextmatch header elements have to implement. */ var et2_INextmatchHeader = new Interface({ /** * The 'setNextmatch' function is called by the parent nextmatch widget * and tells the nextmatch header widgets which widget they should direct * their 'sort', 'search' or 'filter' calls to. */ setNextmatch: function(_nextmatch) {} }); var et2_INextmatchSortable = new Interface({ setSortmode: function(_mode) {} }); /** * Class which implements the "nextmatch" XET-Tag */ var et2_nextmatch = et2_DOMWidget.extend(et2_IResizeable, { attributes: { "template": { "name": "Template", "type": "string", "description": "The id of the template which contains the grid layout." } }, legacyOptions: ["template"], init: function() { this._super.apply(this, arguments); this.div = $j(document.createElement("div")) .addClass("et2_nextmatch"); // Create the dynheight component which dynamically scales the inner // container. this.dynheight = new et2_dynheight(null, this.div, 150); // Create the data provider which cares about streaming the row data // efficiently to the rows this.dataProvider = new et2_dataview_dataProvider(this, 100); // Create the outer grid container this.dataviewContainer = new et2_dataview_gridContainer(this.div, this.dataProvider); this.activeFilters = {}; }, /** * Destroys all */ destroy: function() { this.dataviewContainer.free(); this.dataProvider.free(); this.dynheight.free(); this._super.apply(this, arguments); }, /** * Implements the et2_IResizeable interface - lets the dynheight manager * update the width and height and then update the dataview container. */ resize: function() { this.dynheight.update(function(_w, _h) { this.dataviewContainer.resize(_w, _h); }, this); }, /** * Get Rows callback */ getRows: function(_fetchList, _callback, _context) { console.log("Nextmatch will fetch ", _fetchList); // Create the entries: var entries = {}; for (var i = 0; i < _fetchList.length; i++) { var start = _fetchList[i].startIdx; for (var j = 0; j < _fetchList[i].count; j++) { entries[start + j] = {"info_id":"5","info_type":"email","info_from":"tracker","info_addr":"tracker","info_subject":"InfoLog grid view: problem with Opera; 'permission denied' while saving","info_des":"","info_owner":"5","info_responsible":[],"info_access":"public","info_cat":"0","info_datemodified":1307112528,"info_startdate":1306503000,"info_enddate":"0", "info_id_parent":"0","info_planned_time":"0","info_replanned_time":"0","info_used_time":"0","info_status":"done", "info_confirm":"not","info_modifier":"5","info_link_id":0,"info_priority":"1","pl_id":"0","info_price":null, "info_percent":"100%","info_datecompleted":1307112528,"info_location":"","info_custom_from":1, "info_uid":"infolog-5-18d12c7bf195f6b9d602e1fa5cde28f1","info_cc":"","caldav_name":"5.ics","info_etag":"0", "info_created":1307112528,"info_creator":"5","links":[],"info_anz_subs":0,"sub_class":"normal_done","info_link":{"title":"tracker"},"class":"rowNoClose rowNoCloseAll ","info_type_label":"E-Mail","info_status_label":"done","info_number":"5"} } } _callback.call(_context, entries); }, /** * Sorts the nextmatch widget by the given ID. * * @param _id is the id of the data entry which should be sorted. * @param _asc if true, the elements are sorted ascending, otherwise * descending. If not set, the sort direction will be determined * automatically. */ sortBy: function(_id, _asc) { // Create the "sort" entry in the active filters if it did not exist // yet. if (typeof this.activeFilters["sort"] == "undefined") { this.activeFilters["sort"] = { "id": null, "asc": true }; } // Determine the sort direction automatically if it is not set if (typeof _asc == "undefined") { if (this.activeFilters["sort"].id == _id) { _asc = !this.activeFilters["sort"].asc; } } // Update the entry in the activeFilters object this.activeFilters["sort"] = { "id": _id, "asc": _asc } // Set the sortmode display this.iterateOver(function(_widget) { _widget.setSortmode((_widget.id == _id) ? (_asc ? "asc": "desc") : "none"); }, this, et2_INextmatchSortable); et2_debug("info", "Sorting nextmatch by '" + _id + "' in direction '" + (_asc ? "asc" : "desc") + "'"); }, /** * Removes the sort entry from the active filters object and thus returns to * the natural sort order. */ resetSort: function() { // Check whether the nextmatch widget is currently sorted if (typeof this.activeFilters["sort"] != "undefined") { // Reset the sortmode this.iterateOver(function(_widget) { _widget.setSortmode("none"); }, this, et2_INextmatchSortable); // Delete the "sort" filter entry delete(this.activeFilters["sort"]); this.applyFilters(); } }, applyFilters: function() { et2_debug("info", "Changing nextmatch filters to ", this.activeFilters); }, /** * Generates the column name for the given column widget */ _genColumnCaption: function(_widget) { var result = null; _widget.iterateOver(function(_widget) { if (!result) { result = _widget.options.label; } else { result += ", " + _widget.options.label; } }, this, et2_INextmatchHeader); return result; }, _parseHeaderRow: function(_row, _colData) { // Go over the header row and create the column entries this.columns = new Array(_row.length); var columnData = new Array(_row.length); for (var x = 0; x < _row.length; x++) { this.columns[x] = { "widget": _row[x].widget }; columnData[x] = { "id": "col_" + x, "caption": this._genColumnCaption(_row[x].widget), "visibility": _colData[x].disabled ? ET2_COL_VISIBILITY_INVISIBLE : ET2_COL_VISIBILITY_VISIBLE, "width": _colData[x].width }; // Append the widget to this container this.addChild(_row[x].widget); } // Create the column manager and update the grid container this.dataviewContainer.setColumns(columnData); }, _parseDataRow: function(_row, _colData) { var columnWidgets = new Array(this.columns.length); for (var x = 0; x < columnWidgets.length; x++) { if (typeof _row[x] != "undefined" && _row[x].widget) { columnWidgets[x] = _row[x].widget; // Append the widget to this container this.addChild(_row[x].widget); } else { columnWidgets[x] = _row[x].widget; } } this.dataviewContainer.rowProvider.setDataRowTemplate(columnWidgets, this); }, _parseGrid: function(_grid) { // Search the rows for a header-row - if one is found, parse it for (var y = 0; y < _grid.rowData.length; y++) { if (_grid.rowData[y]["class"] == "th") { this._parseHeaderRow(_grid.cells[y], _grid.colData); } else { this._parseDataRow(_grid.cells[y], _grid.colData); } } }, /** * When the template attribute is set, the nextmatch widget tries to load * that template and to fetch the grid which is inside of it. It then calls */ set_template: function(_value) { if (!this.template) { // Load the template var template = et2_createWidget("template", {"id": _value}, this); if (!template.proxiedTemplate) { et2_debug("error", "Error while loading definition template for" + "nextmatch widget."); return; } // Fetch the grid element and parse it var definitionGrid = template.proxiedTemplate.getChildren()[0]; if (definitionGrid && definitionGrid instanceof et2_grid) { this._parseGrid(definitionGrid); } else { et2_debug("error", "Nextmatch widget expects a grid to be the " + "first child of the defined template."); return; } // Free the template again template.free(); // Call the "setNextmatch" function of all registered // INextmatchHeader widgets. this.iterateOver(function (_node) { _node.setNextmatch(this); }, this, et2_INextmatchHeader); } }, getDOMNode: function(_sender) { if (_sender == this) { return this.div[0]; } for (var i = 0; i < this.columns.length; i++) { if (_sender == this.columns[i].widget) { return this.dataviewContainer.getHeaderContainerNode(i); } } return null; } }); et2_register_widget(et2_nextmatch, ["nextmatch"]); /** * Classes for the nextmatch sortheaders etc. */ var et2_nextmatch_header = et2_baseWidget.extend(et2_INextmatchHeader, { attributes: { "label": { "name": "Caption", "type": "string", "description": "Caption for the nextmatch header", "translate": true } }, init: function() { this._super.apply(this, arguments); this.labelNode = $j(document.createElement("span")); this.nextmatch = null; this.setDOMNode(this.labelNode[0]); }, destroy: function() { this._super.apply(this, arguments); }, /** * Set nextmatch is the function which has to be implemented for the * et2_INextmatchHeader interface. */ setNextmatch: function(_nextmatch) { this.nextmatch = _nextmatch; }, set_label: function(_value) { this.label = _value; this.labelNode.text(_value); } }); et2_register_widget(et2_nextmatch_header, ['nextmatch-header', 'nextmatch-customfilter', 'nextmatch-customfields']); var et2_nextmatch_sortheader = et2_nextmatch_header.extend(et2_INextmatchSortable, { init: function() { this._super.apply(this, arguments); this.sortmode = "none"; this.labelNode.addClass("nextmatch_sortheader none"); }, click: function() { if (this.nextmatch && this._super.apply(this, arguments)) { this.nextmatch.sortBy(this.id); return true; } return false; }, /** * Function which implements the et2_INextmatchSortable function. */ setSortmode: function(_mode) { // Remove the last sortmode class and add the new one this.labelNode.removeClass(this.sortmode) .addClass(_mode); this.sortmode = _mode; } }); et2_register_widget(et2_nextmatch_sortheader, ['nextmatch-sortheader']); var et2_nextmatch_filterheader = et2_selectbox.extend(et2_INextmatchHeader, { /** * Set nextmatch is the function which has to be implemented for the * et2_INextmatchHeader interface. */ setNextmatch: function(_nextmatch) { this.nextmatch = _nextmatch; } }); et2_register_widget(et2_nextmatch_filterheader, ['nextmatch-filterheader', 'nextmatch-accountfilter']);