';
- this.topTabIndex++;
-
- }
- // if record has children and is first element in row add an icon
- } else if (index === 0 && !!this.datagrid.options.childrenPropertyName) {
- if (row[this.datagrid.options.childrenPropertyName] > 0) {
- $cell = '
' + tblCellContent + '
';
- } else {
- $cell = '
' + tblCellContent + '
';
- }
- $cell = this.sandbox.dom.createElement($cell);
- $innerContainer = this.sandbox.dom.find('.' + constants.childrenIndentClass, $cell);
+ this.table.rows[record.id] = {
+ $el: $row,
+ cells: {},
+ childrenLoaded: false,
+ childrenExpanded: false,
+ parent: !!(record.parent) ? record.parent : null,
+ numberChildren: (!!record[this.datagrid.options.childrenPropertyName]) ? record[this.datagrid.options.childrenPropertyName] : 0,
+ level: 1
+ };
+ this.renderRowSelectItem(record.id);
+ this.renderBodyCellsForRow(record);
+ this.renderRowRemoveItem(record.id);
+ this.insertBodyRow(record, $overrideElement, prepend);
+ this.executeRowPostRenderActions(record);
+ },
+
+ /**
+ * Inserts a body row into the dom. Looks if a row needs to be overriden, or if a parent exists etc.
+ * @param record {Object} the data object of the record
+ * @param prepend {Boolean} true to prepend
+ */
+ insertBodyRow: function(record, $overrideElement, prepend) {
+ var $parentElement = (!!this.table.rows[record.parent]) ? this.table.rows[record.parent].$el : null,
+ insertMethod = (prepend === true) ? this.sandbox.dom.prepend : this.sandbox.dom.append;
+
+ // if there already was a row with the same id, override it with the new one
+ if (!!$overrideElement && !!$overrideElement.length) {
+ this.sandbox.dom.after($overrideElement, this.table.rows[record.id].$el);
+ this.sandbox.dom.remove($overrideElement);
+ // if there is a parent insert it after the parent row
+ } else if (!!$parentElement && !!$parentElement.length) {
+ if (this.renderChildrenHidden === true) {
+ this.sandbox.dom.hide(this.table.rows[record.id].$el);
+ this.changeChildrenToggleIcon(record.parent, false);
} else {
- $cell = '
' + tblCellContent + '
';
+ this.table.rows[record.parent].childrenExpanded = true;
+ this.changeChildrenToggleIcon(record.parent, true);
}
- $cell = this.sandbox.dom.createElement($cell);
-
- // add checkbox to first cell if configured
- if (index === 0 && this.options.selectItem.inFirstCell === true) {
- // dont display a checkbox if only leaves should get a checkbox and record has children
- if (!(this.datagrid.options.onlySelectLeaves === true && row[this.datagrid.options.childrenPropertyName] > 0)) {
- this.sandbox.dom.prepend($innerContainer || $cell, this.sandbox.util.template(templates.checkbox)({
- id: '',
- checked: !!row.selected
- }));
- } else {
- this.sandbox.dom.prepend($innerContainer || $cell, templates.checkboxPlaceholder);
- }
- // double the colspan because of the extra cell in the header
- this.sandbox.dom.prop($cell, 'colspan', 2);
- }
-
- this.addIconsToCell($innerContainer || $cell, key, row);
-
- // push the html string to the global array
- this.tblColumns.push(this.sandbox.dom.outerHTML($cell));
+ this.sandbox.dom.after($parentElement, this.table.rows[record.id].$el);
+ // else just append or prepend it
} else {
- this.tblRowAttributes += ' data-' + key + '="' + value + '"';
+ insertMethod(this.table.$body, this.table.rows[record.id].$el);
}
},
/**
- * Adds configured icons to a cell
- * @param $container {Object} the dom-object to append the icons to
- * @param column {String} the identifier of the column
- * @
+ * Manipulates a row of a rendered after it has been rendered. For examples checks the checkbox or focuses an input
+ * @param record {Object} the data of the record
*/
- addIconsToCell: function($container, column) {
- if (!!this.options.icons) {
- var i, length, $icon;
+ executeRowPostRenderActions: function(record) {
+ if (record.selected === true) {
+ this.toggleSelectRecord(record.id, true);
+ } else {
+ this.toggleSelectAllItem(false);
+ }
+ // select first input if record is new and editable is true
+ if (this.options.editable === true && record.id === constants.newRecordId) {
+ this.showInput(record.id);
+ }
+ },
- for (i = -1, length = this.options.icons.length; ++i < length;) {
- if (column === this.options.icons[i].column) {
- $icon = this.sandbox.dom.createElement(this.sandbox.util.template(templates.icon)({
- icon: this.options.icons[i].icon,
- align: this.options.icons[i].align || 'left'
- }));
- this.sandbox.dom.attr($icon, 'data-icon-index', i);
- this.sandbox.dom.append($container, $icon);
- }
+ /**
+ * Renderes the all the content cells in a body row
+ * @param record {Object} the data for the row
+ */
+ renderBodyCellsForRow: function(record) {
+ // foreach matching grab the corresponding data and render the cell with it
+ this.sandbox.util.foreach(this.datagrid.matchings, function(column, index) {
+ if (this.options.excludeFields.indexOf(column.attribute) === -1) {
+ this.renderBodyCell(record, column, index);
}
- }
+ }.bind(this));
},
/**
- * Handles the click an an icon (calls the defined callback)
- * @param event
+ * Renders the remove item for a row in the tbody
+ * @param id {Number|String} the id of the row to add the select-item for
*/
- callIconCallback: function(event) {
- this.sandbox.dom.stopPropagation(event);
- var index = this.sandbox.dom.data(event.currentTarget, 'iconIndex'),
- recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, 'tr'), 'id');
- // call the callback
- if (!!this.options.icons[index] && typeof this.options.icons[index].callback === 'function') {
- this.options.icons[index].callback(recordId);
+ renderRowRemoveItem: function (id) {
+ if (this.options.removeRow === true) {
+ var $cell = this.sandbox.dom.createElement(templates.cell);
+ this.sandbox.dom.html($cell, this.sandbox.util.template(templates.removeCellContent)({
+ icon: this.options.removeIcon
+ }));
+ this.sandbox.dom.addClass($cell, constants.cellFitClass);
+ this.sandbox.dom.append(this.table.rows[id].$el, $cell);
}
},
/**
- * Adds a row to the datagrid
- * @param row
+ * Renders a single cell
+ * @param record {Object} the record to render the cell for
+ * @param column {Object} the column which should be rendered
+ * @param index {Number} the index of the cell in the row
*/
- addRecord: function(row) {
- var $row, $firstInputField, $checkbox, $parent;
- this.removeEmptyListElement();
- // check for other element types when implemented
- $row = this.sandbox.dom.$(this.prepareTableRow(row, true));
-
- // when unsaved new row exists - save it
- this.prepareSave();
-
- if (!!row.parent) {
- $parent = this.sandbox.dom.find('tr[data-id="' + row.parent + '"]', this.$tableContainer);
+ renderBodyCell: function(record, column, index) {
+ var $cell = this.sandbox.dom.createElement(templates.cell),
+ content = this.getCellContent(record, column),
+ selectItem, isCroppable = false;
+ if (!!this.datagrid.options.childrenPropertyName && index === 0) {
+ content = this.wrapChildrenCellContent(content, record);
+ }
+ if (!!this.options.selectItem && !!this.options.selectItem.inFirstCell === true) {
+ this.sandbox.dom.attr($cell, 'colspan', 2);
+ selectItem = this.renderRowSelectItem(record.id, true);
+ if (typeof content === 'string') {
+ content = selectItem + content;
+ } else {
+ this.sandbox.dom.prepend(content, selectItem);
+ }
}
+ this.sandbox.dom.html($cell, content);
+ this.sandbox.dom.data($cell, 'attribute', column.attribute);
- // if record has a parent insert it after its parent else prepend or append row
- if (!!$parent) {
- this.insertChild($row, $parent, row.parent, false);
- } else if (!!this.options.addRowTop) {
- this.sandbox.dom.prepend(this.$table, $row);
- } else {
- this.sandbox.dom.append(this.$table, $row);
+ if (!!this.sandbox.dom.find('.' + constants.textContainerClass, $cell).length &&
+ this.sandbox.dom.children(this.sandbox.dom.find('.' + constants.textContainerClass, $cell)).length === 0) {
+ isCroppable = true;
}
- $firstInputField = this.sandbox.dom.find('input[type=text]', $row)[0];
- this.sandbox.dom.focus($firstInputField);
+ this.table.rows[record.id].cells[column.attribute] = {
+ $el: $cell,
+ originalData: record[column.attribute],
+ originalContent: this.sandbox.dom.html(content),
+ editable: !!column.editable,
+ croppable: isCroppable
+ };
+ // append cell to corresponding row
+ this.sandbox.dom.append(
+ this.table.rows[record.id].$el,
+ this.table.rows[record.id].cells[column.attribute].$el
+ );
+ },
- if (!!this.options.editable) {
- this.lastFocusedRow = this.getInputValuesOfRow($row);
+ /**
+ * Gets the actual content for a cell
+ * @param record {Object} the record to get the content for
+ * @param column {Object} the column for which the content should be returned
+ * @returns {String|Object} the dom object for the cell content or html
+ */
+ getCellContent: function(record, column) {
+ var content = record[column.attribute];
+ if (!!column.type && column.type === this.datagrid.types.THUMBNAILS) {
+ content = this.datagrid.manipulateContent(content, column.type, this.options.thumbnailFormat);
+ content = this.sandbox.util.template(templates.img)({
+ alt: content[constants.thumbAltKey],
+ src: content[constants.thumbSrcKey]
+ });
+ } else {
+ content = this.datagrid.processContentFilter(
+ column.attribute,
+ content,
+ column.type,
+ Object.keys(this.table.rows).length
+ );
}
-
- // if allchecked then disable top checkbox after adding new row
- if (!!this.options.selectItem.type && this.options.selectItem.type === 'checkbox') {
- $checkbox = this.sandbox.dom.find('#' + constants.selectAllName, this.$tableContainer);
- if (this.sandbox.dom.hasClass($checkbox, constants.isSelectedClass)) {
- this.sandbox.dom.prop($checkbox, 'checked', false);
- this.sandbox.dom.removeClass($checkbox, constants.isSelectedClass);
- }
+ if (this.options.editable === true && column.editable === true) {
+ content = this.getEditableCellContent(content);
+ } else {
+ content = this.sandbox.util.template(templates.textContainer)({
+ content: content
+ });
+ }
+ if (!!this.options.icons) {
+ content = this.addIconsToCellContent(content, column);
}
+ return content;
},
/**
- * Inserts a child-element after a parent element and indents it
- * @param $child
- * @param $parent
- * @param parentId {Number|String} the id of the parent
- * @param hidden {Boolean} if true child gets hidden
+ * Wraps content into an additional container an indent and a toggler icon (for children and parents)
+ * @param content {String|Object} the content to wrap. html or dom object
+ * @param record {Object} the record data object
*/
- insertChild: function ($child, $parent, parentId, hidden) {
- var $parentIcon, depth = this.sandbox.dom.data($parent, 'depth') || 0;
- depth = parseInt(depth, 10);
- $parentIcon = this.sandbox.dom.find('.' + constants.slideDownClass + ' .toggle-icon', $parent);
-
- // make sure parent has children-loaded class
- this.sandbox.dom.addClass($parent, constants.childrenLoadedClass);
-
- this.sandbox.dom.attr($child, 'data-parent', parentId);
- this.sandbox.dom.data($child, 'depth', depth + 1);
-
- // indent the children-rows
- this.sandbox.dom.css(this.sandbox.dom.find('.' + constants.childrenIndentClass, $child), {
- 'margin-left': (constants.childrenIndentPx * (depth + 1)) + 'px'
- });
-
- if (hidden === true) {
- // change the icon of the parent
- this.sandbox.dom.removeClass($parentIcon, constants.childrenSlideUpIcon);
- this.sandbox.dom.prependClass($parentIcon, constants.childrenSlideDownIcon);
- this.sandbox.dom.hide($child);
+ wrapChildrenCellContent: function(content, record) {
+ var $wrappedContent = this.sandbox.dom.createElement(templates.childWrapper),
+ $icon;
+ // if has children
+ if (!!record[this.datagrid.options.childrenPropertyName]
+ && record[this.datagrid.options.childrenPropertyName] > 0) {
+ this.sandbox.dom.addClass($wrappedContent, constants.parentClass);
+ $icon = this.sandbox.dom.createElement(templates.toggleIcon);
+ this.sandbox.dom.prependClass($icon, constants.collapsedIcon);
+ this.sandbox.dom.prepend($wrappedContent, $icon);
} else {
- // change the icon of the parent
- this.sandbox.dom.removeClass($parentIcon, constants.childrenSlideDownIcon);
- this.sandbox.dom.prependClass($parentIcon, constants.childrenSlideUpIcon);
+ this.sandbox.dom.addClass($wrappedContent, constants.noChildrenClass);
+ }
+ // if has parent
+ if (!!record.parent) {
+ this.table.rows[record.id].level = this.table.rows[record.parent].level + 1;
+ // give that child an indent, children love indents
+ this.sandbox.dom.css($wrappedContent, {
+ 'padding-left': constants.childIndent * (this.table.rows[record.id].level - 1) + 'px'
+ });
}
-
- this.sandbox.dom.after($parent, $child);
+ this.sandbox.dom.append($wrappedContent, content);
+ return $wrappedContent;
},
/**
- * Perparse to save new/changed data includes validation
+ * Takes a string and retruns the markup for an editable cell
+ * @param content {String} the original value
+ * @returns {String|Object} html or a dom object
*/
- prepareSave: function() {
- if (!!this.lastFocusedRow) {
-
- var $tr = this.sandbox.dom.find('tr[data-dom-id=' + this.lastFocusedRow.domId + ']', this.$tableContainer),
- lastFocusedRowCurrentData = this.getInputValuesOfRow($tr),
-
- data = {},
- key,
- url,
- isValid = true,
- valuesChanged = false,
- isDataEmpty;
-
- data.id = lastFocusedRowCurrentData.id;
-
- // validate locally
- if (!!this.options.validation && !this.sandbox.form.validate('#' + this.datagrid.elId)) {
- isValid = false;
- }
-
- isDataEmpty = this.isDataRowEmpty(lastFocusedRowCurrentData.fields);
-
- // do nothing when data is not valid or no data exists
- if (!!isValid && !isDataEmpty) {
-
- // check which values changed and remember these
- for (key in lastFocusedRowCurrentData.fields) {
- if (this.lastFocusedRow.fields.hasOwnProperty(key) && this.lastFocusedRow.fields[key] !== lastFocusedRowCurrentData.fields[key]) {
- data[key] = lastFocusedRowCurrentData.fields[key];
- valuesChanged = true;
- }
- }
-
- // trigger save action when data changed
- if (!!valuesChanged || true) {
- url = this.datagrid.getUrlWithoutParams();
-
- // pass data to datagrid to save it
- this.datagrid.saveGrid.call(this.datagrid, data, url,
- this.saveSuccess.bind(this, this.lastFocusedRow.domId, $tr),
- this.saveFail.bind(this, this.lastFocusedRow.domId, $tr),
- this.options.addRowTop);
-
- // reset last focused row after save
- this.lastFocusedRow = null;
-
- } else if (this.errorInRow.indexOf(this.lastFocusedRow.domId) !== -1) {
- this.sandbox.logger.log("Error in table row!");
+ getEditableCellContent: function(content) {
+ var returnHTML = this.sandbox.util.template(templates.editableCellContent)({
+ value: content
+ });
+ return returnHTML;
+ },
- } else {
- // nothing changed - reset immediately
- this.sandbox.logger.log("No data changed!");
- this.resetRowInputFields($tr);
- this.unlockWidthsOfColumns(this.sandbox.dom.find('table th', this.$el));
+ /**
+ * Adds icons to a cell content
+ * @param content {String|Object} html or a dom object. If its a string icons get added to the string, if its an object it gets appended
+ * @param column {Object} the column data object
+ * @returns content {String|Object} html or a dom object
+ */
+ addIconsToCellContent: function(content, column) {
+ var iconStr;
+ this.sandbox.util.foreach(this.options.icons, function(icon, index) {
+ if (icon.column === column.attribute) {
+ iconStr = this.sandbox.util.template(templates.icon)({
+ icon: icon.icon,
+ align: icon.align,
+ index: index
+ });
+ if (typeof content === 'object') {
+ this.sandbox.dom.append(content, iconStr);
+ } else if (typeof content === 'string') {
+ content += iconStr;
}
-
- } else {
- this.sandbox.logger.log("There seems to be some invalid or empty data!");
}
-
- }
+ }.bind(this));
+ return content;
},
/**
- * Checks wether data is in row or not
- * @param data fields object
+ * Renders the empty list element
*/
- isDataRowEmpty: function(data) {
- var isEmpty = true, field;
-
- for (field in data) {
- if (data[field] !== '') {
- isEmpty = false;
- break;
- }
- }
-
- return isEmpty;
+ renderEmptyIndicator: function() {
+ this.sandbox.dom.append(this.$el, this.sandbox.util.template(templates.empty)({
+ text: this.sandbox.translate(this.options.noItemsText)
+ }));
},
/**
- * Callback for save success
- * @param $tr
- * @param domId
- * @param data
+ * Removes the "new" record row
*/
- saveSuccess: function(domId, $tr, data) {
- // remove row from error list
- if (this.errorInRow.indexOf(domId) !== -1) {
- this.errorInRow.splice(this.errorInRow.indexOf(domId), 1);
+ removeNewRecordRow: function() {
+ if (!!this.table.rows[constants.newRecordId]) {
+ this.preventFocusoutHandler = true;
+ this.sandbox.dom.remove(this.table.rows[constants.newRecordId].$el);
+ delete this.table.rows[constants.newRecordId];
}
+ },
- this.resetRowInputFields($tr);
- this.unlockWidthsOfColumns(this.sandbox.dom.find('table th', this.$el));
+ /**
+ * Render methods (end)
+ * -------------------------------------------------------------------- */
- // set new returned data
- this.setDataForRow($tr[0], data);
+ /**
+ * Returns true or false whether the container is overflown or not
+ * @returns {boolean}
+ */
+ containerIsOverflown: function() {
+ return this.sandbox.dom.get(this.table.$container, 0).scrollWidth > this.sandbox.dom.width(this.table.$container);
},
/**
- * Callback for save fail
- * @param domId
- * @param $tr
- * @param jqXHR
+ * Gets executed when the table width is bigger than its container width
*/
- saveFail: function(domId, $tr, jqXHR) {
- var message = JSON.parse(jqXHR.responseText);
-
- // remember row with error
- if (this.errorInRow.indexOf(domId) === -1) {
- this.errorInRow.push(domId);
+ overflowHandler: function() {
+ this.toggleCropTable(true);
+ // if still overflown add a css class
+ if (this.containerIsOverflown() === true) {
+ this.sandbox.dom.addClass(this.$el, constants.overflowClass);
}
- // error in context with database constraints
- if (!!message.field) {
- this.showValidationError($tr, message.field);
+ if (this.options.stickyHeader === true) {
+ this.sandbox.dom.width(this.table.header.$el, this.sandbox.dom.width(this.table.$container));
}
},
/**
- * Hides input fields and displays new content for table row
- * @param $tr
+ * Gets executed when the table width is not bigger than its container width
*/
- resetRowInputFields: function($tr) {
- var $inputFields = this.sandbox.dom.find('input[type=text]:not(.hidden)', $tr),
- content, $span;
-
- this.sandbox.util.each($inputFields, function(index, $field) {
-
- // remove css class for server side validation error
- // TODO: remove this when server-validation validation type is implemented
- if (this.sandbox.dom.hasClass($field, constants.serverValidationError)) {
- this.sandbox.dom.removeClass($field, constants.serverValidationError);
+ underflowHandler: function() {
+ if (this.tableCropped === true) {
+ if (this.sandbox.dom.width(this.table.$container) > this.cropBreakPoint) {
+ this.toggleCropTable(false);
}
-
- content = this.sandbox.dom.val(this.sandbox.dom.$($field));
- $span = this.sandbox.dom.prev($field, '.' + constants.editableClass);
- $span.text(content);
-
- this.sandbox.dom.addClass($field, 'hidden');
- this.sandbox.dom.show($span);
-
- }.bind(this));
+ if (this.containerIsOverflown() === false) {
+ this.sandbox.dom.removeClass(this.$el, constants.overflowClass);
+ }
+ if (this.options.stickyHeader === true) {
+ this.sandbox.dom.width(this.table.header.$el, '');
+ }
+ }
},
/**
- * Resets the min-width of columns and
- * @param $th array of th elements
+ * Crops or uncropps all croppable cells in the table
*/
- unlockWidthsOfColumns: function($th) {
- if (!!this.columnWidths) {
- this.sandbox.util.each($th, function(index, $el) {
- // skip columns without data-attribute because the have min/max and normal widths by default
- if (!!this.sandbox.dom.data($el, 'attribute')) {
- this.sandbox.dom.css($el, 'min-width', this.columnWidths[index]);
- this.sandbox.dom.css($el, 'max-width', '');
- this.sandbox.dom.css($el, 'width', '');
- }
+ toggleCropTable: function(crop) {
+ if (this.tableCropped !== crop) {
+ var $contentContainer,
+ content;
+ this.sandbox.util.each(this.table.rows, function(rowId, row) {
+ this.sandbox.util.each(row.cells, function(cellId, cell) {
+ if (cell.croppable === true) {
+ $contentContainer = this.sandbox.dom.find('.' + constants.textContainerClass, cell.$el);
+ if (crop === true) {
+ content = this.sandbox.util.cropMiddle(cell.originalContent, this.options.croppedMaxLength);
+ this.sandbox.dom.attr($contentContainer, 'title', cell.originalContent);
+ this.tableCropped = true;
+ this.cropBreakPoint = this.sandbox.dom.width(this.table.$container);
+ } else {
+ content = cell.originalContent;
+ this.sandbox.dom.removeAttr($contentContainer, 'title');
+ this.tableCropped = false;
+ }
+ this.sandbox.dom.html($contentContainer, content);
+ }
+ }.bind(this));
}.bind(this));
}
},
/**
- * Makes the widths of columns fixed when the table is in edit mode
- * prevents changes in column width
- * @param $th array of th elements
+ * Bindes dom related events
*/
- lockWidthsOfColumns: function($th) {
- var width, minwidth;
- this.columnWidths = [];
-
- this.sandbox.dom.each($th, function(index, $el) {
- minwidth = this.sandbox.dom.css($el, 'min-width');
- this.columnWidths.push(minwidth);
-
- width = this.sandbox.dom.outerWidth($el);
- this.sandbox.dom.css($el, 'min-width', width);
- this.sandbox.dom.css($el, 'max-width', width);
- this.sandbox.dom.css($el, 'width', width);
- }.bind(this));
+ bindDomEvents: function () {
+ // select or deselect items if the body recognizes a change event
+ this.sandbox.dom.on(
+ this.table.$body, 'click', this.selectItemChangeHandler.bind(this),
+ '.' + constants.checkboxClass + ', .' + constants.radioClass
+ );
+ // handle click on body row
+ this.sandbox.dom.on(this.table.$body, 'click', this.bodyRowClickHandler.bind(this), '.' + constants.rowClass);
+ // remove row event
+ if (this.options.removeRow === true) {
+ this.sandbox.dom.on(this.table.$body, 'click', this.removeItemClickHandler.bind(this), '.' + constants.rowRemoverClass);
+ }
+ if (!!this.table.header) {
+ // select all
+ this.sandbox.dom.on(this.table.header.$el, 'change', this.allSelectItemChangeHandler.bind(this));
+
+ // click on sortable item
+ if (this.datagrid.options.sortable === true) {
+ this.sandbox.dom.on(
+ this.table.header.$el, 'click', this.sortItemClickHandler.bind(this),
+ '.' + constants.headerCellClass + '.' + constants.sortableClass
+ );
+ }
+ }
+ // click on editable item
+ if (this.options.editable === true) {
+ this.sandbox.dom.on(this.table.$body, 'click', this.editableItemClickHandler.bind(this), '.' + constants.editableItemClass);
+ this.sandbox.dom.on(this.table.$body, 'focusout', this.editableInputFocusoutHandler.bind(this), '.' + constants.editableInputClass);
+ this.sandbox.dom.on(this.table.$body, 'keypress', this.editableInputKeyHandler.bind(this), '.' + constants.editableInputClass);
+ }
+ if (!!this.options.icons) {
+ this.sandbox.dom.on(this.table.$body, 'click', this.iconClickHandler.bind(this), '.' + constants.gridIconClass);
+ }
+ this.sandbox.dom.on(this.table.$container, 'scroll', this.containerScrollHandler.bind(this));
+ this.sandbox.dom.on(this.table.$body, 'click', this.radioButtonClickHandler.bind(this), 'input[type="radio"]');
},
/**
- * Sets the data for a row
- * @param $tr dom row
- * @param data
+ * Gets the width of the cells in the header clone and applies them to the actual header
*/
- setDataForRow: function($tr, data) {
- var editables, field, $input;
-
- // set id
- this.sandbox.dom.data($tr, 'id', data.id);
- this.sandbox.dom.attr($tr, 'data-id', data.id);
-
- this.sandbox.util.each(this.sandbox.dom.find('td', $tr), function(index, $el) {
-
- editables = this.sandbox.dom.find('.' + constants.editableClass, $el);
- field = this.sandbox.dom.data($el, 'field');
- $input = this.sandbox.dom.find('input[type=text]', $el);
-
- if (!!field) {
- if (!!editables && editables.length === 1) { // set values in spans
- this.sandbox.dom.html(editables[0], data[field]);
- this.sandbox.dom.val($input, data[field]);
- } else { // set values in td
- this.sandbox.dom.html($el, data[field]);
- }
- }
-
- }.bind(this));
+ setHeaderCellWidthFromClone: function() {
+ if (!!this.table.header && !!this.table.header.$clone.length) {
+ var cloneThs = this.sandbox.dom.find('th', this.table.header.$clone),
+ originalThs = this.sandbox.dom.find('th', this.table.header.$el);
+ this.sandbox.dom.width(this.table.header.$row, this.sandbox.dom.width(
+ this.sandbox.dom.find('tr', this.table.header.$clone)
+ ));
+ this.sandbox.util.foreach(cloneThs, function(cloneTh, index) {
+ // min- and max-width because for table-cells normal width has no effect
+ this.sandbox.dom.css(originalThs[index], {
+ 'min-width': this.sandbox.dom.outerWidth(cloneTh) + 'px',
+ 'max-width': this.sandbox.dom.outerWidth(cloneTh) + 'px'
+ });
+ }.bind(this));
+ }
},
/**
- * Sets the validation error class for a dom element
- * @param $domElement
- * @param field name of field which caused the error
+ * Sets the max-height of the container to the maximum available space
*/
- showValidationError: function($domElement, field) {
- var $td = this.sandbox.dom.find('td[data-field=' + field + ']', $domElement)[0],
- $input = this.sandbox.dom.find('input', $td)[0];
+ fixContainerMaxHeight: function() {
+ this.sandbox.dom.css(this.table.$container, 'max-height', this.datagrid.getRemainingViewHeight.call(this.datagrid));
+ },
- if (this.sandbox.dom.hasClass($td, 'husky-validate-success')) {
- this.sandbox.dom.removeClass($td, 'husky-validate-success');
+ /**
+ * Gets executed when the table-container gets scrolled
+ */
+ containerScrollHandler: function() {
+ if (this.options.stickyHeader === true) {
+ this.sandbox.dom.scrollLeft(this.table.header.$el, this.sandbox.dom.scrollLeft(this.table.$container));
}
+ },
- this.sandbox.dom.addClass($td, 'husky-validate-error');
+ /**
+ * Gets executed if a radio button in the table body gets clicked
+ * @param event
+ */
+ radioButtonClickHandler: function(event) {
+ this.sandbox.dom.stopPropagation(event);
+ var recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, '.' + constants.rowClass), 'id'),
+ attribute = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, 'td'), 'attribute');
+ this.sandbox.emit(RADIO_SELECTED.call(this), recordId, attribute);
+ },
- // add class for serverside validation error
- // TODO remove this when correct validation type is implmented
- if (!this.sandbox.dom.hasClass($input, constants.serverValidationError)) {
- this.sandbox.dom.addClass($input, constants.serverValidationError);
+ /**
+ * Handles the click on grid-icons
+ * @param event {Object} the event object
+ */
+ iconClickHandler: function(event) {
+ var icon = this.options.icons[this.sandbox.dom.data(event.currentTarget, 'icon-index')],
+ recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, '.' + constants.rowClass), 'id');
+ if (typeof recordId !== 'undefined' && !!icon && typeof icon.callback === 'function') {
+ icon.callback(recordId);
}
},
/**
- * Returns an object with the current values of the inputfields, id and domId
- * @param $tr table row
- * @returns {{domId: *, id: *, fields: Array}}
+ * Handles the click on a sortable item
+ * @param event {Object} the event object
*/
- getInputValuesOfRow: function($tr) {
- var id = this.sandbox.dom.data($tr, 'id'),
- domId = this.sandbox.dom.data($tr, 'dom-id'),
- $inputs = this.sandbox.dom.find('input[type=text]', $tr),
- fields = [], field, $td;
+ sortItemClickHandler: function(event) {
+ this.sandbox.dom.stopPropagation(event);
+ var attribute = this.sandbox.dom.data(event.currentTarget, 'attribute'),
+ direction = 'asc';
+ if (this.datagrid.sort.attribute === attribute && this.datagrid.sort.direction === direction) {
+ direction = 'desc';
+ }
+ this.startHeaderCellLoader(attribute);
+ // delegate sorting to datagrid
+ this.datagrid.sortGrid.call(this.datagrid, attribute, direction);
+ },
- this.sandbox.dom.each($inputs, function(index, $input) {
- $td = this.sandbox.dom.parent($input, 'td');
- field = this.sandbox.dom.data($td, 'field');
+ /**
+ * Handles the click on an editable item
+ * @param event {Object} the event object
+ */
+ editableItemClickHandler: function(event) {
+ this.sandbox.dom.stopPropagation(event);
+ var recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, '.' + constants.rowClass), 'id'),
+ attribute = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, 'td'), 'attribute');
+ this.showInput(recordId, attribute);
+ },
- fields[field] = $input.value;
+ /**
+ * Shows a edit-input for a row and an attribute if no attribute passed shows the first input in row
+ * @param recordId {Number|String}
+ * @param attribute {String}
+ */
+ showInput: function(recordId, attribute) {
+ var $cell, $inputs;
+ if (!attribute) {
+ $inputs = this.sandbox.dom.find('.' + constants.editableInputClass, this.table.rows[recordId].$el);
+ attribute = this.sandbox.dom.data(this.sandbox.dom.parents($inputs[0], 'td'), 'attribute');
+ }
+ $cell = this.table.rows[recordId].cells[attribute].$el
+ this.sandbox.dom.show(this.sandbox.dom.find('.' + constants.inputWrapperClass, $cell));
+ this.sandbox.dom.focus(this.sandbox.dom.find('.' + constants.editableInputClass, $cell));
+ this.sandbox.dom.select(this.sandbox.dom.find('.' + constants.editableInputClass, $cell));
+ },
- }.bind(this));
- return {
- domId: domId,
- id: id,
- fields: fields
- };
+ /**
+ * Handles keypress events of the editable inputs
+ * @param event {Object} the event object
+ */
+ editableInputKeyHandler: function(event) {
+ var recordId;
+ // on enter
+ if (event.keyCode === 13) {
+ this.sandbox.dom.stopPropagation(event);
+ recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, '.' + constants.rowClass), 'id');
+ this.preventFocusoutHandler = true;
+ this.editRow(recordId);
+ }
+ },
+ /**
+ * Handles the focusout of an editable input
+ * @param event {Object} the event object
+ */
+ editableInputFocusoutHandler: function (event) {
+ if (this.preventFocusoutHandler === false) {
+ this.sandbox.dom.stopPropagation(event);
+ var recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, '.' + constants.rowClass), 'id');
+ this.editRow(recordId);
+ }
},
/**
- * Prepares for removing a row
- * Raises the husky.datagrid.record.remove-click event when auto remove handling is not set to true
- * @param event
+ * Gets the data of all edit-inputs in a row and saves their values
+ * @param recordId {String|Number} the id of the row to edit
*/
- prepareRemoveRow: function(event) {
- this.sandbox.dom.stopPropagation(event);
- this.removeRecord(event);
+ editRow: function(recordId) {
+ var modifiedRecord = {};
+ // build new record object out of the inputs in the row
+ this.sandbox.util.each(this.table.rows[recordId].cells, function (attribute, cell) {
+ if (!!this.sandbox.dom.find('.' + constants.editableInputClass, cell.$el).length) {
+ modifiedRecord[attribute] = this.sandbox.dom.val(
+ this.sandbox.dom.find('.' + constants.editableInputClass, cell.$el)
+ );
+ }
+ }.bind(this));
+ this.saveRow(recordId, modifiedRecord, this.editedSuccessCallback.bind(this), this.editedErrorCallback.bind(this, recordId));
},
/**
- * Removes a row from the datagrid
- * @param event
+ * Clears everything up after a row was edited. (hides the input and updates the values)
+ * @param record {Object} the changed data record
*/
- removeRecord: function(event) {
- var $element, $tblRow, id, $editableElements, $checkboxes;
-
- if (typeof event === 'object') {
- $element = this.sandbox.dom.$(event.currentTarget);
- $tblRow = this.sandbox.dom.closest($element, 'tr')[0];
- id = this.sandbox.dom.data($tblRow, 'id');
- } else {
- id = event;
- $tblRow = this.sandbox.dom.find('tr[data-id="' + id + '"]')[0];
- }
-
- // remove row elements from validation
- if (!!this.options.validation) {
- $editableElements = this.sandbox.dom.find('.' + constants.editableClass, $tblRow);
- this.sandbox.util.each($editableElements, function(index, $element) {
- this.sandbox.form.removeField('#' + this.datagrid.elId, $element);
- }.bind(this));
- }
-
- if (!!id) {
- this.datagrid.removeRecord.call(this.datagrid, id);
+ editedSuccessCallback: function (record) {
+ var $row;
+ if (!!record.id && !!this.table.rows[record.id]) {
+ $row = this.table.rows[record.id].$el
+ } else if (!!this.table.rows[constants.newRecordId]) {
+ $row = this.table.rows[constants.newRecordId].$el
}
- this.sandbox.dom.remove($tblRow);
-
- // when last table row was removed, uncheck thead checkbox if exists
- $checkboxes = this.sandbox.dom.find('input[type=checkbox]', this.$el);
- if ($checkboxes.length === 1) {
- this.sandbox.dom.removeClass(constants.isSelectedClass, $checkboxes[0]);
- this.sandbox.dom.prop($checkboxes[0], 'checked', false);
+ if (!!$row && !!$row.length) {
+ this.sandbox.dom.hide(this.sandbox.dom.find('.' + constants.inputWrapperClass, $row));
+ this.renderBodyRow(record, this.options.addRowTop);
}
-
},
/**
- * Gets automatically executed on window resize
- * responsible for the responsiveness
+ * Adds a css class to all inputs in a row, if the editing request returned with an error
+ * @param recordId
*/
- onResize: function() {
- var finalWidth,
- contentPaddings = 0,
- tableWidth = this.sandbox.dom.width(this.$table),
- tableOffset = this.sandbox.dom.offset(this.$table),
- contentWidth = this.sandbox.dom.width(content),
- windowWidth = this.sandbox.dom.width(this.sandbox.dom.window),
- overlaps = false,
- originalMaxWidth = contentWidth;
-
- tableOffset.right = tableOffset.left + tableWidth;
-
- // if table is greater than max content width
- if (tableWidth > originalMaxWidth - contentPaddings && contentWidth < windowWidth - tableOffset.left) {
- // adding this class, forces table cells to shorten long words
- this.sandbox.dom.addClass(this.$el, constants.oversizedClass);
- overlaps = true;
- // reset table width and offset
- tableWidth = this.sandbox.dom.width(this.$table);
- tableOffset.right = tableOffset.left + tableWidth;
- }
-
- // tablecontainer should have width of table in normal cases
- finalWidth = tableWidth;
-
- // if table > window-size set width to available space
- if (tableOffset.right + this.contentMarginRight > windowWidth) {
- finalWidth = windowWidth - tableOffset.left;
- } else {
- // set scroll position back
- this.sandbox.dom.scrollLeft(this.$el, 0);
- }
-
- // width is not allowed to be smaller than the width of content
- if (finalWidth < contentWidth) {
- finalWidth = contentWidth;
- }
-
- // now set width
- this.sandbox.dom.width(this.$el, finalWidth);
-
- // check scrollwidth and add class
- if (this.sandbox.dom.get(this.$tableContainer, 0).scrollWidth > finalWidth) {
- this.sandbox.dom.addClass(this.$tableContainer, constants.overflowClass);
+ editedErrorCallback: function (recordId) {
+ var $row = this.table.rows[recordId].$el;
+ this.sandbox.dom.addClass(
+ this.sandbox.dom.find('.' + constants.inputWrapperClass, $row),
+ constants.editedErrorClass
+ );
+ },
- // if overflown and in full width mode reduce list-width
- if (this.options.fullWidth === true) {
- finalWidth = finalWidth - constants.overflowIconSpacing;
- this.sandbox.dom.width(this.$el, finalWidth);
+ /**
+ * Replaces a record with an existing one and sends it to a server
+ * @param recordId {Number|String} the id of the record to override
+ * @param newRecordData {Object} the new record data
+ * @param successCallback {Function} gets executed after success
+ * @param errorCallback {Function} gets executed after error
+ */
+ saveRow: function (recordId, newRecordData, successCallback, errorCallback) {
+ var hasChanged = false,
+ record;
+ this.sandbox.util.each(this.table.rows[recordId].cells, function (attribute, cell) {
+ if (cell.editable === true && cell.originalData !== newRecordData[attribute]) {
+ hasChanged = true;
}
-
+ }.bind(this));
+ // merge record data
+ record = this.sandbox.util.extend(
+ true, {}, this.data.embedded[this.datagrid.getRecordIndexById(recordId)], newRecordData
+ );
+ if (recordId === constants.newRecordId) {
+ delete record.id;
+ }
+ if (hasChanged === true) {
+ // pass data to datagrid to save it
+ this.datagrid.saveGrid.call(this.datagrid,
+ record, this.datagrid.getUrlWithoutParams.call(this.datagrid),
+ successCallback, errorCallback, this.options.addRowTop);
} else {
- this.sandbox.dom.removeClass(this.$tableContainer, constants.overflowClass);
+ typeof successCallback === 'function' && successCallback(record);
}
},
/**
- * Selectes or deselects the clicked item
- * @param event
+ * Starts a loader in a cell in the header
+ * @param column {String} the attribute of the header cell to insert the loader in
*/
- selectItem: function(event) {
- this.sandbox.dom.stopPropagation(event);
-
- var $element, itemId, parentTr;
- $element = this.sandbox.dom.$(event.currentTarget);
-
- if (!this.sandbox.dom.is($element, 'input')) {
- $element = this.sandbox.dom.find('input', this.sandbox.dom.parent($element));
- }
-
- parentTr = this.sandbox.dom.parents($element, 'tr');
- itemId = this.sandbox.dom.data(parentTr, 'id');
-
- if (this.sandbox.dom.attr($element, 'type') === 'checkbox') {
- if (this.sandbox.dom.prop($element, 'checked') === false) {
- this.sandbox.dom.removeClass($element, constants.isSelectedClass);
- this.sandbox.dom.prop($element, 'checked', false);
-
- // uncheck 'Select All'-checkbox
- this.sandbox.dom.prop(
- this.sandbox.dom.find('#' + constants.selectAllName, this.$tableContainer),
- 'checked', false
- );
-
- this.datagrid.setItemUnselected.call(this.datagrid, itemId);
- } else {
- this.sandbox.dom.addClass($element, constants.isSelectedClass);
- this.sandbox.dom.prop($element, 'checked', true);
- if (!!itemId) {
- this.datagrid.setItemSelected.call(this.datagrid, itemId);
+ startHeaderCellLoader: function(column) {
+ var $container = this.sandbox.dom.createElement(templates.headerCellLoader);
+ this.sandbox.dom.addClass(this.table.header.cells[column].$el, constants.headerLoadingClass);
+ this.sandbox.dom.append(this.sandbox.dom.find('.' + constants.textContainerClass, this.table.header.cells[column].$el), $container);
+ this.sandbox.start([
+ {
+ name: 'loader@husky',
+ options: {
+ el: $container,
+ size: '10px',
+ color: '#999999'
}
}
+ ]);
+ },
- } else if (this.sandbox.dom.attr($element, 'type') === 'radio') {
-
- this.datagrid.deselectAllItems.call(this.datagrid);
-
- this.sandbox.dom.removeClass(
- this.sandbox.dom.find('tr input[type="radio"]', this.$tableContainer), constants.isSelectedClass);
- this.sandbox.dom.prop(
- this.sandbox.dom.find('tr input[type="radio"]', this.$tableContainer), 'checked', false);
-
- this.sandbox.dom.addClass($element, constants.isSelectedClass);
- this.sandbox.dom.prop($element, 'checked', true);
-
- if (!!itemId) {
- this.datagrid.setItemSelected.call(this.datagrid, itemId);
+ /**
+ * Handles the click on a body row
+ * @param event {Object} the event object
+ */
+ bodyRowClickHandler: function (event) {
+ this.sandbox.dom.stopPropagation(event);
+ var recordId = this.sandbox.dom.data(event.currentTarget, 'id');
+ this.emitRowClickedEvent(event);
+ if (!!this.table.rows[recordId]) {
+ if (this.options.highlightSelected === true) {
+ this.uniqueHighlightRecord(recordId);
+ }
+ if (!!this.table.rows[recordId].numberChildren > 0) {
+ this.toggleChildren(recordId);
}
}
-
},
/**
- * Shows input and hides span
- * @param event
+ * Emits the row clicked event
+ * @param event {Object} the original click event
*/
- editCellValues: function(event) {
- var $target = event.currentTarget,
- $input = this.sandbox.dom.next($target, 'input');
-
- this.lockWidthsOfColumns(this.sandbox.dom.find('table th', this.$tableContainer));
-
- this.sandbox.dom.hide($target);
- this.sandbox.dom.removeClass($input, 'hidden');
- this.sandbox.dom.select($input[0]);
+ emitRowClickedEvent: function (event) {
+ if (this.rowClicked === false) {
+ this.rowClicked = true;
+ var recordId = this.sandbox.dom.data(event.currentTarget, 'id'),
+ parameter = recordId || event;
+ this.datagrid.emitItemClickedEvent.call(this.datagrid, parameter);
+ // delay to prevent multiple emits on double click
+ this.sandbox.util.delay(function () {
+ this.rowClicked = false;
+ }.bind(this), 500);
+ }
},
/**
- * Put focus on table row and remember values
+ * Handles the click on the remove item
+ * @param event {Object} the event object
*/
- focusOnRow: function(event) {
- var $tr = event.currentTarget,
- domId = this.sandbox.dom.data($tr, 'dom-id');
-
+ removeItemClickHandler: function(event) {
this.sandbox.dom.stopPropagation(event);
- this.sandbox.logger.log("focus on row", domId);
+ var recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.currentTarget, '.' + constants.rowClass), 'id');
+ this.removeRecord(recordId);
+ },
- if (!!this.lastFocusedRow && this.lastFocusedRow.domId !== domId) { // new focus
- this.prepareSave();
- this.lastFocusedRow = this.getInputValuesOfRow($tr);
- this.sandbox.logger.log("focused " + this.lastFocusedRow.domId + " now!");
- } else if (!this.lastFocusedRow) { // first focus
- this.lastFocusedRow = this.getInputValuesOfRow($tr);
- this.sandbox.logger.log("focused " + this.lastFocusedRow.domId + " now!");
+ /**
+ * Handles the change event of the select items
+ * @param event {Object} the event object
+ */
+ selectItemChangeHandler: function (event) {
+ this.sandbox.dom.stopPropagation(event);
+ var recordId = this.sandbox.dom.data(this.sandbox.dom.parents(event.target, '.' + constants.rowClass), 'id'),
+ isChecked = this.sandbox.dom.is(event.target, ':checked');
+ if (this.options.selectItem.type === selectItems.CHECKBOX) {
+ this.toggleSelectRecord(recordId, isChecked);
+ } else if (this.options.selectItem.type === selectItems.RADIO) {
+ this.uniqueSelectRecord(recordId);
}
},
/**
- * Selects or deselect all available items of the list
- * @param event
+ * Handles the change event of a select item in the header
+ * @param event {Object} the event object
*/
- selectAllItems: function(event) {
+ allSelectItemChangeHandler: function (event) {
this.sandbox.dom.stopPropagation(event);
-
- var $headCheckbox = this.sandbox.dom.find('th input[type="checkbox"]', this.$tableContainer)[0],
- $checkboxes = this.sandbox.dom.find('input[type="checkbox"]:visible', this.$tableContainer);
-
- if (this.sandbox.dom.prop($headCheckbox, 'checked') === false) {
- this.sandbox.dom.prop($checkboxes, 'checked', false);
- this.sandbox.dom.removeClass($checkboxes, constants.isSelectedClass);
- this.datagrid.deselectAllItems.call(this.datagrid);
+ var isChecked = this.sandbox.dom.is(event.target, ':checked');
+ if (isChecked === true) {
+ this.selectAllRecords();
} else {
- this.sandbox.dom.prop($checkboxes, 'checked', true);
- this.sandbox.dom.addClass($checkboxes, constants.isSelectedClass);
- this.datagrid.selectAllItems.call(this.datagrid);
+ this.deselectAllRecords();
}
},
/**
- * Takes a record id and returns true if the record has children
- * @param id {Number|String} the id of the record
- * @returns {boolean} true if has children, false if not
+ * Highlights a record an unhighlights all other rows
+ * @param id {Number|String} the id of the record to highlight
*/
- recordHasChildren: function(id) {
- for (var i = -1, length = this.data.embedded.length; ++i < length;) {
- if (this.data.embedded[i].id === id) {
- return this.data.embedded[i][this.datagrid.options.childrenPropertyName] > 0;
- }
- }
- return false;
+ uniqueHighlightRecord: function (id) {
+ this.sandbox.dom.removeClass(
+ this.sandbox.dom.find('.' + constants.rowClass + '.' + constants.selectedRowClass, this.table.$body),
+ constants.selectedRowClass
+ );
+ this.sandbox.dom.addClass(this.table.rows[id].$el, constants.selectedRowClass);
},
/**
- * Handles the click on a table row if elements in the grid have children
- *
- * @param event
+ * Selejcts all records
*/
- prepareChildrenLoad: function(event) {
- this.sandbox.dom.stopPropagation(event);
- var recordId = this.sandbox.dom.data(event.currentTarget, 'id');
-
- // only load children if they are not already loaded
- if (!this.sandbox.dom.hasClass(event.currentTarget, constants.childrenLoadedClass)) {
- if (!!recordId && this.recordHasChildren(recordId)) {
- this.sandbox.dom.addClass(event.currentTarget, constants.childrenLoadedClass);
- this.datagrid.loadChildren.call(this.datagrid, recordId);
- }
- } else {
- this.toggleChildren(event.currentTarget, recordId);
- }
+ selectAllRecords: function () {
+ this.datagrid.selectAllItems.call(this.datagrid);
+ this.sandbox.dom.prop(this.sandbox.dom.find('.' + constants.checkboxClass, this.table.$body), 'checked', true);
},
/**
- * Toggles the already loaded children of a parent element
- * @param $parent
- * @param parentId
+ * Deselects all records
*/
- toggleChildren: function($parent, parentId) {
- var $children = this.sandbox.dom.find('tr[data-parent="'+ parentId +'"]', this.$tableContainer);
+ deselectAllRecords: function () {
+ this.datagrid.deselectAllItems.call(this.datagrid);
+ this.sandbox.dom.prop(this.sandbox.dom.find('.' + constants.checkboxClass, this.table.$body), 'checked', false);
+ },
- if (this.sandbox.dom.is($children, ':visible')) {
- this.hideChildren($parent, parentId);
- this.sandbox.emit(CHILDREN_COLLAPSED.call(this));
+ /**
+ * Selects or deselects a record with a given id
+ * @param id {Number|String} the id of the record to select or deselect
+ * @param select {Boolean} true to select false to deselect
+ */
+ toggleSelectRecord: function (id, select) {
+ var areAllSelected;
+ if (select === true) {
+ this.datagrid.setItemSelected.call(this.datagrid, id);
+ // ensure that checkboxes are checked
+ this.sandbox.dom.prop(
+ this.sandbox.dom.find('.' + constants.checkboxClass, this.table.rows[id].$el), 'checked', true
+ );
} else {
- this.showChildren($parent, parentId);
- this.sandbox.emit(CHILDREN_EXPANDED.call(this));
+ this.datagrid.setItemUnselected.call(this.datagrid, id);
+ // ensure that checkboxes are unchecked
+ this.sandbox.dom.prop(
+ this.sandbox.dom.find('.' + constants.checkboxClass, this.table.rows[id].$el), 'checked', false
+ );
+ }
+ // check or uncheck checkboxes in the header
+ if (!!this.table.header) {
+ areAllSelected = this.datagrid.getSelectedItemIds.call(this.datagrid).length === this.data.embedded.length;
+ this.toggleSelectAllItem(areAllSelected);
}
},
/**
- * Opens all parents of a data-record and because of that makes sure that the data-record is visible in the table
- * @param id {Number|String} the id of the data-record
+ * Selects or deselects the select all item
+ * @param select {Boolean} true to select false to deselect the select all item
*/
- openAllParents: function(id) {
- var $child = this.sandbox.dom.find('tr[data-id="'+ id +'"]', this.$tableContainer),
- parentId = this.sandbox.dom.data($child, 'parent'),
- $parent = this.sandbox.dom.find('tr[data-id="'+ parentId +'"]', this.$tableContainer);
- if (!!parentId && !!$parent) {
- if (!!this.sandbox.dom.data($parent, 'parent')) {
- this.openAllParents(parentId);
- }
- this.showChildren($parent, parentId);
+ toggleSelectAllItem: function (select) {
+ if (!!this.table.header) {
+ this.sandbox.dom.prop(
+ this.sandbox.dom.find('.' + constants.checkboxClass, this.table.header.$el), 'checked', select
+ );
}
},
/**
- * Shows the first-level of children of a record
- * @param $parent
- * @param parentId
+ * Selects a record and deselects all other records
+ * @param id {Number|String} the id of the record to select
*/
- showChildren: function($parent, parentId) {
- var $children = this.sandbox.dom.find('tr[data-parent="'+ parentId +'"]', this.$tableContainer),
- $parentIcon = this.sandbox.dom.find('.' + constants.slideDownClass + ' .toggle-icon', $parent);
-
- this.sandbox.dom.removeClass($parentIcon, constants.childrenSlideDownIcon);
- this.sandbox.dom.prependClass($parentIcon, constants.childrenSlideUpIcon);
- this.sandbox.dom.show($children);
+ uniqueSelectRecord: function (id) {
+ this.datagrid.deselectAllItems.call(this.datagrid);
+ this.datagrid.setItemSelected.call(this.datagrid, id);
},
/**
- * Recursivly hides all children of a given parent (with nested ones)
- * @param $parent
- * @param parentId
+ * Removes the empty list element
*/
- hideChildren: function($parent, parentId) {
- var $children = this.sandbox.dom.find('tr[data-parent="'+ parentId +'"]', this.$tableContainer),
- $parentIcon = this.sandbox.dom.find('.' + constants.slideDownClass + ' .toggle-icon', $parent);
+ removeEmptyIndicator: function() {
+ this.sandbox.dom.remove(this.sandbox.dom.find('.' + constants.emptyListElementClass, this.$el));
+ },
- this.sandbox.util.foreach($children, function($child) {
- if (this.sandbox.dom.hasClass($child, constants.childrenLoadedClass)) {
- this.hideChildren($child, this.sandbox.dom.data($child, 'id'));
+ /**
+ * Changes the toggle-icon of a parent row
+ * @param recordId {String|Number} the id of the parent row
+ * @param isExpanded {Boolean} the state for which the icon should be set
+ */
+ changeChildrenToggleIcon: function(recordId, isExpanded) {
+ var $icon = this.sandbox.dom.find('.' + constants.toggleIconClass, this.table.rows[recordId].$el);
+ if (!!$icon && !!$icon.length) {
+ if (isExpanded === true) {
+ this.sandbox.dom.removeClass($icon, constants.collapsedIcon);
+ this.sandbox.dom.prependClass($icon, constants.expandedIcon);
+ } else {
+ this.sandbox.dom.removeClass($icon, constants.expandedIcon);
+ this.sandbox.dom.prependClass($icon, constants.collapsedIcon);
}
- }.bind(this));
- this.sandbox.dom.removeClass($parentIcon, constants.childrenSlideUpIcon);
- this.sandbox.dom.prependClass($parentIcon, constants.childrenSlideDownIcon);
- this.sandbox.dom.hide($children);
+ }
},
/**
- * Handles the click on a sortable column title
- *
- * Creates the function parameters need for sorting
- * and delegates the sorting itself to the datagrid
- * @param event {Object} the event object
+ * Toggles the children of a parent
+ * @param recordId {Number|String} the id of the parent to toggle the children for
*/
- prepareSort: function(event) {
- var $element = event.currentTarget,
- $span = this.sandbox.dom.children($element, 'span')[0],
- attribute = this.sandbox.dom.data($element, 'attribute'),
- direction = this.sandbox.dom.hasClass($span, constants.ascClass) ? 'desc' : 'asc',
- $loaderContainer = this.sandbox.dom.createElement('');
-
- this.sandbox.dom.stopPropagation(event);
+ toggleChildren: function(recordId) {
+ if (this.table.rows[recordId].childrenLoaded === true) {
+ if (this.table.rows[recordId].childrenExpanded === true) {
+ this.hideChildren(recordId);
+ this.sandbox.emit(CHILDREN_COLLAPSED.call(this));
+ } else {
+ this.showChildren(recordId);
+ this.sandbox.emit(CHILDREN_EXPANDED.call(this));
+ }
+ } else {
+ this.loadChildren(recordId);
+ }
+ },
- // start loader beneath th
- this.sandbox.dom.removeClass($span);
- this.sandbox.dom.append($span, $loaderContainer);
- this.sandbox.start([
- {
- name: 'loader@husky',
- options: {
- el: $loaderContainer,
- size: '10px',
- color: '#999999'
+ /**
+ * Hides the children of a parent
+ * @param recordId {Number|String} the id of the parent to hide the children for
+ */
+ hideChildren: function(recordId) {
+ this.sandbox.util.each(this.table.rows, function(rowId, row) {
+ if (row.parent == recordId) {
+ if (row.numberChildren > 0) {
+ this.hideChildren(rowId);
}
+ this.sandbox.dom.hide(row.$el);
}
- ]);
+ }.bind(this));
+ this.table.rows[recordId].childrenExpanded = false;
+ this.changeChildrenToggleIcon(recordId, false);
+ },
- // delegate sorting to datagrid
- this.datagrid.sortGrid.call(this.datagrid, attribute, direction);
+ /**
+ * Shows the children of a parent
+ * @param recordId {Number|String} the id of the parent to show the children for
+ */
+ showChildren: function(recordId) {
+ this.sandbox.util.each(this.table.rows, function(rowId, row) {
+ if (row.parent == recordId) {
+ this.sandbox.dom.show(row.$el);
+ }
+ }.bind(this));
+ this.table.rows[recordId].childrenExpanded = true;
+ this.changeChildrenToggleIcon(recordId, true);
},
/**
- * Removes the dom-element which indicates the list as empty
+ * Loads the children of a parent and displays them
+ * @param recordId {Number|String} the id of the parent to load the children for
*/
- removeEmptyListElement: function() {
- this.sandbox.dom.remove(this.sandbox.dom.find('.empty-list', this.$el));
+ loadChildren: function(recordId) {
+ this.datagrid.loadChildren.call(this.datagrid, recordId);
+ this.table.rows[recordId].childrenLoaded = true;
},
- };
+
+ /**
+ * Opens all parents of a record
+ * @param id {Number|String} the id of the record
+ */
+ openParents: function(recordId) {
+ if (!!this.table.rows[recordId]) {
+ var parentId = this.table.rows[recordId].parent;
+ if (!!parentId) {
+ if (!!this.table.rows[parentId].parent) {
+ this.openParents(parentId);
+ }
+ this.showChildren(parentId);
+ }
+ }
+ }
+ };
});
/**
@@ -30395,6 +30149,7 @@ define('husky_components/datagrid/decorators/group-view',[],function () {
* @param {Function} [initialize] function which gets called once at the start of the view
* @param {Function} [render] function to render data
* @param {Function} [destroy] function to destroy the pagination and unbind events
+ * @param {Function} [getHeight] function which returns the height of the pagination
*/
define('husky_components/datagrid/decorators/dropdown-pagination',[],function() {
@@ -30411,7 +30166,8 @@ define('husky_components/datagrid/decorators/dropdown-pagination',[],function()
prevClass: 'previous',
nextClass: 'next',
pageChangeClass: 'page-change',
- sizeChangeClass: 'size-change'
+ sizeChangeClass: 'size-change',
+ loaderClass: 'pagination-loader'
},
/**
@@ -30433,6 +30189,10 @@ define('husky_components/datagrid/decorators/dropdown-pagination',[],function()
''
].join(''),
+ loader: [
+ ''
+ ].join(''),
+
showElements: [
'
a",n=p.getElementsByTagName("*")||[],r=p.getElementsByTagName("a")[0];if(!r||!r.style||!n.length)return t;u=o.createElement("select"),f=u.appendChild(o.createElement("option")),s=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute=p.className!=="t",t.leadingWhitespace=p.firstChild.nodeType===3,t.tbody=!p.getElementsByTagName("tbody").length,t.htmlSerialize=!!p.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized=r.getAttribute("href")==="/a",t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!s.value,t.optSelected=f.selected,t.enctype=!!o.createElement("form").enctype,t.html5Clone=o.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,s.checked=!0,t.noCloneChecked=s.cloneNode(!0).checked,u.disabled=!0,t.optDisabled=!f.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}s=o.createElement("input"),s.setAttribute("value",""),t.input=s.getAttribute("value")==="",s.value="t",s.setAttribute("type","radio"),t.radioValue=s.value==="t",s.setAttribute("checked","t"),s.setAttribute("name","t"),a=o.createDocumentFragment(),a.appendChild(s),t.appendChecked=s.checked,t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,p.attachEvent&&(p.attachEvent("onclick",function(){t.noCloneEvent=!1}),p.cloneNode(!0).click());for(h in{submit:!0,change:!0,focusin:!0})p.setAttribute(l="on"+h,"t"),t[h+"Bubbles"]=l in e||p.attributes[l].expando===!1;p.style.backgroundClip="content-box",p.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle=p.style.backgroundClip==="content-box";for(h in w(t))break;return t.ownLast=h!=="0",w(function(){var n,r,s,u="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",a=o.getElementsByTagName("body")[0];if(!a)return;n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",a.appendChild(n).appendChild(p),p.innerHTML="