mirror of
https://github.com/zadam/trilium.git
synced 2025-11-06 21:36:05 +01:00
WIP authentication with flask-login, restructuring of static files
This commit is contained in:
585
static/lib/fancytree/src/jquery.fancytree.ariagrid.js
Normal file
585
static/lib/fancytree/src/jquery.fancytree.ariagrid.js
Normal file
@@ -0,0 +1,585 @@
|
||||
/*!
|
||||
* jquery.fancytree.ariagrid.js
|
||||
*
|
||||
* Support ARIA compliant markup and keyboard navigation for tree grids with
|
||||
* embedded input controls.
|
||||
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
|
||||
*
|
||||
* @requires ext-table
|
||||
*
|
||||
* Copyright (c) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;( function( $, window, document, undefined ) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
- References:
|
||||
- https://github.com/w3c/aria-practices/issues/132
|
||||
- https://rawgit.com/w3c/aria-practices/treegrid/examples/treegrid/treegrid-1.html
|
||||
- https://github.com/mar10/fancytree/issues/709
|
||||
|
||||
TODO:
|
||||
|
||||
- In strict mode, how can a user leave an embedded text input, if it is
|
||||
the only control in a row?
|
||||
|
||||
- If rows are hidden I suggest aria-hidden="true" on them (may be optional)
|
||||
=> aria-hidden currently not set (instead: style="display: none") needs to
|
||||
be added to ext-table
|
||||
|
||||
- enable treeOpts.aria by default
|
||||
=> requires some benchmarks, confirm it does not affect performance too much
|
||||
|
||||
- make ext-ariagrid part of ext-table (enable behavior with treeOpts.aria option)
|
||||
=> Requires stable specification
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions and variables
|
||||
*/
|
||||
|
||||
// Allow these navigation keys even when input controls are focused
|
||||
|
||||
var FT = $.ui.fancytree,
|
||||
// TODO: define attribute- and class-names for better compression:
|
||||
clsFancytreeActiveCell = "fancytree-active-cell",
|
||||
clsFancytreeCellMode = "fancytree-cell-mode",
|
||||
clsFancytreeCellNavMode = "fancytree-cell-nav-mode",
|
||||
// Define which keys are handled by embedded control, and should *not* be
|
||||
// passed to tree navigation handler:
|
||||
INPUT_KEYS = {
|
||||
"text": [ "left", "right", "home", "end", "backspace" ],
|
||||
"number": [ "up", "down", "left", "right", "home", "end", "backspace" ],
|
||||
"checkbox": [],
|
||||
"link": [],
|
||||
"radiobutton": [ "up", "down" ],
|
||||
"select-one": [ "up", "down" ],
|
||||
"select-multiple": [ "up", "down" ]
|
||||
},
|
||||
NAV_KEYS = [ "up", "down", "left", "right", "home", "end" ];
|
||||
|
||||
|
||||
/* Calculate TD column index (considering colspans).*/
|
||||
function setActiveDescendant( tree, $target ) {
|
||||
var id = $target ? $target.uniqueId().attr( "id" ) : "";
|
||||
|
||||
tree.$container.attr( "aria-activedescendant", id );
|
||||
}
|
||||
|
||||
|
||||
/* Calculate TD column index (considering colspans).*/
|
||||
function getColIdx( $tr, $td ) {
|
||||
var colspan,
|
||||
td = $td.get( 0 ),
|
||||
idx = 0;
|
||||
|
||||
$tr.children().each( function() {
|
||||
if ( this === td ) {
|
||||
return false;
|
||||
}
|
||||
colspan = $( this ).prop( "colspan" );
|
||||
idx += colspan ? colspan : 1;
|
||||
});
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
/* Find TD at given column index (considering colspans).*/
|
||||
function findTdAtColIdx( $tr, colIdx ) {
|
||||
var colspan,
|
||||
res = null,
|
||||
idx = 0;
|
||||
|
||||
$tr.children().each( function() {
|
||||
if ( idx >= colIdx ) {
|
||||
res = $( this );
|
||||
return false;
|
||||
}
|
||||
colspan = $( this ).prop( "colspan" );
|
||||
idx += colspan ? colspan : 1;
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* Find adjacent cell for a given direction. Skip empty cells and consider merged cells */
|
||||
function findNeighbourTd( tree, $target, keyCode ) {
|
||||
var $td = $target.closest( "td" ),
|
||||
$tr = $td.parent(),
|
||||
treeOpts = tree.options,
|
||||
colIdx = getColIdx( $tr, $td ),
|
||||
$tdNext = null;
|
||||
|
||||
switch ( keyCode ) {
|
||||
case "left":
|
||||
$tdNext = treeOpts.rtl ? $td.next() : $td.prev();
|
||||
break;
|
||||
case "right":
|
||||
$tdNext = treeOpts.rtl ? $td.prev() : $td.next();
|
||||
break;
|
||||
case "up":
|
||||
case "down":
|
||||
while ( true ) {
|
||||
$tr = keyCode === "up" ? $tr.prev() : $tr.next();
|
||||
if ( !$tr.length ) {
|
||||
break;
|
||||
}
|
||||
// Skip hidden rows
|
||||
if ( $tr.is( ":hidden" ) ) {
|
||||
continue;
|
||||
}
|
||||
// Find adjacent cell in the same column
|
||||
$tdNext = findTdAtColIdx( $tr, colIdx );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "ctrl+home":
|
||||
$tdNext = findTdAtColIdx( $tr.siblings().first(), colIdx );
|
||||
if ( $tdNext.is( ":hidden" ) ) {
|
||||
$tdNext = findNeighbourTd( tree, $tdNext.parent(), "down" );
|
||||
}
|
||||
break;
|
||||
case "ctrl+end":
|
||||
$tdNext = findTdAtColIdx( $tr.siblings().last(), colIdx );
|
||||
if ( $tdNext.is( ":hidden" ) ) {
|
||||
$tdNext = findNeighbourTd( tree, $tdNext.parent(), "up" );
|
||||
}
|
||||
break;
|
||||
case "home":
|
||||
$tdNext = treeOpts.rtl ? $tr.children( "td" ).last() : $tr.children( "td" ).first();
|
||||
break;
|
||||
case "end":
|
||||
$tdNext = treeOpts.rtl ? $tr.children( "td" ).first() : $tr.children( "td" ).last();
|
||||
break;
|
||||
}
|
||||
return ( $tdNext && $tdNext.length ) ? $tdNext : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* [ext-ariagrid] Set active cell and activate cell-mode if needed.
|
||||
* Pass $td=null to enter row-mode.
|
||||
*
|
||||
* See also FancytreeNode#setActive(flag, {cell: idx})
|
||||
*
|
||||
* @param {jQuery | Element | integer} [$td]
|
||||
* @alias Fancytree#activateCell
|
||||
* @requires jquery.fancytree.ariagrid.js
|
||||
* @since 2.23
|
||||
*/
|
||||
$.ui.fancytree._FancytreeClass.prototype.activateCell = function( $td ) {
|
||||
var $input, $tr,
|
||||
treeOpts = this.options,
|
||||
opts = treeOpts.ariagrid,
|
||||
$prevTd = this.$activeTd || null,
|
||||
$prevTr = $prevTd ? $prevTd.closest( "tr" ) : null;
|
||||
|
||||
// this.debug( "activateCell: " + ( $prevTd ? $prevTd.text() : "null" ) +
|
||||
// " -> " + ( $td ? $td.text() : "OFF" ) );
|
||||
|
||||
// TODO: make available as event
|
||||
// if( this._triggerNodeEvent("cellActivate", node, event, {activeTd: tree.$activeTd, colIdx: colIdx}) === false ) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
if ( $td ) {
|
||||
FT.assert( $td.length, "Invalid active cell" );
|
||||
this.$container.addClass( clsFancytreeCellMode );
|
||||
$tr = $td.closest( "tr" );
|
||||
if ( $prevTd ) {
|
||||
// cell-mode => cell-mode
|
||||
if ( $prevTd.is( $td ) ) {
|
||||
return;
|
||||
}
|
||||
$prevTd
|
||||
.removeAttr( "tabindex" )
|
||||
.removeClass( clsFancytreeActiveCell );
|
||||
|
||||
if ( !$prevTr.is( $tr ) ) {
|
||||
// We are moving to a different row: only the inputs in the
|
||||
// active row should be tabbable
|
||||
$prevTr.find( ">td :input,a" ).attr( "tabindex", "-1" );
|
||||
}
|
||||
}
|
||||
$tr.find( ">td :input:enabled,a" ).attr( "tabindex", "0" );
|
||||
FT.getNode( $td ).setActive();
|
||||
$td.addClass( clsFancytreeActiveCell );
|
||||
this.$activeTd = $td;
|
||||
|
||||
$input = $td.find( ":input:enabled,a" );
|
||||
this.debug( "Focus input", $input );
|
||||
if ( opts.autoFocusInput && $input.length ) {
|
||||
$input.focus();
|
||||
setActiveDescendant( this, $input );
|
||||
} else {
|
||||
$td.attr( "tabindex", "-1" ).focus();
|
||||
setActiveDescendant( this, $td );
|
||||
}
|
||||
} else {
|
||||
// $td == null: switch back to row-mode
|
||||
this.$container.removeClass( clsFancytreeCellMode + " " + clsFancytreeCellNavMode );
|
||||
// console.log("activateCell: set row-mode for " + this.activeNode, $prevTd);
|
||||
if ( $prevTd ) {
|
||||
// cell-mode => row-mode
|
||||
$prevTd
|
||||
.removeAttr( "tabindex" )
|
||||
.removeClass( clsFancytreeActiveCell );
|
||||
// In row-mode, only embedded inputs of the active row are tabbable
|
||||
$prevTr.find( "td" )
|
||||
.blur() // we need to blur first, because otherwise the focus frame is not reliably removed(?)
|
||||
.removeAttr( "tabindex" );
|
||||
$prevTr.find( ">td :input,a" ).attr( "tabindex", "-1" );
|
||||
this.$activeTd = null;
|
||||
// The cell lost focus, but the tree still needs to capture keys:
|
||||
this.activeNode.setFocus();
|
||||
setActiveDescendant( this, $tr );
|
||||
} else {
|
||||
// row-mode => row-mode (nothing to do)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Extension code
|
||||
*/
|
||||
$.ui.fancytree.registerExtension({
|
||||
name: "ariagrid",
|
||||
version: "2.22.5",
|
||||
// Default options for this extension.
|
||||
options: {
|
||||
// Internal behavior flags, currently controlled via `extendedMode`
|
||||
autoFocusInput: true, // true: user must hit Enter to focus control
|
||||
activateCellOnDoubelclick: true,
|
||||
enterToCellMode: false,
|
||||
// End of internal flags
|
||||
extendedMode: false,
|
||||
cellFocus: "allow",
|
||||
// TODO: document `defaultCellAction` event
|
||||
// TODO: use a global tree option `name` or `title` instead?:
|
||||
label: "Tree Grid" // Added as `aria-label` attribute
|
||||
},
|
||||
|
||||
treeInit: function( ctx ) {
|
||||
var tree = ctx.tree,
|
||||
treeOpts = ctx.options,
|
||||
opts = treeOpts.ariagrid;
|
||||
|
||||
// ariagrid requires the table extension to be loaded before itself
|
||||
this._requireExtension( "table", true, true );
|
||||
if ( !treeOpts.aria ) {
|
||||
$.error( "ext-ariagrid requires `aria: true`" );
|
||||
}
|
||||
|
||||
this._superApply( arguments );
|
||||
|
||||
this.$activeTd = null;
|
||||
this.forceNavMode = false;
|
||||
if ( opts.extendedMode ) {
|
||||
opts.autoFocusInput = true; // false;
|
||||
opts.enterToCellMode = true;
|
||||
opts.activateCellOnDoubelclick = true;
|
||||
this.forceNavMode = true;
|
||||
}
|
||||
|
||||
this.$container
|
||||
.addClass( "fancytree-ext-ariagrid" )
|
||||
.toggleClass( clsFancytreeCellNavMode, !!this.forceNavMode )
|
||||
.attr( "aria-label", "" + opts.label );
|
||||
this.$container.find( "thead > tr > th" )
|
||||
.attr( "role", "columnheader" );
|
||||
|
||||
this.nodeColumnIdx = treeOpts.table.nodeColumnIdx;
|
||||
this.checkboxColumnIdx = treeOpts.table.checkboxColumnIdx;
|
||||
if ( this.checkboxColumnIdx == null ) {
|
||||
this.checkboxColumnIdx = this.nodeColumnIdx;
|
||||
}
|
||||
|
||||
this.$container.on( "focusin", function( event ) {
|
||||
// Activate node if embedded input gets focus (due to a click)
|
||||
var node = FT.getNode( event.target ),
|
||||
$td = $( event.target ).closest( "td" );
|
||||
|
||||
// tree.debug( "focusin: " + ( node ? node.title : "null" ) +
|
||||
// ", target: " + ( $td ? $td.text() : null ) +
|
||||
// ", node was active: " + ( node && node.isActive() ) +
|
||||
// ", last cell: " + ( tree.$activeTd ? tree.$activeTd.text() : null ) );
|
||||
// tree.debug( "focusin: target", event.target );
|
||||
|
||||
if ( node && !$td.is( tree.$activeTd ) && $( event.target ).is( ":input" ) ) {
|
||||
node.debug( "Activate cell on INPUT focus event" );
|
||||
tree.activateCell( $td );
|
||||
}
|
||||
|
||||
}).on( "fancytreeinit", function( event, data ) {
|
||||
if ( opts.cellFocus === "start" ) {
|
||||
tree.debug( "Enforce cell-mode on init" );
|
||||
tree.debug( "init", ( tree.getActiveNode() || tree.getFirstChild() ) );
|
||||
( tree.getActiveNode() || tree.getFirstChild() )
|
||||
.setActive( true, { cell: tree.nodeColumnIdx });
|
||||
tree.debug( "init2", ( tree.getActiveNode() || tree.getFirstChild() ) );
|
||||
}
|
||||
|
||||
}).on( "fancytreefocustree", function( event, data ) {
|
||||
// Enforce cell-mode when container gets focus
|
||||
if ( ( opts.cellFocus === "force" ) && !tree.activeTd ) {
|
||||
var node = tree.getActiveNode() || tree.getFirstChild();
|
||||
tree.debug( "Enforce cell-mode on focusTree event" );
|
||||
node.setActive( true, { cell: 0 });
|
||||
}
|
||||
});
|
||||
},
|
||||
nodeClick: function( ctx ) {
|
||||
var targetType = ctx.targetType,
|
||||
tree = ctx.tree,
|
||||
node = ctx.node,
|
||||
event = ctx.originalEvent,
|
||||
$td = $( event.target ).closest( "td" );
|
||||
|
||||
tree.debug( "nodeClick: node: " + ( node ? node.title : "null" ) +
|
||||
", targetType: " + targetType +
|
||||
", target: " + ( $td.length ? $td.text() : null ) +
|
||||
", node was active: " + ( node && node.isActive() ) +
|
||||
", last cell: " + ( tree.$activeTd ? tree.$activeTd.text() : null ) );
|
||||
|
||||
if ( tree.$activeTd ) {
|
||||
// If already in cell-mode, activate new cell
|
||||
tree.activateCell( $td );
|
||||
return false;
|
||||
}
|
||||
return this._superApply( arguments );
|
||||
},
|
||||
nodeDblclick: function( ctx ) {
|
||||
var tree = ctx.tree,
|
||||
treeOpts = ctx.options,
|
||||
opts = treeOpts.ariagrid,
|
||||
event = ctx.originalEvent,
|
||||
$td = $( event.target ).closest( "td" );
|
||||
|
||||
// console.log("nodeDblclick", tree.$activeTd, ctx.options.ariagrid.cellFocus)
|
||||
if ( opts.activateCellOnDoubelclick && !tree.$activeTd && opts.cellFocus === "allow" ) {
|
||||
// If in row-mode, activate new cell
|
||||
tree.activateCell( $td );
|
||||
return false;
|
||||
}
|
||||
return this._superApply( arguments );
|
||||
},
|
||||
nodeRenderStatus: function( ctx ) {
|
||||
// Set classes for current status
|
||||
var res,
|
||||
node = ctx.node,
|
||||
$tr = $( node.tr );
|
||||
|
||||
res = this._super( ctx );
|
||||
|
||||
if ( node.parent ) {
|
||||
$tr
|
||||
.attr( "aria-level", node.getLevel() )
|
||||
.attr( "aria-setsize", node.parent.children.length )
|
||||
.attr( "aria-posinset", node.getIndex() + 1 );
|
||||
|
||||
if ( $tr.is( ":hidden" ) ) {
|
||||
$tr.attr( "aria-hidden", true );
|
||||
} else {
|
||||
$tr.removeAttr( "aria-hidden" );
|
||||
}
|
||||
// this.debug("nodeRenderStatus: " + this.$activeTd + ", " + $tr.attr("aria-expanded"));
|
||||
// In cell-mode, move aria-expanded attribute from TR to first child TD
|
||||
if ( this.$activeTd && $tr.attr( "aria-expanded" ) != null ) {
|
||||
$tr.remove( "aria-expanded" );
|
||||
$tr.find( "td" ).eq( this.nodeColumnIdx )
|
||||
.attr( "aria-expanded", node.isExpanded() );
|
||||
} else {
|
||||
$tr.find( "td" ).eq( this.nodeColumnIdx ).removeAttr( "aria-expanded" );
|
||||
}
|
||||
}
|
||||
return res;
|
||||
},
|
||||
nodeSetActive: function( ctx, flag, callOpts ) {
|
||||
var $td,
|
||||
node = ctx.node,
|
||||
tree = ctx.tree,
|
||||
// treeOpts = ctx.options,
|
||||
// opts = treeOpts.ariagrid,
|
||||
$tr = $( node.tr );
|
||||
|
||||
flag = ( flag !== false );
|
||||
// node.debug( "nodeSetActive(" + flag + ")" );
|
||||
// Support custom `cell` option
|
||||
if ( flag && callOpts && callOpts.cell != null ) {
|
||||
// `cell` may be a col-index, <td>, or `$(td)`
|
||||
if ( typeof callOpts.cell === "number" ) {
|
||||
$td = findTdAtColIdx( $tr, callOpts.cell );
|
||||
} else {
|
||||
$td = $( callOpts.cell );
|
||||
}
|
||||
tree.activateCell( $td );
|
||||
return;
|
||||
}
|
||||
// tree.debug( "nodeSetActive: activeNode " + this.activeNode );
|
||||
return this._superApply( arguments );
|
||||
},
|
||||
nodeKeydown: function( ctx ) {
|
||||
var handleKeys, inputType, $td,
|
||||
tree = ctx.tree,
|
||||
node = ctx.node,
|
||||
treeOpts = ctx.options,
|
||||
opts = treeOpts.ariagrid,
|
||||
event = ctx.originalEvent,
|
||||
eventString = FT.eventToString( event ),
|
||||
$target = $( event.target ),
|
||||
$activeTd = this.$activeTd,
|
||||
$activeTr = $activeTd ? $activeTd.closest( "tr" ) : null,
|
||||
colIdx = $activeTd ? getColIdx( $activeTr, $activeTd ) : -1,
|
||||
forceNav = $activeTd && opts.extendedMode &&
|
||||
tree.forceNavMode && $.inArray( eventString, NAV_KEYS ) >= 0;
|
||||
|
||||
if ( $target.is( ":input:enabled" ) ) {
|
||||
inputType = $target.prop( "type" );
|
||||
} else if ( $target.is( "a" ) ) {
|
||||
inputType = "link";
|
||||
}
|
||||
ctx.tree.debug( "nodeKeydown(" + eventString + "), activeTd: '" +
|
||||
( $activeTd && $activeTd.text() ) + "', inputType: " + inputType );
|
||||
|
||||
if ( inputType && eventString !== "esc" && !forceNav ) {
|
||||
handleKeys = INPUT_KEYS[ inputType ];
|
||||
if ( handleKeys && $.inArray( eventString, handleKeys ) >= 0 ) {
|
||||
return; // Let input control handle the key
|
||||
}
|
||||
}
|
||||
|
||||
switch ( eventString ) {
|
||||
case "right":
|
||||
if ( $activeTd ) {
|
||||
// Cell mode: move to neighbour (stop on right border)
|
||||
$td = findNeighbourTd( tree, $activeTd, eventString );
|
||||
if ( $td ) {
|
||||
tree.activateCell( $td );
|
||||
}
|
||||
} else if ( node && !node.isExpanded() && node.hasChildren() !== false ) {
|
||||
// Row mode and current node can be expanded:
|
||||
// default handling will expand.
|
||||
break;
|
||||
} else {
|
||||
// Row mode: switch to cell-mode
|
||||
$td = $( node.tr ).find( ">td:first" );
|
||||
tree.activateCell( $td );
|
||||
}
|
||||
return false; // no default handling
|
||||
|
||||
case "left":
|
||||
case "home":
|
||||
case "end":
|
||||
case "ctrl+home":
|
||||
case "ctrl+end":
|
||||
case "up":
|
||||
case "down":
|
||||
if ( $activeTd ) {
|
||||
// Cell mode: move to neighbour
|
||||
$td = findNeighbourTd( tree, $activeTd, eventString );
|
||||
// Note: $td may be null if we move outside bounds. In this case
|
||||
// we switch back to row-mode
|
||||
if ( $td || opts.cellFocus !== "force" ) {
|
||||
tree.activateCell( $td );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case "esc":
|
||||
if ( opts.extendedMode && $activeTd && !tree.forceNavMode ) {
|
||||
// Extended mode: switch from cell-edit-mode to cell-nav-mode
|
||||
// $target.closest( "td" ).focus();
|
||||
tree.forceNavMode = true;
|
||||
tree.$container.addClass( clsFancytreeCellNavMode );
|
||||
tree.debug( "Enter cell-edit-mode" );
|
||||
return false;
|
||||
} else if ( opts.extendedMode && $activeTd && opts.cellFocus !== "force" ) {
|
||||
// Extended mode: switch back from cell-mode to row-mode
|
||||
tree.activateCell( null );
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case "return":
|
||||
// Let caller override the default action:
|
||||
if ( tree._triggerNodeEvent( "defaultCellAction", node, event,
|
||||
{ activeTd: tree.$activeTd, colIdx: colIdx }) === false ) {
|
||||
return false;
|
||||
}
|
||||
if ( $activeTd ) {
|
||||
// Apply 'default action' for embedded cell control
|
||||
if ( colIdx === this.nodeColumnIdx ) {
|
||||
node.toggleExpanded();
|
||||
return false;
|
||||
} else if ( colIdx === this.checkboxColumnIdx ) {
|
||||
node.toggleSelected();
|
||||
return false;
|
||||
} else if ( $activeTd.find( ":checkbox:enabled" ).length ) {
|
||||
// Embedded checkboxes are always toggled (ignoring `autoFocusInput`)
|
||||
$activeTd.find( ":checkbox:enabled" ).click();
|
||||
return false;
|
||||
} else if ( opts.extendedMode && tree.forceNavMode && $target.is( ":input" ) ) {
|
||||
tree.forceNavMode = false;
|
||||
tree.$container.removeClass( clsFancytreeCellNavMode );
|
||||
tree.debug( "enable cell-edit-mode" );
|
||||
// Switch from input-mode back to cell-mode
|
||||
// setTimeout( function() {
|
||||
// $activeTd.focus();
|
||||
// }, 0 );
|
||||
// } else if ( !opts.autoFocusInput && !inputType &&
|
||||
// $activeTd.find( ":input:enabled" ).length ) {
|
||||
// // autoFocusInput is off:
|
||||
// // If we don't focus embedded inputs on navigation by default,
|
||||
// // we do it as ENTER default action
|
||||
// $target.find( ":input:enabled" ).focus();
|
||||
// return false;
|
||||
} else if ( $activeTd.find( "a" ).length ) {
|
||||
$activeTd.find( "a" )[ 0 ].click();
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
// ENTER in row-mode
|
||||
if ( opts.enterToCellMode ) {
|
||||
// Switch from row-mode to cell-mode
|
||||
$td = $( node.tr ).find( ">td:nth(" + this.nodeColumnIdx + ")" );
|
||||
tree.activateCell( $td );
|
||||
return false; // no default handling
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "space":
|
||||
if ( $activeTd ) {
|
||||
if ( colIdx === this.checkboxColumnIdx ) {
|
||||
node.toggleSelected();
|
||||
} else if ( $activeTd.find( ":checkbox:enabled" ).length ) {
|
||||
$activeTd.find( ":checkbox:enabled" ).click();
|
||||
}
|
||||
return false; // no default handling
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Allow to focus input by typing alphanum keys:
|
||||
// TODO: not so easy to simulate keytrokes on inputs!
|
||||
// http://stackoverflow.com/questions/6124692/jquery-simulating-keypress-event-on-an-input-field
|
||||
// if ( opts.extendedMode && $activeTd &&
|
||||
// eventString.length === 1 &&
|
||||
// !$target.is(":input") && $activeTd.find(":input:enabled").length ) {
|
||||
// alert("in");
|
||||
// return false; // no default handling
|
||||
// }
|
||||
}
|
||||
return this._superApply( arguments );
|
||||
}
|
||||
});
|
||||
})( jQuery, window, document );
|
||||
210
static/lib/fancytree/src/jquery.fancytree.childcounter.js
Normal file
210
static/lib/fancytree/src/jquery.fancytree.childcounter.js
Normal file
@@ -0,0 +1,210 @@
|
||||
// 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) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
// 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.updateCounters();
|
||||
//
|
||||
// It is also good practice to add a docstring comment.
|
||||
/**
|
||||
* [ext-childcounter] Update counter badges for `node` and its parents.
|
||||
* May be called in the `loadChildren` event, to update parents of lazy loaded
|
||||
* nodes.
|
||||
* @alias FancytreeNode#updateCounters
|
||||
* @requires jquery.fancytree.childcounters.js
|
||||
*/
|
||||
$.ui.fancytree._FancytreeNodeClass.prototype.updateCounters = function(){
|
||||
var node = this,
|
||||
$badge = $("span.fancytree-childcounter", node.span),
|
||||
extOpts = node.tree.options.childcounter,
|
||||
count = node.countChildren(extOpts.deep);
|
||||
|
||||
node.data.childCounter = count;
|
||||
if( (count || !extOpts.hideZeros) && (!node.isExpanded() || !extOpts.hideExpanded) ) {
|
||||
if( !$badge.length ) {
|
||||
$badge = $("<span class='fancytree-childcounter'/>").appendTo($("span.fancytree-icon", node.span));
|
||||
}
|
||||
$badge.text(count);
|
||||
} else {
|
||||
$badge.remove();
|
||||
}
|
||||
if( extOpts.deep && !node.isTopLevel() && !node.isRoot() ) {
|
||||
node.parent.updateCounters();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 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"></script>
|
||||
// <script src="../src/jquery.fancytree.childcounter.js"></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: "2.22.5",
|
||||
|
||||
// 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._superApply(arguments);
|
||||
// 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._superApply(arguments);
|
||||
},
|
||||
|
||||
// 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
|
||||
// We use `_super()` instead of `_superApply()` here, since it is a little bit
|
||||
// more performant when called often
|
||||
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, callOpts) {
|
||||
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._superApply(arguments).always(function(){
|
||||
tree.nodeRenderTitle(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
// End of extension definition
|
||||
});
|
||||
// End of namespace closure
|
||||
}(jQuery));
|
||||
465
static/lib/fancytree/src/jquery.fancytree.clones.js
Normal file
465
static/lib/fancytree/src/jquery.fancytree.clones.js
Normal file
@@ -0,0 +1,465 @@
|
||||
/*!
|
||||
*
|
||||
* jquery.fancytree.clones.js
|
||||
* Support faster lookup of nodes by key and shared ref-ids.
|
||||
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
|
||||
*
|
||||
* Copyright (c) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return first occurrence of member from array. */
|
||||
function _removeArrayMember(arr, elem) {
|
||||
// TODO: use Array.indexOf for IE >= 9
|
||||
var i;
|
||||
for (i = arr.length - 1; i >= 0; i--) {
|
||||
if (arr[i] === elem) {
|
||||
arr.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * Calculate a 32 bit FNV-1a hash
|
||||
// * Found here: https://gist.github.com/vaiorabbit/5657561
|
||||
// * Ref.: http://isthe.com/chongo/tech/comp/fnv/
|
||||
// *
|
||||
// * @param {string} str the input value
|
||||
// * @param {boolean} [asString=false] set to true to return the hash value as
|
||||
// * 8-digit hex string instead of an integer
|
||||
// * @param {integer} [seed] optionally pass the hash of the previous chunk
|
||||
// * @returns {integer | string}
|
||||
// */
|
||||
// function hashFnv32a(str, asString, seed) {
|
||||
// /*jshint bitwise:false */
|
||||
// var i, l,
|
||||
// hval = (seed === undefined) ? 0x811c9dc5 : seed;
|
||||
|
||||
// for (i = 0, l = str.length; i < l; i++) {
|
||||
// hval ^= str.charCodeAt(i);
|
||||
// hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
|
||||
// }
|
||||
// if( asString ){
|
||||
// // Convert to 8 digit hex string
|
||||
// return ("0000000" + (hval >>> 0).toString(16)).substr(-8);
|
||||
// }
|
||||
// return hval >>> 0;
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* JS Implementation of MurmurHash3 (r136) (as of May 20, 2011)
|
||||
*
|
||||
* @author <a href="mailto:gary.court@gmail.com">Gary Court</a>
|
||||
* @see http://github.com/garycourt/murmurhash-js
|
||||
* @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a>
|
||||
* @see http://sites.google.com/site/murmurhash/
|
||||
*
|
||||
* @param {string} key ASCII only
|
||||
* @param {boolean} [asString=false]
|
||||
* @param {number} seed Positive integer only
|
||||
* @return {number} 32-bit positive integer hash
|
||||
*/
|
||||
function hashMurmur3(key, asString, seed) {
|
||||
/*jshint bitwise:false */
|
||||
var h1b, k1,
|
||||
remainder = key.length & 3,
|
||||
bytes = key.length - remainder,
|
||||
h1 = seed,
|
||||
c1 = 0xcc9e2d51,
|
||||
c2 = 0x1b873593,
|
||||
i = 0;
|
||||
|
||||
while (i < bytes) {
|
||||
k1 =
|
||||
((key.charCodeAt(i) & 0xff)) |
|
||||
((key.charCodeAt(++i) & 0xff) << 8) |
|
||||
((key.charCodeAt(++i) & 0xff) << 16) |
|
||||
((key.charCodeAt(++i) & 0xff) << 24);
|
||||
++i;
|
||||
|
||||
k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff;
|
||||
k1 = (k1 << 15) | (k1 >>> 17);
|
||||
k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = (h1 << 13) | (h1 >>> 19);
|
||||
h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff;
|
||||
h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16));
|
||||
}
|
||||
|
||||
k1 = 0;
|
||||
|
||||
switch (remainder) {
|
||||
/*jshint -W086:true */
|
||||
case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
|
||||
case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
|
||||
case 1: k1 ^= (key.charCodeAt(i) & 0xff);
|
||||
|
||||
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
|
||||
k1 = (k1 << 15) | (k1 >>> 17);
|
||||
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
|
||||
h1 ^= k1;
|
||||
}
|
||||
|
||||
h1 ^= key.length;
|
||||
|
||||
h1 ^= h1 >>> 16;
|
||||
h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
|
||||
h1 ^= h1 >>> 13;
|
||||
h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff;
|
||||
h1 ^= h1 >>> 16;
|
||||
|
||||
if( asString ){
|
||||
// Convert to 8 digit hex string
|
||||
return ("0000000" + (h1 >>> 0).toString(16)).substr(-8);
|
||||
}
|
||||
return h1 >>> 0;
|
||||
}
|
||||
|
||||
// console.info(hashMurmur3("costarring"));
|
||||
// console.info(hashMurmur3("costarring", true));
|
||||
// console.info(hashMurmur3("liquid"));
|
||||
// console.info(hashMurmur3("liquid", true));
|
||||
|
||||
|
||||
/*
|
||||
* Return a unique key for node by calculationg the hash of the parents refKey-list
|
||||
*/
|
||||
function calcUniqueKey(node) {
|
||||
var key,
|
||||
path = $.map(node.getParentList(false, true), function(e){ return e.refKey || e.key; });
|
||||
path = path.join("/");
|
||||
key = "id_" + hashMurmur3(path, true);
|
||||
// node.debug(path + " -> " + key);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* [ext-clones] Return a list of clone-nodes or null.
|
||||
* @param {boolean} [includeSelf=false]
|
||||
* @returns {FancytreeNode[] | null}
|
||||
*
|
||||
* @alias FancytreeNode#getCloneList
|
||||
* @requires jquery.fancytree.clones.js
|
||||
*/
|
||||
$.ui.fancytree._FancytreeNodeClass.prototype.getCloneList = function(includeSelf){
|
||||
var key,
|
||||
tree = this.tree,
|
||||
refList = tree.refMap[this.refKey] || null,
|
||||
keyMap = tree.keyMap;
|
||||
|
||||
if( refList ) {
|
||||
key = this.key;
|
||||
// Convert key list to node list
|
||||
if( includeSelf ) {
|
||||
refList = $.map(refList, function(val){ return keyMap[val]; });
|
||||
} else {
|
||||
refList = $.map(refList, function(val){ return val === key ? null : keyMap[val]; });
|
||||
if( refList.length < 1 ) {
|
||||
refList = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return refList;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* [ext-clones] Return true if this node has at least another clone with same refKey.
|
||||
* @returns {boolean}
|
||||
*
|
||||
* @alias FancytreeNode#isClone
|
||||
* @requires jquery.fancytree.clones.js
|
||||
*/
|
||||
$.ui.fancytree._FancytreeNodeClass.prototype.isClone = function(){
|
||||
var refKey = this.refKey || null,
|
||||
refList = refKey && this.tree.refMap[refKey] || null;
|
||||
return !!(refList && refList.length > 1);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* [ext-clones] Update key and/or refKey for an existing node.
|
||||
* @param {string} key
|
||||
* @param {string} refKey
|
||||
* @returns {boolean}
|
||||
*
|
||||
* @alias FancytreeNode#reRegister
|
||||
* @requires jquery.fancytree.clones.js
|
||||
*/
|
||||
$.ui.fancytree._FancytreeNodeClass.prototype.reRegister = function(key, refKey){
|
||||
key = (key == null) ? null : "" + key;
|
||||
refKey = (refKey == null) ? null : "" + refKey;
|
||||
// this.debug("reRegister", key, refKey);
|
||||
|
||||
var tree = this.tree,
|
||||
prevKey = this.key,
|
||||
prevRefKey = this.refKey,
|
||||
keyMap = tree.keyMap,
|
||||
refMap = tree.refMap,
|
||||
refList = refMap[prevRefKey] || null,
|
||||
// curCloneKeys = refList ? node.getCloneList(true),
|
||||
modified = false;
|
||||
|
||||
// Key has changed: update all references
|
||||
if( key != null && key !== this.key ) {
|
||||
if( keyMap[key] ) {
|
||||
$.error("[ext-clones] reRegister(" + key + "): already exists: " + this);
|
||||
}
|
||||
// Update keyMap
|
||||
delete keyMap[prevKey];
|
||||
keyMap[key] = this;
|
||||
// Update refMap
|
||||
if( refList ) {
|
||||
refMap[prevRefKey] = $.map(refList, function(e){
|
||||
return e === prevKey ? key : e;
|
||||
});
|
||||
}
|
||||
this.key = key;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// refKey has changed
|
||||
if( refKey != null && refKey !== this.refKey ) {
|
||||
// Remove previous refKeys
|
||||
if( refList ){
|
||||
if( refList.length === 1 ){
|
||||
delete refMap[prevRefKey];
|
||||
}else{
|
||||
refMap[prevRefKey] = $.map(refList, function(e){
|
||||
return e === prevKey ? null : e;
|
||||
});
|
||||
}
|
||||
}
|
||||
// Add refKey
|
||||
if( refMap[refKey] ) {
|
||||
refMap[refKey].append(key);
|
||||
}else{
|
||||
refMap[refKey] = [ this.key ];
|
||||
}
|
||||
this.refKey = refKey;
|
||||
modified = true;
|
||||
}
|
||||
return modified;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* [ext-clones] Define a refKey for an existing node.
|
||||
* @param {string} refKey
|
||||
* @returns {boolean}
|
||||
*
|
||||
* @alias FancytreeNode#setRefKey
|
||||
* @requires jquery.fancytree.clones.js
|
||||
* @since 2.16
|
||||
*/
|
||||
$.ui.fancytree._FancytreeNodeClass.prototype.setRefKey = function(refKey){
|
||||
return this.reRegister(null, refKey);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* [ext-clones] Return all nodes with a given refKey (null if not found).
|
||||
* @param {string} refKey
|
||||
* @param {FancytreeNode} [rootNode] optionally restrict results to descendants of this node
|
||||
* @returns {FancytreeNode[] | null}
|
||||
* @alias Fancytree#getNodesByRef
|
||||
* @requires jquery.fancytree.clones.js
|
||||
*/
|
||||
$.ui.fancytree._FancytreeClass.prototype.getNodesByRef = function(refKey, rootNode){
|
||||
var keyMap = this.keyMap,
|
||||
refList = this.refMap[refKey] || null;
|
||||
|
||||
if( refList ) {
|
||||
// Convert key list to node list
|
||||
if( rootNode ) {
|
||||
refList = $.map(refList, function(val){
|
||||
var node = keyMap[val];
|
||||
return node.isDescendantOf(rootNode) ? node : null;
|
||||
});
|
||||
}else{
|
||||
refList = $.map(refList, function(val){ return keyMap[val]; });
|
||||
}
|
||||
if( refList.length < 1 ) {
|
||||
refList = null;
|
||||
}
|
||||
}
|
||||
return refList;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* [ext-clones] Replace a refKey with a new one.
|
||||
* @param {string} oldRefKey
|
||||
* @param {string} newRefKey
|
||||
* @alias Fancytree#changeRefKey
|
||||
* @requires jquery.fancytree.clones.js
|
||||
*/
|
||||
$.ui.fancytree._FancytreeClass.prototype.changeRefKey = function(oldRefKey, newRefKey) {
|
||||
var i, node,
|
||||
keyMap = this.keyMap,
|
||||
refList = this.refMap[oldRefKey] || null;
|
||||
|
||||
if (refList) {
|
||||
for (i = 0; i < refList.length; i++) {
|
||||
node = keyMap[refList[i]];
|
||||
node.refKey = newRefKey;
|
||||
}
|
||||
delete this.refMap[oldRefKey];
|
||||
this.refMap[newRefKey] = refList;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Extension code
|
||||
*/
|
||||
$.ui.fancytree.registerExtension({
|
||||
name: "clones",
|
||||
version: "2.22.5",
|
||||
// Default options for this extension.
|
||||
options: {
|
||||
highlightActiveClones: true, // set 'fancytree-active-clone' on active clones and all peers
|
||||
highlightClones: false // set 'fancytree-clone' class on any node that has at least one clone
|
||||
},
|
||||
|
||||
treeCreate: function(ctx){
|
||||
this._superApply(arguments);
|
||||
ctx.tree.refMap = {};
|
||||
ctx.tree.keyMap = {};
|
||||
},
|
||||
treeInit: function(ctx){
|
||||
this.$container.addClass("fancytree-ext-clones");
|
||||
_assert(ctx.options.defaultKey == null);
|
||||
// Generate unique / reproducible default keys
|
||||
ctx.options.defaultKey = function(node){
|
||||
return calcUniqueKey(node);
|
||||
};
|
||||
// The default implementation loads initial data
|
||||
this._superApply(arguments);
|
||||
},
|
||||
treeClear: function(ctx){
|
||||
ctx.tree.refMap = {};
|
||||
ctx.tree.keyMap = {};
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
treeRegisterNode: function(ctx, add, node) {
|
||||
var refList, len,
|
||||
tree = ctx.tree,
|
||||
keyMap = tree.keyMap,
|
||||
refMap = tree.refMap,
|
||||
key = node.key,
|
||||
refKey = (node && node.refKey != null) ? "" + node.refKey : null;
|
||||
|
||||
// ctx.tree.debug("clones.treeRegisterNode", add, node);
|
||||
|
||||
if( node.isStatusNode() ){
|
||||
return this._super(ctx, add, node);
|
||||
}
|
||||
|
||||
if( add ) {
|
||||
if( keyMap[node.key] != null ) {
|
||||
$.error("clones.treeRegisterNode: node.key already exists: " + node);
|
||||
}
|
||||
keyMap[key] = node;
|
||||
if( refKey ) {
|
||||
refList = refMap[refKey];
|
||||
if( refList ) {
|
||||
refList.push(key);
|
||||
if( refList.length === 2 && ctx.options.clones.highlightClones ) {
|
||||
// Mark peer node, if it just became a clone (no need to
|
||||
// mark current node, since it will be rendered later anyway)
|
||||
keyMap[refList[0]].renderStatus();
|
||||
}
|
||||
} else {
|
||||
refMap[refKey] = [key];
|
||||
}
|
||||
// node.debug("clones.treeRegisterNode: add clone =>", refMap[refKey]);
|
||||
}
|
||||
}else {
|
||||
if( keyMap[key] == null ) {
|
||||
$.error("clones.treeRegisterNode: node.key not registered: " + node.key);
|
||||
}
|
||||
delete keyMap[key];
|
||||
if( refKey ) {
|
||||
refList = refMap[refKey];
|
||||
// node.debug("clones.treeRegisterNode: remove clone BEFORE =>", refMap[refKey]);
|
||||
if( refList ) {
|
||||
len = refList.length;
|
||||
if( len <= 1 ){
|
||||
_assert(len === 1);
|
||||
_assert(refList[0] === key);
|
||||
delete refMap[refKey];
|
||||
}else{
|
||||
_removeArrayMember(refList, key);
|
||||
// Unmark peer node, if this was the only clone
|
||||
if( len === 2 && ctx.options.clones.highlightClones ) {
|
||||
// node.debug("clones.treeRegisterNode: last =>", node.getCloneList());
|
||||
keyMap[refList[0]].renderStatus();
|
||||
}
|
||||
}
|
||||
// node.debug("clones.treeRegisterNode: remove clone =>", refMap[refKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this._super(ctx, add, node);
|
||||
},
|
||||
nodeRenderStatus: function(ctx) {
|
||||
var $span, res,
|
||||
node = ctx.node;
|
||||
|
||||
res = this._super(ctx);
|
||||
|
||||
if( ctx.options.clones.highlightClones ) {
|
||||
$span = $(node[ctx.tree.statusClassPropName]);
|
||||
// Only if span already exists
|
||||
if( $span.length && node.isClone() ){
|
||||
// node.debug("clones.nodeRenderStatus: ", ctx.options.clones.highlightClones);
|
||||
$span.addClass("fancytree-clone");
|
||||
}
|
||||
}
|
||||
return res;
|
||||
},
|
||||
nodeSetActive: function(ctx, flag, callOpts) {
|
||||
var res,
|
||||
scpn = ctx.tree.statusClassPropName,
|
||||
node = ctx.node;
|
||||
|
||||
res = this._superApply(arguments);
|
||||
|
||||
if( ctx.options.clones.highlightActiveClones && node.isClone() ) {
|
||||
$.each(node.getCloneList(true), function(idx, n){
|
||||
// n.debug("clones.nodeSetActive: ", flag !== false);
|
||||
$(n[scpn]).toggleClass("fancytree-active-clone", flag !== false);
|
||||
});
|
||||
}
|
||||
return res;
|
||||
}
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
150
static/lib/fancytree/src/jquery.fancytree.columnview.js
Normal file
150
static/lib/fancytree/src/jquery.fancytree.columnview.js
Normal file
@@ -0,0 +1,150 @@
|
||||
/*!
|
||||
* 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) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(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: "2.22.5",
|
||||
// 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._superApply(arguments);
|
||||
// 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.toggleEffect = 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,
|
||||
node = data.node || data.tree.getFirstChild();
|
||||
switch(event.which){
|
||||
case $.ui.keyCode.DOWN:
|
||||
next = node.getNextSibling();
|
||||
if( next ){
|
||||
next.setFocus();
|
||||
}
|
||||
return false;
|
||||
case $.ui.keyCode.LEFT:
|
||||
next = node.getParent();
|
||||
if( next ){
|
||||
next.setFocus();
|
||||
}
|
||||
return false;
|
||||
case $.ui.keyCode.UP:
|
||||
next = 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));
|
||||
142
static/lib/fancytree/src/jquery.fancytree.debug.js
Normal file
142
static/lib/fancytree/src/jquery.fancytree.debug.js
Normal 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) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(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: "2.22.5",
|
||||
// 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._superApply(arguments);
|
||||
},
|
||||
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, callOpts) {
|
||||
},
|
||||
nodeSetExpanded: function(ctx, flag, callOpts) {
|
||||
},
|
||||
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: "2.22.5",
|
||||
// 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._superApply(arguments);
|
||||
window.console && window.console.timeEnd && window.console.timeEnd(s);
|
||||
}
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
572
static/lib/fancytree/src/jquery.fancytree.dnd.js
Normal file
572
static/lib/fancytree/src/jquery.fancytree.dnd.js
Normal file
@@ -0,0 +1,572 @@
|
||||
/*!
|
||||
* jquery.fancytree.dnd.js
|
||||
*
|
||||
* Drag-and-drop support (jQuery UI draggable/droppable).
|
||||
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
|
||||
*
|
||||
* Copyright (c) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(function($, window, document, undefined) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/* *****************************************************************************
|
||||
* Private functions and variables
|
||||
*/
|
||||
var didRegisterDnd = false,
|
||||
classDropAccept = "fancytree-drop-accept",
|
||||
classDropAfter = "fancytree-drop-after",
|
||||
classDropBefore = "fancytree-drop-before",
|
||||
classDropOver = "fancytree-drop-over",
|
||||
classDropReject = "fancytree-drop-reject",
|
||||
classDropTarget = "fancytree-drop-target";
|
||||
|
||||
/* Convert number to string and prepend +/-; return empty string for 0.*/
|
||||
function offsetString(n){
|
||||
return n === 0 ? "" : (( n > 0 ) ? ("+" + n) : ("" + n));
|
||||
}
|
||||
|
||||
//--- 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 ctx, 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),
|
||||
dndOpts = sourceNode && sourceNode.tree.options.dnd;
|
||||
|
||||
// logObject = sourceNode || prevTargetNode || $.ui.fancytree;
|
||||
// logObject.debug("Drag event:", event, event.shiftKey);
|
||||
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);
|
||||
|
||||
if( dndOpts && dndOpts.updateHelper ) {
|
||||
ctx = sourceNode.tree._makeHookContext(sourceNode, event, {
|
||||
otherNode: targetNode,
|
||||
ui: ui,
|
||||
draggable: draggable,
|
||||
dropMarker: $("#fancytree-drop-marker")
|
||||
});
|
||||
dndOpts.updateHelper.call(sourceNode.tree, sourceNode, ctx);
|
||||
}
|
||||
|
||||
// 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);
|
||||
targetNode.tree.ext.dnd._onDragEvent("over", targetNode, sourceNode, event, ui, draggable);
|
||||
}
|
||||
}
|
||||
// else go ahead with standard event handling
|
||||
},
|
||||
stop: function(event, ui) {
|
||||
var 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,
|
||||
targetNode = ui.helper.data("ftTargetNode") || null,
|
||||
dropped = (event.type === "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;
|
||||
}
|
||||
|
||||
|
||||
/* *****************************************************************************
|
||||
* Drag and drop support
|
||||
*/
|
||||
function _initDragAndDrop(tree) {
|
||||
var dnd = tree.options.dnd || null,
|
||||
glyph = tree.options.glyph || 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,
|
||||
// containment: "parent",
|
||||
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, $nodeTag, opts,
|
||||
sourceNode = $.ui.fancytree.getNode(event.target);
|
||||
|
||||
if(!sourceNode){
|
||||
// #405, DT issue 211: might happen, if dragging a table *header*
|
||||
return "<div>ERROR?: helper requested but sourceNode not found</div>";
|
||||
}
|
||||
opts = sourceNode.tree.options.dnd;
|
||||
$nodeTag = $(sourceNode.span);
|
||||
// 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);
|
||||
|
||||
// Support glyph symbols instead of icons
|
||||
if( glyph ) {
|
||||
$helper.find(".fancytree-drag-helper-img")
|
||||
.addClass(glyph.map.dragHelper);
|
||||
}
|
||||
// Allow to modify the helper, e.g. to add multi-node-drag feedback
|
||||
if( opts.initHelper ) {
|
||||
opts.initHelper.call(sourceNode.tree, sourceNode, {
|
||||
node: sourceNode,
|
||||
tree: sourceNode.tree,
|
||||
originalEvent: event,
|
||||
ui: { helper: $helper }
|
||||
});
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* *****************************************************************************
|
||||
*
|
||||
*/
|
||||
|
||||
$.ui.fancytree.registerExtension({
|
||||
name: "dnd",
|
||||
version: "2.22.5",
|
||||
// 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)
|
||||
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
|
||||
preventRecursiveMoves: true,// Prevent dropping nodes on own descendants
|
||||
smartRevert: true, // set draggable.revert = true if drop was rejected
|
||||
dropMarkerOffsetX: -24, // absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
|
||||
dropMarkerInsertOffsetX: -16, // additional offset for drop-marker with hitMode = "before"/"after"
|
||||
// Events (drag support)
|
||||
dragStart: null, // Callback(sourceNode, data), return true, to enable dnd
|
||||
dragStop: null, // Callback(sourceNode, data)
|
||||
initHelper: null, // Callback(sourceNode, data)
|
||||
updateHelper: null, // Callback(sourceNode, data)
|
||||
// Events (drop support)
|
||||
dragEnter: null, // Callback(targetNode, data)
|
||||
dragOver: null, // Callback(targetNode, data)
|
||||
dragExpand: null, // Callback(targetNode, data), return false to prevent autoExpand
|
||||
dragDrop: null, // Callback(targetNode, data)
|
||||
dragLeave: null // Callback(targetNode, data)
|
||||
},
|
||||
|
||||
treeInit: function(ctx){
|
||||
var tree = ctx.tree;
|
||||
this._superApply(arguments);
|
||||
// issue #270: draggable eats mousedown events
|
||||
if( tree.options.dnd.dragStart ){
|
||||
tree.$container.on("mousedown", function(event){
|
||||
// if( !tree.hasFocus() && ctx.options.dnd.focusOnClick ) {
|
||||
if( ctx.options.dnd.focusOnClick ) { // #270
|
||||
var node = $.ui.fancytree.getNode(event);
|
||||
if (node){
|
||||
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);
|
||||
},
|
||||
/* Display drop marker according to hitMode ('after', 'before', 'over'). */
|
||||
_setDndStatus: function(sourceNode, targetNode, helper, hitMode, accept) {
|
||||
var markerOffsetX,
|
||||
markerAt = "center",
|
||||
instData = this._local,
|
||||
dndOpt = this.options.dnd ,
|
||||
glyphOpt = this.options.glyph,
|
||||
$source = sourceNode ? $(sourceNode.span) : null,
|
||||
$target = $(targetNode.span),
|
||||
$targetTitle = $target.find("span.fancytree-title");
|
||||
|
||||
if( !instData.$dropMarker ) {
|
||||
instData.$dropMarker = $("<div id='fancytree-drop-marker'></div>")
|
||||
.hide()
|
||||
.css({"z-index": 1000})
|
||||
.prependTo($(this.$div).parent());
|
||||
// .prependTo("body");
|
||||
|
||||
if( glyphOpt ) {
|
||||
// instData.$dropMarker.addClass(glyph.map.dragHelper);
|
||||
instData.$dropMarker
|
||||
.addClass(glyphOpt.map.dropMarker);
|
||||
}
|
||||
}
|
||||
if( hitMode === "after" || hitMode === "before" || hitMode === "over" ){
|
||||
markerOffsetX = dndOpt.dropMarkerOffsetX || 0;
|
||||
switch(hitMode){
|
||||
case "before":
|
||||
markerAt = "top";
|
||||
markerOffsetX += (dndOpt.dropMarkerInsertOffsetX || 0);
|
||||
break;
|
||||
case "after":
|
||||
markerAt = "bottom";
|
||||
markerOffsetX += (dndOpt.dropMarkerInsertOffsetX || 0);
|
||||
break;
|
||||
}
|
||||
|
||||
instData.$dropMarker
|
||||
.toggleClass(classDropAfter, hitMode === "after")
|
||||
.toggleClass(classDropOver, hitMode === "over")
|
||||
.toggleClass(classDropBefore, hitMode === "before")
|
||||
.show()
|
||||
.position($.ui.fancytree.fixPositionOptions({
|
||||
my: "left" + offsetString(markerOffsetX) + " center",
|
||||
at: "left " + markerAt,
|
||||
of: $targetTitle
|
||||
}));
|
||||
} else {
|
||||
instData.$dropMarker.hide();
|
||||
}
|
||||
if( $source ){
|
||||
$source
|
||||
.toggleClass(classDropAccept, accept === true)
|
||||
.toggleClass(classDropReject, accept === false);
|
||||
}
|
||||
$target
|
||||
.toggleClass(classDropTarget, hitMode === "after" || hitMode === "before" || hitMode === "over")
|
||||
.toggleClass(classDropAfter, hitMode === "after")
|
||||
.toggleClass(classDropBefore, hitMode === "before")
|
||||
.toggleClass(classDropAccept, accept === true)
|
||||
.toggleClass(classDropReject, accept === false);
|
||||
|
||||
helper
|
||||
.toggleClass(classDropAccept, accept === true)
|
||||
.toggleClass(classDropReject, accept === false);
|
||||
},
|
||||
|
||||
/*
|
||||
* Handles drag'n'drop functionality.
|
||||
*
|
||||
* A standard jQuery drag-and-drop process may generate these calls:
|
||||
*
|
||||
* 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 accept, nodeOfs, parentRect, rect, relPos, relPos2,
|
||||
enterResponse, hitMode, r,
|
||||
opts = this.options,
|
||||
dnd = opts.dnd,
|
||||
ctx = this._makeHookContext(node, event, {otherNode: otherNode, ui: ui, draggable: draggable}),
|
||||
res = null,
|
||||
that = this,
|
||||
$nodeTag = $(node.span);
|
||||
|
||||
if( dnd.smartRevert ) {
|
||||
draggable.options.revert = "invalid";
|
||||
}
|
||||
|
||||
switch (eventName) {
|
||||
|
||||
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 cancelled, when false is returned)
|
||||
// TODO: call this._cancelDrag()?
|
||||
ui.helper.trigger("mouseup")
|
||||
.hide();
|
||||
} else {
|
||||
if( dnd.smartRevert ) {
|
||||
// #567, #593: fix revert position
|
||||
// rect = node.li.getBoundingClientRect();
|
||||
rect = node[ctx.tree.nodeContainerAttrName].getBoundingClientRect();
|
||||
parentRect = $(draggable.options.appendTo)[0].getBoundingClientRect();
|
||||
draggable.originalPosition.left = Math.max(0, rect.left - parentRect.left);
|
||||
draggable.originalPosition.top = Math.max(0, rect.top - parentRect.top);
|
||||
}
|
||||
$nodeTag.addClass("fancytree-drag-source");
|
||||
// Register global handlers to allow cancel
|
||||
$(document)
|
||||
.on("keydown.fancytree-dnd,mousedown.fancytree-dnd", function(event){
|
||||
// node.tree.debug("dnd global event", event.type, event.which);
|
||||
if( event.type === "keydown" && event.which === $.ui.keyCode.ESCAPE ) {
|
||||
that.ext.dnd._cancelDrag();
|
||||
} else if( event.type === "mousedown" ) {
|
||||
that.ext.dnd._cancelDrag();
|
||||
}
|
||||
});
|
||||
}
|
||||
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 !== "before" && hitMode !== "after" && dnd.autoExpandMS &&
|
||||
node.hasChildren() !== false && !node.expanded &&
|
||||
(!dnd.dragExpand || dnd.dragExpand(node, ctx) !== false)
|
||||
) {
|
||||
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);
|
||||
}
|
||||
accept = (res !== false && hitMode !== null);
|
||||
if( dnd.smartRevert ) {
|
||||
draggable.options.revert = !accept;
|
||||
}
|
||||
this._local._setDndStatus(otherNode, node, ui.helper, hitMode, accept);
|
||||
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");
|
||||
$(document).off(".fancytree-dnd");
|
||||
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));
|
||||
580
static/lib/fancytree/src/jquery.fancytree.dnd5.js
Normal file
580
static/lib/fancytree/src/jquery.fancytree.dnd5.js
Normal file
@@ -0,0 +1,580 @@
|
||||
/*!
|
||||
* jquery.fancytree.dnd5.js
|
||||
*
|
||||
* Drag-and-drop support (native HTML5).
|
||||
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
|
||||
*
|
||||
* Copyright (c) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
#TODO
|
||||
- glyph
|
||||
|
||||
Compatiblity when dragging between *separate* windows:
|
||||
|
||||
Drag from Chrome Edge FF IE11 Safari
|
||||
To Chrome ok ok ok NO ?
|
||||
Edge ok ok ok NO ?
|
||||
FF ok ok ok NO ?
|
||||
IE 11 ok ok ok ok ?
|
||||
Safari ? ? ? ? ok
|
||||
|
||||
*/
|
||||
|
||||
;(function($, window, document, undefined) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/* *****************************************************************************
|
||||
* Private functions and variables
|
||||
*/
|
||||
var
|
||||
classDragSource = "fancytree-drag-source",
|
||||
classDragRemove = "fancytree-drag-remove",
|
||||
classDropAccept = "fancytree-drop-accept",
|
||||
classDropAfter = "fancytree-drop-after",
|
||||
classDropBefore = "fancytree-drop-before",
|
||||
classDropOver = "fancytree-drop-over",
|
||||
classDropReject = "fancytree-drop-reject",
|
||||
classDropTarget = "fancytree-drop-target",
|
||||
nodeMimeType = "application/x-fancytree-node",
|
||||
$dropMarker = null,
|
||||
SOURCE_NODE = null,
|
||||
DRAG_ENTER_RESPONSE = null,
|
||||
LAST_HIT_MODE = null;
|
||||
|
||||
/* Convert number to string and prepend +/-; return empty string for 0.*/
|
||||
function offsetString(n){
|
||||
return n === 0 ? "" : (( n > 0 ) ? ("+" + n) : ("" + n));
|
||||
}
|
||||
|
||||
/* Convert a dragEnter() or dragOver() response to a canonical form.
|
||||
* Return false or plain object
|
||||
* @param {string|object|boolean} r
|
||||
* @return {object|false}
|
||||
*/
|
||||
function normalizeDragEnterResponse(r) {
|
||||
var res;
|
||||
|
||||
if( !r ){
|
||||
return false;
|
||||
}
|
||||
if ( $.isPlainObject(r) ) {
|
||||
res = {
|
||||
over: !!r.over,
|
||||
before: !!r.before,
|
||||
after: !!r.after
|
||||
};
|
||||
}else if ( $.isArray(r) ) {
|
||||
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"))
|
||||
};
|
||||
}
|
||||
if( Object.keys(res).length === 0 ) {
|
||||
return false;
|
||||
}
|
||||
// if( Object.keys(res).length === 1 ) {
|
||||
// res.unique = res[0];
|
||||
// }
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Implement auto scrolling when drag cursor is in top/bottom area of scroll parent. */
|
||||
function autoScroll(tree, event) {
|
||||
var spOfs, scrollTop, delta,
|
||||
dndOpts = tree.options.dnd5,
|
||||
sp = tree.$scrollParent[0],
|
||||
sensitivity = dndOpts.scrollSensitivity,
|
||||
speed = dndOpts.scrollSpeed,
|
||||
scrolled = 0;
|
||||
|
||||
if ( sp !== document && sp.tagName !== "HTML" ) {
|
||||
spOfs = tree.$scrollParent.offset();
|
||||
scrollTop = sp.scrollTop;
|
||||
if ( spOfs.top + sp.offsetHeight - event.pageY < sensitivity ) {
|
||||
delta = (sp.scrollHeight - tree.$scrollParent.innerHeight() - scrollTop);
|
||||
// console.log ("sp.offsetHeight: " + sp.offsetHeight
|
||||
// + ", spOfs.top: " + spOfs.top
|
||||
// + ", scrollTop: " + scrollTop
|
||||
// + ", innerHeight: " + tree.$scrollParent.innerHeight()
|
||||
// + ", scrollHeight: " + sp.scrollHeight
|
||||
// + ", delta: " + delta
|
||||
// );
|
||||
if( delta > 0 ) {
|
||||
sp.scrollTop = scrolled = scrollTop + speed;
|
||||
}
|
||||
} else if ( scrollTop > 0 && event.pageY - spOfs.top < sensitivity ) {
|
||||
sp.scrollTop = scrolled = scrollTop - speed;
|
||||
}
|
||||
} else {
|
||||
scrollTop = $(document).scrollTop();
|
||||
if (scrollTop > 0 && event.pageY - scrollTop < sensitivity) {
|
||||
scrolled = scrollTop - speed;
|
||||
$(document).scrollTop(scrolled);
|
||||
} else if ($(window).height() - (event.pageY - scrollTop) < sensitivity) {
|
||||
scrolled = scrollTop + speed;
|
||||
$(document).scrollTop(scrolled);
|
||||
}
|
||||
}
|
||||
if( scrolled ) {
|
||||
tree.debug("autoScroll: " + scrolled + "px");
|
||||
}
|
||||
return scrolled;
|
||||
}
|
||||
|
||||
/* Handle dragover event (fired every x ms) and return hitMode. */
|
||||
function handleDragOver(event, data) {
|
||||
// Implement auto-scrolling
|
||||
if ( data.options.dnd5.scroll ) {
|
||||
autoScroll(data.tree, event);
|
||||
}
|
||||
// Bail out with previous response if we get an invalid dragover
|
||||
if( !data.node ) {
|
||||
data.tree.warn("Ignore dragover for non-node"); //, event, data);
|
||||
return LAST_HIT_MODE;
|
||||
}
|
||||
|
||||
var markerOffsetX, nodeOfs, relPosY, //res,
|
||||
// eventHash = getEventHash(event),
|
||||
hitMode = null,
|
||||
tree = data.tree,
|
||||
options = tree.options,
|
||||
dndOpts = options.dnd5,
|
||||
targetNode = data.node,
|
||||
sourceNode = data.otherNode,
|
||||
markerAt = "center",
|
||||
// glyph = options.glyph || null,
|
||||
// $source = sourceNode ? $(sourceNode.span) : null,
|
||||
$target = $(targetNode.span),
|
||||
$targetTitle = $target.find("span.fancytree-title");
|
||||
|
||||
if(DRAG_ENTER_RESPONSE === false){
|
||||
tree.warn("Ignore dragover, since dragenter returned false"); //, event, data);
|
||||
// $.error("assert failed: dragenter returned false");
|
||||
return false;
|
||||
} else if(typeof DRAG_ENTER_RESPONSE === "string") {
|
||||
$.error("assert failed: dragenter returned string");
|
||||
// Use hitMode from onEnter if provided.
|
||||
// hitMode = DRAG_ENTER_RESPONSE;
|
||||
} else {
|
||||
// Calculate hitMode from relative cursor position.
|
||||
nodeOfs = $target.offset();
|
||||
relPosY = (event.pageY - nodeOfs.top) / $target.height();
|
||||
|
||||
if( DRAG_ENTER_RESPONSE.after && relPosY > 0.75 ){
|
||||
hitMode = "after";
|
||||
} else if(!DRAG_ENTER_RESPONSE.over && DRAG_ENTER_RESPONSE.after && relPosY > 0.5 ){
|
||||
hitMode = "after";
|
||||
} else if(DRAG_ENTER_RESPONSE.before && relPosY <= 0.25) {
|
||||
hitMode = "before";
|
||||
} else if(!DRAG_ENTER_RESPONSE.over && DRAG_ENTER_RESPONSE.before && relPosY <= 0.5) {
|
||||
hitMode = "before";
|
||||
} else if(DRAG_ENTER_RESPONSE.over) {
|
||||
hitMode = "over";
|
||||
}
|
||||
// Prevent no-ops like 'before source node'
|
||||
// TODO: these are no-ops when moving nodes, but not in copy mode
|
||||
if( dndOpts.preventVoidMoves ){
|
||||
if(targetNode === sourceNode){
|
||||
targetNode.debug("drop over source node prevented");
|
||||
hitMode = null;
|
||||
}else if(hitMode === "before" && sourceNode && targetNode === sourceNode.getNextSibling()){
|
||||
targetNode.debug("drop after source node prevented");
|
||||
hitMode = null;
|
||||
}else if(hitMode === "after" && sourceNode && targetNode === sourceNode.getPrevSibling()){
|
||||
targetNode.debug("drop before source node prevented");
|
||||
hitMode = null;
|
||||
}else if(hitMode === "over" && sourceNode && sourceNode.parent === targetNode && sourceNode.isLastSibling() ){
|
||||
targetNode.debug("drop last child over own parent prevented");
|
||||
hitMode = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Let callback modify the calculated hitMode
|
||||
data.hitMode = hitMode;
|
||||
if(hitMode && dndOpts.dragOver){
|
||||
// TODO: http://code.google.com/p/dynatree/source/detail?r=625
|
||||
dndOpts.dragOver(targetNode, data);
|
||||
hitMode = data.hitMode;
|
||||
}
|
||||
// LAST_DROP_EFFECT = data.dataTransfer.dropEffect;
|
||||
// LAST_EFFECT_ALLOWED = data.dataTransfer.effectAllowed;
|
||||
LAST_HIT_MODE = hitMode;
|
||||
//
|
||||
if( hitMode === "after" || hitMode === "before" || hitMode === "over" ){
|
||||
markerOffsetX = dndOpts.dropMarkerOffsetX || 0;
|
||||
switch(hitMode){
|
||||
case "before":
|
||||
markerAt = "top";
|
||||
markerOffsetX += (dndOpts.dropMarkerInsertOffsetX || 0);
|
||||
break;
|
||||
case "after":
|
||||
markerAt = "bottom";
|
||||
markerOffsetX += (dndOpts.dropMarkerInsertOffsetX || 0);
|
||||
break;
|
||||
}
|
||||
|
||||
$dropMarker
|
||||
.toggleClass(classDropAfter, hitMode === "after")
|
||||
.toggleClass(classDropOver, hitMode === "over")
|
||||
.toggleClass(classDropBefore, hitMode === "before")
|
||||
.show()
|
||||
.position($.ui.fancytree.fixPositionOptions({
|
||||
my: "left" + offsetString(markerOffsetX) + " center",
|
||||
at: "left " + markerAt,
|
||||
of: $targetTitle
|
||||
}));
|
||||
} else {
|
||||
$dropMarker.hide();
|
||||
// console.log("hide dropmarker")
|
||||
}
|
||||
// if( $source ){
|
||||
// $source.toggleClass(classDragRemove, isMove);
|
||||
// }
|
||||
$(targetNode.span)
|
||||
.toggleClass(classDropTarget, hitMode === "after" || hitMode === "before" || hitMode === "over")
|
||||
.toggleClass(classDropAfter, hitMode === "after")
|
||||
.toggleClass(classDropBefore, hitMode === "before")
|
||||
.toggleClass(classDropAccept, hitMode === "over")
|
||||
.toggleClass(classDropReject, hitMode === false);
|
||||
|
||||
return hitMode;
|
||||
}
|
||||
|
||||
/* *****************************************************************************
|
||||
*
|
||||
*/
|
||||
|
||||
$.ui.fancytree.registerExtension({
|
||||
name: "dnd5",
|
||||
version: "2.22.5",
|
||||
// Default options for this extension.
|
||||
options: {
|
||||
autoExpandMS: 1500, // Expand nodes after n milliseconds of hovering
|
||||
setTextTypeJson: false, // Allow dragging of nodes to different IE windows
|
||||
preventForeignNodes: false, // Prevent dropping nodes from different Fancytrees
|
||||
preventNonNodes: false, // Prevent dropping items other than Fancytree nodes
|
||||
preventRecursiveMoves: true, // Prevent dropping nodes on own descendants
|
||||
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
|
||||
scroll: true, // Enable auto-scrolling while dragging
|
||||
scrollSensitivity: 20, // Active top/bottom margin in pixel
|
||||
scrollSpeed: 5, // Pixel per event
|
||||
dropMarkerOffsetX: -24, // absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
|
||||
dropMarkerInsertOffsetX: -16,// additional offset for drop-marker with hitMode = "before"/"after"
|
||||
// Events (drag support)
|
||||
dragStart: null, // Callback(sourceNode, data), return true, to enable dnd drag
|
||||
dragDrag: $.noop, // Callback(sourceNode, data)
|
||||
dragEnd: $.noop, // Callback(sourceNode, data)
|
||||
// Events (drop support)
|
||||
dragEnter: null, // Callback(targetNode, data), return true, to enable dnd drop
|
||||
dragOver: $.noop, // Callback(targetNode, data)
|
||||
dragExpand: $.noop, // Callback(targetNode, data), return false to prevent autoExpand
|
||||
dragDrop: $.noop, // Callback(targetNode, data)
|
||||
dragLeave: $.noop // Callback(targetNode, data)
|
||||
},
|
||||
|
||||
treeInit: function(ctx){
|
||||
var tree = ctx.tree,
|
||||
opts = ctx.options,
|
||||
dndOpts = opts.dnd5,
|
||||
getNode = $.ui.fancytree.getNode;
|
||||
|
||||
if( $.inArray("dnd", opts.extensions) >= 0 ) {
|
||||
$.error("Extensions 'dnd' and 'dnd5' are mutually exclusive.");
|
||||
}
|
||||
if( dndOpts.dragStop ) {
|
||||
$.error("dragStop is not used by ext-dnd5. Use dragEnd instead.");
|
||||
}
|
||||
|
||||
// Implement `opts.createNode` event to add the 'draggable' attribute
|
||||
// #680: this must happen before calling super.treeInit()
|
||||
if( dndOpts.dragStart ) {
|
||||
$.ui.fancytree.overrideMethod(ctx.options, "createNode", function(event, data) {
|
||||
// Default processing if any
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
data.node.span.draggable = true;
|
||||
});
|
||||
}
|
||||
this._superApply(arguments);
|
||||
|
||||
this.$container.addClass("fancytree-ext-dnd5");
|
||||
|
||||
// Store the current scroll parent, which may be the tree
|
||||
// container, any enclosing div, or the document
|
||||
this.$scrollParent = this.$container.children(":first").scrollParent();
|
||||
|
||||
$dropMarker = $("#fancytree-drop-marker");
|
||||
if( !$dropMarker.length ) {
|
||||
$dropMarker = $("<div id='fancytree-drop-marker'></div>")
|
||||
.hide()
|
||||
.css({
|
||||
"z-index": 1000,
|
||||
// Drop marker should not steal dragenter/dragover events:
|
||||
"pointer-events": "none"
|
||||
}).prependTo("body");
|
||||
// if( glyph ) {
|
||||
// instData.$dropMarker
|
||||
// .addClass(glyph.map.dropMarker);
|
||||
// }
|
||||
}
|
||||
// Enable drag support if dragStart() is specified:
|
||||
if( dndOpts.dragStart ) {
|
||||
// Bind drag event handlers
|
||||
tree.$container.on("dragstart drag dragend", function(event){
|
||||
var json,
|
||||
node = getNode(event),
|
||||
dataTransfer = event.dataTransfer || event.originalEvent.dataTransfer,
|
||||
isMove = dataTransfer.dropEffect === "move",
|
||||
$source = node ? $(node.span) : null,
|
||||
data = {
|
||||
node: node,
|
||||
tree: tree,
|
||||
options: tree.options,
|
||||
originalEvent: event,
|
||||
dataTransfer: dataTransfer,
|
||||
// dropEffect: undefined, // set by dragend
|
||||
isCancelled: undefined // set by dragend
|
||||
};
|
||||
|
||||
switch( event.type ) {
|
||||
|
||||
case "dragstart":
|
||||
$(node.span).addClass(classDragSource);
|
||||
|
||||
// Store current source node in different formats
|
||||
SOURCE_NODE = node;
|
||||
|
||||
// Set payload
|
||||
// Note:
|
||||
// Transfer data is only accessible on dragstart and drop!
|
||||
// For all other events the formats and kinds in the drag
|
||||
// data store list of items representing dragged data can be
|
||||
// enumerated, but the data itself is unavailable and no new
|
||||
// data can be added.
|
||||
json = JSON.stringify(node.toDict());
|
||||
try {
|
||||
dataTransfer.setData(nodeMimeType, json);
|
||||
dataTransfer.setData("text/html", $(node.span).html());
|
||||
dataTransfer.setData("text/plain", node.title);
|
||||
} catch(ex) {
|
||||
// IE only accepts 'text' type
|
||||
tree.warn("Could not set data (IE only accepts 'text') - " + ex);
|
||||
}
|
||||
// We always need to set the 'text' type if we want to drag
|
||||
// Because IE 11 only accepts this single type.
|
||||
// If we pass JSON here, IE can can access all node properties,
|
||||
// even when the source lives in another window. (D'n'd inside
|
||||
// the same window will always work.)
|
||||
// The drawback is, that in this case ALL browsers will see
|
||||
// the JSON representation as 'text', so dragging
|
||||
// to a text field will insert the JSON string instead of
|
||||
// the node title.
|
||||
if( dndOpts.setTextTypeJson ) {
|
||||
dataTransfer.setData("text", json);
|
||||
} else {
|
||||
dataTransfer.setData("text", node.title);
|
||||
}
|
||||
|
||||
// Set the allowed and current drag mode (move, copy, or link)
|
||||
dataTransfer.effectAllowed = "all"; // "copyMove"
|
||||
// dataTransfer.dropEffect = "move";
|
||||
|
||||
// Set the title as drag image (otherwise it would contain the expander)
|
||||
if( dataTransfer.setDragImage ) {
|
||||
// IE 11 does not support this
|
||||
dataTransfer.setDragImage($(node.span).find(".fancytree-title")[0], -10, -10);
|
||||
// dataTransfer.setDragImage($(node.span)[0], -10, -10);
|
||||
}
|
||||
// Let user modify above settings
|
||||
return dndOpts.dragStart(node, data) !== false;
|
||||
|
||||
case "drag":
|
||||
// Called every few miliseconds
|
||||
$source.toggleClass(classDragRemove, isMove);
|
||||
dndOpts.dragDrag(node, data);
|
||||
break;
|
||||
|
||||
case "dragend":
|
||||
$(node.span).removeClass(classDragSource + " " + classDragRemove);
|
||||
SOURCE_NODE = null;
|
||||
DRAG_ENTER_RESPONSE = null;
|
||||
// data.dropEffect = dataTransfer.dropEffect;
|
||||
data.isCancelled = (dataTransfer.dropEffect === "none");
|
||||
$dropMarker.hide();
|
||||
dndOpts.dragEnd(node, data);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
// Enable drop support if dragEnter() is specified:
|
||||
if( dndOpts.dragEnter ) {
|
||||
// Bind drop event handlers
|
||||
tree.$container.on("dragenter dragover dragleave drop", function(event){
|
||||
var json, nodeData, r, res,
|
||||
allowDrop = null,
|
||||
node = getNode(event),
|
||||
dataTransfer = event.dataTransfer || event.originalEvent.dataTransfer,
|
||||
// glyph = opts.glyph || null,
|
||||
data = {
|
||||
node: node,
|
||||
tree: tree,
|
||||
options: tree.options,
|
||||
hitMode: DRAG_ENTER_RESPONSE,
|
||||
originalEvent: event,
|
||||
dataTransfer: dataTransfer,
|
||||
otherNode: SOURCE_NODE || null,
|
||||
otherNodeData: null, // set by drop event
|
||||
dropEffect: undefined, // set by drop event
|
||||
isCancelled: undefined // set by drop event
|
||||
};
|
||||
|
||||
switch( event.type ) {
|
||||
|
||||
case "dragenter":
|
||||
// The dragenter event is fired when a dragged element or
|
||||
// text selection enters a valid drop target.
|
||||
|
||||
if( !node ) {
|
||||
// Sometimes we get dragenter for the container element
|
||||
tree.debug("Ignore non-node " + event.type + ": " + event.target.tagName + "." + event.target.className);
|
||||
DRAG_ENTER_RESPONSE = false;
|
||||
break;
|
||||
}
|
||||
|
||||
$(node.span)
|
||||
.addClass(classDropOver)
|
||||
.removeClass(classDropAccept + " " + classDropReject);
|
||||
|
||||
if( dndOpts.preventNonNodes && !nodeData ) {
|
||||
node.debug("Reject dropping a non-node");
|
||||
DRAG_ENTER_RESPONSE = false;
|
||||
break;
|
||||
} else if( dndOpts.preventForeignNodes && (!SOURCE_NODE || SOURCE_NODE.tree !== node.tree ) ) {
|
||||
node.debug("Reject dropping a foreign node");
|
||||
DRAG_ENTER_RESPONSE = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: dragenter is fired BEFORE the dragleave event
|
||||
// of the previous element!
|
||||
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=19041
|
||||
setTimeout(function(){
|
||||
// node.info("DELAYED " + event.type, event.target, DRAG_ENTER_RESPONSE);
|
||||
// Auto-expand node (only when 'over' the node, not 'before', or 'after')
|
||||
if( dndOpts.autoExpandMS &&
|
||||
node.hasChildren() !== false && !node.expanded &&
|
||||
(!dndOpts.dragExpand || dndOpts.dragExpand(node, data) !== false)
|
||||
) {
|
||||
node.scheduleAction("expand", dndOpts.autoExpandMS);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
$dropMarker.show();
|
||||
|
||||
// Call dragEnter() to figure out if (and where) dropping is allowed
|
||||
if( dndOpts.preventRecursiveMoves && node.isDescendantOf(data.otherNode) ){
|
||||
res = false;
|
||||
}else{
|
||||
r = dndOpts.dragEnter(node, data);
|
||||
res = normalizeDragEnterResponse(r);
|
||||
}
|
||||
DRAG_ENTER_RESPONSE = res;
|
||||
|
||||
allowDrop = res && ( res.over || res.before || res.after );
|
||||
break;
|
||||
|
||||
case "dragover":
|
||||
// The dragover event is fired when an element or text
|
||||
// selection is being dragged over a valid drop target
|
||||
// (every few hundred milliseconds).
|
||||
LAST_HIT_MODE = handleDragOver(event, data);
|
||||
allowDrop = !!LAST_HIT_MODE;
|
||||
break;
|
||||
|
||||
case "dragleave":
|
||||
// NOTE: dragleave is fired AFTER the dragenter event of the
|
||||
// FOLLOWING element.
|
||||
if( !node ) {
|
||||
tree.debug("Ignore non-node " + event.type + ": " + event.target.tagName + "." + event.target.className);
|
||||
break;
|
||||
}
|
||||
if( !$(node.span).hasClass(classDropOver) ) {
|
||||
node.debug("Ignore dragleave (multi)"); //, event.currentTarget);
|
||||
break;
|
||||
}
|
||||
$(node.span).removeClass(classDropOver + " " + classDropAccept + " " + classDropReject);
|
||||
node.scheduleAction("cancel");
|
||||
dndOpts.dragLeave(node, data);
|
||||
$dropMarker.hide();
|
||||
break;
|
||||
|
||||
case "drop":
|
||||
// Data is only readable in the (dragenter and) drop event:
|
||||
|
||||
if( $.inArray(nodeMimeType, dataTransfer.types) >= 0 ) {
|
||||
nodeData = dataTransfer.getData(nodeMimeType);
|
||||
tree.info(event.type + ": getData('application/x-fancytree-node'): '" + nodeData + "'");
|
||||
}
|
||||
if( !nodeData ) {
|
||||
// 1. Source is not a Fancytree node, or
|
||||
// 2. If the FT mime type was set, but returns '', this
|
||||
// is probably IE 11 (which only supports 'text')
|
||||
nodeData = dataTransfer.getData("text");
|
||||
tree.info(event.type + ": getData('text'): '" + nodeData + "'");
|
||||
}
|
||||
if( nodeData ) {
|
||||
try {
|
||||
// 'text' type may contain JSON if IE is involved
|
||||
// and setTextTypeJson option was set
|
||||
json = JSON.parse(nodeData);
|
||||
if( json.title !== undefined ) {
|
||||
data.otherNodeData = json;
|
||||
}
|
||||
} catch(ex) {
|
||||
// assume 'text' type contains plain text, so `otherNodeData`
|
||||
// should not be set
|
||||
}
|
||||
}
|
||||
tree.debug(event.type + ": nodeData: '" + nodeData + "', otherNodeData: ", data.otherNodeData);
|
||||
|
||||
$(node.span).removeClass(classDropOver + " " + classDropAccept + " " + classDropReject);
|
||||
$dropMarker.hide();
|
||||
|
||||
data.hitMode = LAST_HIT_MODE;
|
||||
data.dropEffect = dataTransfer.dropEffect;
|
||||
data.isCancelled = data.dropEffect === "none";
|
||||
|
||||
// Let user implement the actual drop operation
|
||||
dndOpts.dragDrop(node, data);
|
||||
|
||||
// Prevent browser's default drop handling
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
// Dnd API madness: we must PREVENT default handling to enable dropping
|
||||
if( allowDrop ) {
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
315
static/lib/fancytree/src/jquery.fancytree.edit.js
Normal file
315
static/lib/fancytree/src/jquery.fancytree.edit.js
Normal file
@@ -0,0 +1,315 @@
|
||||
/*!
|
||||
* jquery.fancytree.edit.js
|
||||
*
|
||||
* Make node titles editable.
|
||||
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
|
||||
*
|
||||
* Copyright (c) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(function($, window, document, undefined) {
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions and variables
|
||||
*/
|
||||
|
||||
var isMac = /Mac/.test(navigator.platform),
|
||||
escapeHtml = $.ui.fancytree.escapeHtml,
|
||||
unescapeHtml = $.ui.fancytree.unescapeHtml;
|
||||
|
||||
/**
|
||||
* [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[tree.statusClassPropName]).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: tree.options.escapeTitles ? eventData.orgTitle : 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");
|
||||
|
||||
if( instOpts.trim ) {
|
||||
$input.val($.trim($input.val()));
|
||||
}
|
||||
newVal = $input.val();
|
||||
|
||||
eventData.dirty = ( newVal !== node.title );
|
||||
eventData.originalEvent = _event;
|
||||
|
||||
// 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")
|
||||
.off();
|
||||
// Unbind outer-click handler
|
||||
$(document).off(".fancytree-edit");
|
||||
|
||||
if( eventData.save ) {
|
||||
// # 171: escape user input (not required if global escaping is on)
|
||||
node.setTitle( tree.options.escapeTitles ? newVal : escapeHtml(newVal) );
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* [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
|
||||
* @since 2.4
|
||||
*/
|
||||
$.ui.fancytree._FancytreeNodeClass.prototype.editCreateNode = function(mode, init){
|
||||
var newNode,
|
||||
tree = this.tree,
|
||||
self = 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(){
|
||||
self.editCreateNode(mode, init);
|
||||
});
|
||||
return;
|
||||
}
|
||||
newNode = this.addNode(init, mode);
|
||||
|
||||
// #644: Don't filter new nodes.
|
||||
newNode.match = true;
|
||||
$(newNode[tree.statusClassPropName])
|
||||
.removeClass("fancytree-hide")
|
||||
.addClass("fancytree-match");
|
||||
|
||||
newNode.makeVisible(/*{noAnimation: true}*/).done(function(){
|
||||
$(newNode[tree.statusClassPropName]).addClass("fancytree-edit-new");
|
||||
self.tree.ext.edit.relatedNode = self;
|
||||
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 ? this.ext.edit.currentNode : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* [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 ? this.tree.ext.edit.currentNode === this : false;
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Extension code
|
||||
*/
|
||||
$.ui.fancytree.registerExtension({
|
||||
name: "edit",
|
||||
version: "2.22.5",
|
||||
// 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._superApply(arguments);
|
||||
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._superApply(arguments);
|
||||
},
|
||||
nodeDblclick: function(ctx) {
|
||||
if( $.inArray("dblclick", ctx.options.edit.triggerStart) >= 0 ){
|
||||
ctx.node.editStart();
|
||||
return false;
|
||||
}
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
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._superApply(arguments);
|
||||
}
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
353
static/lib/fancytree/src/jquery.fancytree.filter.js
Normal file
353
static/lib/fancytree/src/jquery.fancytree.filter.js
Normal file
@@ -0,0 +1,353 @@
|
||||
/*!
|
||||
* 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) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(function($, window, document, undefined) {
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions and variables
|
||||
*/
|
||||
|
||||
var KeyNoData = "__not_found__",
|
||||
escapeHtml = $.ui.fancytree.escapeHtml;
|
||||
|
||||
function _escapeRegex(str){
|
||||
/*jshint regexdash:true */
|
||||
return (str + "").replace(/([.?*+\^\$\[\]\\(){}|-])/g, "\\$1");
|
||||
}
|
||||
|
||||
function extractHtmlText(s){
|
||||
if( s.indexOf(">") >= 0 ) {
|
||||
return $("<div/>").html(s).text();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
$.ui.fancytree._FancytreeClass.prototype._applyFilterImpl = function(filter, branchMode, _opts){
|
||||
var match, statusNode, re, reHighlight,
|
||||
count = 0,
|
||||
treeOpts = this.options,
|
||||
escapeTitles = treeOpts.escapeTitles,
|
||||
prevAutoCollapse = treeOpts.autoCollapse,
|
||||
opts = $.extend({}, treeOpts.filter, _opts),
|
||||
hideMode = opts.mode === "hide",
|
||||
leavesOnly = !!opts.leavesOnly && !branchMode;
|
||||
|
||||
// Default to 'match title substring (not case sensitive)'
|
||||
if(typeof filter === "string"){
|
||||
// console.log("rex", filter.split('').join('\\w*').replace(/\W/, ""))
|
||||
if( opts.fuzzy ) {
|
||||
// See https://codereview.stackexchange.com/questions/23899/faster-javascript-fuzzy-string-matching-function/23905#23905
|
||||
// and http://www.quora.com/How-is-the-fuzzy-search-algorithm-in-Sublime-Text-designed
|
||||
// and http://www.dustindiaz.com/autocomplete-fuzzy-matching
|
||||
match = filter.split("").reduce(function(a, b) {
|
||||
return a + "[^" + b + "]*" + b;
|
||||
});
|
||||
} else {
|
||||
match = _escapeRegex(filter); // make sure a '.' is treated literally
|
||||
}
|
||||
re = new RegExp(".*" + match + ".*", "i");
|
||||
reHighlight = new RegExp(_escapeRegex(filter), "gi");
|
||||
filter = function(node){
|
||||
var display,
|
||||
text = escapeTitles ? node.title : extractHtmlText(node.title),
|
||||
res = !!re.test(text);
|
||||
|
||||
if( res && opts.highlight ) {
|
||||
display = escapeTitles ? escapeHtml(node.title) : text;
|
||||
node.titleWithHighlight = display.replace(reHighlight, function(s){
|
||||
return "<mark>" + s + "</mark>";
|
||||
});
|
||||
// node.debug("filter", escapeTitles, text, node.titleWithHighlight);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
this.$div.toggleClass("fancytree-ext-filter-hide-expanders", !!opts.hideExpanders);
|
||||
// Reset current filter
|
||||
this.visit(function(node){
|
||||
delete node.match;
|
||||
delete node.titleWithHighlight;
|
||||
node.subMatchCount = 0;
|
||||
});
|
||||
statusNode = this.getRootNode()._findDirectChild(KeyNoData);
|
||||
if( statusNode ) {
|
||||
statusNode.remove();
|
||||
}
|
||||
|
||||
// Adjust node.hide, .match, and .subMatchCount properties
|
||||
treeOpts.autoCollapse = false; // #528
|
||||
|
||||
this.visit(function(node){
|
||||
if ( leavesOnly && node.children != null ) {
|
||||
return;
|
||||
}
|
||||
var res = filter(node),
|
||||
matchedByBranch = false;
|
||||
|
||||
if( res === "skip" ) {
|
||||
node.visit(function(c){
|
||||
c.match = false;
|
||||
}, true);
|
||||
return "skip";
|
||||
}
|
||||
if( !res && (branchMode || res === "branch") && node.parent.match ) {
|
||||
res = true;
|
||||
matchedByBranch = true;
|
||||
}
|
||||
if( res ) {
|
||||
count++;
|
||||
node.match = true;
|
||||
node.visitParents(function(p){
|
||||
p.subMatchCount += 1;
|
||||
// Expand match (unless this is no real match, but only a node in a matched branch)
|
||||
if( opts.autoExpand && !matchedByBranch && !p.expanded ) {
|
||||
p.setExpanded(true, {noAnimation: true, noEvents: true, scrollIntoView: false});
|
||||
p._filterAutoExpanded = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
treeOpts.autoCollapse = prevAutoCollapse;
|
||||
|
||||
if( count === 0 && opts.nodata && hideMode ) {
|
||||
statusNode = opts.nodata;
|
||||
if( $.isFunction(statusNode) ) {
|
||||
statusNode = statusNode();
|
||||
}
|
||||
if( statusNode === true ) {
|
||||
statusNode = {};
|
||||
} else if( typeof statusNode === "string" ) {
|
||||
statusNode = { title: statusNode };
|
||||
}
|
||||
statusNode = $.extend({
|
||||
statusNodeType: "nodata",
|
||||
key: KeyNoData,
|
||||
title: this.options.strings.noData
|
||||
}, statusNode);
|
||||
|
||||
this.getRootNode().addNode(statusNode).match = true;
|
||||
}
|
||||
// Redraw whole tree
|
||||
this.render();
|
||||
return count;
|
||||
};
|
||||
|
||||
/**
|
||||
* [ext-filter] Dimm or hide nodes.
|
||||
*
|
||||
* @param {function | string} filter
|
||||
* @param {boolean} [opts={autoExpand: false, leavesOnly: false}]
|
||||
* @returns {integer} count
|
||||
* @alias Fancytree#filterNodes
|
||||
* @requires jquery.fancytree.filter.js
|
||||
*/
|
||||
$.ui.fancytree._FancytreeClass.prototype.filterNodes = function(filter, opts) {
|
||||
if( typeof opts === "boolean" ) {
|
||||
opts = { leavesOnly: opts };
|
||||
this.warn("Fancytree.filterNodes() leavesOnly option is deprecated since 2.9.0 / 2015-04-19. Use opts.leavesOnly instead.");
|
||||
}
|
||||
return this._applyFilterImpl(filter, false, opts);
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
$.ui.fancytree._FancytreeClass.prototype.applyFilter = function(filter){
|
||||
this.warn("Fancytree.applyFilter() is deprecated since 2.1.0 / 2014-05-29. Use .filterNodes() instead.");
|
||||
return this.filterNodes.apply(this, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
* [ext-filter] Dimm or hide whole branches.
|
||||
*
|
||||
* @param {function | string} filter
|
||||
* @param {boolean} [opts={autoExpand: false}]
|
||||
* @returns {integer} count
|
||||
* @alias Fancytree#filterBranches
|
||||
* @requires jquery.fancytree.filter.js
|
||||
*/
|
||||
$.ui.fancytree._FancytreeClass.prototype.filterBranches = function(filter, opts){
|
||||
return this._applyFilterImpl(filter, true, opts);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* [ext-filter] Reset the filter.
|
||||
*
|
||||
* @alias Fancytree#clearFilter
|
||||
* @requires jquery.fancytree.filter.js
|
||||
*/
|
||||
$.ui.fancytree._FancytreeClass.prototype.clearFilter = function(){
|
||||
var $title,
|
||||
statusNode = this.getRootNode()._findDirectChild(KeyNoData),
|
||||
escapeTitles = this.options.escapeTitles,
|
||||
enhanceTitle = this.options.enhanceTitle;
|
||||
|
||||
if( statusNode ) {
|
||||
statusNode.remove();
|
||||
}
|
||||
this.visit(function(node){
|
||||
if( node.match && node.span ) { // #491, #601
|
||||
$title = $(node.span).find(">span.fancytree-title");
|
||||
if( escapeTitles ) {
|
||||
$title.text(node.title);
|
||||
} else {
|
||||
$title.html(node.title);
|
||||
}
|
||||
if( enhanceTitle ) {
|
||||
enhanceTitle({type: "enhanceTitle"}, {node: node, $title: $title});
|
||||
}
|
||||
}
|
||||
delete node.match;
|
||||
delete node.subMatchCount;
|
||||
delete node.titleWithHighlight;
|
||||
if ( node.$subMatchBadge ) {
|
||||
node.$subMatchBadge.remove();
|
||||
delete node.$subMatchBadge;
|
||||
}
|
||||
if( node._filterAutoExpanded && node.expanded ) {
|
||||
node.setExpanded(false, {noAnimation: true, noEvents: true, scrollIntoView: false});
|
||||
}
|
||||
delete node._filterAutoExpanded;
|
||||
});
|
||||
this.enableFilter = false;
|
||||
this.lastFilterArgs = null;
|
||||
this.$div.removeClass("fancytree-ext-filter fancytree-ext-filter-dimm fancytree-ext-filter-hide");
|
||||
this.render();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* [ext-filter] Return true if a filter is currently applied.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* @alias Fancytree#isFilterActive
|
||||
* @requires jquery.fancytree.filter.js
|
||||
* @since 2.13
|
||||
*/
|
||||
$.ui.fancytree._FancytreeClass.prototype.isFilterActive = function(){
|
||||
return !!this.enableFilter;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* [ext-filter] Return true if this node is matched by current filter (or no filter is active).
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* @alias FancytreeNode#isMatched
|
||||
* @requires jquery.fancytree.filter.js
|
||||
* @since 2.13
|
||||
*/
|
||||
$.ui.fancytree._FancytreeNodeClass.prototype.isMatched = function(){
|
||||
return !(this.tree.enableFilter && !this.match);
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Extension code
|
||||
*/
|
||||
$.ui.fancytree.registerExtension({
|
||||
name: "filter",
|
||||
version: "2.22.5",
|
||||
// Default options for this extension.
|
||||
options: {
|
||||
autoApply: true, // Re-apply last filter if lazy data is loaded
|
||||
autoExpand: false, // Expand all branches that contain matches while filtered
|
||||
counter: true, // Show a badge with number of matching child nodes near parent icons
|
||||
fuzzy: false, // Match single characters in order, e.g. 'fb' will match 'FooBar'
|
||||
hideExpandedCounter: true, // Hide counter badge if parent is expanded
|
||||
hideExpanders: false, // Hide expanders if all child nodes are hidden by filter
|
||||
highlight: true, // Highlight matches by wrapping inside <mark> tags
|
||||
leavesOnly: false, // Match end nodes only
|
||||
nodata: true, // Display a 'no data' status node if result is empty
|
||||
mode: "dimm" // Grayout unmatched nodes (pass "hide" to remove unmatched node instead)
|
||||
},
|
||||
nodeLoadChildren: function(ctx, source) {
|
||||
return this._superApply(arguments).done(function() {
|
||||
if( ctx.tree.enableFilter && ctx.tree.lastFilterArgs && ctx.options.filter.autoApply ) {
|
||||
ctx.tree._applyFilterImpl.apply(ctx.tree, ctx.tree.lastFilterArgs);
|
||||
}
|
||||
});
|
||||
},
|
||||
nodeSetExpanded: function(ctx, flag, callOpts) {
|
||||
delete ctx.node._filterAutoExpanded;
|
||||
// Make sure counter badge is displayed again, when node is beeing collapsed
|
||||
if( !flag && ctx.options.filter.hideExpandedCounter && ctx.node.$subMatchBadge ) {
|
||||
ctx.node.$subMatchBadge.show();
|
||||
}
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
nodeRenderStatus: function(ctx) {
|
||||
// Set classes for current status
|
||||
var res,
|
||||
node = ctx.node,
|
||||
tree = ctx.tree,
|
||||
opts = ctx.options.filter,
|
||||
$title = $(node.span).find("span.fancytree-title"),
|
||||
$span = $(node[tree.statusClassPropName]),
|
||||
enhanceTitle = ctx.options.enhanceTitle,
|
||||
escapeTitles = ctx.options.escapeTitles;
|
||||
|
||||
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.subMatchCount)
|
||||
.toggleClass("fancytree-hide", !(node.match || node.subMatchCount));
|
||||
// Add/update counter badge
|
||||
if( opts.counter && node.subMatchCount && (!node.isExpanded() || !opts.hideExpandedCounter) ) {
|
||||
if( !node.$subMatchBadge ) {
|
||||
node.$subMatchBadge = $("<span class='fancytree-childcounter'/>");
|
||||
$("span.fancytree-icon, span.fancytree-custom-icon", node.span).append(node.$subMatchBadge);
|
||||
}
|
||||
node.$subMatchBadge.show().text(node.subMatchCount);
|
||||
} else if ( node.$subMatchBadge ) {
|
||||
node.$subMatchBadge.hide();
|
||||
}
|
||||
// node.debug("nodeRenderStatus", node.titleWithHighlight, node.title)
|
||||
// #601: also chek for $title.length, because we don't need to render
|
||||
// if node.span is null (i.e. not rendered)
|
||||
if( node.span && (!node.isEditing || !node.isEditing.call(node)) ) {
|
||||
if( node.titleWithHighlight ) {
|
||||
$title.html(node.titleWithHighlight);
|
||||
} else if ( escapeTitles ) {
|
||||
$title.text(node.title);
|
||||
} else {
|
||||
$title.html(node.title);
|
||||
}
|
||||
if( enhanceTitle ) {
|
||||
enhanceTitle({type: "enhanceTitle"}, {node: node, $title: $title});
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
542
static/lib/fancytree/src/jquery.fancytree.fixed.js
Normal file
542
static/lib/fancytree/src/jquery.fancytree.fixed.js
Normal file
@@ -0,0 +1,542 @@
|
||||
/*!
|
||||
* jquery.fancytree.fixed.js
|
||||
*
|
||||
* Add fixed colums and headers to ext.table.
|
||||
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
|
||||
*
|
||||
* Copyright (c) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(function($, window, document, undefined) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/* *****************************************************************************
|
||||
* Private functions and variables
|
||||
*/
|
||||
|
||||
|
||||
$.ui.fancytree.registerExtension({
|
||||
name: "fixed",
|
||||
version: "0.0.1",
|
||||
// Default options for this extension.
|
||||
options: {
|
||||
fixCol: 1,
|
||||
fixColWidths: null,
|
||||
fixRows: true
|
||||
},
|
||||
// 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){
|
||||
this._requireExtension("table", true, true);
|
||||
// 'fixed' requires the table extension to be loaded before itself
|
||||
|
||||
var _this = this,
|
||||
res = this._superApply(arguments),
|
||||
tree = ctx.tree,
|
||||
options = this.options.fixed,
|
||||
$table = tree.widget.element,
|
||||
fixedColCount = options.fixCols,
|
||||
fixedRowCount = options.fixRows,
|
||||
$tableWrapper = $table.parent().addClass("fancytree-ext-fixed-wrapper"),
|
||||
$topLeftWrapper = $("<div>").addClass("fancytree-fixed-wrapper-tl"),
|
||||
$topRightWrapper = $("<div>").addClass("fancytree-fixed-wrapper-tr"),
|
||||
$bottomLeftWrapper = $("<div>").addClass("fancytree-fixed-wrapper-bl"),
|
||||
$bottomRightWrapper = $("<div>").addClass("fancytree-fixed-wrapper-br"),
|
||||
$topLeftTable = $("<table>").attr("style", $table.attr("style")).attr("class", $table.attr("class")),
|
||||
$topRightTable = $("<table>").attr("style", $table.attr("style")).attr("class", $table.attr("class")),
|
||||
$bottomLeftTable = $("<table>").attr("style", $table.attr("style")).attr("class", $table.attr("class")),
|
||||
$bottomRightTable = $table,
|
||||
$head = $table.find("thead"),
|
||||
// $body = $table.find("tbody"),
|
||||
$colgroup = $table.find("colgroup"),
|
||||
headRowCount = $head.find("tr").length;
|
||||
// remainingHeadRows = headRowCount - fixedRowCount;
|
||||
|
||||
$table.addClass("fancytree-ext-fixed");
|
||||
$tableWrapper.addClass("fancytree-fixed-wrapper");
|
||||
$bottomLeftTable.append($("<tbody>"));
|
||||
|
||||
if ($colgroup.length) {
|
||||
$colgroup.remove();
|
||||
}
|
||||
|
||||
if (typeof fixedRowCount === "boolean") {
|
||||
fixedRowCount = fixedRowCount ? headRowCount : 0;
|
||||
} else {
|
||||
fixedRowCount = Math.max(0, Math.min(fixedRowCount, headRowCount));
|
||||
}
|
||||
|
||||
if (fixedRowCount) {
|
||||
$topLeftTable.append($head.clone());
|
||||
$topRightTable.append($head.clone());
|
||||
$head.hide();
|
||||
}
|
||||
|
||||
// if (remainingHeadRows > 0) {
|
||||
// var $remainingHeadRows = $body.find("tr:lt(" + remainingHeadRows + ")").clone();
|
||||
// $topLeftTable.append($("<tbody>").append($remainingHeadRows));
|
||||
// $topRightTable.append($("<tbody>").append($remainingHeadRows.clone()));
|
||||
// }
|
||||
|
||||
$topLeftTable.find("tr").each(function(idx) {
|
||||
var $tr = $(this);
|
||||
$tr.find("th").slice(fixedColCount).remove();
|
||||
$tr.find("td").slice(fixedColCount).remove();
|
||||
});
|
||||
|
||||
$topRightTable.find("tr").each(function(idx) {
|
||||
var $tr = $(this);
|
||||
$tr.find("th").slice(0, fixedColCount).addClass("fancytree-fixed-hidden");
|
||||
$tr.find("td").slice(0, fixedColCount).addClass("fancytree-fixed-hidden");
|
||||
});
|
||||
|
||||
// if (remainingHeadRows < 0) {
|
||||
// var headTrCount = $head.find("tr").length;
|
||||
// $bottomLeftTable.append($head.clone());
|
||||
//
|
||||
// $bottomLeftTable.find("thead tr:lt(" + (headTrCount + remainingHeadRows) + ")").addClass("hidden").find("td, th").addClass("hidden");
|
||||
// $bottomRightTable.find("thead tr:lt(" + (headTrCount + remainingHeadRows) + ")").addClass("hidden").find("td, th").addClass("hidden");
|
||||
//
|
||||
// $topLeftTable.find("thead tr:gt(" + (headTrCount + remainingHeadRows-1) + ")").remove();
|
||||
// $topRightTable.find("thead tr:gt(" + (headTrCount + remainingHeadRows-1) + ")").remove();
|
||||
// } else {
|
||||
// $bottomRightTable.find("thead").addClass("hidden").find("tr, th").addClass("hidden");
|
||||
// $bottomRightTable.find("tbody tr:lt(" + remainingHeadRows + ")").addClass("hidden");
|
||||
// }
|
||||
|
||||
|
||||
this.$fixedWrapper = $tableWrapper;
|
||||
|
||||
$tableWrapper.append(
|
||||
$topLeftWrapper.append(
|
||||
$topLeftTable
|
||||
),
|
||||
$topRightWrapper.append(
|
||||
$topRightTable
|
||||
),
|
||||
$bottomLeftWrapper.append(
|
||||
$bottomLeftTable
|
||||
),
|
||||
$bottomRightWrapper.append(
|
||||
$bottomRightTable
|
||||
)
|
||||
);
|
||||
|
||||
$.ui.fancytree.overrideMethod(ctx.options, "createNode", function(event, data) {
|
||||
this._super.apply(this, arguments);
|
||||
var node = data.node,
|
||||
$nodeTr = $(node.tr),
|
||||
idx = $nodeTr.index(),
|
||||
$blTableBody = $("div.fancytree-fixed-wrapper-bl table tbody"),
|
||||
$prevLeftNode = $blTableBody.find("tr:eq(" + Math.max(idx - 1, 0) + ")"),
|
||||
$clone = $nodeTr.clone();
|
||||
|
||||
if ($prevLeftNode.length) {
|
||||
$prevLeftNode.after($clone);
|
||||
} else {
|
||||
$blTableBody.append($clone);
|
||||
}
|
||||
var span = $clone.find("span.fancytree-node").get(0);
|
||||
node.span = span;
|
||||
$nodeTr.data("fancytree-node-counterpart", $clone);
|
||||
$clone.data("fancytree-node-counterpart", $nodeTr);
|
||||
$clone.find("th").slice(fixedColCount).remove();
|
||||
$clone.find("td").slice(fixedColCount).remove();
|
||||
$clone.show();
|
||||
$nodeTr.find("th").slice(0, fixedColCount).addClass("fancytree-fixed-hidden");
|
||||
$nodeTr.find("td").slice(0, fixedColCount).addClass("fancytree-fixed-hidden");
|
||||
});
|
||||
|
||||
$tableWrapper.on("click", ".fancytree-fixed-wrapper-bl table tr", function(evt) {
|
||||
var $trLeft = $(this),
|
||||
$trRight = $trLeft.data("fancytree-node-counterpart"),
|
||||
node = $.ui.fancytree.getNode($trRight);
|
||||
if (!node.isActive()) {
|
||||
node.setActive(true);
|
||||
}
|
||||
|
||||
}).on("mouseenter", ".fancytree-fixed-wrapper-bl table tr, .fancytree-fixed-wrapper-br table tr", function(evt) {
|
||||
var $tr = $(this),
|
||||
$trOther = $tr.data("fancytree-node-counterpart");
|
||||
$tr.addClass("fancytree-hover");
|
||||
$trOther.addClass("fancytree-hover");
|
||||
|
||||
}).on("mouseleave", ".fancytree-fixed-wrapper-bl table tr, .fancytree-fixed-wrapper-br table tr", function(evt) {
|
||||
var $tr = $(this),
|
||||
$trOther = $tr.data("fancytree-node-counterpart");
|
||||
$tr.removeClass("fancytree-hover");
|
||||
$trOther.removeClass("fancytree-hover");
|
||||
|
||||
}).on("click", ".fancytree-fixed-wrapper-bl .fancytree-expander", function(evt) {
|
||||
var $trLeft = $(this).closest("tr"),
|
||||
$trRight = $trLeft.data("fancytree-node-counterpart"),
|
||||
node = $.ui.fancytree.getNode($trRight),
|
||||
rootCtx = $.extend({}, ctx, {node: node});
|
||||
_this.nodeSetExpanded(rootCtx, !node.expanded).done(function(){});
|
||||
|
||||
}).on("click", ".fancytree-fixed-wrapper-bl .fancytree-checkbox", function(evt) {
|
||||
var $trLeft = $(this).closest("tr"),
|
||||
$trRight = $trLeft.data("fancytree-node-counterpart"),
|
||||
node = $.ui.fancytree.getNode($trRight);
|
||||
// rootCtx = $.extend({}, ctx, {node: node});
|
||||
node.setSelected(!node.selected);
|
||||
});
|
||||
|
||||
$bottomLeftWrapper.bind("mousewheel DOMMouseScroll", function(event) {
|
||||
var $this = $(this),
|
||||
newScroll = $this.scrollTop();
|
||||
|
||||
if (event.originalEvent.wheelDelta) {
|
||||
newScroll -= event.originalEvent.wheelDelta / 2;
|
||||
} else if (event.originalEvent.detail) {
|
||||
newScroll += event.originalEvent.detail * 14;
|
||||
}
|
||||
$this.scrollTop(newScroll);
|
||||
$bottomRightWrapper.scrollTop(newScroll);
|
||||
});
|
||||
|
||||
$bottomRightWrapper.scroll(function() {
|
||||
var $this = $(this),
|
||||
scrollLeft = $this.scrollLeft(),
|
||||
scrollTop = $this.scrollTop();
|
||||
|
||||
$topLeftWrapper
|
||||
.toggleClass("scrollBorderBottom", scrollTop > 0)
|
||||
.toggleClass("scrollBorderRight", scrollLeft > 0);
|
||||
$topRightWrapper
|
||||
.toggleClass("scrollBorderBottom", scrollTop > 0)
|
||||
.scrollLeft(scrollLeft);
|
||||
$bottomLeftWrapper
|
||||
.toggleClass("scrollBorderRight", scrollLeft > 0)
|
||||
.scrollTop(scrollTop);
|
||||
});
|
||||
|
||||
return res;
|
||||
},
|
||||
treeLoad: function(ctx) {
|
||||
var _this = this,
|
||||
res = this._superApply(arguments);
|
||||
|
||||
res.done(function() {
|
||||
_this.ext.fixed._adjustLayout();
|
||||
});
|
||||
return res;
|
||||
},
|
||||
/* Called by nodeRender to sync node order with tag order.*/
|
||||
// nodeFixOrder: function(ctx) {
|
||||
// },
|
||||
|
||||
nodeLoadChildren: function(ctx, source) {
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
nodeRemoveChildMarkup: function(ctx) {
|
||||
var node = ctx.node;
|
||||
var children = node.children;
|
||||
if (children) {
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = children[i];
|
||||
var $leftTr = $(child.tr).data("fancytree-node-counterpart");
|
||||
if ($leftTr) {
|
||||
$leftTr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
nodeRemoveMarkup: function(ctx) {
|
||||
var node = ctx.node,
|
||||
$rightNode = $(node.tr),
|
||||
$leftNode = $rightNode.data("fancytree-node-counterpart");
|
||||
if ($leftNode) {
|
||||
$leftNode.remove();
|
||||
}
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
nodeSetActive: function(ctx, flag, callOpts) {
|
||||
var node = ctx.node,
|
||||
$rightNode = $(node.tr),
|
||||
$leftNode = $rightNode.data("fancytree-node-counterpart");
|
||||
if ($leftNode) {
|
||||
$leftNode.toggleClass("fancytree-active", flag);
|
||||
$leftNode.toggleClass("fancytree-focused", flag);
|
||||
}
|
||||
var res = this._superApply(arguments);
|
||||
return res;
|
||||
},
|
||||
nodeSetFocus: function(ctx, flag) {
|
||||
var node = ctx.node,
|
||||
$rightNode = $(node.tr),
|
||||
$leftNode = $rightNode.data("fancytree-node-counterpart");
|
||||
if ($leftNode) {
|
||||
$leftNode.toggleClass("fancytree-focused", flag);
|
||||
}
|
||||
var res = this._superApply(arguments);
|
||||
return res;
|
||||
},
|
||||
nodeRender: function(ctx, force, deep, collapsed, _recursive) {
|
||||
var res = this._superApply(arguments);
|
||||
// node = ctx.node,
|
||||
// _this = this,
|
||||
// $rightTr = $(node.tr),
|
||||
// $leftTr = $(node.tr).data("fancytree-node-counterpart");
|
||||
|
||||
// if (!$leftTr && this.$fixedWrapper && !node.isRoot()) {
|
||||
// var index = $rightTr.index();
|
||||
// $leftTr = this.$fixedWrapper.find(".fancytree-fixed-wrapper-bl table tbody tr").eq(index);
|
||||
// if ($leftTr.length) {
|
||||
// $rightTr.data("fancytree-node-counterpart", $leftTr);
|
||||
// $leftTr.data("fancytree-node-counterpart", $rightTr);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if ($leftTr) {
|
||||
// $leftTr.attr("class", $rightTr.attr("class"));
|
||||
// _this.ext.fixed._adjustRowLayout($leftTr, $rightTr);
|
||||
// $leftTr.find("td:not(.fancytree-fixed-hidden), th:not(.fancytree-fixed-hidden)").each(function(idx) {
|
||||
// _this.ext.fixed._adjustColumnLayout($(this), $rightTr.find("td").eq(idx));
|
||||
// });
|
||||
// }
|
||||
return res;
|
||||
},
|
||||
nodeRenderTitle: function(ctx, title) {
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
nodeRenderStatus: function(ctx) {
|
||||
var res = this._superApply(arguments),
|
||||
node = ctx.node,
|
||||
$rightTr = $(node.tr).data("fancytree-node-counterpart");
|
||||
if ($rightTr) {
|
||||
$rightTr.toggleClass("fancytree-selected", node.selected);
|
||||
$rightTr.toggleClass("fancytree-partsel", node.partsel);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
nodeSetExpanded: function(ctx, flag, callOpts) {
|
||||
var res,
|
||||
_this = this,
|
||||
node = ctx.node,
|
||||
// fixCols = this.options.fixed.fixCols,
|
||||
$leftTr = $(node.tr).data("fancytree-node-counterpart");
|
||||
|
||||
if (!$leftTr) {
|
||||
return this._superApply(arguments);
|
||||
}
|
||||
$leftTr.toggleClass("fancytree-expanded", !!flag);
|
||||
if (flag) {
|
||||
res = this._superApply(arguments);
|
||||
res.done(function() {
|
||||
node.visit(function(child) {
|
||||
var $tr = $(child.tr),
|
||||
$clone = $tr.data("fancytree-node-counterpart");
|
||||
$clone.toggleClass("fancytree-fixed-hidden", !flag).find("td, th").toggleClass("fancytree-fixed-hidden", !flag);
|
||||
_this.ext.fixed._adjustRowLayout($tr, $clone);
|
||||
if (!child.expanded) {
|
||||
return "skip";
|
||||
}
|
||||
});
|
||||
|
||||
$leftTr.find("td:not(.fancytree-fixed-hidden), th:not(.fancytree-fixed-hidden)").each(function(idx) {
|
||||
_this.ext.fixed._adjustColumnLayout($(this), null);
|
||||
});
|
||||
_this.ext.fixed._adjustWrapperLayout();
|
||||
});
|
||||
} else {
|
||||
node.visit(function(child) {
|
||||
var $tr = $(child.tr),
|
||||
$clone = $tr.data("fancytree-node-counterpart");
|
||||
if ($clone) {
|
||||
$clone.toggleClass("fancytree-fixed-hidden", !flag).find("td, th").toggleClass("fancytree-fixed-hidden", !flag);
|
||||
_this.ext.fixed._adjustRowLayout($tr, $clone);
|
||||
if (!child.expanded) {
|
||||
return "skip";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$leftTr.find("td:not(.fancytree-fixed-hidden), th:not(.fancytree-fixed-hidden)").each(function(idx) {
|
||||
_this.ext.fixed._adjustColumnLayout($(this), null);
|
||||
});
|
||||
_this.ext.fixed._adjustWrapperLayout();
|
||||
res = this._superApply(arguments);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
nodeSetStatus: function(ctx, status, message, details) {
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
treeClear: function(ctx) {
|
||||
var tree = ctx.tree,
|
||||
$table = tree.widget.element,
|
||||
$wrapper = this.$fixedWrapper;
|
||||
$table.find("tr, td, th, thead").removeClass("fancytree-fixed-hidden").css({
|
||||
"min-width": "auto",
|
||||
"height": "auto"
|
||||
});
|
||||
$wrapper.append($table);
|
||||
$wrapper.find(".fancytree-fixed-wrapper-tl").remove();
|
||||
$wrapper.find(".fancytree-fixed-wrapper-tr").remove();
|
||||
$wrapper.find(".fancytree-fixed-wrapper-bl").remove();
|
||||
$wrapper.find(".fancytree-fixed-wrapper-br").remove();
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
|
||||
treeRegisterNode: function(ctx, add, node) {
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
treeDestroy: function(ctx) {
|
||||
var tree = ctx.tree,
|
||||
$table = tree.widget.element,
|
||||
$wrapper = this.$fixedWrapper;
|
||||
$table.find("tr, td, th, thead").removeClass("fancytree-fixed-hidden").css({
|
||||
"min-width": "auto",
|
||||
"height": "auto"
|
||||
});
|
||||
$wrapper.append($table);
|
||||
$wrapper.find(".fancytree-fixed-wrapper-tl").remove();
|
||||
$wrapper.find(".fancytree-fixed-wrapper-tr").remove();
|
||||
$wrapper.find(".fancytree-fixed-wrapper-bl").remove();
|
||||
$wrapper.find(".fancytree-fixed-wrapper-br").remove();
|
||||
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
|
||||
_adjustColumnLayout: function($td1, $td2) {
|
||||
if (!$td2) {
|
||||
var $table = $td1.closest("table"),
|
||||
$tr2 = null,
|
||||
colIdx = $td1.index(),
|
||||
$tableWrapper = $table.parent(),
|
||||
$wrapper = this.$fixedWrapper,
|
||||
$tlWrapper = $wrapper.find("div.fancytree-fixed-wrapper-tl"),
|
||||
$trWrapper = $wrapper.find("div.fancytree-fixed-wrapper-tr"),
|
||||
$blWrapper = $wrapper.find("div.fancytree-fixed-wrapper-bl"),
|
||||
$brWrapper = $wrapper.find("div.fancytree-fixed-wrapper-br");
|
||||
if ($tableWrapper.is($tlWrapper)) {
|
||||
$tr2 = $blWrapper.find("tr:not(.fancytree-fixed-hidden)").first();
|
||||
} else if ($tableWrapper.is($trWrapper)) {
|
||||
$tr2 = $brWrapper.find("tr:not(.fancytree-fixed-hidden)").first();
|
||||
} else if ($tableWrapper.is($blWrapper)) {
|
||||
$tr2 = $tlWrapper.find("tr:not(.fancytree-fixed-hidden)").first();
|
||||
} else if ($tableWrapper.is($brWrapper)) {
|
||||
$tr2 = $trWrapper.find("tr:not(.fancytree-fixed-hidden)").first();
|
||||
}
|
||||
|
||||
$td2 = $tr2.find("td:not(.fancytree-fixed-hidden):eq(" + colIdx + "), th:not(.fancytree-fixed-hidden):eq(" + colIdx + ")");
|
||||
}
|
||||
$td1.css("min-width", "auto");
|
||||
$td2.css("min-width", "auto");
|
||||
var td1Width = $td1.width(),
|
||||
td2Width = $td2.width(),
|
||||
td1OuterWidth = $td1.outerWidth(),
|
||||
td2OuterWidth = $td2.outerWidth(),
|
||||
newWidth = Math.max(td1OuterWidth, td2OuterWidth);
|
||||
|
||||
$td1.css("min-width", newWidth - (td1OuterWidth - td1Width));
|
||||
$td2.css("min-width", newWidth - (td2OuterWidth - td2Width));
|
||||
},
|
||||
|
||||
_adjustRowLayout: function($row1, $row2) {
|
||||
if (!$row2) {
|
||||
$row2 = $row1.data("fancytree-node-counterpart");
|
||||
}
|
||||
$row1.css("height", "auto");
|
||||
$row2.css("height", "auto");
|
||||
var row1Height = $row1.outerHeight(),
|
||||
row2Height = $row2.outerHeight(),
|
||||
newHeight = Math.max(row1Height, row2Height);
|
||||
$row1.css("height", newHeight+1);
|
||||
$row2.css("height", newHeight+1);
|
||||
},
|
||||
|
||||
_adjustWrapperLayout: function() {
|
||||
var $wrapper = this.$fixedWrapper,
|
||||
$topLeftWrapper = $wrapper.find("div.fancytree-fixed-wrapper-tl"),
|
||||
$topRightWrapper = $wrapper.find("div.fancytree-fixed-wrapper-tr"),
|
||||
$bottomLeftWrapper = $wrapper.find("div.fancytree-fixed-wrapper-bl"),
|
||||
$bottomRightWrapper = $wrapper.find("div.fancytree-fixed-wrapper-br"),
|
||||
$topLeftTable = $topLeftWrapper.find("table"),
|
||||
$topRightTable = $topRightWrapper.find("table"),
|
||||
$bottomLeftTable = $bottomLeftWrapper.find("table"),
|
||||
// $bottomRightTable = $bottomRightWrapper.find("table"),
|
||||
wrapperWidth = $wrapper.width(),
|
||||
wrapperHeight = $wrapper.height(),
|
||||
// fixedWidth = Math.min(wrapperWidth, Math.max($topLeftTable.outerWidth(), $bottomLeftTable.outerWidth())),
|
||||
fixedWidth = Math.min(wrapperWidth, Math.max($topLeftTable.outerWidth(), $bottomLeftTable.get(0).scrollWidth)),
|
||||
fixedHeight = Math.min(wrapperHeight, Math.max($topLeftTable.height(), $topRightTable.height())),
|
||||
vScrollbar = $bottomRightWrapper.get(0).scrollHeight > (wrapperHeight - fixedHeight),
|
||||
hScrollbar = $bottomRightWrapper.get(0).scrollWidth > (wrapperWidth - fixedWidth);
|
||||
|
||||
$topLeftWrapper.css({
|
||||
width: fixedWidth,
|
||||
height: fixedHeight
|
||||
});
|
||||
$topRightWrapper.css({
|
||||
width: wrapperWidth - fixedWidth - (vScrollbar ? 17 : 0),
|
||||
height: fixedHeight,
|
||||
left: fixedWidth
|
||||
});
|
||||
$bottomLeftWrapper.css({
|
||||
width: fixedWidth,
|
||||
height: vScrollbar ? wrapperHeight - fixedHeight - (hScrollbar ? 17 : 0) : "auto",
|
||||
top: fixedHeight
|
||||
});
|
||||
$bottomRightWrapper.css({
|
||||
width: wrapperWidth - fixedWidth,
|
||||
height: vScrollbar ? wrapperHeight - fixedHeight : "auto",
|
||||
top: fixedHeight,
|
||||
left: fixedWidth
|
||||
});
|
||||
},
|
||||
|
||||
_adjustLayout: function() {
|
||||
var _this = this,
|
||||
$wrapper = this.$fixedWrapper,
|
||||
$topLeftTable = $wrapper.find("div.fancytree-fixed-wrapper-tl table"),
|
||||
$topRightTable = $wrapper.find("div.fancytree-fixed-wrapper-tr table"),
|
||||
$bottomLeftTable = $wrapper.find("div.fancytree-fixed-wrapper-bl table"),
|
||||
$bottomRightTable = $wrapper.find("div.fancytree-fixed-wrapper-br table");
|
||||
|
||||
$topLeftTable.find("tr").each(function(idx) {
|
||||
var $row2 = $topRightTable.find("tr:eq(" + idx + ")");
|
||||
_this.ext.fixed._adjustRowLayout($(this), $row2);
|
||||
});
|
||||
|
||||
$bottomLeftTable.find("tr").each(function(idx) {
|
||||
var $row2 = $bottomRightTable.find("tbody").find("tr:eq(" + idx + ")");
|
||||
_this.ext.fixed._adjustRowLayout($(this), $row2);
|
||||
});
|
||||
|
||||
$topLeftTable.find("tr:first-child").find("td, th").each(function(idx) {
|
||||
var $bottomTr = $bottomLeftTable.find("tr:first-child"),
|
||||
$td2 = $bottomTr.find("td:not(.fancytree-fixed-hidden):eq(" + idx + "), th:not(.fancytree-fixed-hidden):eq(" + idx + ")");
|
||||
_this.ext.fixed._adjustColumnLayout($(this), $td2);
|
||||
});
|
||||
|
||||
$topRightTable.find("tr:first-child").find("th:not(.fancytree-fixed-hidden)").each(function(idx) {
|
||||
var $bottomTr = $bottomRightTable.find("tbody tr:first-child:not(.fancytree-fixed-hidden)"),
|
||||
$td2 = $bottomTr.find("td:not(.fancytree-fixed-hidden):eq(" + idx + "), th:not(.fancytree-fixed-hidden):eq(" + idx + ")");
|
||||
_this.ext.fixed._adjustColumnLayout($(this), $td2);
|
||||
});
|
||||
|
||||
_this.ext.fixed._adjustWrapperLayout();
|
||||
}
|
||||
|
||||
|
||||
/*,
|
||||
treeSetFocus: function(ctx, flag) {
|
||||
// alert("treeSetFocus" + ctx.tree.$container);
|
||||
ctx.tree.$container.focus();
|
||||
$.ui.fancytree.focusTree = ctx.tree;
|
||||
}*/
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
143
static/lib/fancytree/src/jquery.fancytree.glyph.js
Normal file
143
static/lib/fancytree/src/jquery.fancytree.glyph.js
Normal file
@@ -0,0 +1,143 @@
|
||||
/*!
|
||||
* jquery.fancytree.glyph.js
|
||||
*
|
||||
* Use glyph fonts as instead of icon sprites.
|
||||
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
|
||||
*
|
||||
* Copyright (c) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(function($, window, document, undefined) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/* *****************************************************************************
|
||||
* Private functions and variables
|
||||
*/
|
||||
|
||||
function _getIcon(opts, type){
|
||||
return opts.map[type];
|
||||
}
|
||||
|
||||
$.ui.fancytree.registerExtension({
|
||||
name: "glyph",
|
||||
version: "2.22.5",
|
||||
// Default options for this extension.
|
||||
options: {
|
||||
map: {
|
||||
// Samples from Font Awesome 3.2
|
||||
// http://fortawesome.github.io/Font-Awesome/3.2.1/icons/
|
||||
// See here for alternatives:
|
||||
// http://fortawesome.github.io/Font-Awesome/icons/
|
||||
// http://getbootstrap.com/components/
|
||||
checkbox: "icon-check-empty",
|
||||
checkboxSelected: "icon-check",
|
||||
checkboxUnknown: "icon-check icon-muted",
|
||||
error: "icon-exclamation-sign",
|
||||
expanderClosed: "icon-caret-right",
|
||||
expanderLazy: "icon-angle-right",
|
||||
expanderOpen: "icon-caret-down",
|
||||
nodata: "icon-meh",
|
||||
noExpander: "",
|
||||
dragHelper: "icon-caret-right",
|
||||
dropMarker: "icon-caret-right",
|
||||
// Default node icons.
|
||||
// (Use tree.options.icon callback to define custom icons
|
||||
// based on node data)
|
||||
doc: "icon-file-alt",
|
||||
docOpen: "icon-file-alt",
|
||||
loading: "icon-refresh icon-spin",
|
||||
folder: "icon-folder-close-alt",
|
||||
folderOpen: "icon-folder-open-alt"
|
||||
}
|
||||
},
|
||||
|
||||
treeInit: function(ctx){
|
||||
var tree = ctx.tree;
|
||||
this._superApply(arguments);
|
||||
tree.$container.addClass("fancytree-ext-glyph");
|
||||
},
|
||||
nodeRenderStatus: function(ctx) {
|
||||
var icon, res, span,
|
||||
node = ctx.node,
|
||||
$span = $(node.span),
|
||||
opts = ctx.options.glyph,
|
||||
map = opts.map;
|
||||
|
||||
res = this._super(ctx);
|
||||
|
||||
if( node.isRoot() ){
|
||||
return res;
|
||||
}
|
||||
span = $span.children("span.fancytree-expander").get(0);
|
||||
if( span ){
|
||||
// if( node.isLoading() ){
|
||||
// icon = "loading";
|
||||
if( node.expanded && node.hasChildren() ){
|
||||
icon = "expanderOpen";
|
||||
}else if( node.isUndefined() ){
|
||||
icon = "expanderLazy";
|
||||
}else if( node.hasChildren() ){
|
||||
icon = "expanderClosed";
|
||||
}else{
|
||||
icon = "noExpander";
|
||||
}
|
||||
span.className = "fancytree-expander " + map[icon];
|
||||
}
|
||||
|
||||
if( node.tr ){
|
||||
span = $("td", node.tr).find("span.fancytree-checkbox").get(0);
|
||||
}else{
|
||||
span = $span.children("span.fancytree-checkbox").get(0);
|
||||
}
|
||||
if( span ){
|
||||
icon = node.selected ? "checkboxSelected" : (node.partsel ? "checkboxUnknown" : "checkbox");
|
||||
span.className = "fancytree-checkbox " + map[icon];
|
||||
}
|
||||
|
||||
// Standard icon (note that this does not match .fancytree-custom-icon,
|
||||
// that might be set by opts.icon callbacks)
|
||||
span = $span.children("span.fancytree-icon").get(0);
|
||||
if( span ){
|
||||
if( node.statusNodeType ){
|
||||
icon = _getIcon(opts, node.statusNodeType); // loading, error
|
||||
}else if( node.folder ){
|
||||
icon = node.expanded && node.hasChildren() ? _getIcon(opts, "folderOpen") : _getIcon(opts, "folder");
|
||||
}else{
|
||||
icon = node.expanded ? _getIcon(opts, "docOpen") : _getIcon(opts, "doc");
|
||||
}
|
||||
span.className = "fancytree-icon " + icon;
|
||||
}
|
||||
return res;
|
||||
},
|
||||
nodeSetStatus: function(ctx, status, message, details) {
|
||||
var res, span,
|
||||
opts = ctx.options.glyph,
|
||||
node = ctx.node;
|
||||
|
||||
res = this._superApply(arguments);
|
||||
|
||||
if( status === "error" || status === "loading" || status === "nodata" ){
|
||||
if(node.parent){
|
||||
span = $("span.fancytree-expander", node.span).get(0);
|
||||
if( span ) {
|
||||
span.className = "fancytree-expander " + _getIcon(opts, status);
|
||||
}
|
||||
}else{ //
|
||||
span = $(".fancytree-statusnode-" + status, node[this.nodeContainerAttrName])
|
||||
.find("span.fancytree-icon").get(0);
|
||||
if( span ) {
|
||||
span.className = "fancytree-icon " + _getIcon(opts, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
203
static/lib/fancytree/src/jquery.fancytree.gridnav.js
Normal file
203
static/lib/fancytree/src/jquery.fancytree.gridnav.js
Normal file
@@ -0,0 +1,203 @@
|
||||
/*!
|
||||
* jquery.fancytree.gridnav.js
|
||||
*
|
||||
* Support keyboard navigation for trees with embedded input controls.
|
||||
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
|
||||
*
|
||||
* Copyright (c) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(function($, window, document, undefined) {
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions and variables
|
||||
*/
|
||||
|
||||
// Allow these navigation keys even when input controls are focused
|
||||
|
||||
var KC = $.ui.keyCode,
|
||||
// which keys are *not* handled by embedded control, but passed to tree
|
||||
// navigation handler:
|
||||
NAV_KEYS = {
|
||||
"text": [KC.UP, KC.DOWN],
|
||||
"checkbox": [KC.UP, KC.DOWN, KC.LEFT, KC.RIGHT],
|
||||
"link": [KC.UP, KC.DOWN, KC.LEFT, KC.RIGHT],
|
||||
"radiobutton": [KC.UP, KC.DOWN, KC.LEFT, KC.RIGHT],
|
||||
"select-one": [KC.LEFT, KC.RIGHT],
|
||||
"select-multiple": [KC.LEFT, KC.RIGHT]
|
||||
};
|
||||
|
||||
|
||||
/* Calculate TD column index (considering colspans).*/
|
||||
function getColIdx($tr, $td) {
|
||||
var colspan,
|
||||
td = $td.get(0),
|
||||
idx = 0;
|
||||
|
||||
$tr.children().each(function () {
|
||||
if( this === td ) {
|
||||
return false;
|
||||
}
|
||||
colspan = $(this).prop("colspan");
|
||||
idx += colspan ? colspan : 1;
|
||||
});
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
/* Find TD at given column index (considering colspans).*/
|
||||
function findTdAtColIdx($tr, colIdx) {
|
||||
var colspan,
|
||||
res = null,
|
||||
idx = 0;
|
||||
|
||||
$tr.children().each(function () {
|
||||
if( idx >= colIdx ) {
|
||||
res = $(this);
|
||||
return false;
|
||||
}
|
||||
colspan = $(this).prop("colspan");
|
||||
idx += colspan ? colspan : 1;
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* Find adjacent cell for a given direction. Skip empty cells and consider merged cells */
|
||||
function findNeighbourTd($target, keyCode){
|
||||
var $tr, colIdx,
|
||||
$td = $target.closest("td"),
|
||||
$tdNext = null;
|
||||
|
||||
switch( keyCode ){
|
||||
case KC.LEFT:
|
||||
$tdNext = $td.prev();
|
||||
break;
|
||||
case KC.RIGHT:
|
||||
$tdNext = $td.next();
|
||||
break;
|
||||
case KC.UP:
|
||||
case KC.DOWN:
|
||||
$tr = $td.parent();
|
||||
colIdx = getColIdx($tr, $td);
|
||||
while( true ) {
|
||||
$tr = (keyCode === KC.UP) ? $tr.prev() : $tr.next();
|
||||
if( !$tr.length ) {
|
||||
break;
|
||||
}
|
||||
// Skip hidden rows
|
||||
if( $tr.is(":hidden") ) {
|
||||
continue;
|
||||
}
|
||||
// Find adjacent cell in the same column
|
||||
$tdNext = findTdAtColIdx($tr, colIdx);
|
||||
// Skip cells that don't conatain a focusable element
|
||||
if( $tdNext && $tdNext.find(":input,a").length ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $tdNext;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Extension code
|
||||
*/
|
||||
$.ui.fancytree.registerExtension({
|
||||
name: "gridnav",
|
||||
version: "2.22.5",
|
||||
// Default options for this extension.
|
||||
options: {
|
||||
autofocusInput: false, // Focus first embedded input if node gets activated
|
||||
handleCursorKeys: true // Allow UP/DOWN in inputs to move to prev/next node
|
||||
},
|
||||
|
||||
treeInit: function(ctx){
|
||||
// gridnav requires the table extension to be loaded before itself
|
||||
this._requireExtension("table", true, true);
|
||||
this._superApply(arguments);
|
||||
|
||||
this.$container.addClass("fancytree-ext-gridnav");
|
||||
|
||||
// Activate node if embedded input gets focus (due to a click)
|
||||
this.$container.on("focusin", function(event){
|
||||
var ctx2,
|
||||
node = $.ui.fancytree.getNode(event.target);
|
||||
|
||||
if( node && !node.isActive() ){
|
||||
// Call node.setActive(), but also pass the event
|
||||
ctx2 = ctx.tree._makeHookContext(node, event);
|
||||
ctx.tree._callHook("nodeSetActive", ctx2, true);
|
||||
}
|
||||
});
|
||||
},
|
||||
nodeSetActive: function(ctx, flag, callOpts) {
|
||||
var $outer,
|
||||
opts = ctx.options.gridnav,
|
||||
node = ctx.node,
|
||||
event = ctx.originalEvent || {},
|
||||
triggeredByInput = $(event.target).is(":input");
|
||||
|
||||
flag = (flag !== false);
|
||||
|
||||
this._superApply(arguments);
|
||||
|
||||
if( flag ){
|
||||
if( ctx.options.titlesTabbable ){
|
||||
if( !triggeredByInput ) {
|
||||
$(node.span).find("span.fancytree-title").focus();
|
||||
node.setFocus();
|
||||
}
|
||||
// If one node is tabbable, the container no longer needs to be
|
||||
ctx.tree.$container.attr("tabindex", "-1");
|
||||
// ctx.tree.$container.removeAttr("tabindex");
|
||||
} else if( opts.autofocusInput && !triggeredByInput ){
|
||||
// Set focus to input sub input (if node was clicked, but not
|
||||
// when TAB was pressed )
|
||||
$outer = $(node.tr || node.span);
|
||||
$outer.find(":input:enabled:first").focus();
|
||||
}
|
||||
}
|
||||
},
|
||||
nodeKeydown: function(ctx) {
|
||||
var inputType, handleKeys, $td,
|
||||
opts = ctx.options.gridnav,
|
||||
event = ctx.originalEvent,
|
||||
$target = $(event.target);
|
||||
|
||||
if( $target.is(":input:enabled") ) {
|
||||
inputType = $target.prop("type");
|
||||
} else if( $target.is("a") ) {
|
||||
inputType = "link";
|
||||
}
|
||||
// ctx.tree.debug("ext-gridnav nodeKeydown", event, inputType);
|
||||
|
||||
if( inputType && opts.handleCursorKeys ){
|
||||
handleKeys = NAV_KEYS[inputType];
|
||||
if( handleKeys && $.inArray(event.which, handleKeys) >= 0 ){
|
||||
$td = findNeighbourTd($target, event.which);
|
||||
if( $td && $td.length ) {
|
||||
// ctx.node.debug("ignore keydown in input", event.which, handleKeys);
|
||||
$td.find(":input:enabled,a").focus();
|
||||
// Prevent Fancytree default navigation
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// ctx.tree.debug("ext-gridnav NOT HANDLED", event, inputType);
|
||||
return this._superApply(arguments);
|
||||
}
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
5128
static/lib/fancytree/src/jquery.fancytree.js
Normal file
5128
static/lib/fancytree/src/jquery.fancytree.js
Normal file
File diff suppressed because it is too large
Load Diff
155
static/lib/fancytree/src/jquery.fancytree.menu.js
Normal file
155
static/lib/fancytree/src/jquery.fancytree.menu.js
Normal 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) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(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._superApply(arguments);
|
||||
|
||||
// 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._superApply(arguments);
|
||||
},
|
||||
_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).off("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._superApply(arguments);
|
||||
// }
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
389
static/lib/fancytree/src/jquery.fancytree.persist.js
Normal file
389
static/lib/fancytree/src/jquery.fancytree.persist.js
Normal file
@@ -0,0 +1,389 @@
|
||||
/*!
|
||||
* 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: js-cookie or jquery-cookie
|
||||
*
|
||||
* Copyright (c) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(function($, window, document, undefined) {
|
||||
|
||||
"use strict";
|
||||
/* global Cookies:false */
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions and variables
|
||||
*/
|
||||
var cookieGetter, cookieRemover, cookieSetter,
|
||||
_assert = $.ui.fancytree.assert,
|
||||
ACTIVE = "active",
|
||||
EXPANDED = "expanded",
|
||||
FOCUS = "focus",
|
||||
SELECTED = "selected";
|
||||
|
||||
if( typeof Cookies === "function" ) {
|
||||
// Assume https://github.com/js-cookie/js-cookie
|
||||
cookieSetter = Cookies.set;
|
||||
cookieGetter = Cookies.get;
|
||||
cookieRemover = Cookies.remove;
|
||||
} else {
|
||||
// Fall back to https://github.com/carhartl/jquery-cookie
|
||||
cookieSetter = cookieGetter = $.cookie;
|
||||
cookieRemover = $.removeCookie;
|
||||
}
|
||||
|
||||
/* Recursively load lazy nodes
|
||||
* @param {string} mode 'load', 'expand', false
|
||||
*/
|
||||
function _loadLazyNodes(tree, local, keyList, mode, dfd) {
|
||||
var i, key, l, node,
|
||||
foundOne = false,
|
||||
expandOpts = tree.options.persist.expandOpts,
|
||||
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(true, expandOpts));
|
||||
} else {
|
||||
deferredList.push(node.load());
|
||||
}
|
||||
} else {
|
||||
tree.debug("_loadLazyNodes: " + node + " already loaded.");
|
||||
node.setExpanded(true, expandOpts);
|
||||
}
|
||||
} 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: "2.22.5",
|
||||
// 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
|
||||
expandOpts: undefined, // optional `opts` argument passed to setExpanded()
|
||||
fireActivate: true, // false: suppress `activate` event after active node was restored
|
||||
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) : cookieGetter(key);
|
||||
} else if ( value === null ) {
|
||||
if( ls ) {
|
||||
ls.removeItem(key);
|
||||
} else {
|
||||
cookieRemover(key);
|
||||
}
|
||||
} else {
|
||||
if( ls ) {
|
||||
ls.setItem(key, value);
|
||||
} else {
|
||||
cookieSetter(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 !== "auto" && instOpts.store !== "cookie") || cookieGetter,
|
||||
"Missing required plugin for 'persist' extension: js.cookie.js or 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){
|
||||
if ( tree._triggerTreeEvent("beforeRestore", null, {}) === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cookie, dfd, i, keyList, node,
|
||||
prevFocus = local._data(local.cookiePrefix + FOCUS), // record this before node.setActive() overrides it;
|
||||
noEvents = instOpts.fireActivate === false;
|
||||
|
||||
// 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.debug("persist: set active", cookie);
|
||||
// We only want to set the focus if the container
|
||||
// had the keyboard focus before
|
||||
node.setActive(true, {
|
||||
noFocus: true,
|
||||
noEvents: noEvents
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if(local.storeFocus && prevFocus){
|
||||
node = tree.getNodeByKey(prevFocus);
|
||||
if(node){
|
||||
// node.debug("persist: set focus", cookie);
|
||||
if( tree.options.titlesTabbable ) {
|
||||
$(node.span).find(".fancytree-title").focus();
|
||||
} else {
|
||||
$(tree.$container).focus();
|
||||
}
|
||||
// node.setFocus();
|
||||
}
|
||||
}
|
||||
tree._triggerTreeEvent("restore", null, {});
|
||||
});
|
||||
});
|
||||
// Init the tree
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
nodeSetActive: function(ctx, flag, callOpts) {
|
||||
var res,
|
||||
local = this._local;
|
||||
|
||||
flag = (flag !== false);
|
||||
res = this._superApply(arguments);
|
||||
|
||||
if(local.storeActive){
|
||||
local._data(local.cookiePrefix + ACTIVE, this.activeNode ? this.activeNode.key : null);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
nodeSetExpanded: function(ctx, flag, callOpts) {
|
||||
var res,
|
||||
node = ctx.node,
|
||||
local = this._local;
|
||||
|
||||
flag = (flag !== false);
|
||||
res = this._superApply(arguments);
|
||||
|
||||
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._superApply(arguments);
|
||||
|
||||
if( 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._superApply(arguments);
|
||||
|
||||
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 {
|
||||
// beforeSelect can prevent the change - flag doesn't reflect the node.selected state
|
||||
local._appendKey(SELECTED, node.key, node.selected);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
446
static/lib/fancytree/src/jquery.fancytree.table.js
Normal file
446
static/lib/fancytree/src/jquery.fancytree.table.js
Normal file
@@ -0,0 +1,446 @@
|
||||
/*!
|
||||
* jquery.fancytree.table.js
|
||||
*
|
||||
* Render tree as table (aka 'treegrid', 'tabletree').
|
||||
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
|
||||
*
|
||||
* Copyright (c) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(function($, window, document, undefined) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/* *****************************************************************************
|
||||
* Private functions and variables
|
||||
*/
|
||||
function _assert(cond, msg){
|
||||
msg = msg || "";
|
||||
if(!cond){
|
||||
$.error("Assertion failed " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
function insertFirstChild(referenceNode, newNode) {
|
||||
referenceNode.insertBefore(newNode, referenceNode.firstChild);
|
||||
}
|
||||
|
||||
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 && prev.children.length){
|
||||
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;
|
||||
}
|
||||
|
||||
/* Render callback for 'wide' mode. */
|
||||
// function _renderStatusNodeWide(event, data) {
|
||||
// var node = data.node,
|
||||
// nodeColumnIdx = data.options.table.nodeColumnIdx,
|
||||
// $tdList = $(node.tr).find(">td");
|
||||
|
||||
// $tdList.eq(nodeColumnIdx).attr("colspan", data.tree.columnCount);
|
||||
// $tdList.not(":eq(" + nodeColumnIdx + ")").remove();
|
||||
// }
|
||||
|
||||
|
||||
$.ui.fancytree.registerExtension({
|
||||
name: "table",
|
||||
version: "2.22.5",
|
||||
// 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, columnCount, n, $row, $tbody,
|
||||
tree = ctx.tree,
|
||||
opts = ctx.options,
|
||||
tableOpts = opts.table,
|
||||
$table = tree.widget.element;
|
||||
|
||||
if( tableOpts.customStatus != null ) {
|
||||
if( opts.renderStatusColumns != null) {
|
||||
$.error("The 'customStatus' option is deprecated since v2.15.0. Use 'renderStatusColumns' only instead.");
|
||||
} else {
|
||||
tree.warn("The 'customStatus' option is deprecated since v2.15.0. Use 'renderStatusColumns' instead.");
|
||||
opts.renderStatusColumns = tableOpts.customStatus;
|
||||
}
|
||||
}
|
||||
if( opts.renderStatusColumns ) {
|
||||
if( opts.renderStatusColumns === true ) {
|
||||
opts.renderStatusColumns = opts.renderColumns;
|
||||
// } else if( opts.renderStatusColumns === "wide" ) {
|
||||
// opts.renderStatusColumns = _renderStatusNodeWide;
|
||||
}
|
||||
}
|
||||
|
||||
$table.addClass("fancytree-container fancytree-ext-table");
|
||||
tree.tbody = $table.find(">tbody")[0];
|
||||
$tbody = $(tree.tbody);
|
||||
|
||||
// Prepare row templates:
|
||||
// Determine column count from table header if any
|
||||
columnCount = $("thead >tr:last >th", $table).length;
|
||||
// Read TR templates from tbody if any
|
||||
$row = $tbody.children("tr:first");
|
||||
if( $row.length ) {
|
||||
n = $row.children("td").length;
|
||||
if( columnCount && n !== columnCount ) {
|
||||
tree.warn("Column count mismatch between thead (" + columnCount + ") and tbody (" + n + "): using tbody.");
|
||||
columnCount = n;
|
||||
}
|
||||
$row = $row.clone();
|
||||
} else {
|
||||
// Only thead is defined: create default row markup
|
||||
_assert(columnCount >= 1, "Need either <thead> or <tbody> with <td> elements to determine column count.");
|
||||
$row = $("<tr />");
|
||||
for(i=0; i<columnCount; i++) {
|
||||
$row.append("<td />");
|
||||
}
|
||||
}
|
||||
$row.find(">td").eq(tableOpts.nodeColumnIdx)
|
||||
.html("<span class='fancytree-node' />");
|
||||
if( opts.aria ) {
|
||||
$row.attr("role", "row");
|
||||
$row.find("td").attr("role", "gridcell");
|
||||
}
|
||||
tree.rowFragment = document.createDocumentFragment();
|
||||
tree.rowFragment.appendChild($row.get(0));
|
||||
|
||||
// // If tbody contains a second row, use this as status node template
|
||||
// $row = $tbody.children("tr:eq(1)");
|
||||
// if( $row.length === 0 ) {
|
||||
// tree.statusRowFragment = tree.rowFragment;
|
||||
// } else {
|
||||
// $row = $row.clone();
|
||||
// tree.statusRowFragment = document.createDocumentFragment();
|
||||
// tree.statusRowFragment.appendChild($row.get(0));
|
||||
// }
|
||||
//
|
||||
$tbody.empty();
|
||||
|
||||
// Make sure that status classes are set on the node's <tr> elements
|
||||
tree.statusClassPropName = "tr";
|
||||
tree.ariaPropName = "tr";
|
||||
this.nodeContainerAttrName = "tr";
|
||||
|
||||
// #489: make sure $container is set to <table>, even if ext-dnd is listed before ext-table
|
||||
tree.$container = $table;
|
||||
|
||||
this._superApply(arguments);
|
||||
|
||||
// standard Fancytree created a root UL
|
||||
$(tree.rootNode.ul).remove();
|
||||
tree.rootNode.ul = null;
|
||||
|
||||
// Add container to the TAB chain
|
||||
// #577: Allow to set tabindex to "0", "-1" and ""
|
||||
this.$container.attr("tabindex", opts.tabindex);
|
||||
// this.$container.attr("tabindex", opts.tabbable ? "0" : "-1");
|
||||
if(opts.aria) {
|
||||
tree.$container
|
||||
.attr("role", "treegrid")
|
||||
.attr("aria-readonly", true);
|
||||
}
|
||||
},
|
||||
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( tree._enableUpdate === false ) {
|
||||
// $.ui.fancytree.debug("*** nodeRender _enableUpdate: false");
|
||||
return;
|
||||
}
|
||||
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 && force ) {
|
||||
this.nodeRemoveMarkup(ctx);
|
||||
}
|
||||
if( !node.tr ) {
|
||||
if( ctx.hasCollapsedParents && !deep ) {
|
||||
// #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
|
||||
// if( node.isStatusNode() ) {
|
||||
// newRow = tree.statusRowFragment.firstChild.cloneNode(true);
|
||||
// } else {
|
||||
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 be system root");
|
||||
// tree.tbody.appendChild(newRow);
|
||||
insertFirstChild(tree.tbody, newRow); // #675
|
||||
}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){
|
||||
// $(node.tr).attr("aria-labelledby", "ftal_" + opts.idPrefix + 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, res,
|
||||
node = ctx.node,
|
||||
opts = ctx.options,
|
||||
isStatusNode = node.isStatusNode();
|
||||
|
||||
res = this._super(ctx, title);
|
||||
|
||||
if( node.isRootNode() ) {
|
||||
return res;
|
||||
}
|
||||
// Move checkbox to custom column
|
||||
if(opts.checkbox && !isStatusNode && opts.table.checkboxColumnIdx != null ){
|
||||
$cb = $("span.fancytree-checkbox", node.span); //.detach();
|
||||
$(node.tr).find("td").eq(+opts.table.checkboxColumnIdx).html($cb);
|
||||
}
|
||||
// Update element classes according to node state
|
||||
this.nodeRenderStatus(ctx);
|
||||
|
||||
if( isStatusNode ) {
|
||||
if( opts.renderStatusColumns ) {
|
||||
// Let user code write column content
|
||||
opts.renderStatusColumns.call(ctx.tree, {type: "renderStatusColumns"}, ctx);
|
||||
} // else: default rendering for status node: leave other cells empty
|
||||
} else if ( opts.renderColumns ) {
|
||||
opts.renderColumns.call(ctx.tree, {type: "renderColumns"}, ctx);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
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({paddingLeft: indent + "px"}); // #460
|
||||
// $(node.span).css({marginLeft: indent + "px"});
|
||||
},
|
||||
/* Expand node, return Deferred.promise. */
|
||||
nodeSetExpanded: function(ctx, flag, callOpts) {
|
||||
// flag defaults to true
|
||||
flag = (flag !== false);
|
||||
|
||||
if((ctx.node.expanded && flag) || (!ctx.node.expanded && !flag)) {
|
||||
// Expanded state isn't changed - just call base implementation
|
||||
return this._superApply(arguments);
|
||||
}
|
||||
|
||||
var dfd = new $.Deferred(),
|
||||
subOpts = $.extend({}, callOpts, {noEvents: true, noAnimation: true});
|
||||
|
||||
callOpts = callOpts || {};
|
||||
|
||||
function _afterExpand(ok) {
|
||||
setChildRowVisibility(ctx.node, flag);
|
||||
if( ok ) {
|
||||
if( flag && ctx.options.autoScroll && !callOpts.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( !callOpts.noEvents ) {
|
||||
ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
|
||||
}
|
||||
dfd.resolveWith(ctx.node);
|
||||
});
|
||||
} else {
|
||||
if( !callOpts.noEvents ) {
|
||||
ctx.tree._triggerNodeEvent(flag ? "expand" : "collapse", ctx);
|
||||
}
|
||||
dfd.resolveWith(ctx.node);
|
||||
}
|
||||
} else {
|
||||
if( !callOpts.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();
|
||||
}
|
||||
}
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
treeClear: function(ctx) {
|
||||
this.nodeRemoveChildMarkup(this._makeHookContext(this.rootNode));
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
treeDestroy: function(ctx) {
|
||||
this.$container.find("tbody").empty();
|
||||
this.$source && this.$source.removeClass("ui-helper-hidden");
|
||||
return this._superApply(arguments);
|
||||
}
|
||||
/*,
|
||||
treeSetFocus: function(ctx, flag) {
|
||||
// alert("treeSetFocus" + ctx.tree.$container);
|
||||
ctx.tree.$container.focus();
|
||||
$.ui.fancytree.focusTree = ctx.tree;
|
||||
}*/
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
98
static/lib/fancytree/src/jquery.fancytree.themeroller.js
Normal file
98
static/lib/fancytree/src/jquery.fancytree.themeroller.js
Normal file
@@ -0,0 +1,98 @@
|
||||
/*!
|
||||
* 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) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(function($, window, document, undefined) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/*******************************************************************************
|
||||
* Extension code
|
||||
*/
|
||||
$.ui.fancytree.registerExtension({
|
||||
name: "themeroller",
|
||||
version: "2.22.5",
|
||||
// Default options for this extension.
|
||||
options: {
|
||||
activeClass: "ui-state-active", // Class added to active node
|
||||
// activeClass: "ui-state-highlight",
|
||||
addClass: "ui-corner-all", // Class added to all nodes
|
||||
focusClass: "ui-state-focus", // Class added to focused node
|
||||
hoverClass: "ui-state-hover", // Class added to hovered node
|
||||
selectedClass: "ui-state-highlight" // Class added to selected nodes
|
||||
// selectedClass: "ui-state-active"
|
||||
},
|
||||
|
||||
treeInit: function(ctx){
|
||||
var $el = ctx.widget.element,
|
||||
opts = ctx.options.themeroller;
|
||||
|
||||
this._superApply(arguments);
|
||||
|
||||
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.tr ? node.tr : node.span)
|
||||
.toggleClass(opts.hoverClass + " " + opts.addClass, flag);
|
||||
});
|
||||
},
|
||||
treeDestroy: function(ctx){
|
||||
this._superApply(arguments);
|
||||
ctx.widget.element.removeClass("ui-widget ui-widget-content ui-corner-all");
|
||||
},
|
||||
nodeRenderStatus: function(ctx){
|
||||
var classes = {},
|
||||
node = ctx.node,
|
||||
$el = $(node.tr ? node.tr : node.span),
|
||||
opts = ctx.options.themeroller;
|
||||
|
||||
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.
|
||||
*/
|
||||
// Set ui-state-* class (handle the case that the same class is assigned
|
||||
// to different states)
|
||||
classes[opts.activeClass] = false;
|
||||
classes[opts.focusClass] = false;
|
||||
classes[opts.selectedClass] = false;
|
||||
if( node.isActive() ) { classes[opts.activeClass] = true; }
|
||||
if( node.hasFocus() ) { classes[opts.focusClass] = true; }
|
||||
// activeClass takes precedence before selectedClass:
|
||||
if( node.isSelected() && !node.isActive() ) { classes[opts.selectedClass] = true; }
|
||||
$el.toggleClass(opts.activeClass, classes[opts.activeClass]);
|
||||
$el.toggleClass(opts.focusClass, classes[opts.focusClass]);
|
||||
$el.toggleClass(opts.selectedClass, classes[opts.selectedClass]);
|
||||
// Additional classes (e.g. 'ui-corner-all')
|
||||
$el.addClass(opts.addClass);
|
||||
}
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
194
static/lib/fancytree/src/jquery.fancytree.wide.js
Normal file
194
static/lib/fancytree/src/jquery.fancytree.wide.js
Normal file
@@ -0,0 +1,194 @@
|
||||
/*!
|
||||
* jquery.fancytree.wide.js
|
||||
* Support for 100% wide selection bars.
|
||||
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
|
||||
*
|
||||
* Copyright (c) 2008-2017, Martin Wendt (http://wwWendt.de)
|
||||
*
|
||||
* Released under the MIT license
|
||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||
*
|
||||
* @version 2.22.5
|
||||
* @date 2017-05-11T17:01:53Z
|
||||
*/
|
||||
|
||||
;(function($, window, document, undefined) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var reNumUnit = /^([+-]?(?:\d+|\d*\.\d+))([a-z]*|%)$/; // split "1.5em" to ["1.5", "em"]
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions and variables
|
||||
*/
|
||||
// var _assert = $.ui.fancytree.assert;
|
||||
|
||||
/* 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));
|
||||
// }
|
||||
|
||||
/* Create a global embedded CSS style for the tree. */
|
||||
function defineHeadStyleElement(id, cssText) {
|
||||
id = "fancytree-style-" + id;
|
||||
var $headStyle = $("#" + id);
|
||||
|
||||
if( !cssText ) {
|
||||
$headStyle.remove();
|
||||
return null;
|
||||
}
|
||||
if( !$headStyle.length ) {
|
||||
$headStyle = $("<style />")
|
||||
.attr("id", id)
|
||||
.addClass("fancytree-style")
|
||||
.prop("type", "text/css")
|
||||
.appendTo("head");
|
||||
}
|
||||
try {
|
||||
$headStyle.html(cssText);
|
||||
} catch ( e ) {
|
||||
// fix for IE 6-8
|
||||
$headStyle[0].styleSheet.cssText = cssText;
|
||||
}
|
||||
return $headStyle;
|
||||
}
|
||||
|
||||
/* Calculate the CSS rules that indent title spans. */
|
||||
function renderLevelCss(containerId, depth, levelOfs, lineOfs, labelOfs, measureUnit)
|
||||
{
|
||||
var i,
|
||||
prefix = "#" + containerId + " span.fancytree-level-",
|
||||
rules = [];
|
||||
|
||||
for(i = 0; i < depth; i++) {
|
||||
rules.push(prefix + (i + 1) + " span.fancytree-title { padding-left: " +
|
||||
(i * levelOfs + lineOfs) + measureUnit + "; }");
|
||||
}
|
||||
// Some UI animations wrap the UL inside a DIV and set position:relative on both.
|
||||
// This breaks the left:0 and padding-left:nn settings of the title
|
||||
rules.push(
|
||||
"#" + containerId + " div.ui-effects-wrapper ul li span.fancytree-title, " +
|
||||
"#" + containerId + " ul.fancytree-animating span.fancytree-title " + // #716
|
||||
"{ padding-left: " + labelOfs + measureUnit + "; position: static; width: auto; }");
|
||||
return rules.join("\n");
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * [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;
|
||||
|
||||
// inst.contWidth = realInnerWidth(this.$container);
|
||||
// // Each title is precceeded by 2 or 3 icons (16px + 3 margin)
|
||||
// // + 1px title border and 3px title padding
|
||||
// // TODO: use code from treeInit() below
|
||||
// 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: "2.22.5",
|
||||
// Default options for this extension.
|
||||
options: {
|
||||
iconWidth: null, // Adjust this if @fancy-icon-width != "16px"
|
||||
iconSpacing: null, // Adjust this if @fancy-icon-spacing != "3px"
|
||||
labelSpacing: null, // Adjust this if padding between icon and label != "3px"
|
||||
levelOfs: null // Adjust this if ul padding != "16px"
|
||||
},
|
||||
|
||||
treeCreate: function(ctx){
|
||||
this._superApply(arguments);
|
||||
this.$container.addClass("fancytree-ext-wide");
|
||||
|
||||
var containerId, cssText, iconSpacingUnit, labelSpacingUnit, iconWidthUnit, levelOfsUnit,
|
||||
instOpts = ctx.options.wide,
|
||||
// css sniffing
|
||||
$dummyLI = $("<li id='fancytreeTemp'><span class='fancytree-node'><span class='fancytree-icon' /><span class='fancytree-title' /></span><ul />")
|
||||
.appendTo(ctx.tree.$container),
|
||||
$dummyIcon = $dummyLI.find(".fancytree-icon"),
|
||||
$dummyUL = $dummyLI.find("ul"),
|
||||
// $dummyTitle = $dummyLI.find(".fancytree-title"),
|
||||
iconSpacing = instOpts.iconSpacing || $dummyIcon.css("margin-left"),
|
||||
iconWidth = instOpts.iconWidth || $dummyIcon.css("width"),
|
||||
labelSpacing = instOpts.labelSpacing || "3px",
|
||||
levelOfs = instOpts.levelOfs || $dummyUL.css("padding-left");
|
||||
|
||||
$dummyLI.remove();
|
||||
|
||||
iconSpacingUnit = iconSpacing.match(reNumUnit)[2];
|
||||
iconSpacing = parseFloat(iconSpacing, 10);
|
||||
labelSpacingUnit = labelSpacing.match(reNumUnit)[2];
|
||||
labelSpacing = parseFloat(labelSpacing, 10);
|
||||
iconWidthUnit = iconWidth.match(reNumUnit)[2];
|
||||
iconWidth = parseFloat(iconWidth, 10);
|
||||
levelOfsUnit = levelOfs.match(reNumUnit)[2];
|
||||
if( iconSpacingUnit !== iconWidthUnit || levelOfsUnit !== iconWidthUnit || labelSpacingUnit !== iconWidthUnit ) {
|
||||
$.error("iconWidth, iconSpacing, and levelOfs must have the same css measure unit");
|
||||
}
|
||||
this._local.measureUnit = iconWidthUnit;
|
||||
this._local.levelOfs = parseFloat(levelOfs);
|
||||
this._local.lineOfs = (1 + (ctx.options.checkbox ? 1 : 0) +
|
||||
(ctx.options.icon === false ? 0 : 1)) * (iconWidth + iconSpacing) +
|
||||
iconSpacing;
|
||||
this._local.labelOfs = labelSpacing;
|
||||
this._local.maxDepth = 10;
|
||||
|
||||
// Get/Set a unique Id on the container (if not already exists)
|
||||
containerId = this.$container.uniqueId().attr("id");
|
||||
// Generated css rules for some levels (extended on demand)
|
||||
cssText = renderLevelCss(containerId, this._local.maxDepth,
|
||||
this._local.levelOfs, this._local.lineOfs, this._local.labelOfs,
|
||||
this._local.measureUnit);
|
||||
defineHeadStyleElement(containerId, cssText);
|
||||
},
|
||||
treeDestroy: function(ctx){
|
||||
// Remove generated css rules
|
||||
defineHeadStyleElement(this.$container.attr("id"), null);
|
||||
return this._superApply(arguments);
|
||||
},
|
||||
nodeRenderStatus: function(ctx) {
|
||||
var containerId, cssText, res,
|
||||
node = ctx.node,
|
||||
level = node.getLevel();
|
||||
|
||||
res = this._super(ctx);
|
||||
// Generate some more level-n rules if required
|
||||
if( level > this._local.maxDepth ) {
|
||||
containerId = this.$container.attr("id");
|
||||
this._local.maxDepth *= 2;
|
||||
node.debug("Define global ext-wide css up to level " + this._local.maxDepth);
|
||||
cssText = renderLevelCss(containerId, this._local.maxDepth,
|
||||
this._local.levelOfs, this._local.lineOfs, this._local.labelSpacing,
|
||||
this._local.measureUnit);
|
||||
defineHeadStyleElement(containerId, cssText);
|
||||
}
|
||||
// Add level-n class to apply indentation padding.
|
||||
// (Setting element style would not work, since it cannot easily be
|
||||
// overriden while animations run)
|
||||
$(node.span).addClass("fancytree-level-" + level);
|
||||
return res;
|
||||
}
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
Reference in New Issue
Block a user