jquery.treegrid.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. /*
  2. * jQuery treegrid Plugin 0.3.0
  3. * https://github.com/maxazan/jquery-treegrid
  4. *
  5. * Copyright 2013, Pomazan Max
  6. * Licensed under the MIT licenses.
  7. */
  8. (function($) {
  9. var methods = {
  10. /**
  11. * Initialize tree
  12. *
  13. * @param {Object} options
  14. * @returns {Object[]}
  15. */
  16. initTree: function(options) {
  17. var settings = $.extend({}, this.treegrid.defaults, options);
  18. return this.each(function() {
  19. var $this = $(this);
  20. $this.treegrid('setTreeContainer', $(this));
  21. $this.treegrid('setSettings', settings);
  22. settings.getRootNodes.apply(this, [$(this)]).treegrid('initNode', settings);
  23. $this.treegrid('getRootNodes').treegrid('render');
  24. });
  25. },
  26. /**
  27. * Initialize node
  28. *
  29. * @param {Object} settings
  30. * @returns {Object[]}
  31. */
  32. initNode: function(settings) {
  33. return this.each(function() {
  34. var $this = $(this);
  35. $this.treegrid('setTreeContainer', settings.getTreeGridContainer.apply(this));
  36. $this.treegrid('getChildNodes').treegrid('initNode', settings);
  37. $this.treegrid('initExpander').treegrid('initIndent').treegrid('initEvents').treegrid('initState').treegrid('initChangeEvent').treegrid("initSettingsEvents");
  38. });
  39. },
  40. initChangeEvent: function() {
  41. var $this = $(this);
  42. //Save state on change
  43. $this.on("change", function() {
  44. var $this = $(this);
  45. $this.treegrid('render');
  46. if ($this.treegrid('getSetting', 'saveState')) {
  47. $this.treegrid('saveState');
  48. }
  49. });
  50. return $this;
  51. },
  52. /**
  53. * Initialize node events
  54. *
  55. * @returns {Node}
  56. */
  57. initEvents: function() {
  58. var $this = $(this);
  59. //Default behavior on collapse
  60. $this.on("collapse", function() {
  61. var $this = $(this);
  62. $this.removeClass('treegrid-expanded');
  63. $this.addClass('treegrid-collapsed');
  64. });
  65. //Default behavior on expand
  66. $this.on("expand", function() {
  67. var $this = $(this);
  68. $this.removeClass('treegrid-collapsed');
  69. $this.addClass('treegrid-expanded');
  70. });
  71. return $this;
  72. },
  73. /**
  74. * Initialize events from settings
  75. *
  76. * @returns {Node}
  77. */
  78. initSettingsEvents: function() {
  79. var $this = $(this);
  80. //Save state on change
  81. $this.on("change", function() {
  82. var $this = $(this);
  83. if (typeof($this.treegrid('getSetting', 'onChange')) === "function") {
  84. $this.treegrid('getSetting', 'onChange').apply($this);
  85. }
  86. });
  87. //Default behavior on collapse
  88. $this.on("collapse", function() {
  89. var $this = $(this);
  90. if (typeof($this.treegrid('getSetting', 'onCollapse')) === "function") {
  91. $this.treegrid('getSetting', 'onCollapse').apply($this);
  92. }
  93. });
  94. //Default behavior on expand
  95. $this.on("expand", function() {
  96. var $this = $(this);
  97. if (typeof($this.treegrid('getSetting', 'onExpand')) === "function") {
  98. $this.treegrid('getSetting', 'onExpand').apply($this);
  99. }
  100. });
  101. return $this;
  102. },
  103. /**
  104. * Initialize expander for node
  105. *
  106. * @returns {Node}
  107. */
  108. initExpander: function() {
  109. var $this = $(this);
  110. var cell = $this.find('td').get($this.treegrid('getSetting', 'treeColumn'));
  111. var tpl = $this.treegrid('getSetting', 'expanderTemplate');
  112. var expander = $this.treegrid('getSetting', 'getExpander').apply(this);
  113. if (expander) {
  114. expander.remove();
  115. }
  116. $(tpl).prependTo(cell).click(function() {
  117. $($(this).closest('tr')).treegrid('toggle');
  118. });
  119. return $this;
  120. },
  121. /**
  122. * Initialize indent for node
  123. *
  124. * @returns {Node}
  125. */
  126. initIndent: function() {
  127. var $this = $(this);
  128. $this.find('.treegrid-indent').remove();
  129. var tpl = $this.treegrid('getSetting', 'indentTemplate');
  130. var expander = $this.find('.treegrid-expander');
  131. var depth = $this.treegrid('getDepth');
  132. for (var i = 0; i < depth; i++) {
  133. $(tpl).insertBefore(expander);
  134. }
  135. return $this;
  136. },
  137. /**
  138. * Initialise state of node
  139. *
  140. * @returns {Node}
  141. */
  142. initState: function() {
  143. var $this = $(this);
  144. if ($this.treegrid('getSetting', 'saveState') && !$this.treegrid('isFirstInit')) {
  145. $this.treegrid('restoreState');
  146. } else {
  147. if ($this.treegrid('getSetting', 'initialState') === "expanded") {
  148. $this.treegrid('expand');
  149. } else {
  150. $this.treegrid('collapse');
  151. }
  152. }
  153. return $this;
  154. },
  155. /**
  156. * Return true if this tree was never been initialised
  157. *
  158. * @returns {Boolean}
  159. */
  160. isFirstInit: function() {
  161. var tree = $(this).treegrid('getTreeContainer');
  162. if (tree.data('first_init') === undefined) {
  163. tree.data('first_init', $.cookie(tree.treegrid('getSetting', 'saveStateName')) === undefined);
  164. }
  165. return tree.data('first_init');
  166. },
  167. /**
  168. * Save state of current node
  169. *
  170. * @returns {Node}
  171. */
  172. saveState: function() {
  173. var $this = $(this);
  174. if ($this.treegrid('getSetting', 'saveStateMethod') === 'cookie') {
  175. var stateArrayString = $.cookie($this.treegrid('getSetting', 'saveStateName')) || '';
  176. var stateArray = (stateArrayString === '' ? [] : stateArrayString.split(','));
  177. var nodeId = $this.treegrid('getNodeId');
  178. if ($this.treegrid('isExpanded')) {
  179. if ($.inArray(nodeId, stateArray) === -1) {
  180. stateArray.push(nodeId);
  181. }
  182. } else if ($this.treegrid('isCollapsed')) {
  183. if ($.inArray(nodeId, stateArray) !== -1) {
  184. stateArray.splice($.inArray(nodeId, stateArray), 1);
  185. }
  186. }
  187. $.cookie($this.treegrid('getSetting', 'saveStateName'), stateArray.join(','));
  188. }
  189. return $this;
  190. },
  191. /**
  192. * Restore state of current node.
  193. *
  194. * @returns {Node}
  195. */
  196. restoreState: function() {
  197. var $this = $(this);
  198. if ($this.treegrid('getSetting', 'saveStateMethod') === 'cookie') {
  199. var stateArray = $.cookie($this.treegrid('getSetting', 'saveStateName')).split(',');
  200. if ($.inArray($this.treegrid('getNodeId'), stateArray) !== -1) {
  201. $this.treegrid('expand');
  202. } else {
  203. $this.treegrid('collapse');
  204. }
  205. }
  206. return $this;
  207. },
  208. /**
  209. * Method return setting by name
  210. *
  211. * @param {type} name
  212. * @returns {unresolved}
  213. */
  214. getSetting: function(name) {
  215. if (!$(this).treegrid('getTreeContainer')) {
  216. return null;
  217. }
  218. return $(this).treegrid('getTreeContainer').data('settings')[name];
  219. },
  220. /**
  221. * Add new settings
  222. *
  223. * @param {Object} settings
  224. */
  225. setSettings: function(settings) {
  226. $(this).treegrid('getTreeContainer').data('settings', settings);
  227. },
  228. /**
  229. * Return tree container
  230. *
  231. * @returns {HtmlElement}
  232. */
  233. getTreeContainer: function() {
  234. return $(this).data('treegrid');
  235. },
  236. /**
  237. * Set tree container
  238. *
  239. * @param {HtmlE;ement} container
  240. */
  241. setTreeContainer: function(container) {
  242. return $(this).data('treegrid', container);
  243. },
  244. /**
  245. * Method return all root nodes of tree.
  246. *
  247. * Start init all child nodes from it.
  248. *
  249. * @returns {Array}
  250. */
  251. getRootNodes: function() {
  252. return $(this).treegrid('getSetting', 'getRootNodes').apply(this, [$(this).treegrid('getTreeContainer')]);
  253. },
  254. /**
  255. * Method return all nodes of tree.
  256. *
  257. * @returns {Array}
  258. */
  259. getAllNodes: function() {
  260. return $(this).treegrid('getSetting', 'getAllNodes').apply(this, [$(this).treegrid('getTreeContainer')]);
  261. },
  262. /**
  263. * Mthod return true if element is Node
  264. *
  265. * @returns {String}
  266. */
  267. isNode: function() {
  268. return $(this).treegrid('getNodeId') !== null;
  269. },
  270. /**
  271. * Mthod return id of node
  272. *
  273. * @returns {String}
  274. */
  275. getNodeId: function() {
  276. if ($(this).treegrid('getSetting', 'getNodeId') === null) {
  277. return null;
  278. } else {
  279. return $(this).treegrid('getSetting', 'getNodeId').apply(this);
  280. }
  281. },
  282. /**
  283. * Method return parent id of node or null if root node
  284. *
  285. * @returns {String}
  286. */
  287. getParentNodeId: function() {
  288. return $(this).treegrid('getSetting', 'getParentNodeId').apply(this);
  289. },
  290. /**
  291. * Method return parent node or null if root node
  292. *
  293. * @returns {Object[]}
  294. */
  295. getParentNode: function() {
  296. if ($(this).treegrid('getParentNodeId') === null) {
  297. return null;
  298. } else {
  299. return $(this).treegrid('getSetting', 'getNodeById').apply(this, [$(this).treegrid('getParentNodeId'), $(this).treegrid('getTreeContainer')]);
  300. }
  301. },
  302. /**
  303. * Method return array of child nodes or null if node is leaf
  304. *
  305. * @returns {Object[]}
  306. */
  307. getChildNodes: function() {
  308. return $(this).treegrid('getSetting', 'getChildNodes').apply(this, [$(this).treegrid('getNodeId'), $(this).treegrid('getTreeContainer')]);
  309. },
  310. /**
  311. * Method return depth of tree.
  312. *
  313. * This method is needs for calculate indent
  314. *
  315. * @returns {Number}
  316. */
  317. getDepth: function() {
  318. if ($(this).treegrid('getParentNode') === null) {
  319. return 0;
  320. }
  321. return $(this).treegrid('getParentNode').treegrid('getDepth') + 1;
  322. },
  323. /**
  324. * Method return true if node is root
  325. *
  326. * @returns {Boolean}
  327. */
  328. isRoot: function() {
  329. return $(this).treegrid('getDepth') === 0;
  330. },
  331. /**
  332. * Method return true if node has no child nodes
  333. *
  334. * @returns {Boolean}
  335. */
  336. isLeaf: function() {
  337. return $(this).treegrid('getChildNodes').length === 0;
  338. },
  339. /**
  340. * Method return true if node last in branch
  341. *
  342. * @returns {Boolean}
  343. */
  344. isLast: function() {
  345. if ($(this).treegrid('isNode')) {
  346. var parentNode = $(this).treegrid('getParentNode');
  347. if (parentNode === null) {
  348. if ($(this).treegrid('getNodeId') === $(this).treegrid('getRootNodes').last().treegrid('getNodeId')) {
  349. return true;
  350. }
  351. } else {
  352. if ($(this).treegrid('getNodeId') === parentNode.treegrid('getChildNodes').last().treegrid('getNodeId')) {
  353. return true;
  354. }
  355. }
  356. }
  357. return false;
  358. },
  359. /**
  360. * Method return true if node first in branch
  361. *
  362. * @returns {Boolean}
  363. */
  364. isFirst: function() {
  365. if ($(this).treegrid('isNode')) {
  366. var parentNode = $(this).treegrid('getParentNode');
  367. if (parentNode === null) {
  368. if ($(this).treegrid('getNodeId') === $(this).treegrid('getRootNodes').first().treegrid('getNodeId')) {
  369. return true;
  370. }
  371. } else {
  372. if ($(this).treegrid('getNodeId') === parentNode.treegrid('getChildNodes').first().treegrid('getNodeId')) {
  373. return true;
  374. }
  375. }
  376. }
  377. return false;
  378. },
  379. /**
  380. * Return true if node expanded
  381. *
  382. * @returns {Boolean}
  383. */
  384. isExpanded: function() {
  385. return $(this).hasClass('treegrid-expanded');
  386. },
  387. /**
  388. * Return true if node collapsed
  389. *
  390. * @returns {Boolean}
  391. */
  392. isCollapsed: function() {
  393. return $(this).hasClass('treegrid-collapsed');
  394. },
  395. /**
  396. * Return true if at least one of parent node is collapsed
  397. *
  398. * @returns {Boolean}
  399. */
  400. isOneOfParentsCollapsed: function() {
  401. var $this = $(this);
  402. if ($this.treegrid('isRoot')) {
  403. return false;
  404. } else {
  405. if ($this.treegrid('getParentNode').treegrid('isCollapsed')) {
  406. return true;
  407. } else {
  408. return $this.treegrid('getParentNode').treegrid('isOneOfParentsCollapsed');
  409. }
  410. }
  411. },
  412. /**
  413. * Expand node
  414. *
  415. * @returns {Node}
  416. */
  417. expand: function() {
  418. if (!this.treegrid('isLeaf') && !this.treegrid("isExpanded")) {
  419. this.trigger("expand");
  420. this.trigger("change");
  421. return this;
  422. }
  423. return this;
  424. },
  425. /**
  426. * Expand all nodes
  427. *
  428. * @returns {Node}
  429. */
  430. expandAll: function() {
  431. var $this = $(this);
  432. $this.treegrid('getRootNodes').treegrid('expandRecursive');
  433. return $this;
  434. },
  435. /**
  436. * Expand current node and all child nodes begin from current
  437. *
  438. * @returns {Node}
  439. */
  440. expandRecursive: function() {
  441. return $(this).each(function() {
  442. var $this = $(this);
  443. $this.treegrid('expand');
  444. if (!$this.treegrid('isLeaf')) {
  445. $this.treegrid('getChildNodes').treegrid('expandRecursive');
  446. }
  447. });
  448. },
  449. /**
  450. * Collapse node
  451. *
  452. * @returns {Node}
  453. */
  454. collapse: function() {
  455. return $(this).each(function() {
  456. var $this = $(this);
  457. if (!$this.treegrid('isLeaf') && !$this.treegrid("isCollapsed")) {
  458. $this.trigger("collapse");
  459. $this.trigger("change");
  460. }
  461. });
  462. },
  463. /**
  464. * Collapse all nodes
  465. *
  466. * @returns {Node}
  467. */
  468. collapseAll: function() {
  469. var $this = $(this);
  470. $this.treegrid('getRootNodes').treegrid('collapseRecursive');
  471. return $this;
  472. },
  473. /**
  474. * Collapse current node and all child nodes begin from current
  475. *
  476. * @returns {Node}
  477. */
  478. collapseRecursive: function() {
  479. return $(this).each(function() {
  480. var $this = $(this);
  481. $this.treegrid('collapse');
  482. if (!$this.treegrid('isLeaf')) {
  483. $this.treegrid('getChildNodes').treegrid('collapseRecursive');
  484. }
  485. });
  486. },
  487. /**
  488. * Expand if collapsed, Collapse if expanded
  489. *
  490. * @returns {Node}
  491. */
  492. toggle: function() {
  493. var $this = $(this);
  494. if ($this.treegrid('isExpanded')) {
  495. $this.treegrid('collapse');
  496. } else {
  497. $this.treegrid('expand');
  498. }
  499. return $this;
  500. },
  501. /**
  502. * Rendering node
  503. *
  504. * @returns {Node}
  505. */
  506. render: function() {
  507. return $(this).each(function() {
  508. var $this = $(this);
  509. //if parent colapsed we hidden
  510. if ($this.treegrid('isOneOfParentsCollapsed')) {
  511. $this.hide();
  512. } else {
  513. $this.show();
  514. }
  515. if (!$this.treegrid('isLeaf')) {
  516. $this.treegrid('renderExpander');
  517. $this.treegrid('getChildNodes').treegrid('render');
  518. }
  519. });
  520. },
  521. /**
  522. * Rendering expander depends on node state
  523. *
  524. * @returns {Node}
  525. */
  526. renderExpander: function() {
  527. return $(this).each(function() {
  528. var $this = $(this);
  529. var expander = $this.treegrid('getSetting', 'getExpander').apply(this);
  530. if (expander) {
  531. if (!$this.treegrid('isCollapsed')) {
  532. expander.removeClass($this.treegrid('getSetting', 'expanderCollapsedClass'));
  533. expander.addClass($this.treegrid('getSetting', 'expanderExpandedClass'));
  534. } else {
  535. expander.removeClass($this.treegrid('getSetting', 'expanderExpandedClass'));
  536. expander.addClass($this.treegrid('getSetting', 'expanderCollapsedClass'));
  537. }
  538. } else {
  539. $this.treegrid('initExpander');
  540. $this.treegrid('renderExpander');
  541. }
  542. });
  543. }
  544. };
  545. $.fn.treegrid = function(method) {
  546. if (methods[method]) {
  547. return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
  548. } else if (typeof method === 'object' || !method) {
  549. return methods.initTree.apply(this, arguments);
  550. } else {
  551. $.error('Method with name ' + method + ' does not exists for jQuery.treegrid');
  552. }
  553. };
  554. /**
  555. * Plugin's default options
  556. */
  557. $.fn.treegrid.defaults = {
  558. initialState: 'expanded',
  559. saveState: false,
  560. saveStateMethod: 'cookie',
  561. saveStateName: 'tree-grid-state',
  562. expanderTemplate: '<span class="treegrid-expander"></span>',
  563. indentTemplate: '<span class="treegrid-indent"></span>',
  564. expanderExpandedClass: 'treegrid-expander-expanded',
  565. expanderCollapsedClass: 'treegrid-expander-collapsed',
  566. treeColumn: 0,
  567. getExpander: function() {
  568. return $(this).find('.treegrid-expander');
  569. },
  570. getNodeId: function() {
  571. var template = /treegrid-([A-Za-z0-9_-]+)/;
  572. if (template.test($(this).attr('class'))) {
  573. return template.exec($(this).attr('class'))[1];
  574. }
  575. return null;
  576. },
  577. getParentNodeId: function() {
  578. var template = /treegrid-parent-([A-Za-z0-9_-]+)/;
  579. if (template.test($(this).attr('class'))) {
  580. return template.exec($(this).attr('class'))[1];
  581. }
  582. return null;
  583. },
  584. getNodeById: function(id, treegridContainer) {
  585. var templateClass = "treegrid-" + id;
  586. return treegridContainer.find('tr.' + templateClass);
  587. },
  588. getChildNodes: function(id, treegridContainer) {
  589. var templateClass = "treegrid-parent-" + id;
  590. return treegridContainer.find('tr.' + templateClass);
  591. },
  592. getTreeGridContainer: function() {
  593. return $(this).closest('table');
  594. },
  595. getRootNodes: function(treegridContainer) {
  596. var result = $.grep(treegridContainer.find('tr'), function(element) {
  597. var classNames = $(element).attr('class');
  598. var templateClass = /treegrid-([A-Za-z0-9_-]+)/;
  599. var templateParentClass = /treegrid-parent-([A-Za-z0-9_-]+)/;
  600. return templateClass.test(classNames) && !templateParentClass.test(classNames);
  601. });
  602. return $(result);
  603. },
  604. getAllNodes: function(treegridContainer) {
  605. var result = $.grep(treegridContainer.find('tr'), function(element) {
  606. var classNames = $(element).attr('class');
  607. var templateClass = /treegrid-([A-Za-z0-9_-]+)/;
  608. return templateClass.test(classNames);
  609. });
  610. return $(result);
  611. },
  612. //Events
  613. onCollapse: null,
  614. onExpand: null,
  615. onChange: null
  616. };
  617. })(jQuery);