PageRenderTime 58ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/static/scripts/jquery.dynatree.js

https://bitbucket.org/cistrome/cistrome-harvard/
JavaScript | 2034 lines | 1446 code | 204 blank | 384 comment | 428 complexity | cebbdac6e84b5d05b26e537eff9a82d1 MD5 | raw file
  1. /*************************************************************************
  2. jquery.dynatree.js
  3. Dynamic tree view control, with support for lazy loading of branches.
  4. Copyright (c) 2008-2010, Martin Wendt (http://wwWendt.de)
  5. Dual licensed under the MIT or GPL Version 2 licenses.
  6. http://code.google.com/p/dynatree/wiki/LicenseInfo
  7. A current version and some documentation is available at
  8. http://dynatree.googlecode.com/
  9. $Version: 0.5.4$
  10. $Revision: 329, 2010-05-05 08:04:39$
  11. @depends: jquery.js
  12. @depends: ui.core.js
  13. @depends: jquery.cookie.js
  14. *************************************************************************/
  15. /*************************************************************************
  16. * Debug functions
  17. */
  18. var _canLog = true;
  19. function _log(mode, msg) {
  20. /**
  21. * Usage: logMsg("%o was toggled", this);
  22. */
  23. if( !_canLog )
  24. return;
  25. // Remove first argument
  26. var args = Array.prototype.slice.apply(arguments, [1]);
  27. // Prepend timestamp
  28. var dt = new Date();
  29. var tag = dt.getHours()+":"+dt.getMinutes()+":"+dt.getSeconds()+"."+dt.getMilliseconds();
  30. args[0] = tag + " - " + args[0];
  31. try {
  32. switch( mode ) {
  33. case "info":
  34. window.console.info.apply(window.console, args);
  35. break;
  36. case "warn":
  37. window.console.warn.apply(window.console, args);
  38. break;
  39. default:
  40. window.console.log.apply(window.console, args);
  41. }
  42. } catch(e) {
  43. if( !window.console )
  44. _canLog = false; // Permanently disable, when logging is not supported by the browser
  45. }
  46. }
  47. function logMsg(msg) {
  48. Array.prototype.unshift.apply(arguments, ["debug"]);
  49. _log.apply(this, arguments);
  50. }
  51. // Forward declaration
  52. var getDynaTreePersistData = undefined;
  53. /*************************************************************************
  54. * Constants
  55. */
  56. var DTNodeStatus_Error = -1;
  57. var DTNodeStatus_Loading = 1;
  58. var DTNodeStatus_Ok = 0;
  59. // Start of local namespace
  60. ;(function($) {
  61. /*************************************************************************
  62. * Common tool functions.
  63. */
  64. var Class = {
  65. create: function() {
  66. return function() {
  67. this.initialize.apply(this, arguments);
  68. }
  69. }
  70. }
  71. /*************************************************************************
  72. * Class DynaTreeNode
  73. */
  74. var DynaTreeNode = Class.create();
  75. DynaTreeNode.prototype = {
  76. initialize: function(parent, tree, data) {
  77. /**
  78. * @constructor
  79. */
  80. this.parent = parent;
  81. this.tree = tree;
  82. if ( typeof data == "string" )
  83. data = { title: data };
  84. if( data.key == undefined )
  85. data.key = "_" + tree._nodeCount++;
  86. this.data = $.extend({}, $.ui.dynatree.nodedatadefaults, data);
  87. this.div = null; // not yet created
  88. this.span = null; // not yet created
  89. this.childList = null; // no subnodes yet
  90. // this.isRead = false; // Lazy content not yet read
  91. this.isLoading = false; // Lazy content is being loaded
  92. this.hasSubSel = false;
  93. },
  94. toString: function() {
  95. return "dtnode<" + this.data.key + ">: '" + this.data.title + "'";
  96. },
  97. toDict: function(recursive, callback) {
  98. var dict = $.extend({}, this.data);
  99. dict.activate = ( this.tree.activeNode === this );
  100. dict.focus = ( this.tree.focusNode === this );
  101. dict.expand = this.bExpanded;
  102. dict.select = this.bSelected;
  103. if( callback )
  104. callback(dict);
  105. if( recursive && this.childList ) {
  106. dict.children = [];
  107. for(var i=0; i<this.childList.length; i++ )
  108. dict.children.push(this.childList[i].toDict(true, callback));
  109. } else {
  110. delete dict.children;
  111. }
  112. return dict;
  113. },
  114. _getInnerHtml: function() {
  115. var opts = this.tree.options;
  116. var cache = this.tree.cache;
  117. // parent connectors
  118. var rootParent = opts.rootVisible ? null : this.tree.tnRoot;
  119. var bHideFirstExpander = (opts.rootVisible && opts.minExpandLevel>0) || opts.minExpandLevel>1;
  120. var bHideFirstConnector = opts.rootVisible || opts.minExpandLevel>0;
  121. var res = "";
  122. var p = this.parent;
  123. while( p ) {
  124. // Suppress first connector column, if visible top level is always expanded
  125. if ( bHideFirstConnector && p==rootParent )
  126. break;
  127. res = ( p.isLastSibling() ? cache.tagEmpty : cache.tagVline) + res;
  128. p = p.parent;
  129. }
  130. // connector (expanded, expandable or simple)
  131. if( bHideFirstExpander && this.parent==rootParent ) {
  132. // skip connector
  133. } else if ( this.childList || this.data.isLazy ) {
  134. res += cache.tagExpander;
  135. } else {
  136. res += cache.tagConnector;
  137. }
  138. // Checkbox mode
  139. if( opts.checkbox && this.data.hideCheckbox!=true && !this.data.isStatusNode ) {
  140. res += cache.tagCheckbox;
  141. }
  142. // folder or doctype icon
  143. if ( this.data.icon ) {
  144. res += "<img src='" + opts.imagePath + this.data.icon + "' alt='' />";
  145. } else if ( this.data.icon == false ) {
  146. // icon == false means 'no icon'
  147. } else {
  148. // icon == null means 'default icon'
  149. res += cache.tagNodeIcon;
  150. }
  151. // node name
  152. var tooltip = ( this.data && typeof this.data.tooltip == "string" ) ? " title='" + this.data.tooltip + "'" : "";
  153. res += "<a href='#' class='" + opts.classNames.title + "'" + tooltip + ">" + this.data.title + "</a>";
  154. return res;
  155. },
  156. _fixOrder: function() {
  157. /**
  158. * Make sure, that <div> order matches childList order.
  159. */
  160. var cl = this.childList;
  161. if( !cl )
  162. return;
  163. var childDiv = this.div.firstChild.nextSibling;
  164. for(var i=0; i<cl.length-1; i++) {
  165. var childNode1 = cl[i];
  166. var childNode2 = childDiv.firstChild.dtnode;
  167. if( childNode1 !== childNode2 ) {
  168. this.tree.logDebug("_fixOrder: mismatch at index " + i + ": " + childNode1 + " != " + childNode2);
  169. this.div.insertBefore(childNode1.div, childNode2.div);
  170. } else {
  171. childDiv = childDiv.nextSibling;
  172. }
  173. }
  174. },
  175. render: function(bDeep, bHidden) {
  176. /**
  177. * Create HTML markup for this node.
  178. *
  179. * <div> // This div contains the node's span and list of child div's.
  180. * <span id='key'>S S S A</span> // Span contains graphic spans and title <a> tag
  181. * <div>child1</div>
  182. * <div>child2</div>
  183. * </div>
  184. */
  185. // this.tree.logDebug("%o.render()", this);
  186. var opts = this.tree.options;
  187. var cn = opts.classNames;
  188. var isLastSib = this.isLastSibling();
  189. // ---
  190. if( ! this.div ) {
  191. this.span = document.createElement("span");
  192. this.span.dtnode = this;
  193. if( this.data.key )
  194. this.span.id = this.tree.options.idPrefix + this.data.key;
  195. this.div = document.createElement("div");
  196. this.div.appendChild(this.span);
  197. if ( this.parent ) {
  198. this.parent.div.appendChild(this.div);
  199. }
  200. if( this.parent==null && !this.tree.options.rootVisible )
  201. this.span.style.display = "none";
  202. }
  203. // set node connector images, links and text
  204. this.span.innerHTML = this._getInnerHtml();
  205. // hide this node, if parent is collapsed
  206. this.div.style.display = ( this.parent==null || this.parent.bExpanded ? "" : "none");
  207. // Set classes for current status
  208. var cnList = [];
  209. cnList.push( ( this.data.isFolder ) ? cn.folder : cn.document );
  210. if( this.bExpanded )
  211. cnList.push(cn.expanded);
  212. if( this.childList != null )
  213. cnList.push(cn.hasChildren);
  214. if( this.data.isLazy && this.childList==null )
  215. cnList.push(cn.lazy);
  216. if( isLastSib )
  217. cnList.push(cn.lastsib);
  218. if( this.bSelected )
  219. cnList.push(cn.selected);
  220. if( this.hasSubSel )
  221. cnList.push(cn.partsel);
  222. if( this.tree.activeNode === this )
  223. cnList.push(cn.active);
  224. if( this.data.addClass )
  225. cnList.push(this.data.addClass);
  226. // IE6 doesn't correctly evaluate multiple class names,
  227. // so we create combined class names that can be used in the CSS
  228. cnList.push(cn.combinedExpanderPrefix
  229. + (this.bExpanded ? "e" : "c")
  230. + (this.data.isLazy && this.childList==null ? "d" : "")
  231. + (isLastSib ? "l" : "")
  232. );
  233. cnList.push(cn.combinedIconPrefix
  234. + (this.bExpanded ? "e" : "c")
  235. + (this.data.isFolder ? "f" : "")
  236. );
  237. this.span.className = cnList.join(" ");
  238. if( bDeep && this.childList && (bHidden || this.bExpanded) ) {
  239. for(var i=0; i<this.childList.length; i++) {
  240. this.childList[i].render(bDeep, bHidden)
  241. }
  242. this._fixOrder();
  243. }
  244. },
  245. hasChildren: function() {
  246. return this.childList != null;
  247. },
  248. isLastSibling: function() {
  249. var p = this.parent;
  250. if ( !p ) return true;
  251. return p.childList[p.childList.length-1] === this;
  252. },
  253. prevSibling: function() {
  254. if( !this.parent ) return null;
  255. var ac = this.parent.childList;
  256. for(var i=1; i<ac.length; i++) // start with 1, so prev(first) = null
  257. if( ac[i] === this )
  258. return ac[i-1];
  259. return null;
  260. },
  261. nextSibling: function() {
  262. if( !this.parent ) return null;
  263. var ac = this.parent.childList;
  264. for(var i=0; i<ac.length-1; i++) // up to length-2, so next(last) = null
  265. if( ac[i] === this )
  266. return ac[i+1];
  267. return null;
  268. },
  269. _setStatusNode: function(data) {
  270. // Create, modify or remove the status child node (pass 'null', to remove it).
  271. var firstChild = ( this.childList ? this.childList[0] : null );
  272. if( !data ) {
  273. if ( firstChild ) {
  274. this.div.removeChild(firstChild.div);
  275. if( this.childList.length == 1 )
  276. this.childList = null;
  277. else
  278. this.childList.shift();
  279. }
  280. } else if ( firstChild ) {
  281. data.isStatusNode = true;
  282. firstChild.data = data;
  283. firstChild.render(false, false);
  284. } else {
  285. data.isStatusNode = true;
  286. firstChild = this.addChild(data);
  287. }
  288. },
  289. setLazyNodeStatus: function(lts, opts) {
  290. var tooltip = (opts && opts.tooltip) ? opts.tooltip : null;
  291. var info = (opts && opts.info) ? " (" + opts.info + ")" : "";
  292. switch( lts ) {
  293. case DTNodeStatus_Ok:
  294. this._setStatusNode(null);
  295. // this.isRead = true;
  296. this.isLoading = false;
  297. this.render(false, false);
  298. if( this.tree.options.autoFocus ) {
  299. if( this === this.tree.tnRoot && !this.tree.options.rootVisible && this.childList ) {
  300. // special case: using ajaxInit
  301. this.childList[0].focus();
  302. } else {
  303. this.focus();
  304. }
  305. }
  306. break;
  307. case DTNodeStatus_Loading:
  308. this.isLoading = true;
  309. this._setStatusNode({
  310. title: this.tree.options.strings.loading + info,
  311. tooltip: tooltip,
  312. addClass: this.tree.options.classNames.nodeWait
  313. });
  314. break;
  315. case DTNodeStatus_Error:
  316. this.isLoading = false;
  317. this._setStatusNode({
  318. title: this.tree.options.strings.loadError + info,
  319. tooltip: tooltip,
  320. addClass: this.tree.options.classNames.nodeError
  321. });
  322. break;
  323. default:
  324. throw "Bad LazyNodeStatus: '" + lts + "'.";
  325. }
  326. },
  327. _parentList: function(includeRoot, includeSelf) {
  328. var l = [];
  329. var dtn = includeSelf ? this : this.parent;
  330. while( dtn ) {
  331. if( includeRoot || dtn.parent )
  332. l.unshift(dtn);
  333. dtn = dtn.parent;
  334. };
  335. return l;
  336. },
  337. getLevel: function() {
  338. var level = 0;
  339. var dtn = this.parent;
  340. while( dtn ) {
  341. level++;
  342. dtn = dtn.parent;
  343. };
  344. return level;
  345. },
  346. _getTypeForOuterNodeEvent: function(event) {
  347. /** Return the inner node span (title, checkbox or expander) if
  348. * event.target points to the outer span.
  349. * This function should fix issue #93:
  350. * FF2 ignores empty spans, when generating events (returning the parent instead).
  351. */
  352. var cns = this.tree.options.classNames;
  353. var target = event.target;
  354. // Only process clicks on an outer node span (probably due to a FF2 event handling bug)
  355. if( target.className.indexOf(cns.folder)<0
  356. && target.className.indexOf(cns.document)<0 ) {
  357. return null
  358. }
  359. // Event coordinates, relative to outer node span:
  360. var eventX = event.pageX - target.offsetLeft;
  361. var eventY = event.pageY - target.offsetTop;
  362. for(var i=0; i<target.childNodes.length; i++) {
  363. var cn = target.childNodes[i];
  364. var x = cn.offsetLeft - target.offsetLeft;
  365. var y = cn.offsetTop - target.offsetTop;
  366. var nx = cn.clientWidth, ny = cn.clientHeight;
  367. // alert (cn.className + ": " + x + ", " + y + ", s:" + nx + ", " + ny);
  368. if( eventX>=x && eventX<=(x+nx) && eventY>=y && eventY<=(y+ny) ) {
  369. // alert("HIT "+ cn.className);
  370. if( cn.className==cns.title )
  371. return "title";
  372. else if( cn.className==cns.expander )
  373. return "expander";
  374. else if( cn.className==cns.checkbox )
  375. return "checkbox";
  376. else if( cn.className==cns.nodeIcon )
  377. return "icon";
  378. }
  379. }
  380. return "prefix";
  381. },
  382. getEventTargetType: function(event) {
  383. // Return the part of a node, that a click event occured on.
  384. // Note: there is no check, if the was fired on TIHS node.
  385. var tcn = event && event.target ? event.target.className : "";
  386. var cns = this.tree.options.classNames;
  387. if( tcn == cns.title )
  388. return "title";
  389. else if( tcn==cns.expander )
  390. return "expander";
  391. else if( tcn==cns.checkbox )
  392. return "checkbox";
  393. else if( tcn==cns.nodeIcon )
  394. return "icon";
  395. else if( tcn==cns.empty || tcn==cns.vline || tcn==cns.connector )
  396. return "prefix";
  397. else if( tcn.indexOf(cns.folder)>=0 || tcn.indexOf(cns.document)>=0 )
  398. // FIX issue #93
  399. return this._getTypeForOuterNodeEvent(event);
  400. return null;
  401. },
  402. isVisible: function() {
  403. // Return true, if all parents are expanded.
  404. var parents = this._parentList(true, false);
  405. for(var i=0; i<parents.length; i++)
  406. if( ! parents[i].bExpanded ) return false;
  407. return true;
  408. },
  409. makeVisible: function() {
  410. // Make sure, all parents are expanded
  411. var parents = this._parentList(true, false);
  412. for(var i=0; i<parents.length; i++)
  413. parents[i]._expand(true);
  414. },
  415. focus: function() {
  416. // TODO: check, if we already have focus
  417. // this.tree.logDebug("dtnode.focus(): %o", this);
  418. this.makeVisible();
  419. try {
  420. $(this.span).find(">a").focus();
  421. } catch(e) { }
  422. },
  423. _activate: function(flag, fireEvents) {
  424. // (De)Activate - but not focus - this node.
  425. this.tree.logDebug("dtnode._activate(%o, fireEvents=%o) - %o", flag, fireEvents, this);
  426. var opts = this.tree.options;
  427. if( this.data.isStatusNode )
  428. return;
  429. if ( fireEvents && opts.onQueryActivate && opts.onQueryActivate.call(this.span, flag, this) == false )
  430. return; // Callback returned false
  431. if( flag ) {
  432. // Activate
  433. if( this.tree.activeNode ) {
  434. if( this.tree.activeNode === this )
  435. return;
  436. this.tree.activeNode.deactivate();
  437. }
  438. if( opts.activeVisible )
  439. this.makeVisible();
  440. this.tree.activeNode = this;
  441. if( opts.persist )
  442. $.cookie(opts.cookieId+"-active", this.data.key, opts.cookie);
  443. this.tree.persistence.activeKey = this.data.key;
  444. $(this.span).addClass(opts.classNames.active);
  445. if ( fireEvents && opts.onActivate ) // Pass element as 'this' (jQuery convention)
  446. opts.onActivate.call(this.span, this);
  447. } else {
  448. // Deactivate
  449. if( this.tree.activeNode === this ) {
  450. var opts = this.tree.options;
  451. if ( opts.onQueryActivate && opts.onQueryActivate.call(this.span, false, this) == false )
  452. return; // Callback returned false
  453. $(this.span).removeClass(opts.classNames.active);
  454. if( opts.persist ) {
  455. // Note: we don't pass null, but ''. So the cookie is not deleted.
  456. // If we pass null, we also have to pass a COPY of opts, because $cookie will override opts.expires (issue 84)
  457. $.cookie(opts.cookieId+"-active", "", opts.cookie);
  458. }
  459. this.tree.persistence.activeKey = null;
  460. this.tree.activeNode = null;
  461. if ( fireEvents && opts.onDeactivate )
  462. opts.onDeactivate.call(this.span, this);
  463. }
  464. }
  465. },
  466. activate: function() {
  467. // Select - but not focus - this node.
  468. // this.tree.logDebug("dtnode.activate(): %o", this);
  469. this._activate(true, true);
  470. },
  471. deactivate: function() {
  472. // this.tree.logDebug("dtnode.deactivate(): %o", this);
  473. this._activate(false, true);
  474. },
  475. isActive: function() {
  476. return (this.tree.activeNode === this);
  477. },
  478. _userActivate: function() {
  479. // Handle user click / [space] / [enter], according to clickFolderMode.
  480. var activate = true;
  481. var expand = false;
  482. if ( this.data.isFolder ) {
  483. switch( this.tree.options.clickFolderMode ) {
  484. case 2:
  485. activate = false;
  486. expand = true;
  487. break;
  488. case 3:
  489. activate = expand = true;
  490. break;
  491. }
  492. }
  493. if( this.parent == null && this.tree.options.minExpandLevel>0 ) {
  494. expand = false;
  495. }
  496. if( expand ) {
  497. this.toggleExpand();
  498. this.focus();
  499. }
  500. if( activate ) {
  501. this.activate();
  502. }
  503. },
  504. _setSubSel: function(hasSubSel) {
  505. if( hasSubSel ) {
  506. this.hasSubSel = true;
  507. $(this.span).addClass(this.tree.options.classNames.partsel);
  508. } else {
  509. this.hasSubSel = false;
  510. $(this.span).removeClass(this.tree.options.classNames.partsel);
  511. }
  512. },
  513. _fixSelectionState: function() {
  514. // fix selection status, for multi-hier mode
  515. // this.tree.logDebug("_fixSelectionState(%o) - %o", this.bSelected, this);
  516. if( this.bSelected ) {
  517. // Select all children
  518. this.visit(function(dtnode){
  519. dtnode.parent._setSubSel(true);
  520. dtnode._select(true, false, false);
  521. });
  522. // Select parents, if all children are selected
  523. var p = this.parent;
  524. while( p ) {
  525. p._setSubSel(true);
  526. var allChildsSelected = true;
  527. for(var i=0; i<p.childList.length; i++) {
  528. var n = p.childList[i];
  529. if( !n.bSelected && !n.data.isStatusNode ) {
  530. allChildsSelected = false;
  531. break;
  532. }
  533. }
  534. if( allChildsSelected )
  535. p._select(true, false, false);
  536. p = p.parent;
  537. }
  538. } else {
  539. // Deselect all children
  540. this._setSubSel(false);
  541. this.visit(function(dtnode){
  542. dtnode._setSubSel(false);
  543. dtnode._select(false, false, false);
  544. });
  545. // Deselect parents, and recalc hasSubSel
  546. var p = this.parent;
  547. while( p ) {
  548. p._select(false, false, false);
  549. var isPartSel = false;
  550. for(var i=0; i<p.childList.length; i++) {
  551. if( p.childList[i].bSelected || p.childList[i].hasSubSel ) {
  552. isPartSel = true;
  553. break;
  554. }
  555. }
  556. p._setSubSel(isPartSel);
  557. p = p.parent;
  558. }
  559. }
  560. },
  561. _select: function(sel, fireEvents, deep) {
  562. // Select - but not focus - this node.
  563. // this.tree.logDebug("dtnode._select(%o) - %o", sel, this);
  564. var opts = this.tree.options;
  565. if( this.data.isStatusNode )
  566. return;
  567. //
  568. if( this.bSelected == sel ) {
  569. // this.tree.logDebug("dtnode._select(%o) IGNORED - %o", sel, this);
  570. return;
  571. }
  572. // Allow event listener to abort selection
  573. if ( fireEvents && opts.onQuerySelect && opts.onQuerySelect.call(this.span, sel, this) == false )
  574. return; // Callback returned false
  575. // Force single-selection
  576. if( opts.selectMode==1 && sel ) {
  577. this.tree.visit(function(dtnode){
  578. if( dtnode.bSelected ) {
  579. // Deselect; assuming that in selectMode:1 there's max. one other selected node
  580. dtnode._select(false, false, false);
  581. return false;
  582. }
  583. });
  584. }
  585. this.bSelected = sel;
  586. // this.tree._changeNodeList("select", this, sel);
  587. if( sel ) {
  588. if( opts.persist )
  589. this.tree.persistence.addSelect(this.data.key);
  590. $(this.span).addClass(opts.classNames.selected);
  591. if( deep && opts.selectMode==3 )
  592. this._fixSelectionState();
  593. if ( fireEvents && opts.onSelect )
  594. opts.onSelect.call(this.span, true, this);
  595. } else {
  596. if( opts.persist )
  597. this.tree.persistence.clearSelect(this.data.key);
  598. $(this.span).removeClass(opts.classNames.selected);
  599. if( deep && opts.selectMode==3 )
  600. this._fixSelectionState();
  601. if ( fireEvents && opts.onSelect )
  602. opts.onSelect.call(this.span, false, this);
  603. }
  604. },
  605. select: function(sel) {
  606. // Select - but not focus - this node.
  607. // this.tree.logDebug("dtnode.select(%o) - %o", sel, this);
  608. if( this.data.unselectable )
  609. return this.bSelected;
  610. return this._select(sel!=false, true, true);
  611. },
  612. toggleSelect: function() {
  613. // this.tree.logDebug("dtnode.toggleSelect() - %o", this);
  614. return this.select(!this.bSelected);
  615. },
  616. isSelected: function() {
  617. return this.bSelected;
  618. },
  619. _loadContent: function() {
  620. try {
  621. var opts = this.tree.options;
  622. this.tree.logDebug("_loadContent: start - %o", this);
  623. this.setLazyNodeStatus(DTNodeStatus_Loading);
  624. if( true == opts.onLazyRead.call(this.span, this) ) {
  625. // If function returns 'true', we assume that the loading is done:
  626. this.setLazyNodeStatus(DTNodeStatus_Ok);
  627. // Otherwise (i.e. if the loading was started as an asynchronous process)
  628. // the onLazyRead(dtnode) handler is expected to call dtnode.setLazyNodeStatus(DTNodeStatus_Ok/_Error) when done.
  629. this.tree.logDebug("_loadContent: succeeded - %o", this);
  630. }
  631. } catch(e) {
  632. // alert(e);
  633. this.setLazyNodeStatus(DTNodeStatus_Error);
  634. this.tree.logWarning("_loadContent: failed - %o", e);
  635. }
  636. },
  637. _expand: function(bExpand) {
  638. // this.tree.logDebug("dtnode._expand(%o) - %o", bExpand, this);
  639. if( this.bExpanded == bExpand ) {
  640. // this.tree.logDebug("dtnode._expand(%o) IGNORED - %o", bExpand, this);
  641. return;
  642. }
  643. var opts = this.tree.options;
  644. if( !bExpand && this.getLevel()<opts.minExpandLevel ) {
  645. this.tree.logDebug("dtnode._expand(%o) forced expand - %o", bExpand, this);
  646. return;
  647. }
  648. if ( opts.onQueryExpand && opts.onQueryExpand.call(this.span, bExpand, this) == false )
  649. return; // Callback returned false
  650. this.bExpanded = bExpand;
  651. // Persist expand state
  652. if( opts.persist ) {
  653. if( bExpand )
  654. this.tree.persistence.addExpand(this.data.key);
  655. else
  656. this.tree.persistence.clearExpand(this.data.key);
  657. }
  658. this.render(false);
  659. // Auto-collapse mode: collapse all siblings
  660. if( this.bExpanded && this.parent && opts.autoCollapse ) {
  661. var parents = this._parentList(false, true);
  662. for(var i=0; i<parents.length; i++)
  663. parents[i].collapseSiblings();
  664. }
  665. // If the currently active node is now hidden, deactivate it
  666. if( opts.activeVisible && this.tree.activeNode && ! this.tree.activeNode.isVisible() ) {
  667. this.tree.activeNode.deactivate();
  668. }
  669. // Expanding a lazy node: set 'loading...' and call callback
  670. if( bExpand && this.data.isLazy && this.childList==null && !this.isLoading ) {
  671. this._loadContent();
  672. return;
  673. }
  674. // this.tree.logDebug("_expand: start div toggle - %o", this);
  675. var fxDuration = opts.fx ? (opts.fx.duration || 200) : 0;
  676. if( this.childList ) {
  677. for(var i=0; i<this.childList.length; i++ ) {
  678. var $child = $(this.childList[i].div);
  679. if( fxDuration ) {
  680. // This is a toggle, so only do it, if not already rendered (in)visible (issue 98)
  681. if( bExpand != $child.is(':visible') )
  682. $child.animate(opts.fx, fxDuration);
  683. } else {
  684. if( bExpand )
  685. $child.show();
  686. else
  687. $child.hide(); // TODO: this seems to be slow, when called the first time for an element
  688. }
  689. }
  690. }
  691. /* issue 109: using selector filter is really SLOW.
  692. // issue 98: only toggle, if render hasn't set visibility already:
  693. var filter = ">DIV" + (bExpand ? ":hidden" : ":visible");
  694. if( opts.fx ) {
  695. var duration = opts.fx.duration || 200;
  696. // $(">DIV", this.div).animate(opts.fx, duration);
  697. $(filter, this.div).animate(opts.fx, duration);
  698. } else {
  699. $(filter, this.div).toggle();
  700. // var $d = $(">DIV", this.div);
  701. // this.tree.logDebug("_expand: got div, start toggle - %o", this);
  702. // $d.toggle();
  703. }
  704. //*/
  705. // this.tree.logDebug("_expand: end div toggle - %o", this);
  706. if ( opts.onExpand )
  707. opts.onExpand.call(this.span, bExpand, this);
  708. },
  709. expand: function(flag) {
  710. if( !this.childList && !this.data.isLazy && flag )
  711. return; // Prevent expanding empty nodes
  712. if( this.parent == null && this.tree.options.minExpandLevel>0 && !flag )
  713. return; // Prevent collapsing the root
  714. this._expand(flag);
  715. },
  716. toggleExpand: function() {
  717. this.expand(!this.bExpanded);
  718. },
  719. collapseSiblings: function() {
  720. if( this.parent == null )
  721. return;
  722. var ac = this.parent.childList;
  723. for (var i=0; i<ac.length; i++) {
  724. if ( ac[i] !== this && ac[i].bExpanded )
  725. ac[i]._expand(false);
  726. }
  727. },
  728. onClick: function(event) {
  729. // this.tree.logDebug("dtnode.onClick(" + event.type + "): dtnode:" + this + ", button:" + event.button + ", which: " + event.which);
  730. var targetType = this.getEventTargetType(event);
  731. if( targetType == "expander" ) {
  732. // Clicking the expander icon always expands/collapses
  733. this.toggleExpand();
  734. this.focus(); // issue 95
  735. } else if( targetType == "checkbox" ) {
  736. // Clicking the checkbox always (de)selects
  737. this.toggleSelect();
  738. this.focus(); // issue 95
  739. } else {
  740. this._userActivate();
  741. // Chrome and Safari don't focus the a-tag on click
  742. this.span.getElementsByTagName("a")[0].focus();
  743. }
  744. // Make sure that clicks stop, otherwise <a href='#'> jumps to the top
  745. return false;
  746. },
  747. onDblClick: function(event) {
  748. // this.tree.logDebug("dtnode.onDblClick(" + event.type + "): dtnode:" + this + ", button:" + event.button + ", which: " + event.which);
  749. },
  750. onKeydown: function(event) {
  751. // this.tree.logDebug("dtnode.onKeydown(" + event.type + "): dtnode:" + this + ", charCode:" + event.charCode + ", keyCode: " + event.keyCode + ", which: " + event.which);
  752. var handled = true;
  753. // alert("keyDown" + event.which);
  754. switch( event.which ) {
  755. // charCodes:
  756. // case 43: // '+'
  757. case 107: // '+'
  758. case 187: // '+' @ Chrome, Safari
  759. if( !this.bExpanded ) this.toggleExpand();
  760. break;
  761. // case 45: // '-'
  762. case 109: // '-'
  763. case 189: // '+' @ Chrome, Safari
  764. if( this.bExpanded ) this.toggleExpand();
  765. break;
  766. //~ case 42: // '*'
  767. //~ break;
  768. //~ case 47: // '/'
  769. //~ break;
  770. // case 13: // <enter>
  771. // <enter> on a focused <a> tag seems to generate a click-event.
  772. // this._userActivate();
  773. // break;
  774. case 32: // <space>
  775. this._userActivate();
  776. break;
  777. case 8: // <backspace>
  778. if( this.parent )
  779. this.parent.focus();
  780. break;
  781. case 37: // <left>
  782. if( this.bExpanded ) {
  783. this.toggleExpand();
  784. this.focus();
  785. } else if( this.parent && (this.tree.options.rootVisible || this.parent.parent) ) {
  786. this.parent.focus();
  787. }
  788. break;
  789. case 39: // <right>
  790. if( !this.bExpanded && (this.childList || this.data.isLazy) ) {
  791. this.toggleExpand();
  792. this.focus();
  793. } else if( this.childList ) {
  794. this.childList[0].focus();
  795. }
  796. break;
  797. case 38: // <up>
  798. var sib = this.prevSibling();
  799. while( sib && sib.bExpanded && sib.childList )
  800. sib = sib.childList[sib.childList.length-1];
  801. if( !sib && this.parent && (this.tree.options.rootVisible || this.parent.parent) )
  802. sib = this.parent;
  803. if( sib ) sib.focus();
  804. break;
  805. case 40: // <down>
  806. var sib;
  807. if( this.bExpanded && this.childList ) {
  808. sib = this.childList[0];
  809. } else {
  810. var parents = this._parentList(false, true);
  811. for(var i=parents.length-1; i>=0; i--) {
  812. sib = parents[i].nextSibling();
  813. if( sib ) break;
  814. }
  815. }
  816. if( sib ) sib.focus();
  817. break;
  818. default:
  819. handled = false;
  820. }
  821. // Return false, if handled, to prevent default processing
  822. return !handled;
  823. },
  824. onKeypress: function(event) {
  825. // onKeypress is only hooked to allow user callbacks.
  826. // We don't process it, because IE and Safari don't fire keypress for cursor keys.
  827. // this.tree.logDebug("dtnode.onKeypress(" + event.type + "): dtnode:" + this + ", charCode:" + event.charCode + ", keyCode: " + event.keyCode + ", which: " + event.which);
  828. },
  829. onFocus: function(event) {
  830. // Handles blur and focus events.
  831. // this.tree.logDebug("dtnode.onFocus(%o): %o", event, this);
  832. var opts = this.tree.options;
  833. if ( event.type=="blur" || event.type=="focusout" ) {
  834. if ( opts.onBlur ) // Pass element as 'this' (jQuery convention)
  835. opts.onBlur.call(this.span, this);
  836. if( this.tree.tnFocused )
  837. $(this.tree.tnFocused.span).removeClass(opts.classNames.focused);
  838. this.tree.tnFocused = null;
  839. if( opts.persist )
  840. $.cookie(opts.cookieId+"-focus", "", opts.cookie);
  841. } else if ( event.type=="focus" || event.type=="focusin") {
  842. // Fix: sometimes the blur event is not generated
  843. if( this.tree.tnFocused && this.tree.tnFocused !== this ) {
  844. this.tree.logDebug("dtnode.onFocus: out of sync: curFocus: %o", this.tree.tnFocused);
  845. $(this.tree.tnFocused.span).removeClass(opts.classNames.focused);
  846. }
  847. this.tree.tnFocused = this;
  848. if ( opts.onFocus ) // Pass element as 'this' (jQuery convention)
  849. opts.onFocus.call(this.span, this);
  850. $(this.tree.tnFocused.span).addClass(opts.classNames.focused);
  851. if( opts.persist )
  852. $.cookie(opts.cookieId+"-focus", this.data.key, opts.cookie);
  853. }
  854. // TODO: return anything?
  855. // return false;
  856. },
  857. visit: function(fn, data, includeSelf) {
  858. // Call fn(dtnode, data) for all child nodes. Stop iteration, if fn() returns false.
  859. var n = 0;
  860. if( includeSelf == true ) {
  861. if( fn(this, data) == false )
  862. return 1;
  863. n++;
  864. }
  865. if ( this.childList )
  866. for (var i=0; i<this.childList.length; i++)
  867. n += this.childList[i].visit(fn, data, true);
  868. return n;
  869. },
  870. remove: function() {
  871. // Remove this node
  872. // this.tree.logDebug ("%o.remove()", this);
  873. if ( this === this.tree.root )
  874. return false;
  875. return this.parent.removeChild(this);
  876. },
  877. removeChild: function(tn) {
  878. // Remove tn from list of direct children.
  879. var ac = this.childList;
  880. if( ac.length == 1 ) {
  881. if( tn !== ac[0] )
  882. throw "removeChild: invalid child";
  883. return this.removeChildren();
  884. }
  885. if( tn === this.tree.activeNode )
  886. tn.deactivate();
  887. if( this.tree.options.persist ) {
  888. if( tn.bSelected )
  889. this.tree.persistence.clearSelect(tn.data.key);
  890. if ( tn.bExpanded )
  891. this.tree.persistence.clearExpand(tn.data.key);
  892. }
  893. tn.removeChildren(true);
  894. this.div.removeChild(tn.div);
  895. for(var i=0; i<ac.length; i++) {
  896. if( ac[i] === tn ) {
  897. this.childList.splice(i, 1);
  898. delete tn;
  899. break;
  900. }
  901. }
  902. },
  903. removeChildren: function(isRecursiveCall, retainPersistence) {
  904. // Remove all child nodes (more efficiently than recursive remove())
  905. // this.tree.logDebug ("%o.removeChildren(%o)", this, isRecursiveCall);
  906. var tree = this.tree;
  907. var ac = this.childList;
  908. if( ac ) {
  909. for(var i=0; i<ac.length; i++) {
  910. var tn=ac[i];
  911. // this.tree.logDebug ("del %o", tn);
  912. if ( tn === tree.activeNode && !retainPersistence )
  913. tn.deactivate();
  914. if( this.tree.options.persist && !retainPersistence ) {
  915. if( tn.bSelected )
  916. this.tree.persistence.clearSelect(tn.data.key);
  917. if ( tn.bExpanded )
  918. this.tree.persistence.clearExpand(tn.data.key);
  919. }
  920. tn.removeChildren(true, retainPersistence);
  921. this.div.removeChild(tn.div);
  922. delete tn;
  923. }
  924. this.childList = null;
  925. }
  926. if( ! isRecursiveCall ) {
  927. // this._expand(false);
  928. // this.isRead = false;
  929. this.isLoading = false;
  930. this.render(false, false);
  931. }
  932. },
  933. reload: function(force) {
  934. // Discard lazy content (and reload, if node was expanded).
  935. if( this.parent == null )
  936. return this.tree.reload();
  937. if( ! this.data.isLazy )
  938. throw "node.reload() requires lazy nodes.";
  939. if( this.bExpanded ) {
  940. this.expand(false);
  941. this.removeChildren();
  942. this.expand(true);
  943. } else {
  944. this.removeChildren();
  945. if( force )
  946. this._loadContent();
  947. }
  948. },
  949. _addChildNode: function(dtnode, beforeNode) {
  950. /**
  951. * Internal function to add one single DynatreeNode as a child.
  952. *
  953. */
  954. var tree = this.tree;
  955. var opts = tree.options;
  956. var pers = tree.persistence;
  957. // tree.logDebug("%o._addChildNode(%o)", this, dtnode);
  958. // --- Update and fix dtnode attributes if necessary
  959. dtnode.parent = this;
  960. // if( beforeNode && (beforeNode.parent !== this || beforeNode === dtnode ) )
  961. // throw "<beforeNode> must be another child of <this>";
  962. // --- Add dtnode as a child
  963. if ( this.childList==null ) {
  964. this.childList = [];
  965. } else if( ! beforeNode ) {
  966. // Fix 'lastsib'
  967. $(this.childList[this.childList.length-1].span).removeClass(opts.classNames.lastsib);
  968. }
  969. if( beforeNode ) {
  970. var iBefore = $.inArray(beforeNode, this.childList);
  971. if( iBefore < 0 )
  972. throw "<beforeNode> must be a child of <this>";
  973. this.childList.splice(iBefore, 0, dtnode);
  974. // alert(this.childList);
  975. } else {
  976. // Append node
  977. this.childList.push(dtnode);
  978. }
  979. // --- Handle persistence
  980. // Initial status is read from cookies, if persistence is active and
  981. // cookies are already present.
  982. // Otherwise the status is read from the data attributes and then persisted.
  983. var isInitializing = tree.isInitializing();
  984. if( opts.persist && pers.cookiesFound && isInitializing ) {
  985. // Init status from cookies
  986. // tree.logDebug("init from cookie, pa=%o, dk=%o", pers.activeKey, dtnode.data.key);
  987. if( pers.activeKey == dtnode.data.key )
  988. tree.activeNode = dtnode;
  989. if( pers.focusedKey == dtnode.data.key )
  990. tree.focusNode = dtnode;
  991. dtnode.bExpanded = ($.inArray(dtnode.data.key, pers.expandedKeyList) >= 0);
  992. dtnode.bSelected = ($.inArray(dtnode.data.key, pers.selectedKeyList) >= 0);
  993. // tree.logDebug(" key=%o, bSelected=%o", dtnode.data.key, dtnode.bSelected);
  994. } else {
  995. // Init status from data (Note: we write the cookies after the init phase)
  996. // tree.logDebug("init from data");
  997. if( dtnode.data.activate ) {
  998. tree.activeNode = dtnode;
  999. if( opts.persist )
  1000. pers.activeKey = dtnode.data.key;
  1001. }
  1002. if( dtnode.data.focus ) {
  1003. tree.focusNode = dtnode;
  1004. if( opts.persist )
  1005. pers.focusedKey = dtnode.data.key;
  1006. }
  1007. dtnode.bExpanded = ( dtnode.data.expand == true ); // Collapsed by default
  1008. if( dtnode.bExpanded && opts.persist )
  1009. pers.addExpand(dtnode.data.key);
  1010. dtnode.bSelected = ( dtnode.data.select == true ); // Deselected by default
  1011. /*
  1012. Doesn't work, cause pers.selectedKeyList may be null
  1013. if( dtnode.bSelected && opts.selectMode==1
  1014. && pers.selectedKeyList && pers.selectedKeyList.length>0 ) {
  1015. tree.logWarning("Ignored multi-selection in single-mode for %o", dtnode);
  1016. dtnode.bSelected = false; // Fixing bad input data (multi selection for mode:1)
  1017. }
  1018. */
  1019. if( dtnode.bSelected && opts.persist )
  1020. pers.addSelect(dtnode.data.key);
  1021. }
  1022. // Always expand, if it's below minExpandLevel
  1023. // tree.logDebug ("%o._addChildNode(%o), l=%o", this, dtnode, dtnode.getLevel());
  1024. if ( opts.minExpandLevel >= dtnode.getLevel() ) {
  1025. // tree.logDebug ("Force expand for %o", dtnode);
  1026. this.bExpanded = true;
  1027. }
  1028. // In multi-hier mode, update the parents selection state
  1029. // issue #82: only if not initializing, because the children may not exist yet
  1030. // if( !dtnode.data.isStatusNode && opts.selectMode==3 && !isInitializing )
  1031. // dtnode._fixSelectionState();
  1032. // In multi-hier mode, update the parents selection state
  1033. if( dtnode.bSelected && opts.selectMode==3 ) {
  1034. var p = this;
  1035. while( p ) {
  1036. if( !p.hasSubSel )
  1037. p._setSubSel(true);
  1038. p = p.parent;
  1039. }
  1040. }
  1041. // render this node and the new child
  1042. if ( tree.bEnableUpdate )
  1043. this.render(true, true);
  1044. return dtnode;
  1045. },
  1046. addChild: function(obj, beforeNode) {
  1047. /**
  1048. * Add a node object as child.
  1049. *
  1050. * This should be the only place, where a DynaTreeNode is constructed!
  1051. * (Except for the root node creation in the tree constructor)
  1052. *
  1053. * @param obj A JS object (may be recursive) or an array of those.
  1054. * @param {DynaTreeNode} beforeNode (optional) sibling node.
  1055. *
  1056. * Data format: array of node objects, with optional 'children' attributes.
  1057. * [
  1058. * { title: "t1", isFolder: true, ... }
  1059. * { title: "t2", isFolder: true, ...,
  1060. * children: [
  1061. * {title: "t2.1", ..},
  1062. * {..}
  1063. * ]
  1064. * }
  1065. * ]
  1066. * A simple object is also accepted instead of an array.
  1067. *
  1068. */
  1069. // this.tree.logDebug("%o.addChild(%o, %o)", this, obj, beforeNode);
  1070. if( !obj || obj.length==0 ) // Passed null or undefined or empty array
  1071. return;
  1072. if( obj instanceof DynaTreeNode )
  1073. return this._addChildNode(obj, beforeNode);
  1074. if( !obj.length ) // Passed a single data object
  1075. obj = [ obj ];
  1076. var prevFlag = this.tree.enableUpdate(false);
  1077. var tnFirst = null;
  1078. for (var i=0; i<obj.length; i++) {
  1079. var data = obj[i];
  1080. var dtnode = this._addChildNode(new DynaTreeNode(this, this.tree, data), beforeNode);
  1081. if( !tnFirst ) tnFirst = dtnode;
  1082. // Add child nodes recursively
  1083. if( data.children )
  1084. dtnode.addChild(data.children, null);
  1085. }
  1086. this.tree.enableUpdate(prevFlag);
  1087. return tnFirst;
  1088. },
  1089. append: function(obj) {
  1090. this.tree.logWarning("node.append() is deprecated (use node.addChild() instead).");
  1091. return this.addChild(obj, null);
  1092. },
  1093. appendAjax: function(ajaxOptions) {
  1094. this.removeChildren(false, true);
  1095. this.setLazyNodeStatus(DTNodeStatus_Loading);
  1096. // Ajax option inheritance: $.ajaxSetup < $.ui.dynatree.defaults.ajaxDefaults < tree.options.ajaxDefaults < ajaxOptions
  1097. var self = this;
  1098. var orgSuccess = ajaxOptions.success;
  1099. var orgError = ajaxOptions.error;
  1100. var options = $.extend({}, this.tree.options.ajaxDefaults, ajaxOptions, {
  1101. /*
  1102. complete: function(req, textStatus){
  1103. alert("ajax complete");
  1104. },
  1105. timeout: 5000, // 5 sec
  1106. */
  1107. success: function(data, textStatus){
  1108. // <this> is the request options
  1109. // self.tree.logDebug("appendAjax().success");
  1110. var prevPhase = self.tree.phase;
  1111. self.tree.phase = "init";
  1112. // self.append(data);
  1113. self.addChild(data, null);
  1114. self.tree.phase = "postInit";
  1115. self.setLazyNodeStatus(DTNodeStatus_Ok);
  1116. if( orgSuccess )
  1117. orgSuccess.call(options, self);
  1118. self.tree.phase = prevPhase;
  1119. },
  1120. error: function(XMLHttpRequest, textStatus, errorThrown){
  1121. // <this> is the request options
  1122. // self.tree.logWarning("appendAjax failed: %o:\n%o\n%o", textStatus, XMLHttpRequest, errorThrown);
  1123. self.tree.logWarning("appendAjax failed:", textStatus, ":\n", XMLHttpRequest, "\n", errorThrown);
  1124. self.setLazyNodeStatus(DTNodeStatus_Error, {info: textStatus, tooltip: ""+errorThrown});
  1125. if( orgError )
  1126. orgError.call(options, self, XMLHttpRequest, textStatus, errorThrown);
  1127. }
  1128. });
  1129. $.ajax(options);
  1130. },
  1131. // --- end of class
  1132. lastentry: undefined
  1133. }
  1134. /*************************************************************************
  1135. * class DynaTreeStatus
  1136. */
  1137. var DynaTreeStatus = Class.create();
  1138. DynaTreeStatus._getTreePersistData = function(cookieId, cookieOpts) {
  1139. // Static member: Return persistence information from cookies
  1140. var ts = new DynaTreeStatus(cookieId, cookieOpts);
  1141. ts.read();
  1142. return ts.toDict();
  1143. }
  1144. // Make available in global scope
  1145. getDynaTreePersistData = DynaTreeStatus._getTreePersistData;
  1146. DynaTreeStatus.prototype = {
  1147. // Constructor
  1148. initialize: function(cookieId, cookieOpts) {
  1149. this._log("DynaTreeStatus: initialize");
  1150. if( cookieId === undefined )
  1151. cookieId = $.ui.dynatree.defaults.cookieId;
  1152. cookieOpts = $.extend({}, $.ui.dynatree.defaults.cookie, cookieOpts);
  1153. this.cookieId = cookieId;
  1154. this.cookieOpts = cookieOpts;
  1155. this.cookiesFound = undefined;
  1156. this.activeKey = null;
  1157. this.focusedKey = null;
  1158. this.expandedKeyList = null;
  1159. this.selectedKeyList = null;
  1160. },
  1161. // member functions
  1162. _log: function(msg) {
  1163. // this.logDebug("_changeNodeList(%o): nodeList:%o, idx:%o", mode, nodeList, idx);
  1164. Array.prototype.unshift.apply(arguments, ["debug"]);
  1165. _log.apply(this, arguments);
  1166. },
  1167. read: function() {
  1168. this._log("DynaTreeStatus: read");
  1169. // Read or init cookies.
  1170. this.cookiesFound = false;
  1171. var cookie = $.cookie(this.cookieId + "-active");
  1172. this.activeKey = ( cookie == null ) ? "" : cookie;
  1173. if( cookie != null ) this.cookiesFound = true;
  1174. cookie = $.cookie(this.cookieId + "-focus");
  1175. this.focusedKey = ( cookie == null ) ? "" : cookie;
  1176. if( cookie != null ) this.cookiesFound = true;
  1177. cookie = $.cookie(this.cookieId + "-expand");
  1178. this.expandedKeyList = ( cookie == null ) ? [] : cookie.split(",");
  1179. if( cookie != null ) this.cookiesFound = true;
  1180. cookie = $.cookie(this.cookieId + "-select");
  1181. this.selectedKeyList = ( cookie == null ) ? [] : cookie.split(",");
  1182. if( cookie != null ) this.cookiesFound = true;
  1183. },
  1184. write: function() {
  1185. this._log("DynaTreeStatus: write");
  1186. $.cookie(this.cookieId + "-active", ( this.activeKey == null ) ? "" : this.activeKey, this.cookieOpts);
  1187. $.cookie(this.cookieId + "-focus", ( this.focusedKey == null ) ? "" : this.focusedKey, this.cookieOpts);
  1188. $.cookie(this.cookieId + "-expand", ( this.expandedKeyList == null ) ? "" : this.expandedKeyList.join(","), this.cookieOpts);
  1189. $.cookie(this.cookieId + "-select", ( this.selectedKeyList == null ) ? "" : this.selectedKeyList.join(","), this.cookieOpts);
  1190. },
  1191. addExpand: function(key) {
  1192. this._log("addExpand(%o)", key);
  1193. if( $.inArray(key, this.expandedKeyList) < 0 ) {
  1194. this.expandedKeyList.push(key);
  1195. $.cookie(this.cookieId + "-expand", this.expandedKeyList.join(","), this.cookieOpts);
  1196. }
  1197. },
  1198. clearExpand: function(key) {
  1199. this._log("clearExpand(%o)", key);
  1200. var idx = $.inArray(key, this.expandedKeyList);
  1201. if( idx >= 0 ) {
  1202. this.expandedKeyList.splice(idx, 1);
  1203. $.cookie(this.cookieId + "-expand", this.expandedKeyList.join(","), this.cookieOpts);
  1204. }
  1205. },
  1206. addSelect: function(key) {
  1207. this._log("addSelect(%o)", key);
  1208. if( $.inArray(key, this.selectedKeyList) < 0 ) {
  1209. this.selectedKeyList.push(key);
  1210. $.cookie(this.cookieId + "-select", this.selectedKeyList.join(","), this.cookieOpts);
  1211. }
  1212. },
  1213. clearSelect: function(key) {
  1214. this._log("clearSelect(%o)", key);
  1215. var idx = $.inArray(key, this.selectedKeyList);
  1216. if( idx >= 0 ) {
  1217. this.selectedKeyList.splice(idx, 1);
  1218. $.cookie(this.cookieId + "-select", this.selectedKeyList.join(","), this.cookieOpts);
  1219. }
  1220. },
  1221. isReloading: function() {
  1222. return this.cookiesFound == true;
  1223. },
  1224. toDict: function() {
  1225. return {
  1226. cookiesFound: this.cookiesFound,
  1227. activeKey: this.activeKey,
  1228. focusedKey: this.activeKey,
  1229. expandedKeyList: this.expandedKeyList,
  1230. selectedKeyList: this.selectedKeyList
  1231. };
  1232. },
  1233. // --- end of class
  1234. lastentry: undefined
  1235. };
  1236. /*************************************************************************
  1237. * class DynaTree
  1238. */
  1239. var DynaTree = Class.create();
  1240. // --- Static members ----------------------------------------------------------
  1241. DynaTree.version = "$Version: 0.5.4$";
  1242. /*
  1243. DynaTree._initTree = function() {
  1244. };
  1245. DynaTree._bind = function() {
  1246. };
  1247. */
  1248. //--- Class members ------------------------------------------------------------
  1249. DynaTree.prototype = {
  1250. // Constructor
  1251. // initialize: function(divContainer, options) {
  1252. initialize: function($widget) {
  1253. // instance members
  1254. this.phase = "init";
  1255. this.$widget = $widget;
  1256. this.options = $widget.options;
  1257. this.$tree = $widget.element;
  1258. // find container element
  1259. this.divTree = this.$tree.get(0);
  1260. },
  1261. // member functions
  1262. _load: function() {
  1263. var $widget = this.$widget;
  1264. var opts = this.options;
  1265. this.bEnableUpdate = true;
  1266. this._nodeCount = 1;
  1267. this.activeNode = null;
  1268. this.focusNode = null;
  1269. // If a 'options.classNames' dictionary was passed, still use defaults
  1270. // for undefined classes:
  1271. if( opts.classNames !== $.ui.dynatree.defaults.classNames ) {
  1272. opts.classNames = $.extend({}, $.ui.dynatree.defaults.classNames, opts.classNames);
  1273. }
  1274. // Guess skin path, if not specified
  1275. if(!opts.imagePath) {
  1276. $("script").each( function () {
  1277. // Eclipse syntax parser breaks on this expression, so put it at the bottom:
  1278. if( this.src.search(_rexDtLibName) >= 0 ) {
  1279. if( this.src.indexOf("/")>=0 ) // issue #47
  1280. opts.imagePath = this.src.slice(0, this.src.lastIndexOf("/")) + "/skin/";
  1281. else
  1282. opts.imagePath = "skin/";
  1283. // logMsg("Guessing imagePath from '%s': '%s'", this.src, opts.imagePath);
  1284. return false; // first match
  1285. }
  1286. });
  1287. }
  1288. this.persistence = new DynaTreeStatus(opts.cookieId, opts.cookie);
  1289. if( opts.persist ) {
  1290. if( !$.cookie )
  1291. _log("warn", "Please include jquery.cookie.js to use persistence.");
  1292. this.persistence.read();
  1293. }
  1294. this.logDebug("DynaTree.persistence: %o", this.persistence.toDict());
  1295. // Cached tag strings
  1296. this.cache = {
  1297. tagEmpty: "<span class='" + opts.classNames.empty + "'></span>",
  1298. tagVline: "<span class='" + opts.classNames.vline + "'></span>",
  1299. tagExpander: "<span class='" + opts.classNames.expander + "'></span>",
  1300. tagConnector: "<span class='" + opts.classNames.connector + "'></span>",
  1301. tagNodeIcon: "<span class='" + opts.classNames.nodeIcon + "'></span>",
  1302. tagCheckbox: "<span class='" + opts.classNames.checkbox + "'></span>",
  1303. lastentry: undefined
  1304. };
  1305. // Clear container, in case it contained some 'waiting' or 'error' text
  1306. // for clients that don't support JS.
  1307. // We don't do this however, if we try to load from an embedded UL element.
  1308. if( opts.children || (opts.initAjax && opts.initAjax.url) || opts.initId )
  1309. $(this.divTree).empty();
  1310. else if( this.divRoot )
  1311. $(this.divRoot).remove();
  1312. // create the root element
  1313. this.tnRoot = new DynaTreeNode(null, this, {title: opts.title, key: "root"});
  1314. this.tnRoot.data.isFolder = true;
  1315. this.tnRoot.render(false, false);
  1316. this.divRoot = this.tnRoot.div;
  1317. this.divRoot.className = opts.classNames.container;
  1318. // add root to container
  1319. // TODO: this should be delayed until all children have been created for performance reasons
  1320. this.divTree.appendChild(this.divRoot);
  1321. var root = this.tnRoot;
  1322. var isReloading = ( opts.persist && this.persistence.isReloading() );
  1323. var isLazy = false;
  1324. var prevFlag = this.enableUpdate(false);
  1325. this.logDebug("Dynatree._load(): read tree structure...");
  1326. // Init tree structure
  1327. if( opts.children ) {
  1328. // Read structure from node array
  1329. root.addChild(opts.children);
  1330. } else if( opts.initAjax && opts.initAjax.url ) {
  1331. // Init tree from AJAX request
  1332. isLazy = true;
  1333. root.data.isLazy = true;
  1334. this._reloadAjax();
  1335. } else if( opts.initId ) {
  1336. // Init tree from another UL element
  1337. this._createFromTag(root, $("#"+opts.initId));
  1338. } else {
  1339. // Init tree from the first UL element inside the container <div>
  1340. var $ul = this.$tree.find(">ul").hide();
  1341. this._createFromTag(root, $ul);
  1342. $ul.remove();
  1343. }
  1344. this._checkConsistency();
  1345. // Render html markup
  1346. this.logDebug("Dynatree._load(): render nodes...");
  1347. this.enableUpdate(prevFlag);
  1348. // bind event handlers
  1349. this.logDebug("Dynatree._load(): bind events...");
  1350. this.$widget.bind();
  1351. // --- Post-load processing
  1352. this.logDebug("Dynatree._load(): postInit...");
  1353. this.phase = "postInit";
  1354. // In persist mode, make sure that cookies are written, even if they are empty
  1355. if( opts.persist ) {
  1356. this.persistence.write();
  1357. }
  1358. // Set focus, if possible (this will also fire an event and write a cookie)
  1359. if( this.focusNode && this.focusNode.isVisible() ) {
  1360. this.logDebug("Focus on init: %o", this.focusNode);
  1361. this.focusNode.focus();
  1362. }
  1363. if( !isLazy && opts.onPostInit ) {
  1364. opts.onPostInit.call(this, isReloading, false);
  1365. }
  1366. this.phase = "idle";
  1367. },
  1368. _reloadAjax: function() {
  1369. // Reload
  1370. var opts = this.options;
  1371. if( ! opts.initAjax || ! opts.initAjax.url )
  1372. throw "tree.reload() requires 'initAjax' mode.";
  1373. var pers = this.persistence;
  1374. var ajaxOpts = $.extend({}, opts.initAjax);
  1375. // Append cookie info to the request
  1376. // this.logDebug("reloadAjax: key=%o, an.key:%o", pers.activeKey, this.activeNode?this.activeNode.data.key:"?");
  1377. if( ajaxOpts.addActiveKey )
  1378. ajaxOpts.data.activeKey = pers.activeKey;
  1379. if( ajaxOpts.addFocusedKey )
  1380. ajaxOpts.data.focusedKey = pers.focusedKey;
  1381. if( ajaxOpts.addExpandedKeyList )
  1382. ajaxOpts.data.expandedKeyList = pers.expandedKeyList.join(",");
  1383. if( ajaxOpts.addSelectedKeyList )
  1384. ajaxOpts.data.selectedKeyList = pers.selectedKeyList.join(",");
  1385. // Set up onPostInit callback to be called when Ajax returns
  1386. if( opts.onPostInit ) {
  1387. if( ajaxOpts.success )
  1388. this.logWarning("initAjax: success callback is ignored when onPostInit was specified.");
  1389. if( ajaxOpts.error )
  1390. this.logWarning("initAjax: error callback is ignored when onPostInit was specified.");
  1391. var isReloading = pers.isReloading();
  1392. ajaxOpts["success"] = function(dtnode) { opts.onPostInit.call(dtnode.tree, isReloading, false); };
  1393. ajaxOpts["error"] = function(dtnode) { opts.onPostInit.call(dtnode.tree, isReloading, true); };
  1394. }
  1395. this.logDebug("Dynatree._init(): send Ajax request...");
  1396. this.tnRoot.appendAjax(ajaxOpts);
  1397. },
  1398. toString: function() {
  1399. return "DynaTree '" + this.options.title + "'";
  1400. },
  1401. toDict: function() {
  1402. return this.tnRoot.toDict(true);
  1403. },
  1404. getPersistData: function() {
  1405. return this.persistence.toDict();
  1406. },
  1407. logDebug: function(msg) {
  1408. if( this.options.debugLevel >= 2 ) {
  1409. Array.prototype.unshift.apply(arguments, ["debug"]);
  1410. _log.apply(this, arguments);
  1411. }
  1412. },
  1413. logInfo: function(msg) {
  1414. if( this.options.debugLevel >= 1 ) {
  1415. Array.prototype.unshift.apply(arguments, ["info"]);
  1416. _log.apply(this, arguments);
  1417. }
  1418. },
  1419. logWarning: function(msg) {
  1420. Array.prototype.unshift.apply(arguments, ["warn"]);
  1421. _log.apply(this, arguments);
  1422. },
  1423. isInitializing: function() {
  1424. return ( this.phase=="init" || this.phase=="postInit" );
  1425. },
  1426. isReloading: function() {
  1427. return ( this.phase=="init" || this.phase=="postInit" ) && this.options.persist && this.persistence.cookiesFound;
  1428. },
  1429. isUserEvent: function() {
  1430. return ( this.phase=="userEvent" );
  1431. },
  1432. redraw: function() {
  1433. this.logDebug("dynatree.redraw()...");
  1434. this.tnRoot.render(true, true);
  1435. this.logDebug("dynatree.redraw() done.");
  1436. },
  1437. reloadAjax: function() {
  1438. this.logWarning("tree.reloadAjax() is deprecated since v0.5.2 (use reload() instead).");
  1439. },
  1440. reload: function() {
  1441. this._load();
  1442. },
  1443. getRoot: function() {
  1444. return this.tnRoot;
  1445. },
  1446. getNodeByKey: function(key) {
  1447. // $("#...") has problems, if the key contains '.', so we use getElementById()
  1448. // return $("#" + this.options.idPrefix + key).attr("dtnode");
  1449. var el = document.getElementById(this.options.idPrefix + key);
  1450. return ( el && el.dtnode ) ? el.dtnode : null;
  1451. },
  1452. getActiveNode: function() {
  1453. return this.activeNode;
  1454. },
  1455. reactivate: function(setFocus) {
  1456. // Re-fire onQueryActivate and onActivate events.
  1457. var node = this.activeNode;
  1458. // this.logDebug("reactivate %o", node);
  1459. if( node ) {
  1460. this.activeNode = null; // Force re-activating
  1461. node.activate();
  1462. if( setFocus )
  1463. node.focus();
  1464. }
  1465. },
  1466. getSelectedNodes: function(stopOnParents) {
  1467. var nodeList = [];
  1468. this.tnRoot.visit(function(dtnode){
  1469. if( dtnode.bSelected ) {
  1470. nodeList.push(dtnode);
  1471. if( stopOnParents == true )
  1472. return false; // stop processing this branch
  1473. }
  1474. });
  1475. return nodeList;
  1476. },
  1477. activateKey: function(key) {
  1478. var dtnode = (key === null) ? null : this.getNodeByKey(key);
  1479. if( !dtnode ) {
  1480. if( this.activeNode )
  1481. this.activeNode.deactivate();
  1482. this.activeNode = null;
  1483. return null;
  1484. }
  1485. dtnode.focus();
  1486. dtnode.activate();
  1487. return dtnode;
  1488. },
  1489. selectKey: function(key, select) {
  1490. var dtnode = this.getNodeByKey(key);
  1491. if( !dtnode )
  1492. return null;
  1493. dtnode.select(select);
  1494. return dtnode;
  1495. },
  1496. enableUpdate: function(bEnable) {
  1497. if ( this.bEnableUpdate==bEnable )
  1498. return bEnable;
  1499. this.bEnableUpdate = bEnable;
  1500. if ( bEnable )
  1501. this.redraw();
  1502. return !bEnable; // return previous value
  1503. },
  1504. visit: function(fn, data, includeRoot) {
  1505. return this.tnRoot.visit(fn, data, includeRoot);
  1506. },
  1507. _createFromTag: function(parentTreeNode, $ulParent) {
  1508. // Convert a <UL>...</UL> list into children of the parent tree node.
  1509. var self = this;
  1510. /*
  1511. TODO: better?
  1512. this.$lis = $("li:has(a[href])", this.element);
  1513. this.$tabs = this.$lis.map(function() { return $("a", this)[0]; });
  1514. */
  1515. $ulParent.find(">li").each(function() {
  1516. var $li = $(this);
  1517. var $liSpan = $li.find(">span:first");
  1518. var title;
  1519. if( $liSpan.length ) {
  1520. // If a <li><span> tag is specified, use it literally.
  1521. title = $liSpan.html();
  1522. } else {
  1523. // If only a <li> tag is specified, use the trimmed string up to the next child <ul> tag.
  1524. title = $li.html();
  1525. var iPos = title.search(/<ul/i);
  1526. if( iPos>=0 )
  1527. title = $.trim(title.substring(0, iPos));
  1528. else
  1529. title = $.trim(title);
  1530. // self.logDebug("%o", title);
  1531. }
  1532. // Parse node options from ID, title and class attributes
  1533. var data = {
  1534. title: title,
  1535. isFolder: $li.hasClass("folder"),
  1536. isLazy: $li.hasClass("lazy"),
  1537. expand: $li.hasClass("expanded"),
  1538. select: $li.hasClass("selected"),
  1539. activate: $li.hasClass("active"),
  1540. focus: $li.hasClass("focused")
  1541. };
  1542. if( $li.attr("title") )
  1543. data.tooltip = $li.attr("title");
  1544. if( $li.attr("id") )
  1545. data.key = $li.attr("id");
  1546. // If a data attribute is present, evaluate as a JavaScript object
  1547. if( $li.attr("data") ) {
  1548. var dataAttr = $.trim($li.attr("data"));
  1549. if( dataAttr ) {
  1550. if( dataAttr.charAt(0) != "{" )
  1551. dataAttr = "{" + dataAttr + "}"
  1552. try {
  1553. $.extend(data, eval("(" + dataAttr + ")"));
  1554. } catch(e) {
  1555. throw ("Error parsing node data: " + e + "\ndata:\n'" + dataAttr + "'");
  1556. }
  1557. }
  1558. }
  1559. childNode = parentTreeNode.addChild(data);
  1560. // Recursive reading of child nodes, if LI tag contains an UL tag
  1561. var $ul = $li.find(">ul:first");
  1562. if( $ul.length ) {
  1563. self._createFromTag(childNode, $ul); // must use 'self', because 'this' is the each() context
  1564. }
  1565. });
  1566. },
  1567. _checkConsistency: function() {
  1568. // this.logDebug("tree._checkConsistency() NOT IMPLEMENTED - %o", this);
  1569. },
  1570. // --- end of class
  1571. lastentry: undefined
  1572. };
  1573. /*************************************************************************
  1574. * widget $(..).dynatree
  1575. */
  1576. $.widget("ui.dynatree", {
  1577. init: function() {
  1578. // ui.core 1.6 renamed init() to _init(): this stub assures backward compatibility
  1579. _log("warn", "ui.dynatree.init() was called; you should upgrade to ui.core.js v1.6 or higher.");
  1580. return this._init();
  1581. },
  1582. _init: function() {
  1583. if( parseFloat($.ui.version) < 1.8 ) {
  1584. // jquery.ui.core 1.8 renamed _init() to _create(): this stub assures backward compatibility
  1585. _log("info", "ui.dynatree._init() was called; consider upgrading to jquery.ui.core.js v1.8 or higher.");
  1586. return this._create();
  1587. }
  1588. // jquery.ui.core 1.8 still uses _init() to perform "default functionality"
  1589. _log("debug", "ui.dynatree._init() was called; no current default functionality.");
  1590. },
  1591. _create: function() {
  1592. if( parseFloat($.ui.version) >= 1.8 ) {
  1593. this.options = $.extend(true, {}, $[this.namespace][this.widgetName].defaults, this.options);
  1594. }
  1595. logMsg("Dynatree._create(): version='%s', debugLevel=%o.", DynaTree.version, this.options.debugLevel);
  1596. var opts = this.options;
  1597. // The widget framework supplies this.element and this.options.
  1598. this.options.event += ".dynatree"; // namespace event
  1599. var divTree = this.element.get(0);
  1600. /* // Clear container, in case it contained some 'waiting' or 'error' text
  1601. // for clients that don't support JS
  1602. if( opts.children || (opts.initAjax && opts.initAjax.url) || opts.initId )
  1603. $(divTree).empty();
  1604. */
  1605. // Create the DynaTree object
  1606. this.tree = new DynaTree(this);
  1607. this.tree._load();
  1608. this.tree.logDebug("Dynatree._create(): done.");
  1609. },
  1610. bind: function() {
  1611. var $this = this.element;
  1612. var o = this.options;
  1613. // Prevent duplicate binding
  1614. this.unbind();
  1615. // Tool function to get dtnode from the event target:
  1616. function __getNodeFromElement(el) {
  1617. var iMax = 5;
  1618. while( el && iMax-- ) {
  1619. if( el.dtnode ) return el.dtnode;
  1620. el = el.parentNode;
  1621. };
  1622. return null;
  1623. }
  1624. var eventNames = "click.dynatree dblclick.dynatree";
  1625. if( o.keyboard ) // Note: leading ' '!
  1626. eventNames += " keypress.dynatree keydown.dynatree";
  1627. $this.bind(eventNames, function(event){
  1628. var dtnode = __getNodeFromElement(event.target);
  1629. if( !dtnode )
  1630. return true; // Allow bubbling of other events
  1631. var prevPhase = dtnode.tree.phase;
  1632. dtnode.tree.phase = "userEvent";
  1633. try {
  1634. dtnode.tree.logDebug("bind(%o): dtnode: %o", event, dtnode);
  1635. switch(event.type) {
  1636. case "click":
  1637. return ( o.onClick && o.onClick(dtnode, event)===false ) ? false : dtnode.onClick(event);
  1638. case "dblclick":
  1639. return ( o.onDblClick && o.onDblClick(dtnode, event)===false ) ? false : dtnode.onDblClick(event);
  1640. case "keydown":
  1641. return ( o.onKeydown && o.onKeydown(dtnode, event)===false ) ? false : dtnode.onKeydown(event);
  1642. case "keypress":
  1643. return ( o.onKeypress && o.onKeypress(dtnode, event)===false ) ? false : dtnode.onKeypress(event);
  1644. };
  1645. } catch(e) {
  1646. var _ = null; // issue 117
  1647. // dtnode.tree.logError("bind(%o): dtnode: %o", event, dtnode);
  1648. } finally {
  1649. dtnode.tree.phase = prevPhase;
  1650. }
  1651. });
  1652. // focus/blur don't bubble, i.e. are not delegated to parent <div> tags,
  1653. // so we use the addEventListener capturing phase.
  1654. // See http://www.howtocreate.co.uk/tutorials/javascript/domevents
  1655. function __focusHandler(event) {
  1656. // Handles blur and focus.
  1657. // Fix event for IE:
  1658. event = arguments[0] = $.event.fix( event || window.event );
  1659. var dtnode = __getNodeFromElement(event.target);
  1660. return dtnode ? dtnode.onFocus(event) : false;
  1661. }
  1662. var div = this.tree.divTree;
  1663. if( div.addEventListener ) {
  1664. div.addEventListener("focus", __focusHandler, true);
  1665. div.addEventListener("blur", __focusHandler, true);
  1666. } else {
  1667. div.onfocusin = div.onfocusout = __focusHandler;
  1668. }
  1669. // EVENTS
  1670. // disable click if event is configured to something else
  1671. // if (!(/^click/).test(o.event))
  1672. // this.$tabs.bind("click.tabs", function() { return false; });
  1673. },
  1674. unbind: function() {
  1675. this.element.unbind(".dynatree");
  1676. },
  1677. /* TODO: we could handle option changes during runtime here (maybe to re-render, ...)
  1678. setData: function(key, value) {
  1679. this.tree.logDebug("dynatree.setData('" + key + "', '" + value + "')");
  1680. },
  1681. */
  1682. enable: function() {
  1683. this.bind();
  1684. // Call default disable(): remove -disabled from css:
  1685. $.widget.prototype.enable.apply(this, arguments);
  1686. },
  1687. disable: function() {
  1688. this.unbind();
  1689. // Call default disable(): add -disabled to css:
  1690. $.widget.prototype.disable.apply(this, arguments);
  1691. },
  1692. // --- getter methods (i.e. NOT returning a reference to $)
  1693. getTree: function() {
  1694. return this.tree;
  1695. },
  1696. getRoot: function() {
  1697. return this.tree.getRoot();
  1698. },
  1699. getActiveNode: function() {
  1700. return this.tree.getActiveNode();
  1701. },
  1702. getSelectedNodes: function() {
  1703. return this.tree.getSelectedNodes();
  1704. },
  1705. // ------------------------------------------------------------------------
  1706. lastentry: undefined
  1707. });
  1708. // The following methods return a value (thus breaking the jQuery call chain):
  1709. $.ui.dynatree.getter = "getTree getRoot getActiveNode getSelectedNodes";
  1710. // Plugin default options:
  1711. $.ui.dynatree.defaults = {
  1712. title: "Dynatree root", // Name of the root node.
  1713. rootVisible: false, // Set to true, to make the root node visible.
  1714. minExpandLevel: 1, // 1: root node is not collapsible
  1715. imagePath: null, // Path to a folder containing icons. Defaults to 'skin/' subdirectory.
  1716. children: null, // Init tree structure from this object array.
  1717. initId: null, // Init tree structure from a <ul> element with this ID.
  1718. initAjax: null, // Ajax options used to initialize the tree strucuture.
  1719. autoFocus: true, // Set focus to first child, when expanding or lazy-loading.
  1720. keyboard: true, // Support keyboard navigation.
  1721. persist: false, // Persist expand-status to a cookie
  1722. autoCollapse: false, // Automatically collapse all siblings, when a node is expanded.
  1723. clickFolderMode: 3, // 1:activate, 2:expand, 3:activate and expand
  1724. activeVisible: true, // Make sure, active nodes are visible (expanded).
  1725. checkbox: false, // Show checkboxes.
  1726. selectMode: 2, // 1:single, 2:multi, 3:multi-hier
  1727. fx: null, // Animations, e.g. null or { height: "toggle", duration: 200 }
  1728. // Low level event handlers: onEvent(dtnode, event): return false, to stop default processing
  1729. onClick: null, // null: generate focus, expand, activate, select events.
  1730. onDblClick: null, // (No default actions.)
  1731. onKeydown: null, // null: generate keyboard navigation (focus, expand, activate).
  1732. onKeypress: null, // (No default actions.)
  1733. onFocus: null, // null: set focus to node.
  1734. onBlur: null, // null: remove focus from node.
  1735. // Pre-event handlers onQueryEvent(flag, dtnode): return false, to stop processing
  1736. onQueryActivate: null, // Callback(flag, dtnode) before a node is (de)activated.
  1737. onQuerySelect: null, // Callback(flag, dtnode) before a node is (de)selected.
  1738. onQueryExpand: null, // Callback(flag, dtnode) before a node is expanded/collpsed.
  1739. // High level event handlers
  1740. onPostInit: null, // Callback(isReloading, isError) when tree was (re)loaded.
  1741. onActivate: null, // Callback(dtnode) when a node is activated.
  1742. onDeactivate: null, // Callback(dtnode) when a node is deactivated.
  1743. onSelect: null, // Callback(flag, dtnode) when a node is (de)selected.
  1744. onExpand: null, // Callback(dtnode) when a node is expanded/collapsed.
  1745. onLazyRead: null, // Callback(dtnode) when a lazy node is expanded for the first time.
  1746. ajaxDefaults: { // Used by initAjax option
  1747. cache: false, // false: Append random '_' argument to the request url to prevent caching.
  1748. dataType: "json" // Expect json format and pass json object to callbacks.
  1749. },
  1750. strings: {
  1751. loading: "Loading&#8230;",
  1752. loadError: "Load error!"
  1753. },
  1754. idPrefix: "ui-dynatree-id-", // Used to generate node id's like <span id="ui-dynatree-id-<key>">.
  1755. // cookieId: "ui-dynatree-cookie", // Choose a more unique name, to allow multiple trees.
  1756. cookieId: "dynatree", // Choose a more unique name, to allow multiple trees.
  1757. cookie: {
  1758. expires: null //7, // Days or Date; null: session cookie
  1759. // path: "/", // Defaults to current page
  1760. // domain: "jquery.com",
  1761. // secure: true
  1762. },
  1763. // Class names used, when rendering the HTML markup.
  1764. // Note: if only single entries are passed for options.classNames, all other
  1765. // values are still set to default.
  1766. classNames: {
  1767. container: "ui-dynatree-container",
  1768. folder: "ui-dynatree-folder",
  1769. document: "ui-dynatree-document",
  1770. empty: "ui-dynatree-empty",
  1771. vline: "ui-dynatree-vline",
  1772. expander: "ui-dynatree-expander",
  1773. connector: "ui-dynatree-connector",
  1774. checkbox: "ui-dynatree-checkbox",
  1775. nodeIcon: "ui-dynatree-icon",
  1776. title: "ui-dynatree-title",
  1777. nodeError: "ui-dynatree-statusnode-error",
  1778. nodeWait: "ui-dynatree-statusnode-wait",
  1779. hidden: "ui-dynatree-hidden",
  1780. combinedExpanderPrefix: "ui-dynatree-exp-",
  1781. combinedIconPrefix: "ui-dynatree-ico-",
  1782. // disabled: "ui-dynatree-disabled",
  1783. hasChildren: "ui-dynatree-has-children",
  1784. active: "ui-dynatree-active",
  1785. selected: "ui-dynatree-selected",
  1786. expanded: "ui-dynatree-expanded",
  1787. lazy: "ui-dynatree-lazy",
  1788. focused: "ui-dynatree-focused",
  1789. partsel: "ui-dynatree-partsel",
  1790. lastsib: "ui-dynatree-lastsib"
  1791. },
  1792. debugLevel: 1,
  1793. // ------------------------------------------------------------------------
  1794. lastentry: undefined
  1795. };
  1796. /**
  1797. * Reserved data attributes for a tree node.
  1798. */
  1799. $.ui.dynatree.nodedatadefaults = {
  1800. title: null, // (required) Displayed name of the node (html is allowed here)
  1801. key: null, // May be used with activate(), select(), find(), ...
  1802. isFolder: false, // Use a folder icon. Also the node is expandable but not selectable.
  1803. isLazy: false, // Call onLazyRead(), when the node is expanded for the first time to allow for delayed creation of children.
  1804. tooltip: null, // Show this popup text.
  1805. icon: null, // Use a custom image (filename relative to tree.options.imagePath). 'null' for default icon, 'false' for no icon.
  1806. addClass: null, // Class name added to the node's span tag.
  1807. activate: false, // Initial active status.
  1808. focus: false, // Initial focused status.
  1809. expand: false, // Initial expanded status.
  1810. select: false, // Initial selected status.
  1811. hideCheckbox: false, // Suppress checkbox display for this node.
  1812. unselectable: false, // Prevent selection.
  1813. // disabled: false,
  1814. // The following attributes are only valid if passed to some functions:
  1815. children: null, // Array of child nodes.
  1816. // NOTE: we can also add custom attributes here.
  1817. // This may then also be used in the onActivate(), onSelect() or onLazyTree() callbacks.
  1818. // ------------------------------------------------------------------------
  1819. lastentry: undefined
  1820. };
  1821. // ---------------------------------------------------------------------------
  1822. })(jQuery);
  1823. // Eclipse syntax parser breaks on this expression, so we put it at the bottom.
  1824. var _rexDtLibName = /.*dynatree[^/]*\.js$/i;