UNPKG

jquery-grid

Version:

jQuery Grid by Gijgo.com is a plug-in for the jQuery Javascript library. It is a very fast and extandable datagrid, and will add advanced interaction controls to any HTML table. This plugin has build-in integration with Bootstrap and Material Design. Free

1,249 lines (1,089 loc) 55.5 kB
/* * Gijgo Tree v1.9.13 * http://gijgo.com/tree * * Copyright 2014, 2019 gijgo.com * Released under the MIT license */ /* global window alert jQuery gj */ /** */ gj.tree = { plugins: {} }; gj.tree.config = { base: { params: {}, /** When this setting is enabled the content of the tree will be loaded automatically after the creation of the tree. */ autoLoad: true, /** The type of the node selection.<br/> * If the type is set to multiple the user will be able to select more then one node in the tree. */ selectionType: 'single', /** This setting enable cascade selection and unselection of children */ cascadeSelection: false, /** The data source of tree. */ dataSource: undefined, /** Primary key field name. */ primaryKey: undefined, /** Text field name. */ textField: 'text', /** Children field name. */ childrenField: 'children', /** The name of the field that indicates if the node has children. Shows expand icon if the node has children. */ hasChildrenField: 'hasChildren', /** Image css class field name. */ imageCssClassField: 'imageCssClass', /** Image url field name. */ imageUrlField: 'imageUrl', /** Image html field name. */ imageHtmlField: 'imageHtml', /** Disabled field name. Assume that the item is not disabled if not set. */ disabledField: 'disabled', /** Width of the tree. */ width: undefined, /** When this setting is enabled the content of the tree will be wrapped by borders. */ border: false, /** The name of the UI library that is going to be in use. */ uiLibrary: 'materialdesign', /** The name of the icons library that is going to be in use. Currently we support Material Icons, Font Awesome and Glyphicons. */ iconsLibrary: 'materialicons', autoGenId: 1, autoGenFieldName: 'autoId_b5497cc5-7ef3-49f5-a7dc-4a932e1aee4a', indentation: 24, style: { wrapper: 'gj-unselectable', list: 'gj-list gj-list-md', item: undefined, active: 'gj-list-md-active', leafIcon: undefined, border: 'gj-tree-md-border' }, icons: { /** Expand icon definition. */ expand: '<i class="gj-icon chevron-right" />', /** Collapse icon definition. */ collapse: '<i class="gj-icon chevron-down" />' } }, bootstrap: { style: { wrapper: 'gj-unselectable gj-tree-bootstrap-3', list: 'gj-list gj-list-bootstrap list-group', item: 'list-group-item', active: 'active', border: 'gj-tree-bootstrap-border' }, iconsLibrary: 'glyphicons' }, bootstrap4: { style: { wrapper: 'gj-unselectable gj-tree-bootstrap-4', list: 'gj-list gj-list-bootstrap', item: 'list-group-item', active: 'active', border: 'gj-tree-bootstrap-border' }, icons: { expand: '<i class="gj-icon plus" />', collapse: '<i class="gj-icon minus" />' } }, materialicons: { style: { expander: 'gj-tree-material-icons-expander' } }, fontawesome: { style: { expander: 'gj-tree-font-awesome-expander' }, icons: { expand: '<i class="fa fa-plus" aria-hidden="true"></i>', collapse: '<i class="fa fa-minus" aria-hidden="true"></i>' } }, glyphicons: { style: { expander: 'gj-tree-glyphicons-expander' }, icons: { expand: '<span class="glyphicon glyphicon-plus" />', collapse: '<span class="glyphicon glyphicon-minus" />' } } }; /** */ gj.tree.events = { /** * Event fires when the tree is initialized */ initialized: function ($tree) { $tree.triggerHandler('initialized'); }, /** * Event fired before data binding takes place. */ dataBinding: function ($tree) { $tree.triggerHandler('dataBinding'); }, /** * Event fires after the loading of the data in the tree. */ dataBound: function ($tree) { $tree.triggerHandler('dataBound'); }, /** * Event fires after selection of tree node. */ select: function ($tree, $node, id) { return $tree.triggerHandler('select', [$node, id]); }, /** * Event fires on un selection of tree node */ unselect: function ($tree, $node, id) { return $tree.triggerHandler('unselect', [$node, id]); }, /** * Event fires before node expand. */ expand: function ($tree, $node, id) { return $tree.triggerHandler('expand', [$node, id]); }, /** * Event fires before node collapse. */ collapse: function ($tree, $node, id) { return $tree.triggerHandler('collapse', [$node, id]); }, /** * Event fires on enable of tree node. */ enable: function ($tree, $node) { return $tree.triggerHandler('enable', [$node]); }, /** * Event fires on disable of tree node. */ disable: function ($tree, $node) { return $tree.triggerHandler('disable', [$node]); }, /** * Event fires before tree destroy */ destroying: function ($tree) { return $tree.triggerHandler('destroying'); }, /** * Event fires when the data is bound to node. */ nodeDataBound: function ($tree, $node, id, record) { return $tree.triggerHandler('nodeDataBound', [$node, id, record]); } } /*global gj $*/ gj.tree.methods = { init: function (jsConfig) { gj.widget.prototype.init.call(this, jsConfig, 'tree'); gj.tree.methods.initialize.call(this); if (this.data('autoLoad')) { this.reload(); } return this; }, initialize: function () { var data = this.data(), $root = $('<ul class="' + data.style.list + '"/>'); this.empty().addClass(data.style.wrapper).append($root); if (data.width) { this.width(data.width); } if (data.border) { this.addClass(data.style.border); } gj.tree.events.initialized(this); }, useHtmlDataSource: function ($tree, data) { data.dataSource = []; }, render: function ($tree, response) { var data; if (response) { if (typeof (response) === 'string' && JSON) { response = JSON.parse(response); } data = $tree.data(); data.records = response; if (!data.primaryKey) { gj.tree.methods.genAutoId(data, data.records); } gj.tree.methods.loadData($tree); } return $tree; }, filter: function ($tree) { return $tree.data().dataSource; }, genAutoId: function (data, records) { var i; for (i = 0; i < records.length; i++) { records[i][data.autoGenFieldName] = data.autoGenId++; if (records[i][data.childrenField] && records[i][data.childrenField].length) { gj.tree.methods.genAutoId(data, records[i][data.childrenField]); } } }, loadData: function ($tree) { var i, records = $tree.data('records'), $root = $tree.children('ul'); gj.tree.events.dataBinding($tree); $root.off().empty(); for (i = 0; i < records.length; i++) { gj.tree.methods.appendNode($tree, $root, records[i], 1); } gj.tree.events.dataBound($tree); }, appendNode: function ($tree, $parent, nodeData, level, position) { var i, $node, $newParent, $span, $img, data = $tree.data(), id = data.primaryKey ? nodeData[data.primaryKey] : nodeData[data.autoGenFieldName]; $node = $('<li data-id="' + id + '" data-role="node" />').addClass(data.style.item), $wrapper = $('<div data-role="wrapper" />'), $expander = $('<span data-role="expander" data-mode="close"></span>').addClass(data.style.expander), $display = $('<span data-role="display">' + nodeData[data.textField] + '</span>'), hasChildren = typeof (nodeData[data.hasChildrenField]) !== 'undefined' && nodeData[data.hasChildrenField].toString().toLowerCase() === 'true', disabled = typeof (nodeData[data.disabledField]) !== 'undefined' && nodeData[data.disabledField].toString().toLowerCase() === 'true'; if (data.indentation) { $wrapper.append('<span data-role="spacer" style="width: ' + (data.indentation * (level - 1)) + 'px;"></span>'); } if (disabled) { gj.tree.methods.disableNode($tree, $node); } else { $expander.on('click', gj.tree.methods.expanderClickHandler($tree)); $display.on('click', gj.tree.methods.displayClickHandler($tree)); } $wrapper.append($expander); $wrapper.append($display); $node.append($wrapper); if (position) { $parent.find('li:eq(' + (position - 1) + ')').before($node); } else { $parent.append($node); } if (data.imageCssClassField && nodeData[data.imageCssClassField]) { $span = $('<span data-role="image"><span class="' + nodeData[data.imageCssClassField] + '"></span></span>'); $span.insertBefore($display); } else if (data.imageUrlField && nodeData[data.imageUrlField]) { $span = $('<span data-role="image"></span>'); $span.insertBefore($display); $img = $('<img src="' + nodeData[data.imageUrlField] + '"></img>'); $img.attr('width', $span.width()).attr('height', $span.height()); $span.append($img); } else if (data.imageHtmlField && nodeData[data.imageHtmlField]) { $span = $('<span data-role="image">' + nodeData[data.imageHtmlField] + '</span>'); $span.insertBefore($display); } if ((nodeData[data.childrenField] && nodeData[data.childrenField].length) || hasChildren) { $expander.empty().append(data.icons.expand); $newParent = $('<ul />').addClass(data.style.list).addClass('gj-hidden'); $node.append($newParent); if (nodeData[data.childrenField] && nodeData[data.childrenField].length) { for (i = 0; i < nodeData[data.childrenField].length; i++) { gj.tree.methods.appendNode($tree, $newParent, nodeData[data.childrenField][i], level + 1); } } } else { data.style.leafIcon ? $expander.addClass(data.style.leafIcon) : $expander.html('&nbsp;'); } gj.tree.events.nodeDataBound($tree, $node, nodeData.id, nodeData); }, expanderClickHandler: function ($tree) { return function (e) { var $expander = $(this), $node = $expander.closest('li'); if ($expander.attr('data-mode') === 'close') { $tree.expand($node); } else { $tree.collapse($node); } } }, expand: function ($tree, $node, cascade) { var $children, i, $expander = $node.find('>[data-role="wrapper"]>[data-role="expander"]'), data = $tree.data(), id = $node.attr('data-id'), $list = $node.children('ul'); if (gj.tree.events.expand($tree, $node, id) !== false && $list && $list.length) { $list.show(); $expander.attr('data-mode', 'open'); $expander.empty().append(data.icons.collapse); if (cascade) { $children = $node.find('ul>li'); for (i = 0; i < $children.length; i++) { gj.tree.methods.expand($tree, $($children[i]), cascade); } } } return $tree; }, collapse: function ($tree, $node, cascade) { var $children, i, $expander = $node.find('>[data-role="wrapper"]>[data-role="expander"]'), data = $tree.data(), id = $node.attr('data-id'), $list = $node.children('ul'); if (gj.tree.events.collapse($tree, $node, id) !== false && $list && $list.length) { $list.hide(); $expander.attr('data-mode', 'close'); $expander.empty().append(data.icons.expand); if (cascade) { $children = $node.find('ul>li'); for (i = 0; i < $children.length; i++) { gj.tree.methods.collapse($tree, $($children[i]), cascade); } } } return $tree; }, expandAll: function ($tree) { var i, $nodes = $tree.find('ul>li'); for (i = 0; i < $nodes.length; i++) { gj.tree.methods.expand($tree, $($nodes[i]), true); } return $tree; }, collapseAll: function ($tree) { var i, $nodes = $tree.find('ul>li'); for (i = 0; i < $nodes.length; i++) { gj.tree.methods.collapse($tree, $($nodes[i]), true); } return $tree; }, displayClickHandler: function ($tree) { return function (e) { var $display = $(this), $node = $display.closest('li'), cascade = $tree.data().cascadeSelection; if ($node.attr('data-selected') === 'true') { gj.tree.methods.unselect($tree, $node, cascade); } else { if ($tree.data('selectionType') === 'single') { gj.tree.methods.unselectAll($tree); } gj.tree.methods.select($tree, $node, cascade); } } }, selectAll: function ($tree) { var i, $nodes = $tree.find('ul>li'); for (i = 0; i < $nodes.length; i++) { gj.tree.methods.select($tree, $($nodes[i]), true); } return $tree; }, select: function ($tree, $node, cascade) { var i, $children, data = $tree.data(); if ($node.attr('data-selected') !== 'true' && gj.tree.events.select($tree, $node, $node.attr('data-id')) !== false) { $node.addClass(data.style.active).attr('data-selected', 'true'); if (cascade) { $children = $node.find('ul>li'); for (i = 0; i < $children.length; i++) { gj.tree.methods.select($tree, $($children[i]), cascade); } } } }, unselectAll: function ($tree) { var i, $nodes = $tree.find('ul>li'); for (i = 0; i < $nodes.length; i++) { gj.tree.methods.unselect($tree, $($nodes[i]), true); } return $tree; }, unselect: function ($tree, $node, cascade) { var i, $children, data = $tree.data(); if ($node.attr('data-selected') === 'true' && gj.tree.events.unselect($tree, $node, $node.attr('data-id')) !== false) { $node.removeClass($tree.data().style.active).removeAttr('data-selected'); if (cascade) { $children = $node.find('ul>li'); for (i = 0; i < $children.length; i++) { gj.tree.methods.unselect($tree, $($children[i]), cascade); } } } }, getSelections: function ($list) { var i, $node, children, result = [], $nodes = $list.children('li'); if ($nodes && $nodes.length) { for (i = 0; i < $nodes.length; i++) { $node = $($nodes[i]); if ($node.attr('data-selected') === 'true') { result.push($node.attr('data-id')); } else if ($node.has('ul')) { children = gj.tree.methods.getSelections($node.children('ul')); if (children.length) { result = result.concat(children); } } } } return result; }, getDataById: function ($tree, id, records) { var i, data = $tree.data(), result = undefined; for (i = 0; i < records.length; i++) { if (data.primaryKey && records[i][data.primaryKey] == id) { result = records[i]; break; } else if (records[i][data.autoGenFieldName] == id) { result = records[i]; break; } else if (records[i][data.childrenField] && records[i][data.childrenField].length) { result = gj.tree.methods.getDataById($tree, id, records[i][data.childrenField]); if (result) { break; } } } return result; }, getDataByText: function ($tree, text, records) { var i, id, result = undefined, data = $tree.data(); for (i = 0; i < records.length; i++) { if (text === records[i][data.textField]) { result = records[i]; break; } else if (records[i][data.childrenField] && records[i][data.childrenField].length) { result = gj.tree.methods.getDataByText($tree, text, records[i][data.childrenField]); if (result) { break; } } } return result; }, getNodeById: function ($list, id) { var i, $node, $result = undefined, $nodes = $list.children('li'); if ($nodes && $nodes.length) { for (i = 0; i < $nodes.length; i++) { $node = $($nodes[i]); if (id == $node.attr('data-id')) { $result = $node; break; } else if ($node.has('ul')) { $result = gj.tree.methods.getNodeById($node.children('ul'), id); if ($result) { break; } } } } return $result; }, getNodeByText: function ($list, text) { var i, $node, $result = undefined, $nodes = $list.children('li'); if ($nodes && $nodes.length) { for (i = 0; i < $nodes.length; i++) { $node = $($nodes[i]); if (text === $node.find('>[data-role="wrapper"]>[data-role="display"]').text()) { $result = $node; break; } else if ($node.has('ul')) { $result = gj.tree.methods.getNodeByText($node.children('ul'), text); if ($result) { break; } } } } return $result; }, addNode: function ($tree, nodeData, $parent, position) { var level, record, data = $tree.data(); if (!$parent || !$parent.length) { $parent = $tree.children('ul'); $tree.data('records').push(nodeData); } else { if ($parent[0].tagName.toLowerCase() === 'li') { if ($parent.children('ul').length === 0) { $parent.find('[data-role="expander"]').empty().append(data.icons.collapse); $parent.append($('<ul />').addClass(data.style.list)); } $parent = $parent.children('ul'); } record = $tree.getDataById($parent.parent().data('id')); if (!record[data.childrenField]) { record[data.childrenField] = []; } record[data.childrenField].push(nodeData); } level = $parent.parentsUntil('[data-type="tree"]', 'ul').length + 1; if (!data.primaryKey) { gj.tree.methods.genAutoId(data, [nodeData]); } gj.tree.methods.appendNode($tree, $parent, nodeData, level, position); return $tree; }, remove: function ($tree, $node) { gj.tree.methods.removeDataById($tree, $node.attr('data-id'), $tree.data('records')); $node.remove(); return $tree; }, removeDataById: function ($tree, id, records) { var i, data = $tree.data(); for (i = 0; i < records.length; i++) { if (data.primaryKey && records[i][data.primaryKey] == id) { records.splice(i, 1); break; } else if (records[i][data.autoGenFieldName] == id) { records.splice(i, 1); break; } else if (records[i][data.childrenField] && records[i][data.childrenField].length) { gj.tree.methods.removeDataById($tree, id, records[i][data.childrenField]); } } }, update: function ($tree, id, newRecord) { var data = $tree.data(), $node = $tree.getNodeById(id), oldRecord = $tree.getDataById(id); oldRecord = newRecord; $node.find('>[data-role="wrapper"]>[data-role="display"]').html(newRecord[data.textField]); gj.tree.events.nodeDataBound($tree, $node, id, newRecord); return $tree; }, getChildren: function ($tree, $node, cascade) { var result = [], i, $children, cascade = typeof (cascade) === 'undefined' ? true : cascade; if (cascade) { $children = $node.find('ul li'); } else { $children = $node.find('>ul>li'); } for (i = 0; i < $children.length; i++) { result.push($($children[i]).data('id')); } return result; }, enableAll: function ($tree) { var i, $children = $tree.find('ul>li'); for (i = 0; i < $children.length; i++) { gj.tree.methods.enableNode($tree, $($children[i]), true); } return $tree; }, enableNode: function ($tree, $node, cascade) { var i, $children, $expander = $node.find('>[data-role="wrapper"]>[data-role="expander"]'), $display = $node.find('>[data-role="wrapper"]>[data-role="display"]'), cascade = typeof (cascade) === 'undefined' ? true : cascade; $node.removeClass('disabled'); $expander.on('click', gj.tree.methods.expanderClickHandler($tree)); $display.on('click', gj.tree.methods.displayClickHandler($tree)); gj.tree.events.enable($tree, $node); if (cascade) { $children = $node.find('ul>li'); for (i = 0; i < $children.length; i++) { gj.tree.methods.enableNode($tree, $($children[i]), cascade); } } }, disableAll: function ($tree) { var i, $children = $tree.find('ul>li'); for (i = 0; i < $children.length; i++) { gj.tree.methods.disableNode($tree, $($children[i]), true); } return $tree; }, disableNode: function ($tree, $node, cascade) { var i, $children, $expander = $node.find('>[data-role="wrapper"]>[data-role="expander"]'), $display = $node.find('>[data-role="wrapper"]>[data-role="display"]'), cascade = typeof (cascade) === 'undefined' ? true : cascade; $node.addClass('disabled'); $expander.off('click'); $display.off('click'); gj.tree.events.disable($tree, $node); if (cascade) { $children = $node.find('ul>li'); for (i = 0; i < $children.length; i++) { gj.tree.methods.disableNode($tree, $($children[i]), cascade); } } }, destroy: function ($tree) { var data = $tree.data(); if (data) { gj.tree.events.destroying($tree); $tree.xhr && $tree.xhr.abort(); $tree.off(); $tree.removeData(); $tree.removeAttr('data-type'); $tree.removeClass().empty(); } return $tree; }, pathFinder: function (data, list, id, parents) { var i, result = false; for (i = 0; i < list.length; i++) { if (list[i].id == id) { result = true; break; } else if (gj.tree.methods.pathFinder(data, list[i][data.childrenField], id, parents)) { parents.push(list[i].data[data.textField]); result = true; break; } } return result; } } /** */ gj.tree.widget = function ($element, jsConfig) { var self = this, methods = gj.tree.methods; /** * Reload the tree. */ self.reload = function (params) { return gj.widget.prototype.reload.call(this, params); }; /** * Render data in the tree */ self.render = function (response) { return methods.render(this, response); }; /** * Add node to the tree. */ self.addNode = function (data, $parentNode, position) { return methods.addNode(this, data, $parentNode, position); }; /** * Remove node from the tree. */ self.removeNode = function ($node) { return methods.remove(this, $node); }; /** * Update node from the tree. */ self.updateNode = function (id, record) { return methods.update(this, id, record); }; /** * Destroy the tree. */ self.destroy = function () { return methods.destroy(this); }; /** * Expand node from the tree. */ self.expand = function ($node, cascade) { return methods.expand(this, $node, cascade); }; /** * Collapse node from the tree. */ self.collapse = function ($node, cascade) { return methods.collapse(this, $node, cascade); }; /** * Expand all tree nodes */ self.expandAll = function () { return methods.expandAll(this); }; /** * Collapse all tree nodes */ self.collapseAll = function () { return methods.collapseAll(this); }; /** * Return node data by id of the record. */ self.getDataById = function (id) { return methods.getDataById(this, id, this.data('records')); }; /** * Return node data by text. */ self.getDataByText = function (text) { return methods.getDataByText(this, text, this.data('records')); }; /** * Return node by id of the record. */ self.getNodeById = function (id) { return methods.getNodeById(this.children('ul'), id); }; /** * Return node by text. */ self.getNodeByText = function (text) { return methods.getNodeByText(this.children('ul'), text); }; /** * Return an array with all records presented in the tree. */ self.getAll = function () { return this.data('records'); }; /** * Select node from the tree. */ self.select = function ($node) { return methods.select(this, $node); }; /** * Unselect node from the tree. */ self.unselect = function ($node) { return methods.unselect(this, $node); }; /** * Select all tree nodes */ self.selectAll = function () { return methods.selectAll(this); }; /** * Unselect all tree nodes */ self.unselectAll = function () { return methods.unselectAll(this); }; /** * Return an array with the ids of the selected nodes. */ self.getSelections = function () { return methods.getSelections(this.children('ul')); }; /** * Return an array with the ids of all children. */ self.getChildren = function ($node, cascade) { return methods.getChildren(this, $node, cascade); }; /** * Return an array with the names of all parents. */ self.parents = function (id) { var parents = [], data = this.data(); methods.pathFinder(data, data.records, id, parents); return parents.reverse(); }; /** * Enable node from the tree. */ self.enable = function ($node, cascade) { return methods.enableNode(this, $node, cascade); }; /** * Enable all nodes from the tree. */ self.enableAll = function () { return methods.enableAll(this); }; /** * Disable node from the tree. */ self.disable = function ($node, cascade) { return methods.disableNode(this, $node, cascade); }; /** * Disable all nodes from the tree. */ self.disableAll = function () { return methods.disableAll(this); }; $.extend($element, self); if ('tree' !== $element.attr('data-type')) { methods.init.call($element, jsConfig); } return $element; }; gj.tree.widget.prototype = new gj.widget(); gj.tree.widget.constructor = gj.tree.widget; (function ($) { $.fn.tree = function (method) { var $widget; if (this && this.length) { if (typeof method === 'object' || !method) { return new gj.tree.widget(this, method); } else { $widget = new gj.tree.widget(this, null); if ($widget[method]) { return $widget[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else { throw 'Method ' + method + ' does not exist.'; } } } }; })(jQuery); /** */ gj.tree.plugins.checkboxes = { config: { base: { /** Add checkbox for each node, if set to true. */ checkboxes: undefined, /** Name of the source field, that indicates if the checkbox is checked. */ checkedField: 'checked', /** This setting enable cascade check and uncheck of children */ cascadeCheck: true, } }, private: { dataBound: function ($tree) { var $nodes; if ($tree.data('cascadeCheck')) { $nodes = $tree.find('li[data-role="node"]'); $.each($nodes, function () { var $node = $(this), state = $node.find('[data-role="checkbox"] input[type="checkbox"]').checkbox('state'); if (state === 'checked') { gj.tree.plugins.checkboxes.private.updateChildrenState($node, state); gj.tree.plugins.checkboxes.private.updateParentState($node, state); } }); } }, nodeDataBound: function ($tree, $node, id, record) { var data, $expander, $checkbox, $wrapper, disabled; if ($node.find('> [data-role="wrapper"] > [data-role="checkbox"]').length === 0) { data = $tree.data(); $expander = $node.find('> [data-role="wrapper"] > [data-role="expander"]'); $checkbox = $('<input type="checkbox"/>'); $wrapper = $('<span data-role="checkbox"></span>').append($checkbox); disabled = typeof (record[data.disabledField]) !== 'undefined' && record[data.disabledField].toString().toLowerCase() === 'true'; $checkbox = $checkbox.checkbox({ uiLibrary: data.uiLibrary, iconsLibrary: data.iconsLibrary, change: function (e, state) { gj.tree.plugins.checkboxes.events.checkboxChange($tree, $node, record, $checkbox.state()); } }); disabled && $checkbox.prop('disabled', true); record[data.checkedField] && $checkbox.state('checked'); $checkbox.on('click', function (e) { var $node = $checkbox.closest('li'), state = $checkbox.state(); if (data.cascadeCheck) { gj.tree.plugins.checkboxes.private.updateChildrenState($node, state); gj.tree.plugins.checkboxes.private.updateParentState($node, state); } }); $expander.after($wrapper); } }, updateParentState: function ($node, state) { var $parentNode, $parentCheckbox, $siblingCheckboxes, allChecked, allUnchecked, parentState; $parentNode = $node.parent('ul').parent('li'); if ($parentNode.length === 1) { $parentCheckbox = $node.parent('ul').parent('li').find('> [data-role="wrapper"] > [data-role="checkbox"] input[type="checkbox"]'); $siblingCheckboxes = $node.siblings().find('> [data-role="wrapper"] > span[data-role="checkbox"] input[type="checkbox"]'); allChecked = (state === 'checked'); allUnchecked = (state === 'unchecked'); parentState = 'indeterminate'; $.each($siblingCheckboxes, function () { var state = $(this).checkbox('state'); if (allChecked && state !== 'checked') { allChecked = false; } if (allUnchecked && state !== 'unchecked') { allUnchecked = false; } }); if (allChecked && !allUnchecked) { parentState = 'checked'; } if (!allChecked && allUnchecked) { parentState = 'unchecked'; } $parentCheckbox.checkbox('state', parentState); gj.tree.plugins.checkboxes.private.updateParentState($parentNode, $parentCheckbox.checkbox('state')); } }, updateChildrenState: function ($node, state) { var $childrenCheckboxes = $node.find('ul li [data-role="wrapper"] [data-role="checkbox"] input[type="checkbox"]'); if ($childrenCheckboxes.length > 0) { $.each($childrenCheckboxes, function () { $(this).checkbox('state', state); }); } }, update: function ($tree, $node, state) { var checkbox = $node.find('[data-role="checkbox"] input[type="checkbox"]').first(); $(checkbox).checkbox('state', state); if ($tree.data().cascadeCheck) { gj.tree.plugins.checkboxes.private.updateChildrenState($node, state); gj.tree.plugins.checkboxes.private.updateParentState($node, state); } } }, public: { /** Get ids of all checked nodes */ getCheckedNodes: function () { var result = [], checkboxes = this.find('li [data-role="checkbox"] input[type="checkbox"]'); $.each(checkboxes, function () { var checkbox = $(this); if (checkbox.checkbox('state') === 'checked') { result.push(checkbox.closest('li').data('id')); } }); return result; }, /** * Check all tree nodes */ checkAll: function () { var $checkboxes = this.find('li [data-role="checkbox"] input[type="checkbox"]'); $.each($checkboxes, function () { $(this).checkbox('state', 'checked'); }); return this; }, /** * Uncheck all tree nodes */ uncheckAll: function () { var $checkboxes = this.find('li [data-role="checkbox"] input[type="checkbox"]'); $.each($checkboxes, function () { $(this).checkbox('state', 'unchecked'); }); return this; }, /** * Check tree node. */ check: function ($node) { gj.tree.plugins.checkboxes.private.update(this, $node, 'checked'); return this; }, /** * Uncheck tree node. */ uncheck: function ($node) { gj.tree.plugins.checkboxes.private.update(this, $node, 'unchecked'); return this; } }, events: { /** * Event fires when the checkbox state is changed. */ checkboxChange: function ($tree, $node, record, state) { return $tree.triggerHandler('checkboxChange', [$node, record, state]); } }, configure: function ($tree) { if ($tree.data('checkboxes') && gj.checkbox) { $.extend(true, $tree, gj.tree.plugins.checkboxes.public); $tree.on('nodeDataBound', function (e, $node, id, record) { gj.tree.plugins.checkboxes.private.nodeDataBound($tree, $node, id, record); }); $tree.on('dataBound', function () { gj.tree.plugins.checkboxes.private.dataBound($tree); }); $tree.on('enable', function (e, $node) { $node.find('>[data-role="wrapper"]>[data-role="checkbox"] input[type="checkbox"]').prop('disabled', false); }); $tree.on('disable', function (e, $node) { $node.find('>[data-role="wrapper"]>[data-role="checkbox"] input[type="checkbox"]').prop('disabled', true); }); } } }; /** */ gj.tree.plugins.dragAndDrop = { config: { base: { /** Enables drag and drop functionality for each node. */ dragAndDrop: undefined, style: { dragEl: 'gj-tree-drag-el gj-tree-md-drag-el', dropAsChildIcon: 'gj-cursor-pointer gj-icon plus', dropAbove: 'gj-tree-drop-above', dropBelow: 'gj-tree-drop-below' } }, bootstrap: { style: { dragEl: 'gj-tree-drag-el gj-tree-bootstrap-drag-el', dropAsChildIcon: 'glyphicon glyphicon-plus', dropAbove: 'drop-above', dropBelow: 'drop-below' } }, bootstrap4: { style: { dragEl: 'gj-tree-drag-el gj-tree-bootstrap-drag-el', dropAsChildIcon: 'gj-cursor-pointer gj-icon plus', dropAbove: 'drop-above', dropBelow: 'drop-below' } } }, private: { nodeDataBound: function ($tree, $node) { var $wrapper = $node.children('[data-role="wrapper"]'), $display = $node.find('>[data-role="wrapper"]>[data-role="display"]'); if ($wrapper.length && $display.length) { $display.on('mousedown', gj.tree.plugins.dragAndDrop.private.createNodeMouseDownHandler($tree)); $display.on('mousemove', gj.tree.plugins.dragAndDrop.private.createNodeMouseMoveHandler($tree, $node, $display)); $display.on('mouseup', gj.tree.plugins.dragAndDrop.private.createNodeMouseUpHandler($tree)); } }, createNodeMouseDownHandler: function ($tree) { return function (e) { $tree.data('dragReady', true); } }, createNodeMouseUpHandler: function ($tree) { return function (e) { $tree.data('dragReady', false); } }, createNodeMouseMoveHandler: function ($tree, $node, $display) { return function (e) { if ($tree.data('dragReady')) { var data = $tree.data(), $dragEl, $wrapper, offsetTop, offsetLeft; $tree.data('dragReady', false); $dragEl = $display.clone().wrap('<div data-role="wrapper"/>').closest('div') .wrap('<li class="' + data.style.item + '" />').closest('li') .wrap('<ul class="' + data.style.list + '" />').closest('ul'); $('body').append($dragEl); $dragEl.attr('data-role', 'draggable-clone').addClass('gj-unselectable').addClass(data.style.dragEl); $dragEl.find('[data-role="wrapper"]').prepend('<span data-role="indicator" />'); $dragEl.draggable({ drag: gj.tree.plugins.dragAndDrop.private.createDragHandler($tree, $node, $display), stop: gj.tree.plugins.dragAndDrop.private.createDragStopHandler($tree, $node, $display) }); $wrapper = $display.parent(); offsetTop = $display.offset().top; offsetTop -= parseInt($wrapper.css("border-top-width")) + parseInt($wrapper.css("margin-top")) + parseInt($wrapper.css("padding-top")); offsetLeft = $display.offset().left; offsetLeft -= parseInt($wrapper.css("border-left-width")) + parseInt($wrapper.css("margin-left")) + parseInt($wrapper.css("padding-left")); offsetLeft -= $dragEl.find('[data-role="indicator"]').outerWidth(true); $dragEl.css({ position: 'absolute', top: offsetTop, left: offsetLeft, width: $display.outerWidth(true) }); if ($display.attr('data-droppable') === 'true') { $display.droppable('destroy'); } gj.tree.plugins.dragAndDrop.private.getTargetDisplays($tree, $node, $display).each(function () { var $dropEl = $(this); if ($dropEl.attr('data-droppable') === 'true') { $dropEl.droppable('destroy'); } $dropEl.droppable(); }); gj.tree.plugins.dragAndDrop.private.getTargetDisplays($tree, $node).each(function () { var $dropEl = $(this); if ($dropEl.attr('data-droppable') === 'true') { $dropEl.droppable('destroy'); } $dropEl.droppable(); }); $dragEl.trigger('mousedown'); } }; }, getTargetDisplays: function ($tree, $node, $display) { return $tree.find('[data-role="display"]').not($display).not($node.find('[data-role="display"]')); }, getTargetWrappers: function ($tree, $node) { return $tree.find('[data-role="wrapper"]').not($node.find('[data-role="wrapper"]')); }, createDragHandler: function ($tree, $node, $display) { var $displays = gj.tree.plugins.dragAndDrop.private.getTargetDisplays($tree, $node, $display), $wrappers = gj.tree.plugins.dragAndDrop.private.getTargetWrappers($tree, $node), data = $tree.data(); return function (e, offset, mousePosition) { var $dragEl = $(this), success = false; $displays.each(function () { var $targetDisplay = $(this), $indicator; if ($targetDisplay.droppable('isOver', mousePosition)) { $indicator = $dragEl.find('[data-role="indicator"]'); data.style.dropAsChildIcon ? $indicator.addClass(data.style.dropAsChildIcon) : $indicator.text('+'); success = true; return false; } else { $dragEl.find('[data-role="indicator"]').removeClass(data.style.dropAsChildIcon).empty(); } }); $wrappers.each(function () { var $wrapper = $(this), $indicator, middle; if (!success && $wrapper.droppable('isOver', mousePosition)) { middle = $wrapper.position().top + ($wrapper.outerHeight() / 2); if (mousePosition.y < middle) { $wrapper.addClass(data.style.dropAbove).removeClass(data.style.dropBelow); } else { $wrapper.addClass(data.style.dropBelow).removeClass(data.style.dropAbove); } } else { $wrapper.removeClass(data.style.dropAbove).removeClass(data.style.dropBelow); } }); }; }, createDragStopHandler: function ($tree, $sourceNode, $sourceDisplay) { var $displays = gj.tree.plugins.dragAndDrop.private.getTargetDisplays($tree, $sourceNode, $sourceDisplay), $wrappers = gj.tree.plugins.dragAndDrop.private.getTargetWrappers($tree, $sourceNode), data = $tree.data(); return function (e, mousePosition) { var success = false, record, $targetNode, $sourceParentNode, parent; $(this).draggable('destroy').remove(); $displays.each(function () { var $targetDisplay = $(this), $ul; if ($targetDisplay.droppable('isOver', mousePosition)) { $targetNode = $targetDisplay.closest('li'); $sourceParentNode = $sourceNode.parent('ul').parent('li'); $ul = $targetNode.children('ul'); if ($ul.length === 0) { $ul = $('<ul />').addClass(data.style.list); $targetNode.append($ul); } if (gj.tree.plugins.dragAndDrop.events.nodeDrop($tree, $sourceNode.data('id'), $targetNode.data('id'), $ul.children('li').length + 1) !== false) { $ul.append($sourceNode); //BEGIN: Change node position inside the backend data record = $tree.getDataById($sourceNode.data('id')); gj.tree.methods.removeDataById($tree, $sourceNode.data('id'), data.records); parent = $tree.getDataById($ul.parent().data('id')); if (parent[data.childrenField] === undefined) { parent[data.childrenField] = []; } parent[data.childrenField].push(record); //END gj.tree.plugins.dragAndDrop.private.refresh($tree, $sourceNode, $targetNode, $sourceParentNode); } success = true; return false; } $targetDisplay.droppable('destroy'); }); if (!success) { $wrappers.each(function () { var $targetWrapper = $(this), prepend, orderNumber, sourceNodeId; if ($targetWrapper.droppable('isOver', mousePosition)) { $targetNode = $targetWrapper.closest('li'); $sourceParentNode = $sourceNode.parent('ul').parent('li'); prepend = mousePosition.y < ($targetWrapper.position().top + ($targetWrapper.outerHeight() / 2)); sourceNodeId = $sourceNode.data('id'); orderNumber = $targetNode.prevAll('li:not([data-id="' + sourceNodeId + '"])').length + (prepend ? 1 : 2); if (gj.tree.plugins.dragAndDrop.events.nodeDrop($tree, sourceNodeId, $targetNode.parent('ul').parent('li').data('id'), orderNumber) !== false) { //BEGIN: Change node position inside the backend data record = $tree.getDataById($sourceNode.data('id')); gj.tree.methods.removeDataById($tree, $sourceNode.data('id'), data.records); $tree.getDataById($targetNode.parent().data('id'))[data.childrenField].splice($targetNode.index() + (prepend ? 0 : 1), 0, record); //END if (prepend) { $sourceNode.insertBefore($targetNode); } else { $sourceNode.insertAfter($targetNode); } gj.tree.plugins.dragAndDrop.private.refr