compass-jquery-plugin 0.2.4.100 → 0.2.5

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