bootstrap-gtreetable.js 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274
  1. /* =========================================================
  2. * bootstrap-gtreetable v2.2.1-alpha
  3. * https://github.com/gilek/bootstrap-gtreetable
  4. * =========================================================
  5. * Copyright 2014 Maciej Kłak
  6. * Licensed under MIT (https://github.com/gilek/bootstrap-gtreetable/blob/master/LICENSE)
  7. * ========================================================= */
  8. (function ($) {
  9. // GTREETABLE CLASSES DEFINITION
  10. // =============================
  11. function GTreeTable(element, options) {
  12. this.options = options;
  13. this.$tree = $(element);
  14. this.language = this.options.languages[this.options.language] === undefined ?
  15. this.options.languages['en-US'] :
  16. $.extend({}, this.options.languages['en-US'], this.options.languages[this.options.language]);
  17. this._isNodeDragging = false;
  18. this._lastId = 0;
  19. this.actions = [];
  20. if (this.options.defaultActions !== null) {
  21. this.actions = this.options.defaultActions;
  22. }
  23. if (this.options.actions !== undefined) {
  24. this.actions.push.apply(this.actions, this.options.actions);
  25. }
  26. if (this.options.cache > 0) {
  27. this.cacheManager = new GTreeTableCache(this);
  28. }
  29. var lang = this.language;
  30. this.template = this.options.template !== undefined ? this.options.template :
  31. '<table class="table gtreetable">' +
  32. '<tr class="' + this.options.classes.node + ' ' + this.options.classes.collapsed + '">' +
  33. '<td>' +
  34. '<span>${draggableIcon}${indent}${ecIcon}${selectedIcon}${typeIcon}${name}</span>' +
  35. '<span class="hide ' + this.options.classes.action + '">${input}${saveButton} ${cancelButton}</span>' +
  36. '<div class="btn-group pull-right ' + this.options.classes.buttons + '">${actionsButton}${actionsList}</div>' +
  37. '</td>' +
  38. '</tr>' +
  39. '</table>';
  40. this.templateParts = this.options.templateParts !== undefined ? this.options.templateParts :
  41. {
  42. draggableIcon: this.options.draggable === true ? '<span class="' + this.options.classes.handleIcon + '">&zwnj;</span><span class="' + this.options.classes.draggablePointer + '">&zwnj;</span>' : '',
  43. indent: '<span class="' + this.options.classes.indent + '">&zwnj;</span>',
  44. ecIcon: '<span class="' + this.options.classes.ceIcon + ' icon"></span>',
  45. selectedIcon: '<span class="' + this.options.classes.selectedIcon + ' icon"></span>',
  46. typeIcon: '<span class="' + this.options.classes.typeIcon + '"></span>',
  47. name: '<span class="' + this.options.classes.name + '"></span>',
  48. input: '<input type="text" name="name" value="" style="width: ' + this.options.inputWidth + '" class="form-control" />',
  49. saveButton: '<button type="button" class="btn btn-sm btn-primary ' + this.options.classes.saveButton + '">' + lang.save + '</button>',
  50. cancelButton: '<button type="button" class="btn btn-sm ' + this.options.classes.cancelButton + '">' + lang.cancel + '</button>',
  51. actionsButton: '<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown">' + lang.action + ' <span class="caret"></span></button>',
  52. actionsList: ''
  53. };
  54. if (this.actions.length > 0) {
  55. var templateActionsList = '<ul class="dropdown-menu" role="menu">' +
  56. '<li role="presentation" class="dropdown-header">' + lang.action + '</li>';
  57. $.each(this.actions, function (index, action) {
  58. if (action.divider === true) {
  59. templateActionsList += '<li class="divider"></li>';
  60. }
  61. else {
  62. var matches = action.name.match(/\$\{([\w\W]+)\}/),
  63. name = matches !== null && matches[1] !== undefined && lang.actions[matches[1]] !== undefined ? lang.actions[matches[1]] : action.name;
  64. templateActionsList += '<li role="presentation"><a href="#notarget" class="node-action-' + index + '" tabindex="-1">' + name + '</a></li>';
  65. }
  66. });
  67. templateActionsList += '</ul>';
  68. this.templateParts.actionsList = templateActionsList;
  69. }
  70. var template = this.template;
  71. $.each(this.templateParts, function(index, value) {
  72. template = template.replace('${'+index+'}', value);
  73. });
  74. this.options.template = template;
  75. if (this.$tree.find('tbody').length === 0) {
  76. this.$tree.append('<tbody></tbody>');
  77. }
  78. if (!this.options.readonly) {
  79. this.$tree.addClass('gtreetable-fullAccess');
  80. }
  81. this.$nodeTemplate = $(this.options.templateSelector !== undefined ?
  82. this.options.templateSelector :
  83. this.options.template).find('.' + this.options.classes.node);
  84. if (this.options.draggable === true) {
  85. this.isNodeDragging(false);
  86. }
  87. this.init();
  88. }
  89. GTreeTable.prototype = {
  90. getNode: function ($node) {
  91. return $node.data('bs.gtreetable.gtreetablenode');
  92. },
  93. getNodeById: function (id) {
  94. return this.getNode(this.$tree.find('.' + this.options.classes.node + "[data-id='" + id + "']"));
  95. },
  96. getSelectedNodes: function () {
  97. var selectedNodes = [],
  98. that = this;
  99. $.each(this.$tree.find('.' + this.options.classes.selected), function () {
  100. selectedNodes.push(that.getNode($(this)));
  101. });
  102. return selectedNodes;
  103. },
  104. getSourceNodes: function (nodeId, force) {
  105. var that = this,
  106. oNode = this.getNodeById(nodeId),
  107. cached = (nodeId > 0 && this.options.cache > 0);
  108. if (cached && force !== true) {
  109. var data = this.cacheManager.get(oNode);
  110. if (data !== undefined) {
  111. var temp = {};
  112. temp[that.options.nodesWrapper] = data;
  113. return temp;
  114. }
  115. }
  116. var sourceOptions = this.options.source(nodeId);
  117. var defaultSourceOptions = {
  118. beforeSend: function () {
  119. if (nodeId > 0) {
  120. oNode.isLoading(true);
  121. }
  122. },
  123. success: function (result) {
  124. if (result[that.options.nodesWrapper] !== undefined) {
  125. data = result[that.options.nodesWrapper];
  126. for (var x = 0; x < data.length; x += 1) {
  127. data[x].parent = nodeId;
  128. }
  129. if (typeof that.options.sort === "function") {
  130. data.sort(that.options.sort);
  131. }
  132. if (cached) {
  133. that.cacheManager.set(oNode, data);
  134. }
  135. }
  136. },
  137. error: function (XMLHttpRequest) {
  138. alert(XMLHttpRequest.status + ': ' + XMLHttpRequest.responseText);
  139. },
  140. complete: function () {
  141. if (nodeId > 0) {
  142. oNode.isLoading(false);
  143. }
  144. }
  145. };
  146. return $.ajax($.extend({}, defaultSourceOptions, sourceOptions));
  147. },
  148. init: function () {
  149. var that = this;
  150. this.getSourceNodes(0).done(function (result) {
  151. var data = result[that.options.nodesWrapper];
  152. for(var x in data) {
  153. var oNewNode = new GTreeTableNode(data[x], that);
  154. oNewNode.insertIntegral(oNewNode);
  155. }
  156. });
  157. },
  158. isNodeDragging: function(action) {
  159. if (action === undefined) {
  160. return this._isNodeDragging;
  161. } else if (action === true) {
  162. this._isNodeDragging = true;
  163. this.$tree.disableSelection();
  164. } else {
  165. this._isNodeDragging = false;
  166. this.$tree.enableSelection();
  167. }
  168. },
  169. generateNewId: function() {
  170. this._lastId += 1;
  171. return 'g' + this._lastId;
  172. }
  173. };
  174. function GTreeTableNode(data, gtreetable) {
  175. this.manager = gtreetable;
  176. this.level = parseInt(data.level);
  177. this.parent = data.parent;
  178. this.name = data.name;
  179. this.type = data.type;
  180. this.id = data.id;
  181. this.insertPosition = undefined;
  182. this.movePosition = undefined;
  183. this.relatedNodeId = undefined;
  184. this._isExpanded = false;
  185. this._isLoading = false;
  186. this._isSaved = data.id === undefined ? false : true;
  187. this._isSelected = false;
  188. this._isHovered = false;
  189. this._isEditable = false;
  190. this.init();
  191. }
  192. GTreeTableNode.prototype = {
  193. getPath: function () {
  194. var oNode = this,
  195. path = [oNode.name],
  196. parent = oNode.parent;
  197. oNode.$node.prevAll('.' + this.manager.options.classes.node).each(function () {
  198. var currentNode = oNode.manager.getNode($(this));
  199. if (currentNode.id === parent) {
  200. parent = currentNode.parent;
  201. path[path.length] = currentNode.name;
  202. }
  203. });
  204. return path;
  205. },
  206. getParents: function () {
  207. var parents = [],
  208. parentId = this.parent;
  209. while (true) {
  210. if (parentId === 0) {
  211. break;
  212. }
  213. var oNode = this.manager.getNodeById(parentId);
  214. parents.push(oNode);
  215. parentId = oNode.parent;
  216. }
  217. return parents;
  218. },
  219. //TODO
  220. // potrzebne tylko w przypadku cache
  221. getIP: function() {
  222. var oNode = this,
  223. ip = '0';
  224. var parents = oNode.getParents();
  225. parents.reverse();
  226. $.each(parents, function() {
  227. ip += '.'+this.id;
  228. });
  229. ip += '.'+oNode.id;
  230. return ip;
  231. },
  232. getSiblings: function () {
  233. var oNode = this,
  234. siblings = [],
  235. findPath = '.' + oNode.manager.options.classes.node + "[data-parent='" + oNode.parent + "']",
  236. prev = oNode.$node.prevAll(findPath);
  237. for (var i = prev.length-1; i >= 0; --i) {
  238. siblings.push(oNode.manager.getNode($(prev[i])));
  239. }
  240. siblings.push(oNode);
  241. oNode.$node
  242. .nextAll(findPath)
  243. .each(function () {
  244. siblings.push(oNode.manager.getNode($(this)));
  245. });
  246. return siblings;
  247. },
  248. getDescendants: function (options) {
  249. var oParentNode = this,
  250. settings = $.extend({},{
  251. depth: 1,
  252. includeNotSaved: false,
  253. index: undefined
  254. },options),
  255. findPath = '.' + oParentNode.manager.options.classes.node,
  256. depth = settings.depth !== -1 || isNaN(settings.depth) ? settings.depth : Infinity,
  257. descendants = [];
  258. if ((settings.includeNotSaved === false)) {
  259. findPath += '.' + oParentNode.manager.options.classes.saved;
  260. }
  261. if (depth > 1) {
  262. oParentNode.$node.nextAll(findPath).each(function () {
  263. var oCurrentNode = oParentNode.manager.getNode($(this));
  264. if ( (oCurrentNode.level <= oParentNode.level) || (oCurrentNode.level === oParentNode.level && oCurrentNode.parent === oParentNode.parent) ) {
  265. if (!(settings.includeNotSaved === true && !oCurrentNode.isSaved())) {
  266. return false;
  267. }
  268. }
  269. descendants.push(oCurrentNode);
  270. });
  271. } else {
  272. oParentNode.$node
  273. .nextAll(findPath + "[data-parent='" + oParentNode.id + "'][data-level='" + (oParentNode.level + 1) + "']")
  274. .each(function () {
  275. descendants.push(oParentNode.manager.getNode($(this)));
  276. });
  277. }
  278. if (!isNaN(settings.index)) {
  279. var index = settings.index >= 0 ? settings.index - 1 : descendants.length + settings.index;
  280. return descendants[index];
  281. }
  282. return descendants;
  283. },
  284. getMovePosition: function() {
  285. return this.movePosition;
  286. },
  287. setMovePosition: function(position, pointerOffset) {
  288. this.$node.removeClass(this.manager.options.classes.draggableContainer);
  289. if (position !== undefined) {
  290. this.$node.addClass(this.manager.options.classes.draggableContainer);
  291. this.movePosition = position;
  292. this.$pointer.css('top', pointerOffset.top + 'px');
  293. this.$pointer.css('left', pointerOffset.left + 'px');
  294. }
  295. },
  296. getId: function () {
  297. return this.id;
  298. },
  299. getName: function () {
  300. return this.isEditable() ? this.$input.val() : this.name;
  301. },
  302. getParent: function () {
  303. return this.parent;
  304. },
  305. getInsertPosition: function () {
  306. return this.insertPosition;
  307. },
  308. getRelatedNodeId: function () {
  309. return this.relatedNodeId;
  310. },
  311. init: function () {
  312. this.$node = this.manager.$nodeTemplate.clone(false);
  313. this.$name = this.$node.find('.' + this.manager.options.classes.name);
  314. this.$ceIcon = this.$node.find('.' + this.manager.options.classes.ceIcon);
  315. this.$typeIcon = this.$node.find('.' + this.manager.options.classes.typeIcon);
  316. this.$icon = this.$node.find('.' + this.manager.options.classes.icon);
  317. this.$action = this.$node.find('.' + this.manager.options.classes.action);
  318. this.$indent = this.$node.find('.' + this.manager.options.classes.indent);
  319. this.$saveButton = this.$node.find('.' + this.manager.options.classes.saveButton);
  320. this.$cancelButton = this.$node.find('.' + this.manager.options.classes.cancelButton);
  321. this.$input = this.$node.find('input');
  322. this.$pointer = this.$node.find('.' + this.manager.options.classes.draggablePointer);
  323. this.render();
  324. this.attachEvents();
  325. this.$node.data('bs.gtreetable.gtreetablenode', this);
  326. },
  327. render: function() {
  328. this.$name.html(this.name);
  329. if (this.id !== undefined) {
  330. this.$node.attr('data-id', this.id);
  331. this.isSaved(true);
  332. if (this.manager.options.draggable === true) {
  333. this.$node.addClass(this.manager.options.classes.draggable);
  334. }
  335. }
  336. this.$node.attr('data-parent', this.parent);
  337. this.$node.attr('data-level', this.level);
  338. this.$indent.css('marginLeft', ((parseInt(this.level) - this.manager.options.rootLevel) * this.manager.options.nodeIndent) + 'px').html('&zwnj;');
  339. if (this.type !== undefined && this.manager.options.types && this.manager.options.types[this.type] !== undefined) {
  340. this.$typeIcon.addClass(this.manager.options.types[this.type]).show();
  341. }
  342. },
  343. attachEvents: function () {
  344. var that = this,
  345. selectLimit = parseInt(this.manager.options.selectLimit);
  346. this.$node.mouseover(function () {
  347. if (!(that.manager.options.draggable === true && that.manager.isNodeDragging() === true)) {
  348. that.$node.addClass(that.manager.options.classes.hovered);
  349. that.isHovered(true);
  350. }
  351. });
  352. this.$node.mouseleave(function () {
  353. that.$node.removeClass(that.manager.options.classes.hovered);
  354. that.isHovered(false);
  355. });
  356. if (isNaN(selectLimit) === false && (selectLimit > 0 || selectLimit === -1) ) {
  357. this.$name.click(function (e) {
  358. if (that.isSelected()) {
  359. if ($.isFunction(that.manager.options.onUnselect)) {
  360. that.manager.options.onUnselect(that);
  361. }
  362. that.isSelected(false);
  363. } else {
  364. var selectedNodes = that.manager.getSelectedNodes();
  365. if (selectLimit === 1 && selectedNodes.length === 1) {
  366. selectedNodes[0].isSelected(false);
  367. selectedNodes = [];
  368. } else if (selectedNodes.length === selectLimit) {
  369. if ($.isFunction(that.manager.options.onSelectOverflow)) {
  370. that.options.onSelectOverflow(that);
  371. }
  372. e.preventDefault();
  373. }
  374. if (selectedNodes.length < selectLimit || selectLimit === -1) {
  375. that.isSelected(true);
  376. }
  377. if ($.isFunction(that.manager.options.onSelect)) {
  378. that.manager.options.onSelect(that);
  379. }
  380. }
  381. });
  382. } else {
  383. this.$name.click(function (e) { that.$ceIcon.click(); });
  384. }
  385. this.$ceIcon.click(function (e) {
  386. if (!that.isExpanded()) {
  387. that.expand({
  388. isAltPressed: e.altKey
  389. });
  390. } else {
  391. that.collapse();
  392. }
  393. });
  394. if (that.manager.options.dragCanExpand === true) {
  395. this.$ceIcon.mouseover(function (e) {
  396. if (that.manager.options.draggable === true && that.manager.isNodeDragging() === true) {
  397. if (!that.isExpanded()) {
  398. that.expand();
  399. }
  400. }
  401. });
  402. }
  403. $.each(this.manager.actions, function (index, action) {
  404. that.$node.find('.' + that.manager.options.classes.action + '-' + index).click(function (event) {
  405. action.event(that, that.manager);
  406. });
  407. });
  408. this.$saveButton.click(function () {
  409. that.save();
  410. });
  411. this.$cancelButton.click(function () {
  412. that.saveCancel();
  413. });
  414. if (that.manager.options.draggable === true) {
  415. var getMoveData = function (ui, $droppable) {
  416. var draggableOffsetTop = ui.offset.top - $droppable.offset().top,
  417. containerOffsetTop = $droppable.offset().top,
  418. containerHeight = $droppable.outerHeight(),
  419. containerWorkspace = containerHeight - Math.round(ui.helper.outerHeight() / 2),
  420. movePosition,
  421. pointerOffset = {left: that.manager.$tree.offset().left + 5 };
  422. if (draggableOffsetTop <= (containerWorkspace * 0.3)) {
  423. movePosition = 'before';
  424. pointerOffset.top = (containerOffsetTop + 3);
  425. } else if (draggableOffsetTop <= (containerWorkspace * 0.7)) {
  426. movePosition = 'lastChild';
  427. pointerOffset.top = containerOffsetTop + (containerWorkspace / 2);
  428. } else {
  429. movePosition = 'after';
  430. pointerOffset.top = containerOffsetTop + containerWorkspace;
  431. }
  432. pointerOffset.top += 2;
  433. return {
  434. position: movePosition,
  435. pointerOffset: pointerOffset
  436. };
  437. };
  438. this.$node
  439. .draggable( {
  440. scroll:true,
  441. refreshPositions: that.manager.options.dragCanExpand,
  442. helper: function (e) {
  443. var oName = that.manager.getNode($(this));
  444. return '<mark class="' + that.manager.options.classes.draggableHelper + '">' + oName.name + '</mark>';
  445. },
  446. cursorAt: {top:0, left: 0 },
  447. handle: '.'+ that.manager.options.classes.handleIcon,
  448. start: function (e) {
  449. if (!$.browser.webkit) {
  450. $(this).data("bs.gtreetable.gtreetablenode.scrollTop", $(window).scrollTop());
  451. }
  452. },
  453. stop: function (e) {
  454. that.manager.isNodeDragging(false);
  455. },
  456. drag: function (e, ui) {
  457. if (!$.browser.webkit) {
  458. var strollTop = $(window).scrollTop(),
  459. delta = ($(this).data("bs.gtreetable.gtreetablenode.scrollTop") - strollTop);
  460. ui.position.top -= strollTop + delta;
  461. $(this).data("bs.gtreetable.gtreetablenode.startingScrollTop", strollTop);
  462. }
  463. var $droppable = $(this).data("bs.gtreetable.gtreetablenode.currentDroppable");
  464. if ($droppable) {
  465. var data = getMoveData(ui, $droppable);
  466. that.manager.getNode($droppable).setMovePosition(data.position, data.pointerOffset);
  467. }
  468. }
  469. })
  470. .droppable({
  471. accept: '.' + that.manager.options.classes.node,
  472. over: function(event, ui) {
  473. var $this = $(this),
  474. data = getMoveData(ui, $this);
  475. that.manager.getNode($this).setMovePosition(data.position, data.pointerOffset);
  476. ui.draggable.data("bs.gtreetable.gtreetablenode.currentDroppable", $this);
  477. },
  478. out: function(event, ui) {
  479. ui.draggable.removeData("bs.gtreetable.gtreetablenode.currentDroppable");
  480. that.manager.getNode($(this)).setMovePosition();
  481. },
  482. drop: function(event, ui) {
  483. var $this = $(this),
  484. oNode = that.manager.getNode($this),
  485. movePosition = oNode.getMovePosition();
  486. ui.draggable.removeData("bs.gtreetable.gtreetablenode.currentDroppable");
  487. oNode.setMovePosition();
  488. that.manager.getNode(ui.draggable).move(oNode, movePosition);
  489. }
  490. });
  491. }
  492. },
  493. makeEditable: function () {
  494. this.showForm(true);
  495. },
  496. save: function () {
  497. var oNode = this;
  498. if ($.isFunction(oNode.manager.options.onSave)) {
  499. $.when($.ajax(oNode.manager.options.onSave(oNode))).done(function (data) {
  500. oNode._save(data);
  501. });
  502. } else {
  503. oNode._save({
  504. name: oNode.getName(),
  505. id: oNode.manager.generateNewId()
  506. });
  507. }
  508. },
  509. _save: function(data) {
  510. var oNode = this;
  511. oNode.id = data.id;
  512. oNode.name = data.name;
  513. if ($.isFunction(oNode.manager.options.sort)) {
  514. oNode.sort();
  515. }
  516. if (this.manager.options.cache > 0) {
  517. this.manager.cacheManager.synchronize(oNode.isSaved() ? 'edit' : 'add', oNode);
  518. }
  519. oNode.render();
  520. oNode.showForm(false);
  521. oNode.isHovered(false);
  522. },
  523. saveCancel: function () {
  524. this.showForm(false);
  525. if (!this.isSaved()) {
  526. this._remove();
  527. }
  528. },
  529. expand: function (options) {
  530. var oNode = this,
  531. prevNode = oNode,
  532. settings = $.extend({}, {
  533. isAltPressed: false,
  534. onAfterFill: function (oNode, data) {
  535. oNode.isExpanded(true);
  536. if (data.length === 0) {
  537. if (oNode.manager.options.showExpandIconOnEmpty === true) {
  538. oNode.isExpanded(false);
  539. } else {
  540. oNode.showCeIcon(false);
  541. }
  542. }
  543. }
  544. },options);
  545. $.when(this.manager.getSourceNodes(oNode.id, settings.isAltPressed)).done(function (result) {
  546. var data = result[oNode.manager.options.nodesWrapper];
  547. for(var x in data) {
  548. var newNode = new GTreeTableNode(data[x], oNode.manager);
  549. oNode.insertIntegral(newNode, prevNode);
  550. prevNode = newNode;
  551. }
  552. if (settings && typeof $.isFunction(settings.onAfterFill)) {
  553. settings.onAfterFill(oNode, data);
  554. }
  555. });
  556. },
  557. collapse: function () {
  558. this.isExpanded(false);
  559. $.each(this.getDescendants({ depth: -1, includeNotSaved: true }), function () {
  560. this.$node.remove();
  561. });
  562. },
  563. _canAdd: function(oNewNode) {
  564. var data = { result: !(oNewNode.parent === 0 && this.manager.options.manyroots === false) };
  565. if (!data.result) {
  566. data.message = this.manager.language.messages.onNewRootNotAllowed;
  567. }
  568. return data;
  569. },
  570. add: function (position, type) {
  571. var oTriggerNode = this,
  572. childPosition = (position === 'lastChild' || position === 'firstChild'),
  573. oNewNode = new GTreeTableNode({
  574. level: oTriggerNode.level + (childPosition ? 1 : 0),
  575. parent: oTriggerNode.level === this.manager.options.rootLevel && !childPosition ? 0 : (childPosition ? oTriggerNode.id : oTriggerNode.parent),
  576. type: type
  577. },this.manager),
  578. canAddData = this._canAdd(oNewNode);
  579. if (!canAddData.result) {
  580. alert(canAddData.message);
  581. return false;
  582. }
  583. function ins() {
  584. if (childPosition) {
  585. oTriggerNode.isExpanded(true);
  586. oTriggerNode.showCeIcon(true);
  587. }
  588. oNewNode.insert(position, oTriggerNode);
  589. oNewNode.insertPosition = position;
  590. oNewNode.relatedNodeId = oTriggerNode.id;
  591. oNewNode.showForm(true);
  592. }
  593. if ( childPosition && !oTriggerNode.isExpanded() ) {
  594. oTriggerNode.expand({
  595. onAfterFill: function () {
  596. ins();
  597. }
  598. });
  599. } else {
  600. ins();
  601. }
  602. },
  603. insert: function (position, oRelatedNode) {
  604. var oNode = this,
  605. oLastChild,
  606. oContext;
  607. if (position === 'before') {
  608. oRelatedNode.$node.before(oNode.$node);
  609. } else if (position === 'after') {
  610. oContext = oRelatedNode;
  611. if (oRelatedNode.isExpanded()) {
  612. oLastChild = oRelatedNode.getDescendants({ depth: 1, index: -1, includeNotSaved: true });
  613. oContext = oLastChild === undefined ? oContext : oLastChild;
  614. }
  615. oContext.$node.after(oNode.$node);
  616. } else if (position === 'firstChild') {
  617. this.manager.getNodeById(oRelatedNode.id).$node.after(oNode.$node);
  618. } else if (position === 'lastChild') {
  619. oLastChild = oRelatedNode.getDescendants({ depth: 1, index: -1, includeNotSaved: true });
  620. oContext = oLastChild === undefined ? oRelatedNode : oLastChild;
  621. oContext.$node.after(oNode.$node);
  622. } else {
  623. throw "Wrong position.";
  624. }
  625. },
  626. insertIntegral: function (oNewNode, oNode) {
  627. if (oNode === undefined) {
  628. this.manager.$tree.append(oNewNode.$node);
  629. } else {
  630. oNode.$node.after(oNewNode.$node);
  631. }
  632. },
  633. remove: function () {
  634. var oNode = this;
  635. if (oNode.isSaved() && $.isFunction(oNode.manager.options.onDelete)) {
  636. $.when($.ajax(oNode.manager.options.onDelete(oNode))).done(function () {
  637. oNode._remove();
  638. });
  639. } else {
  640. this._remove();
  641. }
  642. },
  643. _remove: function () {
  644. if (this.isExpanded() === true) {
  645. this.collapse();
  646. }
  647. this.$node.remove();
  648. if (this.parent > 0) {
  649. var oParent = this.manager.getNodeById(this.parent);
  650. if (oParent.getDescendants({ depth: 1, includeNotSaved: true }).length === 0) {
  651. oParent.collapse();
  652. }
  653. }
  654. if (this.manager.options.cache > 0) {
  655. this.manager.cacheManager.synchronize('delete', this);
  656. }
  657. },
  658. _canMove: function(oDestination, position) {
  659. var oNode = this,
  660. data = { result: true };
  661. if (oDestination.parent === 0 && this.manager.options.manyroots === false && position !== 'lastChild') {
  662. data.result = false;
  663. data.message = this.manager.language.messages.onMoveAsRoot;
  664. } else {
  665. $.each(oDestination.getParents(), function () {
  666. if (this.id === oNode.id) {
  667. data.result = false;
  668. data.message = this.manager.language.messages.onMoveInDescendant;
  669. return false;
  670. }
  671. });
  672. }
  673. return data;
  674. },
  675. move: function(oDestination, position) {
  676. var oNode = this,
  677. moveData = this._canMove(oDestination, position);
  678. if (moveData.result === false) {
  679. alert(moveData.message);
  680. return false;
  681. }
  682. if ($.isFunction(oNode.manager.options.onMove)) {
  683. $.when($.ajax(oNode.manager.options.onMove(oNode, oDestination, position))).done(function (data) {
  684. oNode._move(oDestination, position);
  685. });
  686. } else {
  687. oNode._move(oDestination, position);
  688. }
  689. },
  690. _move: function(oDestination, position) {
  691. var oNode = this,
  692. oNodeDescendants = oNode.getDescendants({ depth: -1, includeNotSaved: true }),
  693. oOldNode = $.extend({}, oNode),
  694. oldIP = oNode.getIP(),
  695. delta = oDestination.level - oNode.level;
  696. oNode.parent = position === 'lastChild' ? oDestination.id : oDestination.parent;
  697. oNode.level = oDestination.level;
  698. if (position === 'lastChild' && !oDestination.isExpanded()) {
  699. oNode.$node.remove();
  700. $.each(oNodeDescendants, function () {
  701. this.$node.remove();
  702. });
  703. } else {
  704. if (position === 'lastChild') {
  705. oNode.level += 1;
  706. oDestination.showCeIcon(true);
  707. }
  708. oNode.render();
  709. oNode.insert(position, oDestination);
  710. if (oNodeDescendants.length > 0) {
  711. var prevNode = oNode.$node;
  712. if (position === 'lastChild') {
  713. delta += 1;
  714. }
  715. $.each(oNodeDescendants, function() {
  716. var oNode = this;
  717. oNode.level += delta;
  718. oNode.render();
  719. prevNode.after(oNode.$node);
  720. prevNode = oNode.$node;
  721. });
  722. }
  723. }
  724. // sprawdza, czy nie byl przeniesiony ostatni element
  725. // oOldSourceParent !== undefined => parent = 0
  726. var oOldNodeParent = oNode.manager.getNodeById(oOldNode.parent);
  727. if (oOldNodeParent !== undefined && oOldNodeParent.getDescendants({depth: 1, includeNotSaved: true}).length === 0) {
  728. oOldNodeParent.isExpanded(false);
  729. }
  730. if ($.isFunction(oNode.manager.options.sort)) {
  731. oNode.sort();
  732. }
  733. if (this.manager.options.cache > 0) {
  734. this.manager.cacheManager.synchronize('move', oNode, { 'oOldNode': oOldNode, 'oldIP': oldIP });
  735. }
  736. },
  737. sort: function() {
  738. var oNode = this,
  739. oSiblings = oNode.getSiblings();
  740. // nie ma rodzenstwa = sortowanie nie jest potrzebne
  741. if (oSiblings.length > 0) {
  742. var oDescendants = !oNode.isExpanded() ? [] : oNode.getDescendants({ depth: -1, includeNotSaved: true }),
  743. oRelated;
  744. $.each(oSiblings, function () {
  745. if (oNode.manager.options.sort(oNode, this) === -1) {
  746. oRelated = this;
  747. return false;
  748. }
  749. });
  750. if (oRelated === undefined) {
  751. oRelated = oSiblings[oSiblings.length-1];
  752. if (oRelated.isExpanded()) {
  753. oRelated = oNode.manager.getNodeById(oNode.parent).getDescendants({ depth: -1, index: -1, includeNotSaved: true });
  754. }
  755. oRelated.$node.after(oNode.$node);
  756. } else {
  757. oRelated.$node.before(oNode.$node);
  758. }
  759. var prevNode = oNode.$node;
  760. $.each(oDescendants, function() {
  761. var oCurrentNode = this;
  762. prevNode.after(oCurrentNode.$node);
  763. prevNode = oCurrentNode.$node;
  764. });
  765. }
  766. },
  767. isLoading: function (action) {
  768. if (action === undefined) {
  769. return this._isLoading;
  770. } else if (action) {
  771. this.$name.addClass(this.manager.options.classes.loading);
  772. this._isLoading = true;
  773. } else {
  774. this.$name.removeClass(this.manager.options.classes.loading);
  775. this._isLoading = false;
  776. }
  777. },
  778. isSaved: function (action) {
  779. if (action === undefined) {
  780. return this._isSaved;
  781. } else if (action) {
  782. this.$node.addClass(this.manager.options.classes.saved);
  783. this._isSaved = true;
  784. } else {
  785. this.$node.removeClass(this.manager.options.classes.saved);
  786. this._isSaved = false;
  787. }
  788. },
  789. isSelected: function (action) {
  790. if (action === undefined) {
  791. return this._isSelected;
  792. } else if (action) {
  793. this.$node.addClass(this.manager.options.classes.selected);
  794. this._isSelected = true;
  795. } else {
  796. this.$node.removeClass(this.manager.options.classes.selected);
  797. this._isSelected = false;
  798. }
  799. },
  800. isExpanded: function (action) {
  801. if (action === undefined) {
  802. return this._isExpanded;
  803. } else if (action) {
  804. this.$node.addClass(this.manager.options.classes.expanded).removeClass(this.manager.options.classes.collapsed);
  805. this._isExpanded = true;
  806. } else {
  807. this.$node.addClass(this.manager.options.classes.collapsed).removeClass(this.manager.options.classes.expanded);
  808. this._isExpanded = false;
  809. }
  810. },
  811. isHovered: function (action) {
  812. if (action === undefined) {
  813. return this._isHovered;
  814. } else if (action) {
  815. this.$node.addClass(this.manager.options.classes.hovered);
  816. this._isHovered = true;
  817. } else {
  818. this.$node.removeClass(this.manager.options.classes.hovered);
  819. this.$node.find('.btn-group').removeClass('open');
  820. this._isHovered = false;
  821. }
  822. },
  823. isEditable: function (action) {
  824. if (action === undefined) {
  825. return this._isEditable;
  826. } else {
  827. this._isEditable = action;
  828. }
  829. },
  830. showCeIcon: function (action) {
  831. this.$ceIcon.css('visibility', action ? 'visible' : 'hidden');
  832. },
  833. showForm: function (action) {
  834. if (action === true) {
  835. this.isEditable(true);
  836. this.$input.val(this.name);
  837. this.$name.addClass('hide');
  838. this.$action.removeClass('hide');
  839. //TODO nie dziala zawsze
  840. this.$input.focus();
  841. } else {
  842. this.isEditable(false);
  843. this.$name.removeClass('hide');
  844. this.$action.addClass('hide');
  845. }
  846. }
  847. };
  848. function GTreeTableCache(manager) {
  849. this._cached = {};
  850. this.manager = manager;
  851. }
  852. GTreeTableCache.prototype = {
  853. _getIP: function (param) {
  854. return typeof param === "string" ? param : param.getIP();
  855. },
  856. get: function(param) {
  857. return this._cached[this._getIP(param)];
  858. },
  859. set: function (param, data) {
  860. this._cached[this._getIP(param)] = data;
  861. },
  862. remove: function (param) {
  863. this._cached[this._getIP(param)] = undefined;
  864. },
  865. synchronize: function (method, oNode, params) {
  866. if (oNode.parent > 0) {
  867. switch (method) {
  868. case 'add':
  869. this._synchronizeAdd(oNode);
  870. break;
  871. case 'edit':
  872. this._synchronizeEdit(oNode);
  873. break;
  874. case 'delete':
  875. this._synchronizeDelete(oNode);
  876. break;
  877. case 'move':
  878. this._synchronizeMove(oNode, params);
  879. break;
  880. default:
  881. throw "Wrong method.";
  882. }
  883. }
  884. },
  885. _synchronizeAdd: function (oNode) {
  886. var oParentNode = this.manager.getNodeById(oNode.parent);
  887. if (this.manager.options.cache > 1) {
  888. var data = this.get(oParentNode);
  889. if (data !== undefined) {
  890. data.push({
  891. id: oNode.id,
  892. name: oNode.getName(),
  893. level: oNode.level,
  894. type: oNode.type,
  895. parent: oNode.parent
  896. });
  897. this.set(oParentNode, this.isSortDefined() ? this.sort(data) : data);
  898. }
  899. } else {
  900. this.remove(oParentNode);
  901. }
  902. },
  903. _synchronizeEdit: function (oNode) {
  904. var oParentNode = this.manager.getNodeById(oNode.parent);
  905. if (this.manager.options.cache > 1) {
  906. var data = this.get(oParentNode);
  907. $.each(data, function () {
  908. if (this.id === oNode.id) {
  909. this.name = oNode.getName();
  910. return false;
  911. }
  912. });
  913. this.set(oParentNode, this.isSortDefined() ? this.sort(data) : data);
  914. } else {
  915. this.remove(oParentNode);
  916. }
  917. },
  918. _synchronizeDelete: function(oNode) {
  919. var oParentNode = this.manager.getNodeById(oNode.parent);
  920. if (this.manager.options.cache > 1) {
  921. var data = this.get(oParentNode),
  922. position;
  923. // pobieranie pozycji
  924. $.each(data, function(index) {
  925. if (this.id === oNode.id) {
  926. position = index;
  927. return false;
  928. }
  929. });
  930. if (position !== undefined) {
  931. data.splice(position, 1);
  932. this.set(oParentNode, data);
  933. }
  934. } else {
  935. this.remove(oParentNode);
  936. }
  937. },
  938. _synchronizeMove: function(oNode, params) {
  939. var that = this,
  940. newIP = oNode.getIP(),
  941. delta = oNode.level - params.oOldNode.level;
  942. $.each(this._cached, function (index) {
  943. if (index === params.oldIP || index.indexOf(params.oldIP+'.') === 0) {
  944. if (that.manager.options.cache > 1) {
  945. var newData = [],
  946. newIndex = index !== params.oldIP ? newIP + index.substr(params.oldIP.length) : newIP;
  947. $(that.get(index)).each(function () {
  948. this.level += delta;
  949. newData.push(this);
  950. });
  951. that.set(newIndex, newData);
  952. }
  953. that.remove(index);
  954. }
  955. });
  956. this.synchronize('delete', params.oOldNode);
  957. this.synchronize('add', oNode);
  958. },
  959. isSortDefined: function () {
  960. return $.isFunction(this.manager.options.sort);
  961. },
  962. sort: function (data) {
  963. return data.sort(this.manager.options.sort);
  964. }
  965. };
  966. // OVERLAYINPUT PLUGIN DEFINITION
  967. // ==============================
  968. function Plugin(option, _relatedTarget) {
  969. var retval = null;
  970. this.each(function () {
  971. var $this = $(this),
  972. data = $this.data('bs.gtreetable'),
  973. options = $.extend({}, $.fn.gtreetable.defaults, $this.data(), typeof option === 'object' && option);
  974. if (!data) {
  975. data = new GTreeTable(this, options);
  976. $this.data('bs.gtreetable', data);
  977. }
  978. if (typeof option === 'string') {
  979. retval = data[option](_relatedTarget);
  980. }
  981. });
  982. if (!retval) {
  983. retval = this;
  984. }
  985. return retval;
  986. }
  987. var old = $.fn.gtreetable;
  988. $.fn.gtreetable = Plugin;
  989. $.fn.gtreetable.Constructor = GTreeTable;
  990. $.fn.gtreetable.defaults = {
  991. nodesWrapper: 'nodes',
  992. nodeIndent: 16,
  993. language: 'en',
  994. inputWidth: '60%',
  995. cache: 2,
  996. readonly: false,
  997. selectLimit: 1,
  998. rootLevel: 0,
  999. manyroots: false,
  1000. draggable: false,
  1001. dragCanExpand: false,
  1002. showExpandIconOnEmpty: false,
  1003. languages: {
  1004. 'en-US': {
  1005. save: 'Save',
  1006. cancel: 'Cancel',
  1007. action: 'Action',
  1008. actions: {
  1009. createBefore: 'Create before',
  1010. createAfter: 'Create after',
  1011. createFirstChild: 'Create first child',
  1012. createLastChild: 'Create last child',
  1013. update: 'Update',
  1014. 'delete': 'Delete'
  1015. },
  1016. messages: {
  1017. onDelete: 'Are you sure?',
  1018. onNewRootNotAllowed: 'Adding the now node as root is not allowed.',
  1019. onMoveInDescendant: 'The target node should not be descendant.',
  1020. onMoveAsRoot: 'The target node should not be root.'
  1021. }
  1022. }
  1023. },
  1024. defaultActions: [
  1025. {
  1026. name: '${createBefore}',
  1027. event: function (oNode, oManager) {
  1028. oNode.add('before', 'default');
  1029. }
  1030. },
  1031. {
  1032. name: '${createAfter}',
  1033. event: function (oNode, oManager) {
  1034. oNode.add('after', 'default');
  1035. }
  1036. },
  1037. {
  1038. name: '${createFirstChild}',
  1039. event: function (oNode, oManager) {
  1040. oNode.add('firstChild', 'default');
  1041. }
  1042. },
  1043. {
  1044. name: '${createLastChild}',
  1045. event: function (oNode, oManager) {
  1046. oNode.add('lastChild', 'default');
  1047. }
  1048. },
  1049. {
  1050. divider: true
  1051. },
  1052. {
  1053. name: '${update}',
  1054. event: function (oNode, oManager) {
  1055. oNode.makeEditable();
  1056. }
  1057. },
  1058. {
  1059. name: '${delete}',
  1060. event: function (oNode, oManager) {
  1061. if (confirm(oManager.language.messages.onDelete)) {
  1062. oNode.remove();
  1063. }
  1064. }
  1065. }
  1066. ],
  1067. classes: {
  1068. node: 'node',
  1069. loading: 'node-loading',
  1070. selected: 'node-selected',
  1071. hovered: 'node-hovered',
  1072. expanded: 'node-expanded',
  1073. collapsed : 'node-collapsed',
  1074. draggable : 'node-draggable',
  1075. draggableHelper : 'node-draggable-helper',
  1076. draggablePointer : 'node-draggable-pointer',
  1077. draggableContainer : 'node-draggable-container',
  1078. saved: 'node-saved',
  1079. name: 'node-name',
  1080. icon: 'node-icon',
  1081. selectedIcon: 'node-icon-selected',
  1082. ceIcon: 'node-icon-ce',
  1083. typeIcon: 'node-icon-type',
  1084. handleIcon : 'node-icon-handle',
  1085. action: 'node-action',
  1086. indent: 'node-indent',
  1087. saveButton: 'node-save',
  1088. cancelButton: 'node-cancel',
  1089. buttons: 'node-buttons'
  1090. }
  1091. };
  1092. // OVERLAYINPUT NO CONFLICT
  1093. // ========================
  1094. $.fn.gtreetable.noConflict = function () {
  1095. $.fn.gtreetable = old;
  1096. return this;
  1097. };
  1098. }(jQuery));