jquery.treegrid.js 21 KB

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