Skip to content

Commit eab115c

Browse files
committed
Tagging the 2.1.0 release.
1 parent 8271e9a commit eab115c

39 files changed

+1018
-567
lines changed

dist/jquery.fancytree-all.js

Lines changed: 299 additions & 187 deletions
Large diffs are not rendered by default.

dist/jquery.fancytree-custom.min.js

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/jquery.fancytree.js

Lines changed: 109 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
* Released under the MIT license
88
* https://github.com/mar10/fancytree/wiki/LicenseInfo
99
*
10-
* @version 2.0.0
11-
* @date 2014-05-01T21:48
10+
* @version 2.1.0
11+
* @date 2014-05-29T16:44
1212
*/
1313

1414
/** Core Fancytree module.
@@ -61,6 +61,11 @@ function consoleApply(method, args){
6161
}
6262
}
6363

64+
/*Return true if x is a FancytreeNode.*/
65+
function _isNode(x){
66+
return !!(x.tree && x.statusNodeType !== undefined);
67+
}
68+
6469
/** Return true if dotted version string is equal or higher than requested version.
6570
*
6671
* See http://jsfiddle.net/mar10/FjSAN/
@@ -275,6 +280,8 @@ function FancytreeNode(parent, obj){
275280
} else {
276281
this.key = "_" + (FT._nextNodeKey++);
277282
}
283+
} else {
284+
this.key = "" + this.key; // Convert to string (#217)
278285
}
279286

280287
// Fix tree.activeNode
@@ -683,9 +690,8 @@ FancytreeNode.prototype = /** @lends FancytreeNode# */{
683690
// recursively set children and render
684691
this.removeChildren();
685692
this.addChildren(dict.children);
686-
}else{
687-
this.renderTitle();
688693
}
694+
this.renderTitle();
689695
/*
690696
var children = dict.children;
691697
if(children === undefined){
@@ -1350,79 +1356,93 @@ FancytreeNode.prototype = /** @lends FancytreeNode# */{
13501356
/**
13511357
*
13521358
* @param {boolean | PlainObject} [effects=false] animation options.
1353-
* @param {FancytreeNode} [topNode=null] this node will remain visible in
1359+
* @param {object} [options=null] {topNode: null, effects: ..., parent: ...} this node will remain visible in
13541360
* any case, even if `this` is outside the scroll pane.
13551361
* @returns {$.Promise}
13561362
*/
1357-
scrollIntoView: function(effects, topNode) {
1358-
effects = (effects === true) ? {duration: 200, queue: false} : effects;
1359-
var topNodeY,
1363+
scrollIntoView: function(effects, options) {
1364+
if( options !== undefined && _isNode(options) ) {
1365+
this.warn("scrollIntoView() with 'topNode' option is deprecated since 2014-05-08. Use 'options.topNode' instead.");
1366+
options = {topNode: options};
1367+
}
1368+
// this.$scrollParent = (this.options.scrollParent === "auto") ? $ul.scrollParent() : $(this.options.scrollParent);
1369+
// this.$scrollParent = this.$scrollParent.length ? this.$scrollParent || this.$container;
1370+
1371+
var topNodeY, nodeY, horzScrollbarHeight, containerOffsetTop,
1372+
opts = $.extend({
1373+
effects: (effects === true) ? {duration: 200, queue: false} : effects,
1374+
scrollOfs: this.tree.options.scrollOfs,
1375+
scrollParent: this.tree.options.scrollParent || this.tree.$container,
1376+
topNode: null
1377+
}, options),
13601378
dfd = new $.Deferred(),
13611379
that = this,
1362-
nodeY = $(this.span).position().top,
13631380
nodeHeight = $(this.span).height(),
1364-
$container = this.tree.$container,
1365-
scrollTop = $container[0].scrollTop,
1366-
horzScrollHeight = Math.max(0, ($container.innerHeight() - $container[0].clientHeight)),
1367-
// containerHeight = $container.height(),
1368-
containerHeight = $container.height() - horzScrollHeight,
1381+
$container = $(opts.scrollParent),
1382+
topOfs = opts.scrollOfs.top || 0,
1383+
bottomOfs = opts.scrollOfs.bottom || 0,
1384+
containerHeight = $container.height(),// - topOfs - bottomOfs,
1385+
scrollTop = $container.scrollTop(),
1386+
$animateTarget = $container,
1387+
isParentWindow = $container[0] === window,
1388+
topNode = opts.topNode || null,
13691389
newScrollTop = null;
13701390

1371-
// console.log("horzScrollHeight: " + horzScrollHeight);
1372-
// console.log("$container[0].scrollTop: " + $container[0].scrollTop);
1373-
// console.log("$container[0].scrollHeight: " + $container[0].scrollHeight);
1374-
// console.log("$container[0].clientHeight: " + $container[0].clientHeight);
1375-
// console.log("$container.innerHeight(): " + $container.innerHeight());
1376-
// console.log("$container.height(): " + $container.height());
1377-
1378-
if(nodeY < 0){
1379-
newScrollTop = scrollTop + nodeY;
1380-
}else if((nodeY + nodeHeight) > containerHeight){
1381-
newScrollTop = scrollTop + nodeY - containerHeight + nodeHeight;
1391+
// this.debug("scrollIntoView(), scrollTop=", scrollTop, opts.scrollOfs);
1392+
_assert($(this.span).is(":visible"), "scrollIntoView node is invisible"); // otherwise we cannot calc offsets
1393+
1394+
if( isParentWindow ) {
1395+
nodeY = $(this.span).offset().top;
1396+
topNodeY = topNode ? $(topNode.span).offset().top : 0;
1397+
$animateTarget = $("html,body");
1398+
1399+
} else {
1400+
_assert($container[0] !== document && $container[0] !== document.body, "scrollParent should be an simple element or `window`, not document or body.");
1401+
1402+
containerOffsetTop = $container.offset().top,
1403+
nodeY = $(this.span).offset().top - containerOffsetTop + scrollTop; // relative to scroll parent
1404+
topNodeY = topNode ? $(topNode.span).offset().top - containerOffsetTop + scrollTop : 0;
1405+
horzScrollbarHeight = Math.max(0, ($container.innerHeight() - $container[0].clientHeight));
1406+
containerHeight -= horzScrollbarHeight;
1407+
}
1408+
1409+
// this.debug(" scrollIntoView(), nodeY=", nodeY, "containerHeight=", containerHeight);
1410+
if( nodeY < (scrollTop + topOfs) ){
1411+
// Node is above visible container area
1412+
newScrollTop = nodeY - topOfs;
1413+
// this.debug(" scrollIntoView(), UPPER newScrollTop=", newScrollTop);
1414+
1415+
}else if((nodeY + nodeHeight) > (scrollTop + containerHeight - bottomOfs)){
1416+
newScrollTop = nodeY + nodeHeight - containerHeight + bottomOfs;
1417+
// this.debug(" scrollIntoView(), LOWER newScrollTop=", newScrollTop);
13821418
// If a topNode was passed, make sure that it is never scrolled
13831419
// outside the upper border
13841420
if(topNode){
1385-
topNodeY = topNode ? $(topNode.span).position().top : 0;
1386-
if((nodeY - topNodeY) > containerHeight){
1387-
newScrollTop = scrollTop + topNodeY;
1421+
_assert($(topNode.span).is(":visible"));
1422+
if( topNodeY < newScrollTop ){
1423+
newScrollTop = topNodeY - topOfs;
1424+
// this.debug(" scrollIntoView(), TOP newScrollTop=", newScrollTop);
13881425
}
13891426
}
13901427
}
1428+
13911429
if(newScrollTop !== null){
1392-
if(effects){
1393-
// TODO: resolve dfd after animation
1394-
// var that = this;
1395-
effects.complete = function(){
1430+
// this.debug(" scrollIntoView(), SET newScrollTop=", newScrollTop);
1431+
if(opts.effects){
1432+
opts.effects.complete = function(){
13961433
dfd.resolveWith(that);
13971434
};
1398-
$container.animate({
1435+
$animateTarget.stop(true).animate({
13991436
scrollTop: newScrollTop
1400-
}, effects);
1437+
}, opts.effects);
14011438
}else{
1402-
$container[0].scrollTop = newScrollTop;
1439+
$animateTarget[0].scrollTop = newScrollTop;
14031440
dfd.resolveWith(this);
14041441
}
14051442
}else{
14061443
dfd.resolveWith(this);
14071444
}
14081445
return dfd.promise();
1409-
/* from jQuery.menu:
1410-
var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
1411-
if ( this._hasScroll() ) {
1412-
borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
1413-
paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
1414-
offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
1415-
scroll = this.activeMenu.scrollTop();
1416-
elementHeight = this.activeMenu.height();
1417-
itemHeight = item.height();
1418-
1419-
if ( offset < 0 ) {
1420-
this.activeMenu.scrollTop( scroll + offset );
1421-
} else if ( offset + itemHeight > elementHeight ) {
1422-
this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
1423-
}
1424-
}
1425-
*/
14261446
},
14271447

14281448
/**Activate this node.
@@ -2817,8 +2837,8 @@ $.extend(Fancytree.prototype,
28172837
// folder or doctype icon
28182838
role = aria ? " role='img'" : "";
28192839
if ( icon && typeof icon === "string" ) {
2820-
imageSrc = (icon.charAt(0) === "/") ? icon : (opts.imagePath + icon);
2821-
ares.push("<img src='" + imageSrc + "' alt='' />");
2840+
imageSrc = (icon.charAt(0) === "/") ? icon : ((opts.imagePath || "") + icon);
2841+
ares.push("<img src='" + imageSrc + "' class='fancytree-icon' alt='' />");
28222842
} else if ( node.data.iconclass ) {
28232843
// TODO: review and test and document
28242844
ares.push("<span " + role + " class='fancytree-custom-icon" + " " + node.data.iconclass + "'></span>");
@@ -2834,7 +2854,6 @@ $.extend(Fancytree.prototype,
28342854
nodeTitle = opts.renderTitle.call(tree, {type: "renderTitle"}, ctx) || "";
28352855
}
28362856
if(!nodeTitle){
2837-
// TODO: escape tooltip string
28382857
tooltip = node.tooltip ? " title='" + FT.escapeHtml(node.tooltip) + "'" : "";
28392858
id = aria ? " id='ftal_" + node.key + "'" : "";
28402859
role = aria ? " role='treeitem'" : "";
@@ -2979,7 +2998,6 @@ $.extend(Fancytree.prototype,
29792998
node = ctx.node,
29802999
tree = ctx.tree,
29813000
opts = ctx.options,
2982-
// userEvent = !!ctx.originalEvent,
29833001
noEvents = (callOpts.noEvents === true),
29843002
isActive = (node === tree.activeNode);
29853003

@@ -3003,7 +3021,7 @@ $.extend(Fancytree.prototype,
30033021
}
30043022
if(opts.activeVisible){
30053023
// tree.nodeMakeVisible(ctx);
3006-
node.makeVisible();
3024+
node.makeVisible({scrollIntoView: false}); // nodeSetFocus will scroll
30073025
}
30083026
tree.activeNode = node;
30093027
tree.nodeRenderStatus(ctx);
@@ -3080,9 +3098,9 @@ $.extend(Fancytree.prototype,
30803098
}
30813099
// Trigger expand/collapse after expanding
30823100
dfd.done(function(){
3083-
if( opts.autoScroll && !noAnimation ) {
3101+
if( flag && opts.autoScroll && !noAnimation ) {
30843102
// Scroll down to last child, but keep current node visible
3085-
node.getLastChild().scrollIntoView(true, node).always(function(){
3103+
node.getLastChild().scrollIntoView(true, {topNode: node}).always(function(){
30863104
if( !noEvents ) {
30873105
ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
30883106
}
@@ -3093,7 +3111,6 @@ $.extend(Fancytree.prototype,
30933111
}
30943112
}
30953113
});
3096-
30973114
// vvv Code below is executed after loading finished:
30983115
_afterLoad = function(callback){
30993116
var duration, easing, isVisible, isExpanded;
@@ -3201,7 +3218,7 @@ $.extend(Fancytree.prototype,
32013218
this._callHook("treeSetFocus", ctx, true, true);
32023219
}
32033220
// this.nodeMakeVisible(ctx);
3204-
node.makeVisible();
3221+
node.makeVisible({scrollIntoView: false});
32053222
tree.focusNode = node;
32063223
// node.debug("FOCUS...");
32073224
// $(node.span).find(".fancytree-title").focus();
@@ -3512,6 +3529,8 @@ $.widget("ui.fancytree",
35123529
keyboard: true,
35133530
keyPathSeparator: "/",
35143531
minExpandLevel: 1,
3532+
scrollOfs: {top: 0, bottom: 0},
3533+
scrollParent: null,
35153534
selectMode: 2,
35163535
strings: {
35173536
loading: "Loading&#8230;",
@@ -3772,7 +3791,7 @@ $.extend($.ui.fancytree,
37723791
/** @lends Fancytree_Static# */
37733792
{
37743793
/** @type {string} */
3775-
version: "2.0.0", // Set to semver by 'grunt release'
3794+
version: "2.1.0", // Set to semver by 'grunt release'
37763795
/** @type {string} */
37773796
buildType: "production", // Set to 'production' by 'grunt build'
37783797
/** @type {int} */
@@ -3800,6 +3819,29 @@ $.extend($.ui.fancytree,
38003819
assert: function(cond, msg){
38013820
return _assert(cond, msg);
38023821
},
3822+
/** Return a function that executes *fn* at most every *timeout* ms.
3823+
* @param {integer} timeout
3824+
* @param {function} fn
3825+
* @param {boolean} [invokeAsap=false]
3826+
* @param {any} [ctx]
3827+
*/
3828+
debounce : function(timeout, fn, invokeAsap, ctx) {
3829+
var timer;
3830+
if(arguments.length === 3 && typeof invokeAsap !== "boolean") {
3831+
ctx = invokeAsap;
3832+
invokeAsap = false;
3833+
}
3834+
return function() {
3835+
var args = arguments;
3836+
ctx = ctx || this;
3837+
invokeAsap && !timer && fn.apply(ctx, args);
3838+
clearTimeout(timer);
3839+
timer = setTimeout(function() {
3840+
invokeAsap || fn.apply(ctx, args);
3841+
timer = null;
3842+
}, timeout);
3843+
};
3844+
},
38033845
/** Write message to console if debugLevel >= 2
38043846
* @param {string} msg
38053847
*/
@@ -3850,8 +3892,7 @@ $.extend($.ui.fancytree,
38503892
getEventTarget: function(event){
38513893
var tcn = event && event.target ? event.target.className : "",
38523894
res = {node: this.getNode(event.target), type: undefined};
3853-
// tcn may contains UI themeroller or Font Awesome classes, so we use
3854-
// a fast version of $(res.node).hasClass()
3895+
// We use a fast version of $(res.node).hasClass()
38553896
// See http://jsperf.com/test-for-classname/2
38563897
if( /\bfancytree-title\b/.test(tcn) ){
38573898
res.type = "title";
@@ -3862,8 +3903,10 @@ $.extend($.ui.fancytree,
38623903
}else if( /\bfancytree-icon\b/.test(tcn) ){
38633904
res.type = "icon";
38643905
}else if( /\bfancytree-node\b/.test(tcn) ){
3865-
// TODO: (http://code.google.com/p/dynatree/issues/detail?id=93)
3866-
// res.type = this._getTypeForOuterNodeEvent(event);
3906+
// Somewhere near the title
3907+
res.type = "title";
3908+
}else if( event && event.target && $(event.target).closest(".fancytree-title").length ) {
3909+
// #228: clicking an embedded element inside a title
38673910
res.type = "title";
38683911
}
38693912
return res;

dist/jquery.fancytree.min.js

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)