jquery.dynatree.js 64 KB

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