/** * Created by asantoni on 11/09/15. */ var AIRTIME = (function(AIRTIME) { //Module initialization if (AIRTIME.widgets === undefined) { AIRTIME.widgets = {}; } //Table widget constructor /** * * * @param wrapperDOMNode * @param {boolean} bItemSelection * @param {Object} toolbarButtons * @param {Object} dataTablesOptions * @param {Object} [emptyPlaceholder] * @param {string} emptyPlaceholder.html * @param {string} emptyPlaceholder.iconClass * * @returns {Table} * @constructor */ var Table = function(wrapperDOMNode, bItemSelection, toolbarButtons, dataTablesOptions, emptyPlaceholder) { var self = this; self.HUGE_INT = Math.pow(2, 53) - 1; //Constants and enumerations self.SELECTION_MODE = { SINGLE : 0, MULTI_SHIFT : 1, MULTI_CTRL : 2 }; //Member variables self._datatable = null; self._selectedRows = []; //An array containing the underlying objects for each selected row. (Easy to use!) //self._selectedRowVisualIdxMap = []; //A map of the visual index of a selected rows onto the actual row data. self._selectedRowVisualIdxMin = self.HUGE_INT; self._selectedRowVisualIdxMax = -1; self._$wrapperDOMNode = null; self._toolbarButtons = null; //Save some of the constructor parameters self._$wrapperDOMNode = $(wrapperDOMNode); self._toolbarButtons = toolbarButtons; self._emptyPlaceholder = emptyPlaceholder; // Exclude the leftmost column if we're implementing item selection self._colVisExcludeColumns = bItemSelection ? [0] : []; //Finish initialization of the datatable since everything is declared by now. // If selection is enabled, add in the checkbox column. if (bItemSelection) { dataTablesOptions["aoColumns"].unshift( /* Checkbox */ { "sTitle" : "", "mData" : self._datatablesCheckboxDataDelegate.bind(this), "bSortable" : false , "bSearchable" : false , "sWidth" : "24px" , "sClass" : "airtime_table_checkbox" } ); } var options = { "aoColumns": [ /* Title */ { "sTitle" : $.i18n._("Make sure to override me") , "mDataProp" : "track_title" , "sClass" : "library_title" , "sWidth" : "170px" }, ], "bProcessing": true, "bServerSide": true, "sAjaxSource": baseUrl+"rest/media", //Override me "sAjaxDataProp": "aaData", "bScrollCollapse": false, "deferLoading" : 1, //0 tells it there's zero elements loaded and disables the automatic AJAX. We don't want to load until after we bind all our event handlers, to prevent a race condition with the "init" event callback. "sPaginationType": "full_numbers", "bJQueryUI": true, "bAutoWidth": false, "aaSorting": [], "iDisplayLength": 25, "aLengthMenu": [25, 50, 100], "oLanguage" : getDatatablesStrings({ "sEmptyTable": $.i18n._(""), "sZeroRecords": $.i18n._("No matching results found.") }), "oColVis": { "sAlign": "right", "aiExclude": self._colVisExcludeColumns, "buttonText": $.i18n._("Columns"), "iOverlayFade": 0 }, // z = ColResize, R = ColReorder, C = ColVis "sDom": 'Rf<"dt-process-rel"r><"H"<"table_toolbar"C>><"dataTables_scrolling"t<".empty_placeholder"<".empty_placeholder_image"><".empty_placeholder_text">>><"F"lip>>', "fnPreDrawCallback": function () { $("#draggingContainer").remove(); }, "fnServerData": self._fetchData.bind(self), //"fnInitComplete" : function() { self._setupEventHandlers(bItemSelection) } "fnDrawCallback": function () { self.clearSelection(); } }; //Override any options with those passed in as arguments to this constructor. for (var key in dataTablesOptions) { options[key] = dataTablesOptions[key]; } if (options.fnCreatedRow) { options.fnCreatedRow = options.fnCreatedRow.bind(self); } if (options.fnDrawCallback) { options.fnDrawCallback = options.fnDrawCallback.bind(self); } self._datatable = self._$wrapperDOMNode.dataTable(options); // self._datatable.fnDraw(); //Load the AJAX data now that our event handlers have been bound. self._setupEventHandlers(bItemSelection); //return self._datatable; return self; }; Table.prototype.assignDblClickHandler = function(fn) { $(this._datatable, 'tbody tr').on('dblclick', this._SELECTORS.SELECTION_TABLE_ROW, fn); }; /* Set up global event handlers for the datatable. * @param bItemSelection Whether or not row selection behaviour should be enabled for this widget. * */ Table.prototype._setupEventHandlers = function(bItemSelection) { var self = this; /** This table row event handler is created once and catches events for any row. (It's less resource intensive * than having a per-row callback...) */ if (bItemSelection) { $(self._datatable, 'tbody tr').on('click contextmenu', self._SELECTORS.SELECTION_TABLE_ROW, function (e) { var aData = self._datatable.fnGetData(this); var iDisplayIndex = $(this).index(); // The index of the row in the current page in the table. var nRow = this; e.stopPropagation(); e.preventDefault(); document.getSelection().removeAllRanges(); var selectionMode = self.SELECTION_MODE.SINGLE; if (e.shiftKey) { selectionMode = self.SELECTION_MODE.MULTI_SHIFT; } else if (e.ctrlKey) { selectionMode = self.SELECTION_MODE.MULTI_CTRL; } if (e.button == 2) { selectionMode = self.SELECTION_MODE.SINGLE; } self.selectRow(nRow, aData, selectionMode, iDisplayIndex); }); $(self._datatable, 'tbody tr').on('click', self._SELECTORS.SELECTION_CHECKBOX, function(e) { $this = $(this); var iVisualRowIdx = $this.parent().index(); var aData = self._datatable.fnGetData(iVisualRowIdx); var selectionMode = self.SELECTION_MODE.MULTI_CTRL; //Behaviour for checkboxes. if (e.shiftKey) { selectionMode = self.SELECTION_MODE.MULTI_SHIFT; } self.selectRow($this.parent(), aData, selectionMode, iVisualRowIdx); //Always multiselect for checkboxes e.stopPropagation(); return true; }); // Clear selection when switching pages $(self._datatable).on('page', function () { self.clearSelection(); }); } // On filter, display the number of total and filtered results in the search bar $(self._datatable).on('filter', function() { var dt = self._datatable, f = dt.closest(".dataTables_wrapper").find(".filter-message"), totalRecords = dt.fnSettings().fnRecordsTotal(), totalDisplayRecords = dt.fnSettings().fnRecordsDisplay(); if (f.length === 0) { var el = document.createElement("span"); el.setAttribute("class", "filter-message"); f = dt.closest(".dataTables_wrapper").find(".dataTables_filter").append(el).find(".filter-message"); } f.text(totalRecords > totalDisplayRecords ? $.i18n._("Filtering out ") + (totalRecords - totalDisplayRecords) + $.i18n._(" of ") + totalRecords + $.i18n._(" records") : "" ); dt.closest(".dataTables_wrapper").find('.dataTables_filter input[type="text"]') .css('padding-right', f.outerWidth()); }); //Since this function is already called when the datatables initialization is complete, we know the DOM //structure for the datatable exists and can just proceed to setup the toolbar DOM elements now. self._setupToolbarButtons(self._toolbarButtons); }; /** * Member functions * */ /** Populate the toolbar with buttons. * * @param buttons A list of objects which contain button definitions. See self.TOOLBAR_BUTTON_ROLES for an example, or use getStandardToolbarButtons() to get a list of them. * @private */ Table.prototype._setupToolbarButtons = function(buttons) { var self = this; var $menu = self._$wrapperDOMNode.parent().parent().find("div.table_toolbar"); $menu.addClass("btn-toolbar"); //Create the toolbar buttons. $.each(buttons, function(idx, btn) { var buttonElement = self._createToolbarButton(btn.title, btn.iconClass, btn.extraBtnClass, btn.elementId); $menu.append(buttonElement); btn.element = buttonElement; //Save this guy in case you need it later. //Bind event handlers to each button $.each(btn.eventHandlers, function(eventName, eventCallback) { $(buttonElement).on(eventName, function () { if ($(buttonElement).find("button").is(':disabled')) { return; } eventCallback(); }); }); }); self._checkToolbarButtons(); }; /** * Check each of the toolbar buttons for the table and disable them if their constraints are invalid. * * Passes current Table object context to function calls. */ Table.prototype._checkToolbarButtons = function () { var self = this; $.each(self._toolbarButtons, function (idx, btn) { var btnNode = $(btn.element).find("button").get(0); btnNode.disabled = btn.disabled = !btn.validateConstraints.call(self); }); }; /** Create the DOM element for a toolbar button and return it. */ Table.prototype._createToolbarButton = function(title, iconClass, extraBtnClass, elementId) { if (!iconClass) { iconClass = 'icon-plus'; } // var title = $.i18n._('Delete'); var outerDiv = document.createElement("div"); outerDiv.className = 'btn-group'; outerDiv.title = title; var innerButton = document.createElement("button"); //innerButton.className = 'btn btn-small ' + extraBtnClass; innerButton.className = 'btn ' + extraBtnClass; innerButton.id = elementId; var innerIcon = document.createElement("i"); innerIcon.className = 'icon-white ' + iconClass; var innerTextSpan = document.createElement('span'); var innerText = document.createTextNode(title); innerTextSpan.appendChild(innerText); innerButton.appendChild(innerIcon); innerButton.appendChild(innerTextSpan); outerDiv.appendChild(innerButton); /* Here's an example of what the button HTML should look like: "