2009-09-22 15:26:41 +00:00
/// <reference path="/umbraco_client/Application/NamespaceManager.js" />
/// <reference path="/umbraco_client/Application/UmbracoUtils.js" />
2009-06-19 07:39:16 +00:00
/// <reference path="/umbraco_client/ui/jquery.js" />
/// <reference path="/umbraco_client/ui/jqueryui.js" />
/// <reference path="tree_component.js" />
/// <reference path="/umbraco_client/Application/UmbracoApplicationActions.js" />
/// <reference path="NodeDefinition.js" />
/// <reference name="MicrosoftAjax.js"/>
Umbraco . Sys . registerNamespace ( "Umbraco.Controls" ) ;
( function ( $ ) {
2010-02-08 02:22:42 +00:00
2010-03-15 15:17:38 +00:00
$ . fn . UmbracoTree = function ( opts ) {
/// <summary>jQuery plugin to create an Umbraco tree. See option remarks below.</summary>
2009-06-19 07:39:16 +00:00
return this . each ( function ( ) {
2010-03-15 15:17:38 +00:00
new Umbraco . Controls . UmbracoTree ( ) . init ( $ ( this ) , opts ) ;
2009-06-19 07:39:16 +00:00
} ) ;
} ;
$ . fn . UmbracoTreeAPI = function ( ) {
/// <summary>exposes the Umbraco Tree api for the selected object</summary>
2010-02-08 02:22:42 +00:00
if ( $ ( this ) . length != 1 ) {
2010-03-03 15:19:51 +00:00
throw "UmbracoTreeAPI selector requires that there be exactly one control selected, this selector returns " + $ ( this ) . length ;
2010-02-08 02:22:42 +00:00
} ;
// check if there's an api stored for the id of the object specified, if there's not
// check if the first child is a div and if that has the api specified
var api = Umbraco . Controls . UmbracoTree . inst [ $ ( this ) . attr ( "id" ) ] || null ;
if ( api == null )
return Umbraco . Controls . UmbracoTree . inst [ $ ( this ) . children ( "div" ) . attr ( "id" ) ] || null ;
return api ;
2009-06-19 07:39:16 +00:00
} ;
2010-03-15 15:17:38 +00:00
Umbraco . Controls . TreeDefaultOptions = function ( ) {
return {
doNotInit : false , //this is used for the main umbraco tree so that the tree doesn't fully initialize until rebuildTree is explicitly called
jsonFullMenu : { } , //The tree menu, by default is empty
appActions : null , //A reference to a MenuActions object
deletingText : "Deleting..." , //the txt to display when a node is deleting
treeMode : "standard" , //determines the type of tree: false/null = normal, 'checkbox' = checkboxes enabled, 'inheritedcheckbox' = parent nodes have checks inherited from children
recycleBinId : - 20 , //the id of the recycle bin for the current tree
serviceUrl : "" , //Url path for the tree client service
dataUrl : "" , //Url path for the tree data service
//These are all properties of the ITreeService and are
//used to pass the properties in to the InitAppTreeData service
app : "" , //the application name to render
treeType : "" , //the active tree application
showContext : true , //boolean indicating whether or not to show a context menu
isDialog : false ,
dialogMode : "none" , //boolean indicating whether or not the tree is in dialog mode
functionToCall : "" ,
nodeKey : ""
} ;
}
2009-06-19 07:39:16 +00:00
Umbraco . Controls . UmbracoTree = function ( ) {
/// <summary>
/// The object that manages the Umbraco tree.
/// Has these events: syncNotFound, syncFound, rebuiltTree, newchildNodeFound, nodeMoved, nodeCopied, ajaxError, nodeClicked
/// </summary>
return {
2010-03-15 15:17:38 +00:00
_opts : { } ,
_cntr : ++ Umbraco . Controls . UmbracoTree . cntr , //increments the number of tree instances.
2010-02-08 02:22:42 +00:00
_containerId : null ,
_context : null , //the jquery context used to get the container
2009-06-19 07:39:16 +00:00
_actionNode : new Umbraco . Controls . NodeDefinition ( ) , //the most recent node right clicked for context menu
_activeTreeType : "content" , //tracks which is the active tree type, this is used in searching and syncing.
_tree : null , //reference to the jsTree object
2010-03-15 15:17:38 +00:00
_isEditMode : false , //not really used YET
2010-06-23 15:24:39 +00:00
_isDebug : false , //set to true to enable alert debugging
2009-06-19 07:39:16 +00:00
_loadedApps : [ ] , //stores the application names that have been loaded to track which JavaScript code has been inserted into the DOM
_treeClass : "umbTree" , //used for other libraries to detect which elements are an umbraco tree
2009-06-20 21:59:38 +00:00
_currenAJAXRequest : false , //used to determine if there is currently an ajax request being executed.
2010-06-07 10:49:52 +00:00
_isSyncing : false ,
2009-06-19 07:39:16 +00:00
addEventHandler : function ( fnName , fn ) {
/// <summary>Adds an event listener to the event name event</summary>
2010-04-08 13:27:33 +00:00
this . _getContainer ( ) . bind ( fnName , fn ) ;
2009-06-19 07:39:16 +00:00
} ,
removeEventHandler : function ( fnName , fn ) {
/// <summary>Removes an event listener to the event name event</summary>
2010-04-08 13:27:33 +00:00
this . _getContainer ( ) . unbind ( fnName , fn ) ;
} ,
_raiseEvent : function ( evName , args ) {
/// <summary>Raises an event and attaches it to the container</summary>
this . _getContainer ( ) . trigger ( evName , args ) ;
2009-06-19 07:39:16 +00:00
} ,
2010-03-15 15:17:38 +00:00
init : function ( jItem , opts ) {
2009-06-19 07:39:16 +00:00
/// <summary>Initializes the tree with the options and stores the tree API in the jQuery data object for the current element</summary>
2010-02-08 02:22:42 +00:00
this . _debug ( "init: creating new tree with class/id: " + jItem . attr ( "class" ) + " / " + jItem . attr ( "id" ) ) ;
2010-03-15 15:17:38 +00:00
this . _opts = $ . extend ( Umbraco . Controls . TreeDefaultOptions ( ) , opts ) ;
2010-02-08 02:22:42 +00:00
2010-03-15 15:17:38 +00:00
this . _context = jItem . get ( 0 ) . ownerDocument ;
2010-02-08 02:22:42 +00:00
//wire up event handlers
2010-03-15 15:17:38 +00:00
if ( this . _opts . appActions != null ) {
2010-02-08 02:22:42 +00:00
var _this = this ;
//wrapped functions maintain scope
2010-03-15 15:17:38 +00:00
this . _opts . appActions . addEventHandler ( "nodeDeleting" , function ( E ) { _this . onNodeDeleting ( E ) } ) ;
this . _opts . appActions . addEventHandler ( "nodeDeleted" , function ( E ) { _this . onNodeDeleted ( E ) } ) ;
this . _opts . appActions . addEventHandler ( "nodeRefresh" , function ( E ) { _this . onNodeRefresh ( E ) } ) ;
2010-11-02 09:40:53 +00:00
this . _opts . appActions . addEventHandler ( "publicError" , function ( E , err ) { _this . onPublicError ( E , err ) } ) ;
2010-02-08 02:22:42 +00:00
}
this . _containerId = jItem . attr ( "id" ) ;
2010-03-15 15:17:38 +00:00
if ( ! this . _opts . doNotInit ) {
//initializes the jsTree
this . _tree = $ . tree . create ( ) ;
this . _tree . init ( this . _getContainer ( ) , this . _getInitOptions ( ) ) ;
}
2010-02-08 02:22:42 +00:00
2009-06-19 07:39:16 +00:00
jItem . addClass ( this . _treeClass ) ;
2010-02-08 02:22:42 +00:00
//store a reference to this api by the id and the counter
Umbraco . Controls . UmbracoTree . inst [ this . _cntr ] = this ;
if ( ! this . _getContainer ( ) . attr ( "id" ) ) this . _getContainer ( ) . attr ( "id" , "UmbTree_" + this . _cntr ) ;
Umbraco . Controls . UmbracoTree . inst [ this . _getContainer ( ) . attr ( "id" ) ] = Umbraco . Controls . UmbracoTree . inst [ this . _cntr ] ;
2009-06-19 07:39:16 +00:00
} ,
2009-08-31 15:05:47 +00:00
setRecycleBinNodeId : function ( id ) {
2010-03-15 15:17:38 +00:00
this . _opts . recycleBinId = id ;
2009-08-31 15:05:47 +00:00
} ,
2010-10-05 12:59:44 +00:00
//TODO: add public method to clear a specific tree cache
2009-08-12 16:53:24 +00:00
clearTreeCache : function ( ) {
// <summary>This will remove all stored trees in client side cache so that the next time a tree needs loading it will be refreshed</summary>
this . _debug ( "clearTreeCache..." ) ;
2010-04-15 13:28:45 +00:00
this . _loadedApps = [ ] ;
2009-08-12 16:53:24 +00:00
} ,
2009-09-21 15:39:59 +00:00
toggleEditMode : function ( enable ) {
this . _debug ( "Edit mode. Currently: " + this . _tree . settings . rules . draggable ) ;
this . _isEditMode = enable ;
2009-10-19 13:35:14 +00:00
2010-03-15 15:17:38 +00:00
this . saveTreeState ( this . _opts . app ) ;
2009-09-21 15:39:59 +00:00
//need to trick the system so it thinks it's a different app, then rebuild with new rules
2010-03-15 15:17:38 +00:00
var app = this . _opts . app ;
this . _opts . app = "temp" ;
2009-09-21 15:39:59 +00:00
this . rebuildTree ( app ) ;
2009-10-19 13:35:14 +00:00
2009-09-21 15:39:59 +00:00
this . _debug ( "Edit mode. New Mode: " + this . _tree . settings . rules . draggable ) ;
2010-03-15 15:17:38 +00:00
if ( this . _opts . appActions )
this . _opts . appActions . showSpeachBubble ( "info" , "Tree Edit Mode" , "The tree is now operating in edit mode" ) ;
2009-09-21 15:39:59 +00:00
} ,
2010-04-27 14:17:15 +00:00
refreshTree : function ( treeType ) {
/// <summary>This wraps the standard jsTree functionality unless a treeType is specified. If one is, then it will just reload that nodes children</summary>
this . _debug ( "refreshTree: " + treeType ) ;
if ( ! treeType ) {
2010-06-20 11:26:49 +00:00
this . rebuildTree ( ) ;
2010-04-27 14:17:15 +00:00
}
else {
var allRoots = this . _getContainer ( ) . find ( "li[rel='rootNode']" ) ;
var _this = this ;
var root = allRoots . filter ( function ( ) {
return ( $ ( this ) . attr ( "umb:type" ) == _this . _activeTreeType ) ; //filter based on custom namespace requires custom function
} ) ;
if ( root . length == 1 ) {
this . _debug ( "refreshTree: reloading tree type: " + treeType ) ;
this . _loadChildNodes ( root ) ;
}
else {
//couldn't find it, so refresh the whole tree
2010-06-20 11:26:49 +00:00
this . rebuildTree ( ) ;
2010-04-27 14:17:15 +00:00
}
}
2010-02-08 02:22:42 +00:00
} ,
2010-04-08 13:27:33 +00:00
rebuildTree : function ( app , callback ) {
2009-06-19 07:39:16 +00:00
/// <summary>This will rebuild the tree structure for the application specified</summary>
this . _debug ( "rebuildTree" ) ;
2009-07-13 14:51:26 +00:00
2010-06-20 11:26:49 +00:00
//if app is null, then we will rebuild the current app which also means clearing the cache.
if ( ! app ) {
this . clearTreeCache ( ) ;
2011-01-20 13:51:47 -01:00
app = this . _opts . app ; // zb-00011 #29447 : bugfix assignment
2010-06-20 11:26:49 +00:00
}
2010-10-05 12:59:44 +00:00
else if ( this . _tree && ( this . _opts . app . toLowerCase ( ) == app . toLowerCase ( ) ) ) {
2009-06-19 07:39:16 +00:00
this . _debug ( "not rebuilding" ) ;
2010-06-20 11:26:49 +00:00
//don't rebuild if the tree object exists, the app that's being requested to be loaded is
//flagged as already loaded, and the tree actually has nodes in it
2009-06-19 07:39:16 +00:00
return ;
}
else {
2010-03-15 15:17:38 +00:00
this . _opts . app = app ;
2009-06-19 07:39:16 +00:00
}
//kill the tree
2010-04-08 13:27:33 +00:00
if ( this . _tree ) {
this . _tree . destroy ( ) ;
}
2009-10-19 13:35:14 +00:00
2009-06-19 07:39:16 +00:00
var _this = this ;
2009-07-13 14:51:26 +00:00
2009-06-20 13:17:06 +00:00
//check if we should rebuild from a saved tree
2010-02-08 02:22:42 +00:00
var saveData = this . _loadedApps [ "tree_" + app ] ;
2010-10-05 12:59:44 +00:00
this . setActiveTreeType ( app ) ;
2009-06-20 13:17:06 +00:00
if ( saveData != null ) {
2009-08-12 16:53:24 +00:00
this . _debug ( "rebuildTree: rebuilding from cache: app = " + app ) ;
2009-07-13 14:51:26 +00:00
2009-06-20 13:17:06 +00:00
//create the tree from the saved data.
2009-10-11 12:23:27 +00:00
//this._initNode = saveData.d;
this . _tree = $ . tree . create ( ) ;
2010-02-08 02:22:42 +00:00
this . _tree . init ( this . _getContainer ( ) , this . _getInitOptions ( saveData . d ) ) ;
2009-10-19 13:35:14 +00:00
//ensure the static data is gone
this . _tree . settings . data . opts . static = null ;
2010-02-08 02:22:42 +00:00
this . _configureNodes ( this . _getContainer ( ) . find ( "li" ) , true ) ;
2009-06-20 21:59:38 +00:00
//select the last node
var lastSelected = saveData . selected != null ? $ ( saveData . selected [ 0 ] ) . attr ( "id" ) : null ;
2009-07-13 14:51:26 +00:00
if ( lastSelected != null ) {
2009-06-20 21:59:38 +00:00
//create an event handler for the tree sync
var _this = this ;
var foundHandler = function ( EV , node ) {
//remove the event handler from firing again
_this . removeEventHandler ( "syncFound" , foundHandler ) ;
2010-04-08 13:27:33 +00:00
_this . _debug ( "rebuildTree: node synced, selecting node..." ) ;
2009-06-20 21:59:38 +00:00
//ensure the node is selected, ensure the event is fired and reselect is true since jsTree thinks this node is already selected by id
_this . selectNode ( node , false , true ) ;
} ;
this . _debug ( "rebuildTree: syncing to last selected: " + lastSelected ) ;
//add the event handler for the tree sync and sync the tree
2010-10-26 11:40:31 +00:00
this . addEventHandler ( "syncFound" , foundHandler ) ;
this . setActiveTreeType ( $ ( saveData . selected [ 0 ] ) . attr ( "umb:type" ) ) ;
2009-07-13 14:51:26 +00:00
this . syncTree ( lastSelected ) ;
2009-06-20 21:59:38 +00:00
}
2009-07-13 14:51:26 +00:00
2010-04-08 13:27:33 +00:00
if ( typeof callback == "function" ) callback . apply ( this , [ lastSelected ] ) ;
2009-06-20 13:17:06 +00:00
}
2009-10-19 13:35:14 +00:00
else {
this . _debug ( "rebuildTree: rebuilding from scratch: app = " + app ) ;
2010-04-08 13:27:33 +00:00
this . _currentAJAXRequest = true ;
2009-07-13 14:51:26 +00:00
2010-04-08 13:27:33 +00:00
_this . _tree = $ . tree . create ( ) ;
_this . _tree . init ( _this . _getContainer ( ) , _this . _getInitOptions ( ) ) ;
2009-10-19 13:35:14 +00:00
2010-04-08 13:27:33 +00:00
if ( typeof callback == "function" ) callback . apply ( this , [ ] ) ;
}
2009-06-19 07:39:16 +00:00
} ,
2009-07-13 14:51:26 +00:00
2009-06-20 13:17:06 +00:00
saveTreeState : function ( appAlias ) {
/// <summary>
/// Saves the state of the current application trees so we can restore it next time the user visits the app
/// </summary>
2009-07-13 14:51:26 +00:00
2009-06-20 21:59:38 +00:00
this . _debug ( "saveTreeState: " + appAlias + " : ajax request? " + this . _currentAJAXRequest ) ;
2009-07-13 14:51:26 +00:00
2009-10-13 14:01:24 +00:00
//clear the saved data for the current app before saving
2010-02-08 02:22:42 +00:00
this . _loadedApps [ "tree_" + appAlias ] = null ;
2009-10-19 13:35:14 +00:00
2009-06-20 21:59:38 +00:00
//if an ajax request is currently in progress, abort saving the tree state and set the
//data object for the application to null.
2009-10-19 13:35:14 +00:00
if ( ! this . _currentAJAXRequest ) {
2009-10-13 14:01:24 +00:00
//only save the data if there are nodes
2010-02-08 02:22:42 +00:00
var nodeCount = this . _getContainer ( ) . find ( "li[rel='dataNode']" ) . length ;
2009-10-13 14:01:24 +00:00
if ( nodeCount > 0 ) {
this . _debug ( "saveTreeState: node count = " + nodeCount ) ;
2009-10-19 13:35:14 +00:00
var treeData = this . _tree . get ( ) ;
2009-10-13 14:01:24 +00:00
//need to update the 'state' of the data. jsTree get doesn't return the state of nodes properly!
2009-10-19 13:35:14 +00:00
this . _updateJSONNodeState ( treeData ) ;
2009-10-13 14:01:24 +00:00
this . _debug ( "saveTreeState: treeData = " + treeData ) ;
2010-02-08 02:22:42 +00:00
this . _loadedApps [ "tree_" + appAlias ] = { selected : this . _tree . selected , d : treeData } ;
2009-10-13 14:01:24 +00:00
}
2009-06-20 21:59:38 +00:00
}
2009-06-20 13:17:06 +00:00
} ,
2009-07-13 14:51:26 +00:00
2009-06-20 13:17:06 +00:00
_updateJSONNodeState : function ( obj ) {
/// <summary>
/// A recursive function to store the state of the node for the JSON object when using saveTreeState.
/// This is required since jsTree doesn't output the state of the tree nodes with the request to getJSON method.
2009-10-11 12:23:27 +00:00
/// This is also required to save the correct title for each node since we store our title in a div tag, not just the a tag
2009-06-20 21:59:38 +00:00
/// </summary>
2009-07-13 14:51:26 +00:00
2009-06-20 21:59:38 +00:00
var node = $ ( "li[id='" + obj . attributes . id + "']" ) . filter ( function ( ) {
return ( $ ( this ) . attr ( "umb:type" ) == obj . attributes [ "umb:type" ] ) ; //filter based on custom namespace requires custom function
2009-07-13 14:51:26 +00:00
} ) ;
2009-10-19 13:35:14 +00:00
2009-10-11 12:23:27 +00:00
//saves the correct title
obj . data . title = $ . trim ( node . children ( "a" ) . children ( "div" ) . text ( ) ) ;
obj . state = obj . data . state ;
2010-03-15 15:17:38 +00:00
//ensures that the style property of the data contains only single quotes since jsTree puts double around the attr
for ( var i in obj . data . attributes ) {
if ( ! obj . data . attributes . hasOwnProperty ( i ) ) continue ;
2010-03-16 15:05:05 +00:00
if ( i == "style" || i == "class" ) {
obj . data . attributes [ i ] = obj . data . attributes [ i ] . replace ( /\"/g , "'" ) ;
}
2010-03-15 15:17:38 +00:00
}
2009-10-19 13:35:14 +00:00
2009-10-11 12:23:27 +00:00
//recurse through children
2009-06-20 13:17:06 +00:00
if ( obj . children != null ) {
for ( var x in obj . children ) {
this . _updateJSONNodeState ( obj . children [ x ] ) ;
}
}
} ,
2009-07-13 14:51:26 +00:00
2009-06-19 07:39:16 +00:00
syncTree : function ( path , forceReload ) {
/// <summary>
/// Syncronizes the tree with the path supplied and makes that node visible/selected.
/// </summary>
/// <param name="path">The path of the node</param>
/// <param name="forceReload">If true, will ensure that the node to be synced is synced with data from the server</param>
this . _debug ( "syncTree: " + path + ", " + forceReload ) ;
2010-06-07 10:49:52 +00:00
//set the flag so that multiple synces aren't attempted
this . _isSyncing = true ;
2009-06-19 07:39:16 +00:00
this . _syncTree . call ( this , path , forceReload , null , null ) ;
} ,
childNodeCreated : function ( ) {
/// <summary>
/// Reloads the children of the current action node and selects the node that didn't exist there before.
/// If it cannot determine which node is new, then no node is selected. If the children are not already
/// loaded, then it is impossible for this method to determine which child is new.
/// </summary>
this . _debug ( "childNodeCreated" ) ;
//store the current child ids so we can determine which one is the new one
var childrenIds = new Array ( ) ;
this . _actionNode . jsNode . find ( "ul > li" ) . each ( function ( ) {
childrenIds . push ( $ ( this ) . attr ( "id" ) ) ;
} ) ;
var _this = this ;
var currId = this . _actionNode . nodeId ;
this . reloadActionNode ( true , false , function ( success ) {
if ( success && childrenIds . length > 0 ) {
var found = false ;
var actionNode = _this . findNode ( currId ) ;
if ( actionNode ) {
actionNode . find ( "ul > li" ) . each ( function ( ) {
//if the id of the current child is not found in the original list, then this is the new one, store it
if ( $ . inArray ( $ ( this ) . attr ( "id" ) , childrenIds ) == - 1 ) {
found = $ ( this ) ;
}
} ) ;
}
if ( found ) {
_this . _debug ( "childNodeCreated: selecting new child node: " + found . attr ( "id" ) ) ;
_this . selectNode ( found , true , true ) ;
2010-04-08 13:27:33 +00:00
_this . _raiseEvent ( "newChildNodeFound" , [ found ] ) ;
2009-06-19 07:39:16 +00:00
return ;
}
}
_this . _debug ( "childNodeCreated: could not select new child!" ) ;
} ) ;
} ,
moveNode : function ( nodeId , parentPath ) {
/// <summary>Moves a node in the tree. This will remove the existing node by id and sync the tree to the new path</summary>
this . _debug ( "moveNode" ) ;
//remove the old node
var old = this . findNode ( nodeId ) ;
if ( old ) old . remove ( ) ;
//build the path to the new node
var newPath = parentPath + "," + nodeId ;
//create an event handler for the tree sync
var _this = this ;
var foundHandler = function ( EV , node ) {
//remove the event handler from firing again
_this . removeEventHandler ( "syncFound" , foundHandler ) ;
//ensure the node is selected, ensure the event is fired and reselect is true since jsTree thinks this node is already selected by id
_this . selectNode ( node , false , true ) ;
2010-04-08 13:27:33 +00:00
_this . _raiseEvent ( "nodeMoved" , [ node ] ) ;
2009-06-19 07:39:16 +00:00
} ;
//add the event handler for the tree sync and sync the tree
this . addEventHandler ( "syncFound" , foundHandler ) ;
this . syncTree ( newPath ) ;
} ,
copyNode : function ( nodeId , parentPath ) {
/// <summary>Copies a node in the tree. This will keep the current node selected but will sync the tree to show the copied node too</summary>
this . _debug ( "copyNode" ) ;
var originalNode = this . findNode ( nodeId ) ;
//create an event handler for the tree sync
var _this = this ;
var foundHandler = function ( EV , node ) {
//remove the event handler from firing again
_this . removeEventHandler ( "syncFound" , foundHandler ) ;
//now that the new parent node is found, expand it
_this . _loadChildNodes ( node , null ) ;
//reselect the original node since sync will select the one that was copied
if ( originalNode ) _this . selectNode ( originalNode , true ) ;
2010-04-08 13:27:33 +00:00
_this . _raiseEvent ( "nodeCopied" , [ node ] ) ;
2009-06-19 07:39:16 +00:00
} ;
//add the event handler for the tree sync and sync the to the parent path
this . addEventHandler ( "syncFound" , foundHandler ) ;
this . syncTree ( parentPath ) ;
} ,
findNode : function ( nodeId , findGlobal ) {
/// <summary>Returns either the found branch or false if not found in the tree</summary>
/// <param name="findGlobal">Optional. If true, disregards the tree type and searches the entire tree for the id</param>
var _this = this ;
2010-02-08 02:22:42 +00:00
var branch = this . _getContainer ( ) . find ( "li[id='" + nodeId + "']" ) ;
2009-06-19 07:39:16 +00:00
if ( ! findGlobal ) branch = branch . filter ( function ( ) {
return ( $ ( this ) . attr ( "umb:type" ) == _this . _activeTreeType ) ; //filter based on custom namespace requires custom function
} ) ;
var found = branch . length > 0 ? branch : false ;
2010-04-27 14:17:15 +00:00
this . _debug ( "findNode: " + nodeId + " in '" + this . _activeTreeType + "' tree. Found? " + found ) ;
2009-06-19 07:39:16 +00:00
return found ;
} ,
selectNode : function ( node , supressEvent , reselect ) {
2009-09-21 15:39:59 +00:00
/// <summary>
/// Makes the selected node the active node, but only if it is not already selected or if reselect is true.
/// </summary>
2009-06-19 07:39:16 +00:00
/// <param name="supressEvent">If set to true, will select the node but will supress the onSelected event</param>
/// <param name="reselect">If set to true, will call the select_branch method even if the node is already selected</param>
2009-10-19 13:35:14 +00:00
2010-04-08 13:27:33 +00:00
//this._debug("selectNode, edit mode? " + this._isEditMode);
2009-06-19 07:39:16 +00:00
var selectedId = this . _tree . selected != null ? $ ( this . _tree . selected [ 0 ] ) . attr ( "id" ) : null ;
2010-04-08 13:27:33 +00:00
this . _debug ( "selectNode (" + node . attr ( "id" ) + "). supressEvent? " + supressEvent + ", reselect? " + reselect ) ;
2009-06-19 07:39:16 +00:00
if ( reselect || ( selectedId == null || selectedId != node . attr ( "id" ) ) ) {
//if we don't wan the event to fire, we'll set the callback to a null method and set it back after we call the select_branch method
2009-09-21 15:39:59 +00:00
if ( supressEvent || this . _isEditMode ) {
2009-06-19 07:39:16 +00:00
this . _tree . settings . callback . onselect = function ( ) { } ;
}
this . _tree . select _branch ( node ) ;
//reset the method / maintain scope in callback
var _this = this ;
this . _tree . settings . callback . onselect = function ( N , T ) { _this . onSelect ( N , T ) } ;
}
2009-10-19 13:35:14 +00:00
2009-06-19 07:39:16 +00:00
} ,
reloadActionNode : function ( supressSelect , supressChildReload , callback ) {
/// <summary>
/// Gets the current action node's parent's data source url, then passes this url and the current action node's id
/// to a web service. The webservice will find the JSON data for the current action node and return it. This
/// will parse the returned JSON into html and replace the current action nodes' markup with the refreshed server data.
/// If by chance, the ajax call fails because of inconsistent data (a developer has implemented poor tree design), then
/// this use the build in jsTree reload which works ok.
/// </summary>
/// <param name="callback">
/// A callback function which will have a boolean parameter passed. True = the reload was succesful,
/// False = the reload failed and the generic _tree.refresh() method was used.
/// </param>
this . _debug ( "reloadActionNode: supressSelect = " + supressSelect + ", supressChildReload = " + supressChildReload ) ;
if ( this . _actionNode != null && this . _actionNode . jsNode != null ) {
var nodeParent = this . _actionNode . jsNode . parents ( "li:first" ) ;
this . _debug ( "reloadActionNode: found " + nodeParent . length + " parent nodes" ) ;
if ( nodeParent . length == 1 ) {
2009-10-11 12:23:27 +00:00
var nodeDef = this . getNodeDef ( nodeParent ) ;
2009-06-19 07:39:16 +00:00
this . _debug ( "reloadActionNode: loading ajax for node: " + nodeDef . nodeId ) ;
var _this = this ;
//replace the node to refresh with loading and return the new loading element
2009-10-11 12:23:27 +00:00
var toReplace = $ ( "<li class='last'><a class='loading' href='#'><ins></ins><div>" + ( this . _tree . settings . lang . loading || "Loading ..." ) + "</div></a></li>" ) . replaceAll ( this . _actionNode . jsNode ) ;
2009-06-19 07:39:16 +00:00
$ . get ( this . _getUrl ( nodeDef . sourceUrl ) , null ,
function ( msg ) {
if ( ! msg || msg . length == 0 ) {
_this . _debug ( "reloadActionNode: error loading ajax data, performing jsTree refresh" ) ;
2010-06-20 11:26:49 +00:00
_this . rebuildTree ( ) ; /*try jsTree refresh as last resort */
2009-06-19 07:39:16 +00:00
if ( callback != null ) callback . call ( _this , false ) ;
return ;
}
//filter the results to find the object corresponding to the one we want refreshed
var oFound = null ;
for ( var o in msg ) {
if ( msg [ o ] . attributes != null && msg [ o ] . attributes . id == _this . _actionNode . nodeId ) {
2009-10-11 12:23:27 +00:00
oFound = $ . tree . datastores . json ( ) . parse ( msg [ o ] , _this . _tree ) ;
2009-06-19 07:39:16 +00:00
//ensure the tree type is the same too
if ( $ ( oFound ) . attr ( "umb:type" ) == _this . _actionNode . treeType ) { break ; }
else { oFound = null ; }
}
}
if ( oFound != null ) {
2009-10-11 12:23:27 +00:00
_this . _debug ( "reloadActionNode: node is refreshed! : " + supressSelect ) ;
2009-06-19 07:39:16 +00:00
var reloaded = $ ( oFound ) . replaceAll ( toReplace ) ;
_this . _configureNodes ( reloaded , true ) ;
2009-10-11 12:23:27 +00:00
if ( ! supressSelect ) _this . selectNode ( reloaded , true , true ) ;
2009-06-19 07:39:16 +00:00
if ( ! supressChildReload ) {
_this . _loadChildNodes ( reloaded , function ( ) {
if ( callback != null ) callback . call ( _this , true ) ;
} ) ;
}
else { if ( callback != null ) callback . call ( _this , true ) ; }
}
else {
_this . _debug ( "reloadActionNode: error finding child node in ajax data, performing jsTree refresh" ) ;
2010-06-20 11:26:49 +00:00
_this . rebuildTree ( ) ; /*try jsTree refresh as last resort */
2009-06-19 07:39:16 +00:00
if ( callback != null ) callback . call ( _this , false ) ;
}
} , "json" ) ;
return ;
}
this . _debug ( "reloadActionNode: error finding parent node, performing jsTree refresh" ) ;
2010-06-20 11:26:49 +00:00
this . rebuildTree ( ) ; /*try jsTree refresh as last resort */
2009-06-19 07:39:16 +00:00
if ( callback != null ) callback . call ( this , false ) ;
}
} ,
getActionNode : function ( ) {
/// <summary>Returns the latest node interacted with</summary>
2009-10-11 12:23:27 +00:00
this . _debug ( "getActionNode: " + this . _actionNode . nodeId ) ;
2009-06-19 07:39:16 +00:00
return this . _actionNode ;
} ,
setActiveTreeType : function ( treeType ) {
/// <summary>
/// All interactions with the tree are done so based on the current tree type (i.e. content, media).
/// When sycning, or searching, the operations will be done on the current tree type so developers
/// can explicitly specify on with this method before performing the operations.
/// The active tree type is always updated any time a node interaction takes place.
/// </summary>
this . _activeTreeType = treeType ;
} ,
onNodeDeleting : function ( EV ) {
/// <summary>Event handler for when a tree node is about to be deleted</summary>
2009-06-20 13:17:06 +00:00
this . _debug ( "onNodeDeleting" )
2009-06-19 07:39:16 +00:00
//first, close the branch
this . _tree . close _branch ( this . _actionNode . jsNode ) ;
2010-03-16 15:05:05 +00:00
//show the deleting text
this . _actionNode . jsNode . find ( "a div" )
. html ( this . _opts . deletingText )
. effect ( "highlight" , { } , 1000 ) ;
2009-06-19 07:39:16 +00:00
} ,
2010-11-02 09:40:53 +00:00
onNodeDeleted : function ( EV ) {
2009-06-19 07:39:16 +00:00
/// <summary>Event handler for when a tree node is deleted after ajax call</summary>
2009-06-20 13:17:06 +00:00
this . _debug ( "onNodeDeleted" ) ;
2010-03-01 12:55:01 +00:00
var nodeToDel = this . _actionNode . jsNode ;
2009-06-19 07:39:16 +00:00
//ensure the branch is closed
2010-03-01 12:55:01 +00:00
this . _tree . close _branch ( nodeToDel ) ;
2009-06-19 07:39:16 +00:00
//make the node disapear
2010-11-02 09:40:53 +00:00
nodeToDel . hide ( "drop" , { direction : "down" } , 400 , function ( ) {
2010-04-27 11:45:05 +00:00
//remove the node from the DOM, do this after 1 second as IE doesn't like it when you try this right away.
2010-11-02 09:40:53 +00:00
setTimeout ( function ( ) { nodeToDel . remove ( ) ; } , 1000 ) ;
2010-03-01 12:55:01 +00:00
} ) ;
2009-06-19 07:39:16 +00:00
this . _updateRecycleBin ( ) ;
} ,
onNodeRefresh : function ( EV ) {
/// <summary>Handles the nodeRefresh event of the context menu and does the refreshing</summary>
this . _debug ( "onNodeRefresh" ) ;
this . _loadChildNodes ( this . _actionNode . jsNode , null ) ;
} ,
onSelect : function ( NODE , TREE _OBJ ) {
2009-09-21 15:39:59 +00:00
/// <summary>Fires the JS associated with the node, if the tree is in edit mode, allows for rename instead</summary>
2010-04-08 13:27:33 +00:00
//this._debug("onSelect, edit mode? " + this._isEditMode);
this . _debug ( "onSelect" ) ;
2009-10-19 13:35:14 +00:00
if ( this . _isEditMode ) {
2009-09-21 15:39:59 +00:00
this . _tree . rename ( NODE ) ;
return false ;
}
else {
this . setActiveTreeType ( $ ( NODE ) . attr ( "umb:type" ) ) ;
var js = $ ( NODE ) . children ( "a" ) . attr ( "href" ) . replace ( "javascript:" , "" ) ;
2009-06-19 07:39:16 +00:00
2009-09-21 15:39:59 +00:00
this . _debug ( "onSelect: js: " + js ) ;
2009-06-19 07:39:16 +00:00
2009-09-21 15:39:59 +00:00
try {
var func = eval ( js ) ;
if ( func != null ) {
func . call ( ) ;
}
} catch ( e ) { }
2009-06-19 07:39:16 +00:00
2009-09-21 15:39:59 +00:00
return true ;
}
2009-10-19 13:35:14 +00:00
2009-06-19 07:39:16 +00:00
} ,
onBeforeOpen : function ( NODE , TREE _OBJ ) {
2009-10-11 12:23:27 +00:00
/// <summary>Before opening child nodes, ensure that the data method and url are set properly</summary>
2009-10-19 13:35:14 +00:00
this . _currentAJAXRequest = true ;
2010-03-15 15:17:38 +00:00
TREE _OBJ . settings . data . opts . url = this . _opts . dataUrl ;
2009-10-11 12:23:27 +00:00
TREE _OBJ . settings . data . opts . method = "GET" ;
2009-06-19 07:39:16 +00:00
} ,
2009-07-13 14:51:26 +00:00
2009-06-20 21:59:38 +00:00
onJSONData : function ( DATA , TREE _OBJ ) {
this . _debug ( "onJSONData" ) ;
2009-10-19 13:35:14 +00:00
2010-02-08 02:22:42 +00:00
this . _ensureContext ( ) ;
2009-06-20 21:59:38 +00:00
this . _currentAJAXRequest = false ;
2009-10-19 13:35:14 +00:00
2009-10-11 12:23:27 +00:00
if ( typeof DATA . d != "undefined" ) {
2009-10-19 13:35:14 +00:00
var msg = DATA . d ;
2009-10-11 12:23:27 +00:00
//recreates the tree
if ( $ . inArray ( msg . app , this . _loadedApps ) == - 1 ) {
this . _debug ( "loading js for app: " + msg . app ) ;
this . _loadedApps . push ( msg . app ) ;
//inject the scripts
2010-02-08 02:22:42 +00:00
this . _getContainer ( ) . after ( "<script>" + msg . js + "</script>" ) ;
2009-10-11 12:23:27 +00:00
}
return eval ( msg . json ) ;
}
2009-10-19 13:35:14 +00:00
2009-06-20 21:59:38 +00:00
return DATA ;
} ,
2009-10-19 13:35:14 +00:00
2009-10-11 12:23:27 +00:00
onBeforeRequest : function ( NODE , TREE _OBJ ) {
2010-02-08 02:22:42 +00:00
this . _ensureContext ( ) ;
2009-10-11 12:23:27 +00:00
if ( TREE _OBJ . settings . data . opts . method == "POST" ) {
2010-03-15 15:17:38 +00:00
var parameters = "{'app':'" + this . _opts . app + "','showContextMenu':'" + this . _opts . showContext + "', 'isDialog':'" + this . _opts . isDialog + "', 'dialogMode':'" + this . _opts . dialogMode + "', 'treeType':'" + this . _opts . treeType + "', 'functionToCall':'" + this . _opts . functionToCall + "', 'nodeKey':'" + this . _opts . nodeKey + "'}"
2009-10-19 13:35:14 +00:00
return parameters ;
2009-06-19 07:39:16 +00:00
}
2009-10-11 12:23:27 +00:00
else {
var nodeDef = this . getNodeDef ( $ ( NODE ) ) ;
return this . _getUrlParams ( nodeDef . sourceUrl ) ;
2009-06-19 07:39:16 +00:00
}
2009-10-11 12:23:27 +00:00
} ,
2009-10-19 13:35:14 +00:00
2009-10-11 12:23:27 +00:00
onChange : function ( NODE , TREE _OBJ ) {
2009-06-19 07:39:16 +00:00
//bubble an event!
2010-04-08 13:27:33 +00:00
this . _raiseEvent ( "nodeClicked" , [ NODE ] ) ;
2009-06-19 07:39:16 +00:00
} ,
2009-10-11 12:23:27 +00:00
onBeforeContext : function ( NODE , TREE _OBJ , EV ) {
2009-10-19 13:35:14 +00:00
2009-06-19 07:39:16 +00:00
//update the action node's NodeDefinition and set the active tree type
2009-10-11 12:23:27 +00:00
this . _actionNode = this . getNodeDef ( $ ( NODE ) ) ;
2009-06-19 07:39:16 +00:00
this . setActiveTreeType ( $ ( NODE ) . attr ( "umb:type" ) ) ;
2009-10-11 12:23:27 +00:00
this . _debug ( "onBeforeContext: " + this . _actionNode . menu ) ;
2009-10-19 13:35:14 +00:00
return this . _actionNode . menu ;
2009-08-11 14:03:30 +00:00
} ,
2009-10-19 13:35:14 +00:00
2009-10-11 12:23:27 +00:00
onLoad : function ( TREE _OBJ ) {
/// <summary>When the application first loads, load the child nodes</summary>
2009-10-19 13:35:14 +00:00
2009-10-11 12:23:27 +00:00
this . _debug ( "onLoad" ) ;
2009-10-19 13:35:14 +00:00
2009-10-11 12:23:27 +00:00
//ensure the static data is gone
this . _tree . settings . data . opts . static = null ;
var _this = this ;
2010-02-08 02:22:42 +00:00
_this . _loadChildNodes ( $ ( _this . _getContainer ( ) ) . find ( "li" ) , null ) ;
2009-10-11 12:23:27 +00:00
} ,
2009-10-19 13:35:14 +00:00
2009-10-11 12:23:27 +00:00
onParse : function ( STR , TREE _OBJ ) {
this . _debug ( "onParse" ) ;
2009-10-19 13:35:14 +00:00
2010-02-08 02:22:42 +00:00
this . _ensureContext ( ) ;
2009-10-11 12:23:27 +00:00
var obj = $ ( STR ) ;
this . _configureNodes ( obj ) ;
//this will return the full html of the configured node
return $ ( '<div>' ) . append ( $ ( obj ) . clone ( ) ) . remove ( ) . html ( ) ;
2009-06-19 07:39:16 +00:00
} ,
2009-10-19 13:35:14 +00:00
2010-04-08 13:27:33 +00:00
onDestroy : function ( TREE _OBJ ) {
/// <summary>
/// When the tree is destroyed we need to ensure that all of the events both
/// live and bind are gone. For some reason the jstree unbinding doesn't seem to do it's job
/// so instead we need to clear all of the events ourselves
/// </summary>
this . _debug ( "onDestroy: " + TREE _OBJ . container . attr ( "id" ) ) ;
TREE _OBJ . container
. unbind ( "contextmenu" )
. unbind ( "click" )
. unbind ( "dblclick" )
. unbind ( "mouseover" )
. unbind ( "mousedown" )
. unbind ( "mouseup" ) ;
$ ( "a" , TREE _OBJ . container . get ( 0 ) )
. die ( "contextmenu" )
. die ( "click" )
. die ( "dblclick" )
. die ( "mouseover" )
. die ( "mousedown" ) ;
2010-06-23 23:19:49 +00:00
//also need to kill the custom selector we've fixed in jstree source
$ ( "#" + TREE _OBJ . container . attr ( "id" ) + " li" ) . die ( "click" ) ;
2010-04-08 13:27:33 +00:00
$ ( "li" , TREE _OBJ . container . get ( 0 ) )
. die ( "click" ) ;
} ,
2010-02-08 02:22:42 +00:00
onError : function ( ERR , TREE _OBJ ) {
this . _debug ( "ERROR!!!!! " + ERR ) ;
} ,
2010-11-02 09:40:53 +00:00
onPublicError : function ( ev , errorObj ) {
/// <summary>Event handler for when a tree node fails an ajax call</summary>
this . _debug ( "onPublicError" ) ;
var errorNode = this . _actionNode . jsNode ;
// reload parent
this . reloadActionNode ( false , true , null ) ;
if ( this . _isDebug ) {
alert ( 'There was an error processing the request\n' +
'=========================================\n\n' +
'Error Message:\n ' +
errorObj . get _message ( ) + '\n\n' +
'Technical information:\n ' +
'=========================================\n\n' +
'Status Code: ' + errorObj . get _statusCode ( ) + '\n\n' +
'Exception Type: ' + errorObj . get _exceptionType ( ) + '\n\n' +
'Timed Out: ' + errorObj . get _timedOut ( ) + '\n\n' +
'Full Stacktrace:\n' + errorObj . get _stackTrace ( ) ) ;
} else {
this . _opts . appActions . showSpeachBubble ( "error" , "Error handling action" , errorObj . get _message ( ) ) ;
}
} ,
2010-02-08 02:22:42 +00:00
2009-06-19 07:39:16 +00:00
_debug : function ( strMsg ) {
2010-03-01 12:55:01 +00:00
if ( this . _isDebug && Sys && Sys . Debug ) {
2009-06-19 07:39:16 +00:00
Sys . Debug . trace ( "UmbracoTree: " + strMsg ) ;
}
} ,
_configureNodes : function ( nodes , reconfigure ) {
/// <summary>
/// Ensures the node is configured properly after it's loaded via ajax.
/// This includes setting overlays and ensuring the correct icon paths are used.
/// This also ensures that the correct markup is rendered for the tree (i.e. inserts html nodes for text, etc...)
/// </summary>
var _this = this ;
//don't process the nodes that have already been loaded, unless reconfigure is true
if ( ! reconfigure ) {
nodes = nodes . not ( "li[class*='loaded']" ) ;
}
this . _debug ( "_configureNodes: " + nodes . length ) ;
var rxInput = new RegExp ( "\\boverlay-\\w+\\b" , "gi" ) ;
nodes . each ( function ( ) {
//if it is checkbox tree (not standard), don't worry about overlays and remove the default icon.
2010-03-15 15:17:38 +00:00
if ( _this . _opts . treeMode != "standard" ) {
2009-06-19 07:39:16 +00:00
$ ( this ) . children ( "a:first" ) . css ( "background" , "" ) ;
return ;
}
//remove all overlays if reconfiguring
$ ( this ) . children ( "div" ) . remove ( ) ;
var m = $ ( this ) . attr ( "class" ) . match ( rxInput ) ;
if ( m != null ) {
for ( i = 0 ; i < m . length ; i ++ ) {
_this . _debug ( "_configureNodes: adding overlay: " + m [ i ] + " for node: " + $ ( this ) . attr ( "id" ) ) ;
$ ( this ) . children ( "a:first" ) . before ( "<div class='overlay " + m [ i ] + "'></div>" ) ;
}
}
2009-10-11 12:23:27 +00:00
//create a div for the text
var a = $ ( this ) . children ( "a" ) ;
var ins = a . children ( "ins" ) ;
2009-11-02 21:58:02 +00:00
ins . remove ( ) ; //need to remove before you do a .text() otherwise whitespace is included
2009-10-11 12:23:27 +00:00
var txt = $ ( "<div>" + a . text ( ) + "</div>" ) ;
//check if it's not a sprite, if not then move the ins node just after the anchor, otherwise remove
if ( a . hasClass ( "noSpr" ) ) {
a . attr ( "style" , ins . attr ( "style" ) ) ;
}
2009-10-19 13:35:14 +00:00
else {
2009-10-11 12:23:27 +00:00
}
a . html ( txt ) ;
2009-06-19 07:39:16 +00:00
//add the loaded class to each element so we know not to process it again
$ ( this ) . addClass ( "loaded" ) ;
} ) ;
} ,
2009-10-11 12:23:27 +00:00
getNodeDef : function ( NODE ) {
2009-06-19 07:39:16 +00:00
/// <summary>Converts a jquery node with metadata to a NodeDefinition</summary>
//get our meta data stored with our node
var nodedata = $ ( NODE ) . children ( "a" ) . metadata ( { type : 'attr' , name : 'umb:nodedata' } ) ;
2009-10-11 12:23:27 +00:00
this . _debug ( "getNodeDef: " + $ ( NODE ) . attr ( "id" ) + ", " + nodedata . nodeType + ", " + nodedata . source ) ;
2009-06-19 07:39:16 +00:00
var def = new Umbraco . Controls . NodeDefinition ( ) ;
def . updateDefinition ( this . _tree , $ ( NODE ) , $ ( NODE ) . attr ( "id" ) , $ ( NODE ) . find ( "a > div" ) . html ( ) , nodedata . nodeType , nodedata . source , nodedata . menu , $ ( NODE ) . attr ( "umb:type" ) ) ;
return def ;
} ,
_updateRecycleBin : function ( ) {
2010-03-01 12:55:01 +00:00
/// <summary>Generally used for when a node is deleted. This will set the actionNode to the recycle bin node and force a refresh of it's children</summary>
2010-03-15 15:17:38 +00:00
this . _debug ( "_updateRecycleBin BinId: " + this . _opts . recycleBinId ) ;
2009-06-19 07:39:16 +00:00
2010-03-15 15:17:38 +00:00
var rNode = this . findNode ( this . _opts . recycleBinId , true ) ;
2009-06-19 07:39:16 +00:00
if ( rNode ) {
2009-10-11 12:23:27 +00:00
this . _actionNode = this . getNodeDef ( rNode ) ;
2009-06-19 07:39:16 +00:00
var _this = this ;
this . reloadActionNode ( true , true , function ( success ) {
if ( success ) {
2010-03-15 15:17:38 +00:00
_this . findNode ( _this . _opts . recycleBinId , true ) . effect ( "highlight" , { } , 1000 ) ;
2009-06-19 07:39:16 +00:00
}
} ) ;
}
} ,
2010-02-08 02:22:42 +00:00
_ensureContext : function ( ) {
/// <summary>
/// ensure that the tree object always has the correct context.
/// this is a fix for the TinyMCE dialog window, as it tends to lose object context for some wacky reason
/// when ajax calls are made. Works fine in all other instances.
/// </summary>
this . _tree . container = this . _getContainer ( ) ;
} ,
2009-06-19 07:39:16 +00:00
_loadChildNodes : function ( liNode , callback ) {
/// <summary>jsTree won't allow you to open a node that doesn't explitly have childen, this will force it to try</summary>
/// <param name="node">a jquery object for the current li node</param>
2009-10-19 13:35:14 +00:00
this . _debug ( "_loadChildNodes: " + liNode . attr ( "id" ) ) ;
2009-06-19 07:39:16 +00:00
liNode . removeClass ( "leaf" ) ;
2010-06-21 22:26:14 +00:00
var _this = this ;
//close branch will actually cause a select to happen so we'll intercept the select callback and then reset it once complete
//if we don't wan the event to fire, we'll set the callback to a null method and set it back after we call the select_branch method
this . _tree . settings . callback . onselect = function ( ) { } ;
this . _tree . close _branch ( liNode , true ) ;
this . _tree . settings . callback . onselect = function ( N , T ) { _this . onSelect ( N , T ) } ;
2009-10-19 13:35:14 +00:00
liNode . children ( "ul:eq(0)" ) . remove ( ) ;
2009-06-19 07:39:16 +00:00
this . _tree . open _branch ( liNode , false , callback ) ;
} ,
_syncTree : function ( path , forceReload , numPaths , numAsync ) {
/// <summary>
/// This is the internal method that will recursively search for the nodes to sync. If an invalid path is
/// passed to this method, it will raise an event which can be handled.
/// </summary>
/// <param name="path">The path of the node to find</param>
/// <param name="forceReload">If true, will ensure that the node to be synced is synced with data from the server</param>
/// <param name="numPaths">the number of id's deep to search starting from the end of the path. Used in recursion.</param>
/// <param name="numAsync">the number of async calls made so far to sync. Used in recursion and used to determine if the found node has been loaded by ajax.</param>
this . _debug ( "_syncTree" ) ;
var paths = path . split ( "," ) ;
var found = null ;
var foundIndex = null ;
if ( numPaths == null ) numPaths = ( paths . length - 0 ) ;
for ( var i = 0 ; i < numPaths ; i ++ ) {
foundIndex = paths . length - ( 1 + i ) ;
found = this . findNode ( paths [ foundIndex ] ) ;
this . _debug ( "_syncTree: finding... " + paths [ foundIndex ] + " found? " + found ) ;
if ( found ) break ;
}
//if no node has been found at all in the entire path, then bubble an error event
if ( ! found ) {
this . _debug ( "no node found in path: " + path + " : " + numPaths ) ;
2010-06-07 10:49:52 +00:00
this . _isSyncing = false ; //reset flag
2010-04-08 13:27:33 +00:00
this . _raiseEvent ( "syncNotFound" , [ path ] ) ;
2009-06-19 07:39:16 +00:00
return ;
}
//if the found node was not the end of the path, we need to load them in recursively.
if ( found . attr ( "id" ) != paths [ paths . length - 1 ] ) {
var _this = this ;
this . _loadChildNodes ( found , function ( NODE , TREE _OBJ ) {
//check if the next node to be found is in the children, if it is not, there's a problem bubble an event!
var pathsToSearch = paths . length - ( Number ( foundIndex ) + 1 ) ;
if ( _this . findNode ( paths [ foundIndex + 1 ] ) ) {
_this . _syncTree ( path , forceReload , pathsToSearch , ( numAsync == null ? numAsync == 1 : ++ numAsync ) ) ;
}
else {
_this . _debug ( "node not found in children: " + path + " : " + numPaths ) ;
2010-06-07 10:49:52 +00:00
this . _isSyncing = false ; //reset flag
2010-04-08 13:27:33 +00:00
_this . _raiseEvent ( "syncNotFound" , [ path ] ) ;
2009-06-19 07:39:16 +00:00
}
} ) ;
}
else {
//only force the reload of this nodes data if forceReload is specified and the node has not already come from the server
var doReload = ( forceReload && ( numAsync == null || numAsync < 1 ) ) ;
2010-06-07 10:49:52 +00:00
this . _debug ( "_syncTree: found! numAsync: " + numAsync + ", forceReload: " + forceReload + ", doReload: " + doReload ) ;
2009-06-19 07:39:16 +00:00
if ( doReload ) {
2009-10-11 12:23:27 +00:00
this . _actionNode = this . getNodeDef ( found ) ;
this . reloadActionNode ( false , true , null ) ;
2009-06-19 07:39:16 +00:00
}
else {
//we have found our node, select it but supress the selecting event
if ( found . attr ( "id" ) != "-1" ) this . selectNode ( found , true ) ;
this . _configureNodes ( found , doReload ) ;
}
2010-06-07 10:49:52 +00:00
this . _isSyncing = false ; //reset flag
2009-06-19 07:39:16 +00:00
//bubble event
2010-04-08 13:27:33 +00:00
this . _raiseEvent ( "syncFound" , [ found ] ) ;
2009-06-19 07:39:16 +00:00
}
} ,
2009-10-11 12:23:27 +00:00
_getUrlParams : function ( nodeSource ) {
/// <summary>This converts Url query string params to json</summary>
var p = { } ;
2010-06-21 14:55:18 +00:00
if ( nodeSource ) {
var urlSplit = nodeSource . split ( "?" ) ;
if ( urlSplit . length > 1 ) {
var sp = urlSplit [ 1 ] . split ( "&" ) ;
for ( var i = 0 ; i < sp . length ; i ++ ) {
var e = sp [ i ] . split ( "=" ) ;
p [ e [ 0 ] ] = e [ 1 ] ;
}
p [ "rnd2" ] = Umbraco . Utils . generateRandom ( ) ;
2010-06-21 14:52:22 +00:00
}
2010-06-21 14:55:18 +00:00
}
2009-10-11 12:23:27 +00:00
return p ;
2009-06-19 07:39:16 +00:00
} ,
2009-10-19 13:35:14 +00:00
2009-06-19 07:39:16 +00:00
_getUrl : function ( nodeSource ) {
/// <summary>Returns the json service url</summary>
if ( nodeSource == null || nodeSource == "" ) {
2010-03-15 15:17:38 +00:00
return this . _opts . dataUrl ;
2009-06-19 07:39:16 +00:00
}
var params = nodeSource . split ( "?" ) [ 1 ] ;
2010-03-15 15:17:38 +00:00
return this . _opts . dataUrl + "?" + params + "&rnd2=" + Umbraco . Utils . generateRandom ( ) ;
2009-06-19 07:39:16 +00:00
} ,
2010-02-08 02:22:42 +00:00
_getContainer : function ( ) {
return $ ( "#" + this . _containerId , this . _context ) ;
} ,
2009-10-11 12:23:27 +00:00
_getInitOptions : function ( initData ) {
2009-06-19 07:39:16 +00:00
/// <summary>return the initialization objects for the tree</summary>
2009-06-20 13:17:06 +00:00
this . _debug ( "_getInitOptions" ) ;
2009-07-13 14:51:26 +00:00
2009-06-19 07:39:16 +00:00
var _this = this ;
var options = {
data : {
type : "json" ,
async : true ,
2009-10-19 13:35:14 +00:00
opts : {
static : initData == null ? null : initData ,
2009-10-11 12:23:27 +00:00
method : "POST" ,
2010-03-15 15:17:38 +00:00
url : _this . _opts . serviceUrl ,
2009-10-11 12:23:27 +00:00
outer _attrib : [ "id" , "umb:type" , "class" , "rel" ] ,
inner _attrib : [ "umb:nodedata" , "href" , "class" , "style" ]
}
2009-06-19 07:39:16 +00:00
} ,
ui : {
dots : false ,
rtl : false ,
animation : false ,
hover _mode : true ,
2010-03-09 13:52:11 +00:00
theme _path : false ,
2009-10-11 12:23:27 +00:00
theme _name : "umbraco"
2009-06-19 07:39:16 +00:00
} ,
2009-10-11 12:23:27 +00:00
langs : {
2009-06-20 14:23:15 +00:00
new _node : "New folder" ,
loading : "<div>" + ( this . _tree . settings . lang . loading || "Loading ..." ) + "</div>"
2009-06-19 09:32:18 +00:00
} ,
2009-06-19 07:39:16 +00:00
callback : {
2009-10-11 12:23:27 +00:00
//ensures that the node id isn't appended to the async url
2009-10-19 13:35:14 +00:00
beforedata : function ( N , T ) { return _this . onBeforeRequest ( N , T ) ; } ,
2009-06-19 07:39:16 +00:00
//wrapped functions maintain scope in callback
2009-10-11 12:23:27 +00:00
beforeopen : function ( N , T ) { _this . onBeforeOpen ( N , T ) ; } ,
onselect : function ( N , T ) { _this . onSelect ( N , T ) ; } ,
onchange : function ( N , T ) { _this . onChange ( N , T ) ; } ,
ondata : function ( D , T ) { return _this . onJSONData ( D , T ) ; } ,
onload : function ( T ) { if ( initData == null ) _this . onLoad ( T ) ; } ,
2010-02-08 02:22:42 +00:00
onparse : function ( S , T ) { return _this . onParse ( S , T ) ; } ,
2010-04-08 13:27:33 +00:00
error : function ( E , T ) { _this . onError ( E , T ) ; } ,
ondestroy : function ( T ) { _this . onDestroy ( T ) ; }
2009-10-11 12:23:27 +00:00
} ,
plugins : {
//UmbracoContext comes before context menu so that the events fire first
UmbracoContext : {
2010-03-15 15:17:38 +00:00
fullMenu : _this . _opts . jsonFullMenu ,
2009-10-19 13:35:14 +00:00
onBeforeContext : function ( N , T , E ) { return _this . onBeforeContext ( N , T , E ) ; }
2009-10-11 12:23:27 +00:00
} ,
contextmenu : { }
2009-06-19 07:39:16 +00:00
}
} ;
2010-03-15 15:17:38 +00:00
if ( this . _opts . treeMode != "standard" ) {
2009-10-19 13:35:14 +00:00
options . plugins . checkbox = { three _state : false }
2009-10-11 12:23:27 +00:00
}
2009-10-19 13:35:14 +00:00
2010-03-01 12:55:01 +00:00
//if there's no service URL, then disable ajax requests
2010-03-15 15:17:38 +00:00
if ( this . _opts . serviceUrl == "" || this . _opts . dataUrl == "" ) {
2010-03-01 12:55:01 +00:00
options . data . async = false ;
options . data . opts . static = { } ;
}
2009-10-11 12:23:27 +00:00
//set global ajax settings:
$ . ajaxSetup ( {
2009-10-19 13:35:14 +00:00
contentType : "application/json; charset=utf-8"
2009-10-11 12:23:27 +00:00
} ) ;
2009-06-19 07:39:16 +00:00
2010-03-01 12:55:01 +00:00
this . _debug ( "_getInitOptions. Async enabled = " + options . data . async ) ;
2009-10-19 13:35:14 +00:00
2009-06-19 07:39:16 +00:00
return options ;
}
} ;
}
2010-02-08 02:22:42 +00:00
// instance manager
Umbraco . Controls . UmbracoTree . cntr = 0 ;
Umbraco . Controls . UmbracoTree . inst = { } ;
2010-04-08 13:27:33 +00:00
} ) ( jQuery ) ;