first commit

This commit is contained in:
2024-11-17 19:56:17 +01:00
commit 81b1185f8e
6599 changed files with 832395 additions and 0 deletions

View File

@@ -0,0 +1,185 @@
// Extending Fancytree
// ===================
//
// See also the [live demo](http://wwwendt.de/tech/fancytree/demo/sample-ext-childcounter.html) of this code.
//
// Every extension should have a comment header containing some information
// about the author, copyright and licensing. Also a pointer to the latest
// source code.
// Prefix with `/*!` so the comment is not removed by the minifier.
/*!
* jquery.fancytree.childcounter.js
*
* Add a child counter bubble to tree nodes.
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
*
* Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
*
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.6.0
* @date 2014-11-29T08:33
*/
// To keep the global namespace clean, we wrap everything in a closure
;(function($, undefined) {
// Consider to use [strict mode](http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/)
"use strict";
// The [coding guidelines](http://contribute.jquery.org/style-guide/js/)
// require jshint compliance.
// But for this sample, we want to allow unused variables for demonstration purpose.
/*jshint unused:false */
// Adding methods
// --------------
// New member functions can be added to the `Fancytree` class.
// This function will be available for every tree instance.
//
// var tree = $("#tree").fancytree("getTree");
// tree.countSelected(false);
$.ui.fancytree._FancytreeClass.prototype.countSelected = function(topOnly){
var tree = this,
treeOptions = tree.options;
return tree.getSelectedNodes(topOnly).length;
};
// The `FancytreeNode` class can also be easily extended. This would be called
// like
//
// node.toUpper();
$.ui.fancytree._FancytreeNodeClass.prototype.toUpper = function(){
var node = this;
return node.setTitle(node.title.toUpperCase());
};
// Finally, we can extend the widget API and create functions that are called
// like so:
//
// $("#tree").fancytree("widgetMethod1", "abc");
$.ui.fancytree.prototype.widgetMethod1 = function(arg1){
var tree = this.tree;
return arg1;
};
// Register a Fancytree extension
// ------------------------------
// A full blown extension, extension is available for all trees and can be
// enabled like so (see also the [live demo](http://wwwendt.de/tech/fancytree/demo/sample-ext-childcounter.html)):
//
// <script src="../src/jquery.fancytree.js" type="text/javascript"></script>
// <script src="../src/jquery.fancytree.childcounter.js" type="text/javascript"></script>
// ...
//
// $("#tree").fancytree({
// extensions: ["childcounter"],
// childcounter: {
// hideExpanded: true
// },
// ...
// });
//
/* 'childcounter' extension */
$.ui.fancytree.registerExtension({
// Every extension must be registered by a unique name.
name: "childcounter",
// Version information should be compliant with [semver](http://semver.org)
version: "1.0.0",
// Extension specific options and their defaults.
// This options will be available as `tree.options.childcounter.hideExpanded`
options: {
deep: true,
hideZeros: true,
hideExpanded: false
},
// Attributes other than `options` (or functions) can be defined here, and
// will be added to the tree.ext.EXTNAME namespace, in this case `tree.ext.childcounter.foo`.
// They can also be accessed as `this._local.foo` from within the extension
// methods.
foo: 42,
// Local functions are prefixed with an underscore '_'.
// Callable as `this._local._appendCounter()`.
_appendCounter: function(bar){
var tree = this;
},
// **Override virtual methods for this extension.**
//
// Fancytree implements a number of 'hook methods', prefixed by 'node...' or 'tree...'.
// with a `ctx` argument (see [EventData](http://www.wwwendt.de/tech/fancytree/doc/jsdoc/global.html#EventData)
// for details) and an extended calling context:<br>
// `this` : the Fancytree instance<br>
// `this._local`: the namespace that contains extension attributes and private methods (same as this.ext.EXTNAME)<br>
// `this._super`: the virtual function that was overridden (member of previous extension or Fancytree)
//
// See also the [complete list of available hook functions](http://www.wwwendt.de/tech/fancytree/doc/jsdoc/Fancytree_Hooks.html).
/* Init */
// `treeInit` is triggered when a tree is initalized. We can set up classes or
// bind event handlers here...
treeInit: function(ctx){
var tree = this, // same as ctx.tree,
opts = ctx.options,
extOpts = ctx.options.childcounter;
// Optionally check for dependencies with other extensions
/* this._requireExtension("glyph", false, false); */
// Call the base implementation
this._super(ctx);
// Add a class to the tree container
this.$container.addClass("fancytree-ext-childcounter");
},
// Destroy this tree instance (we only call the default implementation, so
// this method could as well be omitted).
treeDestroy: function(ctx){
this._super(ctx);
},
// Overload the `renderTitle` hook, to append a counter badge
nodeRenderTitle: function(ctx, title) {
var node = ctx.node,
extOpts = ctx.options.childcounter,
count = (node.data.childCounter == null) ? node.countChildren(extOpts.deep) : +node.data.childCounter;
// Let the base implementation render the title
this._super(ctx, title);
// Append a counter badge
if( (count || ! extOpts.hideZeros) && (!node.isExpanded() || !extOpts.hideExpanded) ){
$("span.fancytree-icon", node.span).append($("<span class='fancytree-childcounter'/>").text(count));
}
},
// Overload the `setExpanded` hook, so the counters are updated
nodeSetExpanded: function(ctx, flag, opts) {
var tree = ctx.tree,
node = ctx.node;
// Let the base implementation expand/collapse the node, then redraw the title
// after the animation has finished
return this._super(ctx, flag, opts).always(function(){
tree.nodeRenderTitle(ctx);
});
}
// End of extension definition
});
// End of namespace closure
}(jQuery));

View File

@@ -0,0 +1,149 @@
/*!
* jquery.fancytree.columnview.js
*
* Render tree like a Mac Finder's column view.
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
*
* Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
*
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.6.0
* @date 2014-11-29T08:33
*/
;(function($, window, document, undefined) {
"use strict";
// prevent duplicate loading
// if ( $.ui.fancytree && $.ui.fancytree.version ) {
// $.ui.fancytree.warn("Fancytree: duplicate include");
// return;
// }
/*******************************************************************************
* Private functions and variables
*/
/*
function _assert(cond, msg){
msg = msg || "";
if(!cond){
$.error("Assertion failed " + msg);
}
}
*/
/*******************************************************************************
* Private functions and variables
*/
$.ui.fancytree.registerExtension({
name: "columnview",
version: "0.0.1",
// Default options for this extension.
options: {
},
// Overide virtual methods for this extension.
// `this` : is this extension object
// `this._base` : the Fancytree instance
// `this._super`: the virtual function that was overriden (member of prev. extension or Fancytree)
treeInit: function(ctx){
var $tdFirst, $ul,
tree = ctx.tree,
$table = tree.widget.element;
tree.tr = $("tbody tr", $table)[0];
tree.columnCount = $(">td", tree.tr).length;
// Perform default behavior
this._super(ctx);
// Standard Fancytree created a root <ul>. Now move this into first table cell
$ul = $(tree.rootNode.ul);
$tdFirst = $(">td", tree.tr).eq(0);
$ul.removeClass("fancytree-container");
$ul.removeAttr("tabindex");
tree.$container = $table;
$table.addClass("fancytree-container fancytree-ext-columnview");
$table.attr("tabindex", "0");
$tdFirst.empty();
$ul.detach().appendTo($tdFirst);
// Force some required options
tree.widget.options.autoCollapse = true;
// tree.widget.options.autoActivate = true;
tree.widget.options.fx = false;
tree.widget.options.clickFolderMode = 1;
// Make sure that only active path is expanded when a node is activated:
$table.bind("fancytreeactivate", function(event, data){
var i, tdList,
node = data.node,
tree = data.tree,
level = node.getLevel();
tree._callHook("nodeCollapseSiblings", node);
// Clear right neighbours
if(level <= tree.columnCount){
tdList = $(">td", tree.tr);
for(i=level; i<tree.columnCount; i++){
tdList.eq(i).empty();
}
}
// Expand nodes on activate, so we populate the right neighbor cell
if(!node.expanded && (node.children || node.lazy)) {
node.setExpanded();
}
// Adjust keyboard behaviour:
}).bind("fancytreekeydown", function(event, data){
var next = null;
switch(event.which){
case $.ui.keyCode.DOWN:
next = data.node.getNextSibling();
if( next ){
next.setFocus();
}
return false;
case $.ui.keyCode.LEFT:
next = data.node.getParent();
if( next ){
next.setFocus();
}
return false;
case $.ui.keyCode.UP:
next = data.node.getPrevSibling();
if( next ){
next.setFocus();
}
return false;
}
});
},
nodeRender: function(ctx, force, deep, collapsed, _recursive) {
// Render standard nested <ul> - <li> hierarchy
this._super(ctx, force, deep, collapsed, _recursive);
// Remove expander and add a trailing triangle instead
var level, $tdChild, $ul,
tree = ctx.tree,
node = ctx.node,
$span = $(node.span);
$span.find("span.fancytree-expander").remove();
if(node.hasChildren() !== false && !$span.find("span.fancytree-cv-right").length){
$span.append($("<span class='fancytree-icon fancytree-cv-right'>"));
}
// Move <ul> with children into the appropriate <td>
if(node.ul){
node.ul.style.display = ""; // might be hidden if RIGHT was pressed
level = node.getLevel();
if(level < tree.columnCount){
$tdChild = $(">td", tree.tr).eq(level);
$ul = $(node.ul).detach();
$tdChild.empty().append($ul);
}
}
}
});
}(jQuery, window, document));

View File

@@ -0,0 +1,142 @@
/*!
* jquery.fancytree.debug.js
*
* Miscellaneous debug extensions.
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
*
* Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
*
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.6.0
* @date 2014-11-29T08:33
*/
;(function($, window, document, undefined) {
"use strict";
// prevent duplicate loading
// if ( $.ui.fancytree && $.ui.fancytree.version ) {
// $.ui.fancytree.warn("Fancytree: duplicate include");
// return;
// }
/* *****************************************************************************
* Private functions and variables
*/
var i,
HOOK_NAMES = "nodeClick nodeCollapseSiblings".split(" "),
EVENT_NAMES = "activate beforeActivate".split(" "),
HOOK_NAME_MAP = {},
EVENT_NAME_MAP = {};
for(i=0; i<HOOK_NAMES.length; i++){ HOOK_NAME_MAP[HOOK_NAMES[i]] = true; }
for(i=0; i<EVENT_NAMES.length; i++){ EVENT_NAME_MAP[EVENT_NAMES[i]] = true; }
/* *****************************************************************************
* Extension code
*/
$.ui.fancytree.registerExtension({
name: "tracecalls",
version: "0.0.1",
// Default options for this extension.
options: {
logTarget: null, // optional redirect logging to this <div> tag
traceEvents: false, // `true`or list of hook names
traceHooks: false // `true`or list of event names
},
// Overide virtual methods for this extension.
// `this` : is this Fancytree object
// `this._super`: the virtual function that was overridden (member of prev. extension or Fancytree)
treeInit: function(ctx){
var tree = ctx.tree;
// Bind init-handler to apply cookie state
tree.$div.bind("fancytreeinit", function(event){
tree.debug("COOKIE " + document.cookie);
});
// Init the tree
this._super(ctx);
},
nodeClick: function(ctx) {
if(this.options.tracecalls.traceHooks){
this.debug();
}
},
nodeCollapseSiblings: function(ctx) {
},
nodeDblclick: function(ctx) {
},
nodeKeydown: function(ctx) {
},
nodeLoadChildren: function(ctx, source) {
},
nodeOnFocusInOut: function(ctx) {
},
nodeRemoveChildMarkup: function(ctx) {
},
nodeRemoveMarkup: function(ctx) {
},
nodeRender: function(ctx, force, deep, collapsed, _recursive) {
},
nodeRenderStatus: function(ctx) {
},
nodeRenderTitle: function(ctx, title) {
},
nodeSetActive: function(ctx, flag, opts) {
},
nodeSetExpanded: function(ctx, flag, opts) {
},
nodeSetFocus: function(ctx) {
},
nodeSetSelected: function(ctx, flag) {
},
nodeSetStatus: function(ctx, status, message, details) {
},
nodeToggleExpanded: function(ctx) {
},
nodeToggleSelected: function(ctx) {
},
treeClear: function(ctx) {
},
treeCreate: function(ctx) {
},
treeDestroy: function(ctx) {
},
// treeInit: function(ctx) {
// },
treeLoad: function(ctx, source) {
},
treeSetFocus: function(ctx, flag) {
}
});
}(jQuery, window, document));
/* *****************************************************************************
* Fancytree extension: profiler
*/
;(function($, window, document, undefined) {
$.ui.fancytree.registerExtension({
name: "profiler",
version: "0.0.1",
// Default options for this extension
options: {
prefix: ""
},
// Overide virtual methods for this extension
nodeRender: function(ctx, force, deep, collapsed){
// ctx.tree.debug("**** PROFILER nodeRender");
var s = this.options.prefix + "render '" + ctx.node + "'";
/*jshint expr:true */
window.console && window.console.time && window.console.time(s);
this._super(ctx, force, deep, collapsed);
window.console && window.console.timeEnd && window.console.timeEnd(s);
}
});
}(jQuery, window, document));

View File

@@ -0,0 +1,563 @@
/*!
* jquery.fancytree.dnd.js
*
* Drag-and-drop support.
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
*
* Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
*
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.6.0
* @date 2014-11-29T08:33
*/
;(function($, window, document, undefined) {
"use strict";
/* *****************************************************************************
* Private functions and variables
*/
var didRegisterDnd = false;
/* Convert number to string and prepend +/-; return empty string for 0.*/
function offsetString(n){
return n === 0 ? "" : (( n > 0 ) ? ("+" + n) : ("" + n));
}
/* *****************************************************************************
* Drag and drop support
*/
function _initDragAndDrop(tree) {
var dnd = tree.options.dnd || null;
// Register 'connectToFancytree' option with ui.draggable
if( dnd ) {
_registerDnd();
}
// Attach ui.draggable to this Fancytree instance
if(dnd && dnd.dragStart ) {
tree.widget.element.draggable($.extend({
addClasses: false,
// DT issue 244: helper should be child of scrollParent:
appendTo: tree.$container,
// appendTo: "body",
containment: false,
delay: 0,
distance: 4,
revert: false,
scroll: true, // to disable, also set css 'position: inherit' on ul.fancytree-container
scrollSpeed: 7,
scrollSensitivity: 10,
// Delegate draggable.start, drag, and stop events to our handler
connectToFancytree: true,
// Let source tree create the helper element
helper: function(event) {
var $helper,
sourceNode = $.ui.fancytree.getNode(event.target),
$nodeTag = $(sourceNode.span);
if(!sourceNode){
// DT issue 211: might happen, if dragging a table *header*
return "<div>ERROR?: helper requested but sourceNode not found</div>";
}
// Only event and node argument is available
$helper = $("<div class='fancytree-drag-helper'><span class='fancytree-drag-helper-img' /></div>")
.css({zIndex: 3, position: "relative"}) // so it appears above ext-wide selection bar
.append($nodeTag.find("span.fancytree-title").clone());
// Attach node reference to helper object
$helper.data("ftSourceNode", sourceNode);
// we return an unconnected element, so `draggable` will add this
// to the parent specified as `appendTo` option
return $helper;
},
start: function(event, ui) {
var sourceNode = ui.helper.data("ftSourceNode");
return !!sourceNode; // Abort dragging if no node could be found
}
}, tree.options.dnd.draggable));
}
// Attach ui.droppable to this Fancytree instance
if(dnd && dnd.dragDrop) {
tree.widget.element.droppable($.extend({
addClasses: false,
tolerance: "intersect",
greedy: false
/*
activate: function(event, ui) {
tree.debug("droppable - activate", event, ui, this);
},
create: function(event, ui) {
tree.debug("droppable - create", event, ui);
},
deactivate: function(event, ui) {
tree.debug("droppable - deactivate", event, ui);
},
drop: function(event, ui) {
tree.debug("droppable - drop", event, ui);
},
out: function(event, ui) {
tree.debug("droppable - out", event, ui);
},
over: function(event, ui) {
tree.debug("droppable - over", event, ui);
}
*/
}, tree.options.dnd.droppable));
}
}
//--- Extend ui.draggable event handling --------------------------------------
function _registerDnd() {
if(didRegisterDnd){
return;
}
// Register proxy-functions for draggable.start/drag/stop
$.ui.plugin.add("draggable", "connectToFancytree", {
start: function(event, ui) {
// 'draggable' was renamed to 'ui-draggable' since jQueryUI 1.10
var draggable = $(this).data("ui-draggable") || $(this).data("draggable"),
sourceNode = ui.helper.data("ftSourceNode") || null;
if(sourceNode) {
// Adjust helper offset, so cursor is slightly outside top/left corner
draggable.offset.click.top = -2;
draggable.offset.click.left = + 16;
// Trigger dragStart event
// TODO: when called as connectTo..., the return value is ignored(?)
return sourceNode.tree.ext.dnd._onDragEvent("start", sourceNode, null, event, ui, draggable);
}
},
drag: function(event, ui) {
var isHelper, logObject,
// 'draggable' was renamed to 'ui-draggable' since jQueryUI 1.10
draggable = $(this).data("ui-draggable") || $(this).data("draggable"),
sourceNode = ui.helper.data("ftSourceNode") || null,
prevTargetNode = ui.helper.data("ftTargetNode") || null,
targetNode = $.ui.fancytree.getNode(event.target);
if(event.target && !targetNode){
// We got a drag event, but the targetNode could not be found
// at the event location. This may happen,
// 1. if the mouse jumped over the drag helper,
// 2. or if a non-fancytree element is dragged
// We ignore it:
isHelper = $(event.target).closest("div.fancytree-drag-helper,#fancytree-drop-marker").length > 0;
if(isHelper){
logObject = sourceNode || prevTargetNode || $.ui.fancytree;
logObject.debug("Drag event over helper: ignored.");
return;
}
}
ui.helper.data("ftTargetNode", targetNode);
// Leaving a tree node
if(prevTargetNode && prevTargetNode !== targetNode ) {
prevTargetNode.tree.ext.dnd._onDragEvent("leave", prevTargetNode, sourceNode, event, ui, draggable);
}
if(targetNode){
if(!targetNode.tree.options.dnd.dragDrop) {
// not enabled as drop target
} else if(targetNode === prevTargetNode) {
// Moving over same node
targetNode.tree.ext.dnd._onDragEvent("over", targetNode, sourceNode, event, ui, draggable);
}else{
// Entering this node first time
targetNode.tree.ext.dnd._onDragEvent("enter", targetNode, sourceNode, event, ui, draggable);
}
}
// else go ahead with standard event handling
},
stop: function(event, ui) {
// 'draggable' was renamed to 'ui-draggable' since jQueryUI 1.10
var logObject,
draggable = $(this).data("ui-draggable") || $(this).data("draggable"),
sourceNode = ui.helper.data("ftSourceNode") || null,
targetNode = ui.helper.data("ftTargetNode") || null,
// mouseDownEvent = draggable._mouseDownEvent,
eventType = event.type,
dropped = (eventType === "mouseup" && event.which === 1);
if(!dropped){
logObject = sourceNode || targetNode || $.ui.fancytree;
logObject.debug("Drag was cancelled");
}
if(targetNode) {
if(dropped){
targetNode.tree.ext.dnd._onDragEvent("drop", targetNode, sourceNode, event, ui, draggable);
}
targetNode.tree.ext.dnd._onDragEvent("leave", targetNode, sourceNode, event, ui, draggable);
}
if(sourceNode){
sourceNode.tree.ext.dnd._onDragEvent("stop", sourceNode, null, event, ui, draggable);
}
}
});
didRegisterDnd = true;
}
/* *****************************************************************************
*
*/
$.ui.fancytree.registerExtension({
name: "dnd",
version: "0.1.0",
// Default options for this extension.
options: {
// Make tree nodes accept draggables
autoExpandMS: 1000, // Expand nodes after n milliseconds of hovering.
draggable: null, // Additional options passed to jQuery draggable
droppable: null, // Additional options passed to jQuery droppable
focusOnClick: false, // Focus, although draggable cancels mousedown event (#270)
// helper: null, // Callback
// helperParent: null, // jQuery object (defaults to Fancytree container)
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
preventRecursiveMoves: true, // Prevent dropping nodes on own descendants
// Events (drag support)
dragStart: null, // Callback(sourceNode, data), return true, to enable dnd
dragStop: null, // Callback(sourceNode, data)
// Events (drop support)
dragEnter: null, // Callback(targetNode, data)
dragOver: null, // Callback(targetNode, data)
dragDrop: null, // Callback(targetNode, data)
dragLeave: null // Callback(targetNode, data)
},
treeInit: function(ctx){
var tree = ctx.tree;
this._super(ctx);
// issue #270: draggable eats mousedown events
if( tree.options.dnd.dragStart ){
tree.$container.on("mousedown", function(event){
if( !tree.hasFocus() && ctx.options.dnd.focusOnClick ) {
var node = $.ui.fancytree.getNode(event);
node.debug("Re-enable focus that was prevented by jQuery UI draggable.");
// node.setFocus();
// $(node.span).closest(":tabbable").focus();
// $(event.target).trigger("focus");
// $(event.target).closest(":tabbable").trigger("focus");
setTimeout(function() { // #300
$(event.target).closest(":tabbable").focus();
}, 10);
}
});
}
_initDragAndDrop(tree);
},
/* Override key handler in order to cancel dnd on escape.*/
nodeKeydown: function(ctx) {
var event = ctx.originalEvent;
if( event.which === $.ui.keyCode.ESCAPE) {
this._local._cancelDrag();
}
return this._super(ctx);
},
nodeClick: function(ctx) {
// if( ctx.options.dnd.dragStart ){
// ctx.tree.$container.focus();
// }
return this._super(ctx);
},
/* Display drop marker according to hitMode ('after', 'before', 'over', 'out', 'start', 'stop'). */
_setDndStatus: function(sourceNode, targetNode, helper, hitMode, accept) {
var posOpts,
markerOffsetX = 0,
markerAt = "center",
instData = this._local,
$source = sourceNode ? $(sourceNode.span) : null,
$target = $(targetNode.span);
if( !instData.$dropMarker ) {
instData.$dropMarker = $("<div id='fancytree-drop-marker'></div>")
.hide()
.css({"z-index": 1000})
.prependTo($(this.$div).parent());
// .prependTo("body");
}
// this.$dropMarker.attr("class", hitMode);
if(hitMode === "after" || hitMode === "before" || hitMode === "over"){
// $source && $source.addClass("fancytree-drag-source");
// $target.addClass("fancytree-drop-target");
switch(hitMode){
case "before":
instData
.$dropMarker.removeClass("fancytree-drop-after fancytree-drop-over")
.addClass("fancytree-drop-before");
markerAt = "top";
break;
case "after":
instData.$dropMarker.removeClass("fancytree-drop-before fancytree-drop-over")
.addClass("fancytree-drop-after");
markerAt = "bottom";
break;
default:
instData.$dropMarker.removeClass("fancytree-drop-after fancytree-drop-before")
.addClass("fancytree-drop-over");
$target.addClass("fancytree-drop-target");
markerOffsetX = 8;
}
if( $.ui.fancytree.jquerySupports.positionMyOfs ){
posOpts = {
my: "left" + offsetString(markerOffsetX) + " center",
at: "left " + markerAt,
of: $target
};
} else {
posOpts = {
my: "left center",
at: "left " + markerAt,
of: $target,
offset: "" + markerOffsetX + " 0"
};
}
instData.$dropMarker
.show()
.position(posOpts);
// helper.addClass("fancytree-drop-hover");
} else {
// $source && $source.removeClass("fancytree-drag-source");
$target.removeClass("fancytree-drop-target");
instData.$dropMarker.hide();
// helper.removeClass("fancytree-drop-hover");
}
if(hitMode === "after"){
$target.addClass("fancytree-drop-after");
} else {
$target.removeClass("fancytree-drop-after");
}
if(hitMode === "before"){
$target.addClass("fancytree-drop-before");
} else {
$target.removeClass("fancytree-drop-before");
}
if(accept === true){
if($source){
$source.addClass("fancytree-drop-accept");
}
$target.addClass("fancytree-drop-accept");
helper.addClass("fancytree-drop-accept");
}else{
if($source){
$source.removeClass("fancytree-drop-accept");
}
$target.removeClass("fancytree-drop-accept");
helper.removeClass("fancytree-drop-accept");
}
if(accept === false){
if($source){
$source.addClass("fancytree-drop-reject");
}
$target.addClass("fancytree-drop-reject");
helper.addClass("fancytree-drop-reject");
}else{
if($source){
$source.removeClass("fancytree-drop-reject");
}
$target.removeClass("fancytree-drop-reject");
helper.removeClass("fancytree-drop-reject");
}
},
/*
* Handles drag'n'drop functionality.
*
* A standard jQuery drag-and-drop process may generate these calls:
*
* draggable helper():
* _onDragEvent("helper", sourceNode, null, event, null, null);
* start:
* _onDragEvent("start", sourceNode, null, event, ui, draggable);
* drag:
* _onDragEvent("leave", prevTargetNode, sourceNode, event, ui, draggable);
* _onDragEvent("over", targetNode, sourceNode, event, ui, draggable);
* _onDragEvent("enter", targetNode, sourceNode, event, ui, draggable);
* stop:
* _onDragEvent("drop", targetNode, sourceNode, event, ui, draggable);
* _onDragEvent("leave", targetNode, sourceNode, event, ui, draggable);
* _onDragEvent("stop", sourceNode, null, event, ui, draggable);
*/
_onDragEvent: function(eventName, node, otherNode, event, ui, draggable) {
if(eventName !== "over"){
this.debug("tree.ext.dnd._onDragEvent(%s, %o, %o) - %o", eventName, node, otherNode, this);
}
var /*$helper, $helperParent,*/ nodeOfs, relPos, relPos2,
enterResponse, hitMode, r,
opts = this.options,
dnd = opts.dnd,
ctx = this._makeHookContext(node, event, {otherNode: otherNode, ui: ui, draggable: draggable}),
res = null,
$nodeTag = $(node.span);
switch (eventName) {
// case "helper":
// // Only event and node argument is available
// $helper = $("<div class='fancytree-drag-helper'><span class='fancytree-drag-helper-img' /></div>")
// .css({zIndex: 3, position: "relative"}) // so it appears above ext-wide selection bar
// .append($nodeTag.find("span.fancytree-title").clone());
// // #345: helper parent is now set using draggable.appendTo
// // $helperParent = dnd.helperParent || $("ul.fancytree-container", node.tree.$div);
// // DT issue 244: helper should be child of scrollParent
// // $helperParent.append($helper);
// // Attach node reference to helper object
// $helper.data("ftSourceNode", node);
// // this.debug("helper=%o", $helper);
// // this.debug("helper.sourceNode=%o", $helper.data("ftSourceNode"));
// res = $helper;
// break;
case "start":
if( node.isStatusNode() ) {
res = false;
} else if(dnd.dragStart) {
res = dnd.dragStart(node, ctx);
}
if(res === false) {
this.debug("tree.dragStart() cancelled");
//draggable._clear();
// NOTE: the return value seems to be ignored (drag is not canceled, when false is returned)
// TODO: call this._cancelDrag()?
ui.helper.trigger("mouseup")
.hide();
} else {
$nodeTag.addClass("fancytree-drag-source");
}
break;
case "enter":
if(dnd.preventRecursiveMoves && node.isDescendantOf(otherNode)){
r = false;
}else{
r = dnd.dragEnter ? dnd.dragEnter(node, ctx) : null;
}
if(!r){
// convert null, undefined, false to false
res = false;
}else if ( $.isArray(r) ) {
// TODO: also accept passing an object of this format directly
res = {
over: ($.inArray("over", r) >= 0),
before: ($.inArray("before", r) >= 0),
after: ($.inArray("after", r) >= 0)
};
}else{
res = {
over: ((r === true) || (r === "over")),
before: ((r === true) || (r === "before")),
after: ((r === true) || (r === "after"))
};
}
ui.helper.data("enterResponse", res);
this.debug("helper.enterResponse: %o", res);
break;
case "over":
enterResponse = ui.helper.data("enterResponse");
hitMode = null;
if(enterResponse === false){
// Don't call dragOver if onEnter returned false.
// break;
} else if(typeof enterResponse === "string") {
// Use hitMode from onEnter if provided.
hitMode = enterResponse;
} else {
// Calculate hitMode from relative cursor position.
nodeOfs = $nodeTag.offset();
relPos = { x: event.pageX - nodeOfs.left,
y: event.pageY - nodeOfs.top };
relPos2 = { x: relPos.x / $nodeTag.width(),
y: relPos.y / $nodeTag.height() };
if( enterResponse.after && relPos2.y > 0.75 ){
hitMode = "after";
} else if(!enterResponse.over && enterResponse.after && relPos2.y > 0.5 ){
hitMode = "after";
} else if(enterResponse.before && relPos2.y <= 0.25) {
hitMode = "before";
} else if(!enterResponse.over && enterResponse.before && relPos2.y <= 0.5) {
hitMode = "before";
} else if(enterResponse.over) {
hitMode = "over";
}
// Prevent no-ops like 'before source node'
// TODO: these are no-ops when moving nodes, but not in copy mode
if( dnd.preventVoidMoves ){
if(node === otherNode){
this.debug(" drop over source node prevented");
hitMode = null;
}else if(hitMode === "before" && otherNode && node === otherNode.getNextSibling()){
this.debug(" drop after source node prevented");
hitMode = null;
}else if(hitMode === "after" && otherNode && node === otherNode.getPrevSibling()){
this.debug(" drop before source node prevented");
hitMode = null;
}else if(hitMode === "over" && otherNode && otherNode.parent === node && otherNode.isLastSibling() ){
this.debug(" drop last child over own parent prevented");
hitMode = null;
}
}
// this.debug("hitMode: %s - %s - %s", hitMode, (node.parent === otherNode), node.isLastSibling());
ui.helper.data("hitMode", hitMode);
}
// Auto-expand node (only when 'over' the node, not 'before', or 'after')
if(hitMode === "over" && dnd.autoExpandMS && node.hasChildren() !== false && !node.expanded) {
node.scheduleAction("expand", dnd.autoExpandMS);
}
if(hitMode && dnd.dragOver){
// TODO: http://code.google.com/p/dynatree/source/detail?r=625
ctx.hitMode = hitMode;
res = dnd.dragOver(node, ctx);
}
// DT issue 332
// this._setDndStatus(otherNode, node, ui.helper, hitMode, res!==false);
this._local._setDndStatus(otherNode, node, ui.helper, hitMode, res!==false && hitMode !== null);
break;
case "drop":
hitMode = ui.helper.data("hitMode");
if(hitMode && dnd.dragDrop){
ctx.hitMode = hitMode;
dnd.dragDrop(node, ctx);
}
break;
case "leave":
// Cancel pending expand request
node.scheduleAction("cancel");
ui.helper.data("enterResponse", null);
ui.helper.data("hitMode", null);
this._local._setDndStatus(otherNode, node, ui.helper, "out", undefined);
if(dnd.dragLeave){
dnd.dragLeave(node, ctx);
}
break;
case "stop":
$nodeTag.removeClass("fancytree-drag-source");
if(dnd.dragStop){
dnd.dragStop(node, ctx);
}
break;
default:
$.error("Unsupported drag event: " + eventName);
}
return res;
},
_cancelDrag: function() {
var dd = $.ui.ddmanager.current;
if(dd){
dd.cancel();
}
}
});
}(jQuery, window, document));

View File

@@ -0,0 +1,346 @@
/*!
* jquery.fancytree.edit.js
*
* Make node titles editable.
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
*
* Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
*
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.6.0
* @date 2014-11-29T08:33
*/
;(function($, window, document, undefined) {
"use strict";
/*******************************************************************************
* Private functions and variables
*/
var isMac = /Mac/.test(navigator.platform),
escapeHtml = $.ui.fancytree.escapeHtml,
unescapeHtml = $.ui.fancytree.unescapeHtml;
// modifiers = {shift: "shiftKey", ctrl: "ctrlKey", alt: "altKey", meta: "metaKey"},
// specialKeys = {
// 8: "backspace", 9: "tab", 10: "return", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
// 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
// 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
// 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
// 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
// 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
// 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 186: ";", 191: "/",
// 220: "\\", 222: "'", 224: "meta"
// },
// shiftNums = {
// "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
// "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
// ".": ">", "/": "?", "\\": "|"
// };
// $.ui.fancytree.isKeydownEvent = function(e, code){
// var i, part, partmap, partlist = code.split("+"), len = parts.length;
// var c = String.fromCharCode(e.which).toLowerCase();
// for( i = 0; i < len; i++ ) {
// }
// alert (parts.unshift());
// alert (parts.unshift());
// alert (parts.unshift());
// };
/**
* [ext-edit] Start inline editing of current node title.
*
* @alias FancytreeNode#editStart
* @requires Fancytree
*/
$.ui.fancytree._FancytreeNodeClass.prototype.editStart = function(){
var $input,
node = this,
tree = this.tree,
local = tree.ext.edit,
instOpts = tree.options.edit,
$title = $(".fancytree-title", node.span),
eventData = {
node: node,
tree: tree,
options: tree.options,
isNew: $(node.span).hasClass("fancytree-edit-new"),
orgTitle: node.title,
input: null,
dirty: false
};
// beforeEdit may want to modify the title before editing
if( instOpts.beforeEdit.call(node, {type: "beforeEdit"}, eventData) === false ) {
return false;
}
$.ui.fancytree.assert(!local.currentNode, "recursive edit");
local.currentNode = this;
local.eventData = eventData;
// Disable standard Fancytree mouse- and key handling
tree.widget._unbind();
// #116: ext-dnd prevents the blur event, so we have to catch outer clicks
$(document).on("mousedown.fancytree-edit", function(event){
if( ! $(event.target).hasClass("fancytree-edit-input") ){
node.editEnd(true, event);
}
});
// Replace node with <input>
$input = $("<input />", {
"class": "fancytree-edit-input",
type: "text",
value: unescapeHtml(eventData.orgTitle)
});
local.eventData.input = $input;
if ( instOpts.adjustWidthOfs != null ) {
$input.width($title.width() + instOpts.adjustWidthOfs);
}
if ( instOpts.inputCss != null ) {
$input.css(instOpts.inputCss);
}
$title.html($input);
// Focus <input> and bind keyboard handler
$input
.focus()
.change(function(event){
$input.addClass("fancytree-edit-dirty");
}).keydown(function(event){
switch( event.which ) {
case $.ui.keyCode.ESCAPE:
node.editEnd(false, event);
break;
case $.ui.keyCode.ENTER:
node.editEnd(true, event);
return false; // so we don't start editmode on Mac
}
event.stopPropagation();
}).blur(function(event){
return node.editEnd(true, event);
});
instOpts.edit.call(node, {type: "edit"}, eventData);
};
/**
* [ext-edit] Stop inline editing.
* @param {Boolean} [applyChanges=false] false: cancel edit, true: save (if modified)
* @alias FancytreeNode#editEnd
* @requires jquery.fancytree.edit.js
*/
$.ui.fancytree._FancytreeNodeClass.prototype.editEnd = function(applyChanges, _event){
var newVal,
node = this,
tree = this.tree,
local = tree.ext.edit,
eventData = local.eventData,
instOpts = tree.options.edit,
$title = $(".fancytree-title", node.span),
$input = $title.find("input.fancytree-edit-input");
// eventData.isNew = $(node.span).hasClass("fancytree-edit-new");
if( instOpts.trim ) {
$input.val($.trim($input.val()));
}
newVal = $input.val();
// eventData.dirty = $input.hasClass("fancytree-edit-dirty") || ;
eventData.dirty = ( newVal !== node.title );
// Find out, if saving is required
if( applyChanges === false ) {
// If true/false was passed, honor this (except in rename mode, if unchanged)
eventData.save = false;
} else if( eventData.isNew ) {
// In create mode, we save everyting, except for empty text
eventData.save = (newVal !== "");
} else {
// In rename mode, we save everyting, except for empty or unchanged text
eventData.save = eventData.dirty && (newVal !== "");
}
// Allow to break (keep editor open), modify input, or re-define data.save
if( instOpts.beforeClose.call(node, {type: "beforeClose"}, eventData) === false){
return false;
}
if( eventData.save && instOpts.save.call(node, {type: "save"}, eventData) === false){
return false;
}
$input
.removeClass("fancytree-edit-dirty")
.unbind();
// Unbind outer-click handler
$(document).off(".fancytree-edit");
if( eventData.save ) {
node.setTitle( escapeHtml(newVal) );
// $(node.span).removeClass("fancytree-edit-new");
node.setFocus();
}else{
if( eventData.isNew ) {
node.remove();
node = eventData.node = null;
local.relatedNode.setFocus();
} else {
node.renderTitle();
node.setFocus();
}
}
local.eventData = null;
local.currentNode = null;
local.relatedNode = null;
// Re-enable mouse and keyboard handling
tree.widget._bind();
// Set keyboard focus, even if setFocus() claims 'nothing to do'
$(tree.$container).focus();
eventData.input = null;
instOpts.close.call(node, {type: "close"}, eventData);
return true;
};
// $.ui.fancytree._FancytreeNodeClass.prototype.startEdit = function(){
// this.warn("FancytreeNode.startEdit() is deprecated since 2014-01-04. Use .editStart() instead.");
// return this.editStart.apply(this, arguments);
// };
// $.ui.fancytree._FancytreeNodeClass.prototype.endEdit = function(){
// this.warn("FancytreeNode.endEdit() is deprecated since 2014-01-04. Use .editEnd() instead.");
// return this.editEnd.apply(this, arguments);
// };
/**
* [ext-edit] Create a new child or sibling node and start edit mode.
*
* @param {String} [mode='child'] 'before', 'after', or 'child'
* @param {Object} [init] NodeData (or simple title string)
* @alias FancytreeNode#editCreateNode
* @requires jquery.fancytree.edit.js
*/
$.ui.fancytree._FancytreeNodeClass.prototype.editCreateNode = function(mode, init){
var newNode,
that = this;
mode = mode || "child";
if( init == null ) {
init = { title: "" };
} else if( typeof init === "string" ) {
init = { title: init };
} else {
$.ui.fancytree.assert($.isPlainObject(init));
}
// Make sure node is expanded (and loaded) in 'child' mode
if( mode === "child" && !this.isExpanded() && this.hasChildren() !== false ) {
this.setExpanded().done(function(){
that.editCreateNode(mode, init);
});
return;
}
newNode = this.addNode(init, mode);
newNode.makeVisible();
$(newNode.span).addClass("fancytree-edit-new");
this.tree.ext.edit.relatedNode = this;
newNode.editStart();
};
/**
* [ext-edit] Check if any node in this tree in edit mode.
*
* @returns {FancytreeNode | null}
* @alias Fancytree#isEditing
* @requires jquery.fancytree.edit.js
*/
$.ui.fancytree._FancytreeClass.prototype.isEditing = function(){
return this.ext.edit.currentNode;
};
/**
* [ext-edit] Check if this node is in edit mode.
* @returns {Boolean} true if node is currently beeing edited
* @alias FancytreeNode#isEditing
* @requires jquery.fancytree.edit.js
*/
$.ui.fancytree._FancytreeNodeClass.prototype.isEditing = function(){
return this.tree.ext.edit.currentNode === this;
};
/*******************************************************************************
* Extension code
*/
$.ui.fancytree.registerExtension({
name: "edit",
version: "0.2.0",
// Default options for this extension.
options: {
adjustWidthOfs: 4, // null: don't adjust input size to content
allowEmpty: false, // Prevent empty input
inputCss: {minWidth: "3em"},
triggerCancel: ["esc", "tab", "click"],
// triggerStart: ["f2", "dblclick", "shift+click", "mac+enter"],
triggerStart: ["f2", "shift+click", "mac+enter"],
trim: true, // Trim whitespace before save
// Events:
beforeClose: $.noop, // Return false to prevent cancel/save (data.input is available)
beforeEdit: $.noop, // Return false to prevent edit mode
close: $.noop, // Editor was removed
edit: $.noop, // Editor was opened (available as data.input)
// keypress: $.noop, // Not yet implemented
save: $.noop // Save data.input.val() or return false to keep editor open
},
// Local attributes
currentNode: null,
treeInit: function(ctx){
this._super(ctx);
this.$container.addClass("fancytree-ext-edit");
},
nodeClick: function(ctx) {
if( $.inArray("shift+click", ctx.options.edit.triggerStart) >= 0 ){
if( ctx.originalEvent.shiftKey ){
ctx.node.editStart();
return false;
}
}
return this._super(ctx);
},
nodeDblclick: function(ctx) {
if( $.inArray("dblclick", ctx.options.edit.triggerStart) >= 0 ){
ctx.node.editStart();
return false;
}
return this._super(ctx);
},
nodeKeydown: function(ctx) {
switch( ctx.originalEvent.which ) {
case 113: // [F2]
if( $.inArray("f2", ctx.options.edit.triggerStart) >= 0 ){
ctx.node.editStart();
return false;
}
break;
case $.ui.keyCode.ENTER:
if( $.inArray("mac+enter", ctx.options.edit.triggerStart) >= 0 && isMac ){
ctx.node.editStart();
return false;
}
break;
}
return this._super(ctx);
}
});
}(jQuery, window, document));

View File

@@ -0,0 +1,174 @@
/*!
* jquery.fancytree.filter.js
*
* Remove or highlight tree nodes, based on a filter.
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
*
* Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
*
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.6.0
* @date 2014-11-29T08:33
*/
;(function($, window, document, undefined) {
"use strict";
/*******************************************************************************
* Private functions and variables
*/
function _escapeRegex(str){
/*jshint regexdash:true */
return (str + "").replace(/([.?*+\^\$\[\]\\(){}|-])/g, "\\$1");
}
$.ui.fancytree._FancytreeClass.prototype._applyFilterImpl = function(filter, branchMode, leavesOnly){
var match, re,
count = 0,
hideMode = this.options.filter.mode === "hide";
// leavesOnly = !branchMode && this.options.filter.leavesOnly;
leavesOnly = !!leavesOnly && !branchMode;
// Default to 'match title substring (not case sensitive)'
if(typeof filter === "string"){
match = _escapeRegex(filter); // make sure a '.' is treated literally
re = new RegExp(".*" + match + ".*", "i");
filter = function(node){
return !!re.exec(node.title);
};
}
this.enableFilter = true;
this.lastFilterArgs = arguments;
this.$div.addClass("fancytree-ext-filter");
if( hideMode ){
this.$div.addClass("fancytree-ext-filter-hide");
} else {
this.$div.addClass("fancytree-ext-filter-dimm");
}
// Reset current filter
this.visit(function(node){
delete node.match;
delete node.subMatch;
});
// Adjust node.hide, .match, .subMatch flags
this.visit(function(node){
if ((!leavesOnly || node.children == null) && filter(node)) {
count++;
node.match = true;
node.visitParents(function(p){
p.subMatch = true;
});
if( branchMode ) {
node.visit(function(p){
p.match = true;
});
return "skip";
}
}
});
// Redraw
this.render();
return count;
};
/**
* [ext-filter] Dimm or hide nodes.
*
* @param {function | string} filter
* @param {boolean} [leavesOnly=false]
* @returns {integer} count
* @alias Fancytree#filterNodes
* @requires jquery.fancytree.filter.js
*/
$.ui.fancytree._FancytreeClass.prototype.filterNodes = function(filter, leavesOnly){
return this._applyFilterImpl(filter, false, leavesOnly);
};
/**
* @deprecated
*/
$.ui.fancytree._FancytreeClass.prototype.applyFilter = function(filter){
this.warn("Fancytree.applyFilter() is deprecated since 2014-05-10. Use .filterNodes() instead.");
return this.filterNodes.apply(this, arguments);
};
/**
* [ext-filter] Dimm or hide whole branches.
*
* @param {function | string} filter
* @returns {integer} count
* @alias Fancytree#filterBranches
* @requires jquery.fancytree.filter.js
*/
$.ui.fancytree._FancytreeClass.prototype.filterBranches = function(filter){
return this._applyFilterImpl(filter, true, null);
};
/**
* [ext-filter] Reset the filter.
*
* @alias Fancytree#clearFilter
* @requires jquery.fancytree.filter.js
*/
$.ui.fancytree._FancytreeClass.prototype.clearFilter = function(){
this.visit(function(node){
delete node.match;
delete node.subMatch;
});
this.enableFilter = false;
this.lastFilterArgs = null;
this.$div.removeClass("fancytree-ext-filter fancytree-ext-filter-dimm fancytree-ext-filter-hide");
this.render();
};
/*******************************************************************************
* Extension code
*/
$.ui.fancytree.registerExtension({
name: "filter",
version: "0.3.0",
// Default options for this extension.
options: {
autoApply: true, // re-apply last filter if lazy data is loaded
mode: "dimm"
},
treeInit: function(ctx){
this._super(ctx);
},
nodeLoadChildren: function(ctx, source) {
return this._super(ctx, source).done(function() {
if( ctx.tree.enableFilter && ctx.tree.lastFilterArgs && ctx.options.filter.autoApply ) {
ctx.tree._applyFilterImpl.apply(ctx.tree, ctx.tree.lastFilterArgs);
}
});
},
nodeRenderStatus: function(ctx) {
// Set classes for current status
var res,
node = ctx.node,
tree = ctx.tree,
$span = $(node[tree.statusClassPropName]);
res = this._super(ctx);
// nothing to do, if node was not yet rendered
if( !$span.length || !tree.enableFilter ) {
return res;
}
$span
.toggleClass("fancytree-match", !!node.match)
.toggleClass("fancytree-submatch", !!node.subMatch)
.toggleClass("fancytree-hide", !(node.match || node.subMatch));
return res;
}
});
}(jQuery, window, document));

View File

@@ -0,0 +1,155 @@
/*!
* jquery.fancytree.menu.js
*
* Enable jQuery UI Menu as context menu for tree nodes.
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
*
* @see http://api.jqueryui.com/menu/
*
* Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
*
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.6.0
* @date 2014-11-29T08:33
*/
;(function($, window, document, undefined) {
"use strict";
// prevent duplicate loading
// if ( $.ui.fancytree && $.ui.fancytree.version ) {
// $.ui.fancytree.warn("Fancytree: duplicate include");
// return;
// }
$.ui.fancytree.registerExtension({
name: "menu",
version: "0.0.1",
// Default options for this extension.
options: {
enable: true,
selector: null, //
position: {}, //
// Events:
create: $.noop, //
beforeOpen: $.noop, //
open: $.noop, //
focus: $.noop, //
select: $.noop, //
close: $.noop //
},
// Override virtual methods for this extension.
// `this` : is this extension object
// `this._base` : the Fancytree instance
// `this._super`: the virtual function that was overridden (member of prev. extension or Fancytree)
treeInit: function(ctx){
var opts = ctx.options,
tree = ctx.tree;
this._super(ctx);
// Prepare an object that will be passed with menu events
tree.ext.menu.data = {
tree: tree,
node: null,
$menu: null,
menuId: null
};
// tree.$container[0].oncontextmenu = function() {return false;};
// Replace the standard browser context menu with out own
tree.$container.delegate("span.fancytree-node", "contextmenu", function(event) {
var node = $.ui.fancytree.getNode(event),
ctx = {node: node, tree: node.tree, originalEvent: event, options: tree.options};
tree.ext.menu._openMenu(ctx);
return false;
});
// Use jquery.ui.menu
$(opts.menu.selector).menu({
create: function(event, ui){
tree.ext.menu.data.$menu = $(this).menu("widget");
var data = $.extend({}, tree.ext.menu.data);
opts.menu.create.call(tree, event, data);
},
focus: function(event, ui){
var data = $.extend({}, tree.ext.menu.data, {
menuItem: ui.item,
menuId: ui.item.find(">a").attr("href")
});
opts.menu.focus.call(tree, event, data);
},
select: function(event, ui){
var data = $.extend({}, tree.ext.menu.data, {
menuItem: ui.item,
menuId: ui.item.find(">a").attr("href")
});
if( opts.menu.select.call(tree, event, data) !== false){
tree.ext.menu._closeMenu(ctx);
}
}
}).hide();
},
treeDestroy: function(ctx){
this._super(ctx);
},
_openMenu: function(ctx){
var data,
tree = ctx.tree,
opts = ctx.options,
$menu = $(opts.menu.selector);
tree.ext.menu.data.node = ctx.node;
data = $.extend({}, tree.ext.menu.data);
if( opts.menu.beforeOpen.call(tree, ctx.originalEvent, data) === false){
return;
}
$(document).bind("keydown.fancytree", function(event){
if( event.which === $.ui.keyCode.ESCAPE ){
tree.ext.menu._closeMenu(ctx);
}
}).bind("mousedown.fancytree", function(event){
// Close menu when clicked outside menu
if( $(event.target).closest(".ui-menu-item").length === 0 ){
tree.ext.menu._closeMenu(ctx);
}
});
// $menu.position($.extend({my: "left top", at: "left bottom", of: event}, opts.menu.position));
$menu
.css("position", "absolute")
.show()
.position({my: "left top", at: "right top", of: ctx.originalEvent, collision: "fit"})
.focus();
opts.menu.open.call(tree, ctx.originalEvent, data);
},
_closeMenu: function(ctx){
var $menu,
tree = ctx.tree,
opts = ctx.options,
data = $.extend({}, tree.ext.menu.data);
if( opts.menu.close.call(tree, ctx.originalEvent, data) === false){
return;
}
$menu = $(opts.menu.selector);
$(document).unbind("keydown.fancytree, mousedown.fancytree");
$menu.hide();
tree.ext.menu.data.node = null;
}
// ,
// nodeClick: function(ctx) {
// var event = ctx.originalEvent;
// if(event.which === 2 || (event.which === 1 && event.ctrlKey)){
// event.preventDefault();
// ctx.tree.ext.menu._openMenu(ctx);
// return false;
// }
// this._super(ctx);
// }
});
}(jQuery, window, document));

View File

@@ -0,0 +1,356 @@
/*!
* jquery.fancytree.persist.js
*
* Persist tree status in cookiesRemove or highlight tree nodes, based on a filter.
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
*
* @depends: jquery.cookie.js
*
* Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
*
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.6.0
* @date 2014-11-29T08:33
*/
;(function($, window, document, undefined) {
"use strict";
/*******************************************************************************
* Private functions and variables
*/
var _assert = $.ui.fancytree.assert,
ACTIVE = "active",
EXPANDED = "expanded",
FOCUS = "focus",
SELECTED = "selected";
/* Recursively load lazy nodes
* @param {string} mode 'load', 'expand', false
*/
function _loadLazyNodes(tree, local, keyList, mode, dfd) {
var i, key, l, node,
foundOne = false,
deferredList = [],
missingKeyList = [];
keyList = keyList || [];
dfd = dfd || $.Deferred();
for( i=0, l=keyList.length; i<l; i++ ) {
key = keyList[i];
node = tree.getNodeByKey(key);
if( node ) {
if( mode && node.isUndefined() ) {
foundOne = true;
tree.debug("_loadLazyNodes: " + node + " is lazy: loading...");
if( mode === "expand" ) {
deferredList.push(node.setExpanded());
} else {
deferredList.push(node.load());
}
} else {
tree.debug("_loadLazyNodes: " + node + " already loaded.");
node.setExpanded();
}
} else {
missingKeyList.push(key);
tree.debug("_loadLazyNodes: " + node + " was not yet found.");
}
}
$.when.apply($, deferredList).always(function(){
// All lazy-expands have finished
if( foundOne && missingKeyList.length > 0 ) {
// If we read new nodes from server, try to resolve yet-missing keys
_loadLazyNodes(tree, local, missingKeyList, mode, dfd);
} else {
if( missingKeyList.length ) {
tree.warn("_loadLazyNodes: could not load those keys: ", missingKeyList);
for( i=0, l=missingKeyList.length; i<l; i++ ) {
key = keyList[i];
local._appendKey(EXPANDED, keyList[i], false);
}
}
dfd.resolve();
}
});
return dfd;
}
/**
* [ext-persist] Remove persistence cookies of the given type(s).
* Called like
* $("#tree").fancytree("getTree").clearCookies("active expanded focus selected");
*
* @alias Fancytree#clearCookies
* @requires jquery.fancytree.persist.js
*/
$.ui.fancytree._FancytreeClass.prototype.clearCookies = function(types){
var local = this.ext.persist,
prefix = local.cookiePrefix;
types = types || "active expanded focus selected";
if(types.indexOf(ACTIVE) >= 0){
local._data(prefix + ACTIVE, null);
}
if(types.indexOf(EXPANDED) >= 0){
local._data(prefix + EXPANDED, null);
}
if(types.indexOf(FOCUS) >= 0){
local._data(prefix + FOCUS, null);
}
if(types.indexOf(SELECTED) >= 0){
local._data(prefix + SELECTED, null);
}
};
/**
* [ext-persist] Return persistence information from cookies
*
* Called like
* $("#tree").fancytree("getTree").getPersistData();
*
* @alias Fancytree#getPersistData
* @requires jquery.fancytree.persist.js
*/
$.ui.fancytree._FancytreeClass.prototype.getPersistData = function(){
var local = this.ext.persist,
prefix = local.cookiePrefix,
delim = local.cookieDelimiter,
res = {};
res[ACTIVE] = local._data(prefix + ACTIVE);
res[EXPANDED] = (local._data(prefix + EXPANDED) || "").split(delim);
res[SELECTED] = (local._data(prefix + SELECTED) || "").split(delim);
res[FOCUS] = local._data(prefix + FOCUS);
return res;
};
/* *****************************************************************************
* Extension code
*/
$.ui.fancytree.registerExtension({
name: "persist",
version: "0.3.0",
// Default options for this extension.
options: {
cookieDelimiter: "~",
cookiePrefix: undefined, // 'fancytree-<treeId>-' by default
cookie: {
raw: false,
expires: "",
path: "",
domain: "",
secure: false
},
expandLazy: false, // true: recursively expand and load lazy nodes
overrideSource: true, // true: cookie takes precedence over `source` data attributes.
store: "auto", // 'cookie': force cookie, 'local': force localStore, 'session': force sessionStore
types: "active expanded focus selected"
},
/* Generic read/write string data to cookie, sessionStorage or localStorage. */
_data: function(key, value){
var ls = this._local.localStorage; // null, sessionStorage, or localStorage
if( value === undefined ) {
return ls ? ls.getItem(key) : $.cookie(key);
} else if ( value === null ) {
if( ls ) {
ls.removeItem(key);
} else {
$.removeCookie(key);
}
} else {
if( ls ) {
ls.setItem(key, value);
} else {
$.cookie(key, value, this.options.persist.cookie);
}
}
},
/* Append `key` to a cookie. */
_appendKey: function(type, key, flag){
key = "" + key; // #90
var local = this._local,
instOpts = this.options.persist,
delim = instOpts.cookieDelimiter,
cookieName = local.cookiePrefix + type,
data = local._data(cookieName),
keyList = data ? data.split(delim) : [],
idx = $.inArray(key, keyList);
// Remove, even if we add a key, so the key is always the last entry
if(idx >= 0){
keyList.splice(idx, 1);
}
// Append key to cookie
if(flag){
keyList.push(key);
}
local._data(cookieName, keyList.join(delim));
},
treeInit: function(ctx){
var tree = ctx.tree,
opts = ctx.options,
local = this._local,
instOpts = this.options.persist;
// For 'auto' or 'cookie' mode, the cookie plugin must be available
_assert(instOpts.store === "localStore" || $.cookie, "Missing required plugin for 'persist' extension: jquery.cookie.js");
local.cookiePrefix = instOpts.cookiePrefix || ("fancytree-" + tree._id + "-");
local.storeActive = instOpts.types.indexOf(ACTIVE) >= 0;
local.storeExpanded = instOpts.types.indexOf(EXPANDED) >= 0;
local.storeSelected = instOpts.types.indexOf(SELECTED) >= 0;
local.storeFocus = instOpts.types.indexOf(FOCUS) >= 0;
if( instOpts.store === "cookie" || !window.localStorage ) {
local.localStorage = null;
} else {
local.localStorage = (instOpts.store === "local") ? window.localStorage : window.sessionStorage;
}
// Bind init-handler to apply cookie state
tree.$div.bind("fancytreeinit", function(event){
var cookie, dfd, i, keyList, node,
prevFocus = $.cookie(local.cookiePrefix + FOCUS); // record this before node.setActive() overrides it;
// tree.debug("document.cookie:", document.cookie);
cookie = local._data(local.cookiePrefix + EXPANDED);
keyList = cookie && cookie.split(instOpts.cookieDelimiter);
if( local.storeExpanded ) {
// Recursively load nested lazy nodes if expandLazy is 'expand' or 'load'
// Also remove expand-cookies for unmatched nodes
dfd = _loadLazyNodes(tree, local, keyList, instOpts.expandLazy ? "expand" : false , null);
} else {
// nothing to do
dfd = new $.Deferred().resolve();
}
dfd.done(function(){
if(local.storeSelected){
cookie = local._data(local.cookiePrefix + SELECTED);
if(cookie){
keyList = cookie.split(instOpts.cookieDelimiter);
for(i=0; i<keyList.length; i++){
node = tree.getNodeByKey(keyList[i]);
if(node){
if(node.selected === undefined || instOpts.overrideSource && (node.selected === false)){
// node.setSelected();
node.selected = true;
node.renderStatus();
}
}else{
// node is no longer member of the tree: remove from cookie also
local._appendKey(SELECTED, keyList[i], false);
}
}
}
// In selectMode 3 we have to fix the child nodes, since we
// only stored the selected *top* nodes
if( tree.options.selectMode === 3 ){
tree.visit(function(n){
if( n.selected ) {
n.fixSelection3AfterClick();
return "skip";
}
});
}
}
if(local.storeActive){
cookie = local._data(local.cookiePrefix + ACTIVE);
if(cookie && (opts.persist.overrideSource || !tree.activeNode)){
node = tree.getNodeByKey(cookie);
if(node){
node.setActive();
}
}
}
if(local.storeFocus && prevFocus){
node = tree.getNodeByKey(prevFocus);
if(node){
node.setFocus();
}
}
tree._triggerTreeEvent("restore", null, {});
});
});
// Init the tree
return this._super(ctx);
},
nodeSetActive: function(ctx, flag, opts) {
var res,
local = this._local;
flag = (flag !== false);
res = this._super(ctx, flag, opts);
if(local.storeActive){
local._data(local.cookiePrefix + ACTIVE, this.activeNode ? this.activeNode.key : null);
}
return res;
},
nodeSetExpanded: function(ctx, flag, opts) {
var res,
node = ctx.node,
local = this._local;
flag = (flag !== false);
res = this._super(ctx, flag, opts);
if(local.storeExpanded){
local._appendKey(EXPANDED, node.key, flag);
}
return res;
},
nodeSetFocus: function(ctx, flag) {
var res,
local = this._local;
flag = (flag !== false);
res = this._super(ctx, flag);
if(flag && local.storeFocus){
local._data(local.cookiePrefix + FOCUS, this.focusNode ? this.focusNode.key : null);
}
return res;
},
nodeSetSelected: function(ctx, flag) {
var res, selNodes,
tree = ctx.tree,
node = ctx.node,
local = this._local;
flag = (flag !== false);
res = this._super(ctx, flag);
if(local.storeSelected){
if( tree.options.selectMode === 3 ){
// In selectMode 3 we only store the the selected *top* nodes.
// De-selecting a node may also de-select some parents, so we
// calculate the current status again
selNodes = $.map(tree.getSelectedNodes(true), function(n){
return n.key;
});
selNodes = selNodes.join(ctx.options.persist.cookieDelimiter);
local._data(local.cookiePrefix + SELECTED, selNodes);
} else {
local._appendKey(SELECTED, node.key, flag);
}
}
return res;
}
});
}(jQuery, window, document));

View File

@@ -0,0 +1,359 @@
/*!
* jquery.fancytree.table.js
*
* Render tree as table (aka 'treegrid', 'tabletree').
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
*
* Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
*
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.6.0
* @date 2014-11-29T08:33
*/
;(function($, window, document, undefined) {
"use strict";
/* *****************************************************************************
* Private functions and variables
*/
function _assert(cond, msg){
msg = msg || "";
if(!cond){
$.error("Assertion failed " + msg);
}
}
function insertSiblingAfter(referenceNode, newNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
/* Show/hide all rows that are structural descendants of `parent`. */
function setChildRowVisibility(parent, flag) {
parent.visit(function(node){
var tr = node.tr;
// currentFlag = node.hide ? false : flag; // fix for ext-filter
if(tr){
tr.style.display = (node.hide || !flag) ? "none" : "";
}
if(!node.expanded){
return "skip";
}
});
}
/* Find node that is rendered in previous row. */
function findPrevRowNode(node){
var i, last, prev,
parent = node.parent,
siblings = parent ? parent.children : null;
if(siblings && siblings.length > 1 && siblings[0] !== node){
// use the lowest descendant of the preceeding sibling
i = $.inArray(node, siblings);
prev = siblings[i - 1];
_assert(prev.tr);
// descend to lowest child (with a <tr> tag)
while(prev.children){
last = prev.children[prev.children.length - 1];
if(!last.tr){
break;
}
prev = last;
}
}else{
// if there is no preceding sibling, use the direct parent
prev = parent;
}
return prev;
}
$.ui.fancytree.registerExtension({
name: "table",
version: "0.2.0",
// Default options for this extension.
options: {
checkboxColumnIdx: null, // render the checkboxes into the this column index (default: nodeColumnIdx)
customStatus: false, // true: generate renderColumns events for status nodes
indentation: 16, // indent every node level by 16px
nodeColumnIdx: 0 // render node expander, icon, and title to this column (default: #0)
},
// Overide virtual methods for this extension.
// `this` : is this extension object
// `this._super`: the virtual function that was overriden (member of prev. extension or Fancytree)
treeInit: function(ctx){
var i, $row, tdRole,
tree = ctx.tree,
$table = tree.widget.element;
$table.addClass("fancytree-container fancytree-ext-table");
tree.tbody = $table.find("> tbody")[0];
tree.columnCount = $("thead >tr >th", $table).length;
$(tree.tbody).empty();
tree.rowFragment = document.createDocumentFragment();
$row = $("<tr />");
tdRole = "";
if(ctx.options.aria){
$row.attr("role", "row");
tdRole = " role='gridcell'";
}
for(i=0; i<tree.columnCount; i++) {
if(ctx.options.table.nodeColumnIdx === i){
$row.append("<td" + tdRole + "><span class='fancytree-node' /></td>");
}else{
$row.append("<td" + tdRole + " />");
}
}
tree.rowFragment.appendChild($row.get(0));
// Make sure that status classes are set on the node's <tr> elements
tree.statusClassPropName = "tr";
tree.ariaPropName = "tr";
this.nodeContainerAttrName = "tr";
this._super(ctx);
// standard Fancytree created a root UL
$(tree.rootNode.ul).remove();
tree.rootNode.ul = null;
tree.$container = $table;
// Add container to the TAB chain
this.$container.attr("tabindex", this.options.tabbable ? "0" : "-1");
if(this.options.aria){
tree.$container
.attr("role", "treegrid")
.attr("aria-readonly", true);
}
},
/* Called by nodeRender to sync node order with tag order.*/
// nodeFixOrder: function(ctx) {
// },
nodeRemoveChildMarkup: function(ctx) {
var node = ctx.node;
// node.debug("nodeRemoveChildMarkup()");
node.visit(function(n){
if(n.tr){
$(n.tr).remove();
n.tr = null;
}
});
},
nodeRemoveMarkup: function(ctx) {
var node = ctx.node;
// node.debug("nodeRemoveMarkup()");
if(node.tr){
$(node.tr).remove();
node.tr = null;
}
this.nodeRemoveChildMarkup(ctx);
},
/* Override standard render. */
nodeRender: function(ctx, force, deep, collapsed, _recursive) {
var children, firstTr, i, l, newRow, prevNode, prevTr, subCtx,
tree = ctx.tree,
node = ctx.node,
opts = ctx.options,
isRootNode = !node.parent;
if( !_recursive ){
ctx.hasCollapsedParents = node.parent && !node.parent.expanded;
}
// $.ui.fancytree.debug("*** nodeRender " + node + ", isRoot=" + isRootNode, "tr=" + node.tr, "hcp=" + ctx.hasCollapsedParents, "parent.tr=" + (node.parent && node.parent.tr));
if( !isRootNode ){
if(!node.tr){
if( ctx.hasCollapsedParents /*&& !node.parent.tr*/ ) {
// #166: we assume that the parent will be (recursively) rendered
// later anyway.
node.debug("nodeRender ignored due to unrendered parent");
return;
}
// Create new <tr> after previous row
newRow = tree.rowFragment.firstChild.cloneNode(true);
prevNode = findPrevRowNode(node);
// $.ui.fancytree.debug("*** nodeRender " + node + ": prev: " + prevNode.key);
_assert(prevNode);
if(collapsed === true && _recursive){
// hide all child rows, so we can use an animation to show it later
newRow.style.display = "none";
}else if(deep && ctx.hasCollapsedParents){
// also hide this row if deep === true but any parent is collapsed
newRow.style.display = "none";
// newRow.style.color = "red";
}
if(!prevNode.tr){
_assert(!prevNode.parent, "prev. row must have a tr, or is system root");
tree.tbody.appendChild(newRow);
}else{
insertSiblingAfter(prevNode.tr, newRow);
}
node.tr = newRow;
if( node.key && opts.generateIds ){
node.tr.id = opts.idPrefix + node.key;
}
node.tr.ftnode = node;
if(opts.aria){
// TODO: why doesn't this work:
// node.li.role = "treeitem";
$(node.tr).attr("aria-labelledby", "ftal_" + node.key);
}
node.span = $("span.fancytree-node", node.tr).get(0);
// Set icon, link, and title (normally this is only required on initial render)
this.nodeRenderTitle(ctx);
// Allow tweaking, binding, after node was created for the first time
// tree._triggerNodeEvent("createNode", ctx);
if ( opts.createNode ){
opts.createNode.call(tree, {type: "createNode"}, ctx);
}
} else {
if( force ) {
// Set icon, link, and title (normally this is only required on initial render)
this.nodeRenderTitle(ctx); // triggers renderColumns()
} else {
// Update element classes according to node state
this.nodeRenderStatus(ctx);
}
}
}
// Allow tweaking after node state was rendered
// tree._triggerNodeEvent("renderNode", ctx);
if ( opts.renderNode ){
opts.renderNode.call(tree, {type: "renderNode"}, ctx);
}
// Visit child nodes
// Add child markup
children = node.children;
if(children && (isRootNode || deep || node.expanded)){
for(i=0, l=children.length; i<l; i++) {
subCtx = $.extend({}, ctx, {node: children[i]});
subCtx.hasCollapsedParents = subCtx.hasCollapsedParents || !node.expanded;
this.nodeRender(subCtx, force, deep, collapsed, true);
}
}
// Make sure, that <tr> order matches node.children order.
if(children && !_recursive){ // we only have to do it once, for the root branch
prevTr = node.tr || null;
firstTr = tree.tbody.firstChild;
// Iterate over all descendants
node.visit(function(n){
if(n.tr){
if(!n.parent.expanded && n.tr.style.display !== "none"){
// fix after a node was dropped over a collapsed
n.tr.style.display = "none";
setChildRowVisibility(n, false);
}
if(n.tr.previousSibling !== prevTr){
node.debug("_fixOrder: mismatch at node: " + n);
var nextTr = prevTr ? prevTr.nextSibling : firstTr;
tree.tbody.insertBefore(n.tr, nextTr);
}
prevTr = n.tr;
}
});
}
// Update element classes according to node state
// if(!isRootNode){
// this.nodeRenderStatus(ctx);
// }
},
nodeRenderTitle: function(ctx, title) {
var $cb,
node = ctx.node,
opts = ctx.options;
this._super(ctx);
// Move checkbox to custom column
if(opts.checkbox && opts.table.checkboxColumnIdx != null ){
$cb = $("span.fancytree-checkbox", node.span).detach();
$(node.tr).find("td:first").html($cb);
}
// Let user code write column content
// ctx.tree._triggerNodeEvent("renderColumns", node);
// Update element classes according to node state
if( ! node.isRoot() ){
this.nodeRenderStatus(ctx);
}
if( !opts.table.customStatus && node.isStatusNode() ) {
// default rendering for status node: leave other cells empty
} else if ( opts.renderColumns ) {
opts.renderColumns.call(ctx.tree, {type: "renderColumns"}, ctx);
}
},
nodeRenderStatus: function(ctx) {
var indent,
node = ctx.node,
opts = ctx.options;
this._super(ctx);
$(node.tr).removeClass("fancytree-node");
// indent
indent = (node.getLevel() - 1) * opts.table.indentation;
$(node.span).css({marginLeft: indent + "px"});
},
/* Expand node, return Deferred.promise. */
nodeSetExpanded: function(ctx, flag, opts) {
var dfd = new $.Deferred(),
subOpts = $.extend({}, opts, {noEvents: true, noAnimation: true});
opts = opts || {};
function _afterExpand(ok) {
flag = (flag !== false);
setChildRowVisibility(ctx.node, flag);
if( ok ) {
if( flag && ctx.options.autoScroll && !opts.noAnimation && ctx.node.hasChildren() ) {
// Scroll down to last child, but keep current node visible
ctx.node.getLastChild().scrollIntoView(true, {topNode: ctx.node}).always(function(){
if( !opts.noEvents ) {
ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
}
dfd.resolveWith(ctx.node);
});
} else {
if( !opts.noEvents ) {
ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
}
dfd.resolveWith(ctx.node);
}
} else {
if( !opts.noEvents ) {
ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
}
dfd.rejectWith(ctx.node);
}
}
// Call base-expand with disabled events and animation
this._super(ctx, flag, subOpts).done(function () {
_afterExpand(true);
}).fail(function () {
_afterExpand(false);
});
return dfd.promise();
},
nodeSetStatus: function(ctx, status, message, details) {
if(status === "ok"){
var node = ctx.node,
firstChild = ( node.children ? node.children[0] : null );
if ( firstChild && firstChild.isStatusNode() ) {
$(firstChild.tr).remove();
}
}
this._super(ctx, status, message, details);
},
treeClear: function(ctx) {
this.nodeRemoveChildMarkup(this._makeHookContext(this.rootNode));
return this._super(ctx);
}
/*,
treeSetFocus: function(ctx, flag) {
// alert("treeSetFocus" + ctx.tree.$container);
ctx.tree.$container.focus();
$.ui.fancytree.focusTree = ctx.tree;
}*/
});
}(jQuery, window, document));

View File

@@ -0,0 +1,79 @@
/*!
* jquery.fancytree.themeroller.js
*
* Enable jQuery UI ThemeRoller styles.
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
*
* @see http://jqueryui.com/themeroller/
*
* Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
*
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.6.0
* @date 2014-11-29T08:33
*/
;(function($, window, document, undefined) {
"use strict";
/*******************************************************************************
* Extension code
*/
$.ui.fancytree.registerExtension({
name: "themeroller",
version: "0.0.1",
// Default options for this extension.
options: {
activeClass: "ui-state-active",
foccusClass: "ui-state-focus",
hoverClass: "ui-state-hover",
selectedClass: "ui-state-highlight"
},
treeInit: function(ctx){
this._super(ctx);
var $el = ctx.widget.element;
if($el[0].nodeName === "TABLE"){
$el.addClass("ui-widget ui-corner-all");
$el.find(">thead tr").addClass("ui-widget-header");
$el.find(">tbody").addClass("ui-widget-conent");
}else{
$el.addClass("ui-widget ui-widget-content ui-corner-all");
}
$el.delegate(".fancytree-node", "mouseenter mouseleave", function(event){
var node = $.ui.fancytree.getNode(event.target),
flag = (event.type === "mouseenter");
node.debug("hover: " + flag);
$(node.span).toggleClass("ui-state-hover ui-corner-all", flag);
});
},
treeDestroy: function(ctx){
this._super(ctx);
ctx.widget.element.removeClass("ui-widget ui-widget-content ui-corner-all");
},
nodeRenderStatus: function(ctx){
var node = ctx.node,
$el = $(node.span);
this._super(ctx);
/*
.ui-state-highlight: Class to be applied to highlighted or selected elements. Applies "highlight" container styles to an element and its child text, links, and icons.
.ui-state-error: Class to be applied to error messaging container elements. Applies "error" container styles to an element and its child text, links, and icons.
.ui-state-error-text: An additional class that applies just the error text color without background. Can be used on form labels for instance. Also applies error icon color to child icons.
.ui-state-default: Class to be applied to clickable button-like elements. Applies "clickable default" container styles to an element and its child text, links, and icons.
.ui-state-hover: Class to be applied on mouseover to clickable button-like elements. Applies "clickable hover" container styles to an element and its child text, links, and icons.
.ui-state-focus: Class to be applied on keyboard focus to clickable button-like elements. Applies "clickable hover" container styles to an element and its child text, links, and icons.
.ui-state-active: Class to be applied on mousedown to clickable button-like elements. Applies "clickable active" container styles to an element and its child text, links, and icons.
*/
$el.toggleClass("ui-state-active", node.isActive());
$el.toggleClass("ui-state-focus", node.hasFocus());
$el.toggleClass("ui-state-highlight", node.isSelected());
// node.debug("ext-themeroller.nodeRenderStatus: ", node.span.className);
}
});
}(jQuery, window, document));

View File

@@ -0,0 +1,124 @@
/*!
*
* jquery.fancytree.wide.js
* Support for 100% wide selection bars.
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
*
* Copyright (c) 2014, Martin Wendt (http://wwWendt.de)
*
* Released under the MIT license
* https://github.com/mar10/fancytree/wiki/LicenseInfo
*
* @version 2.6.0
* @date 2014-11-29T08:33
*/
;(function($, window, document, undefined) {
"use strict";
/*******************************************************************************
* Private functions and variables
*/
// function _assert(cond, msg){
// // TODO: see qunit.js extractStacktrace()
// if(!cond){
// msg = msg ? ": " + msg : "";
// $.error("Assertion failed" + msg);
// }
// }
/* Calculate inner width without scrollbar */
function realInnerWidth($el) {
// http://blog.jquery.com/2012/08/16/jquery-1-8-box-sizing-width-csswidth-and-outerwidth/
// inst.contWidth = parseFloat(this.$container.css("width"), 10);
// 'Client width without scrollbar' - 'padding'
return $el[0].clientWidth - ($el.innerWidth() - parseFloat($el.css("width"), 10));
}
/**
* [ext-wide] Recalculate the width of the selection bar after the tree container
* was resized.<br>
* May be called explicitly on container resize, since there is no resize event
* for DIV tags.
*
* @alias Fancytree#wideUpdate
* @requires jquery.fancytree.wide.js
*/
$.ui.fancytree._FancytreeClass.prototype.wideUpdate = function(){
var inst = this.ext.wide,
prevCw = inst.contWidth,
prevLo = inst.lineOfs;
// http://blog.jquery.com/2012/08/16/jquery-1-8-box-sizing-width-csswidth-and-outerwidth/
// inst.contWidth = parseFloat(this.$container.css("width"), 10);
inst.contWidth = realInnerWidth(this.$container);
// Each title is precceeded by 2 or 3 icons (16px + 3 margin)
// + 1px title border and 3px title padding
inst.lineOfs = (this.options.checkbox ? 3 : 2) * 19;
if( prevCw !== inst.contWidth || prevLo !== inst.lineOfs ) {
this.debug("wideUpdate: " + inst.contWidth);
this.visit(function(node){
node.tree._callHook("nodeRenderTitle", node);
});
}
};
/*******************************************************************************
* Extension code
*/
$.ui.fancytree.registerExtension({
name: "wide",
version: "0.0.2",
// Default options for this extension.
options: {
autoResize: true, // call wideUpdate() on window.resize events
cheap: false, // true: use static css only
margin: {left: 3, right: 3} // free margins near the selection bar
},
treeCreate: function(ctx){
this.$container.addClass("fancytree-ext-wide");
this._super(ctx);
// http://blog.jquery.com/2012/08/16/jquery-1-8-box-sizing-width-csswidth-and-outerwidth/
// this._local.contWidth = parseFloat(ctx.tree.$container.css("width"), 10);
this._local.contWidth = realInnerWidth(ctx.tree.$container);
// Every nested UL is indented by 16px
// Each title is precceeded by 2 or 3 icons (16px + 3 margin)
// + 1px title border and 3px title padding
this._local.lineOfs = (ctx.options.checkbox ? 3 : 2) * 19;
this._local.levelOfs = 16;
this._local.titlePadding = 3;
$(window).on("resize" + ctx.widget.eventNamespace, $.ui.fancytree.debounce(200, function(event){
if( ctx.options.wide.autoResize && !ctx.options.wide.cheap ) {
ctx.tree.wideUpdate();
}
}));
},
treeDestroy: function(ctx){
$(window).off("resize" + ctx.widget.eventNamespace);
this._super(ctx);
},
nodeRenderTitle: function(ctx) {
var ofs, res, margin,
instOpts = ctx.options.wide,
inst = this._local,
cw = inst.contWidth,
node = ctx.node;
res = this._super(ctx);
if( !instOpts.cheap ) {
margin = instOpts.margin;
ofs = (node.getLevel() - 1) * inst.levelOfs + inst.lineOfs;
$(node.span).find(".fancytree-title").css({
width: cw - margin.left - margin.right - ofs,
marginLeft: -ofs + margin.left,
paddingLeft: +ofs - margin.left + inst.titlePadding,
paddingRight: inst.titlePadding
});
}
return res;
}
});
}(jQuery, window, document));