1function settingstree_show_export(opts){
2	if (!opts.on_complete || typeof window[opts.on_complete] !== 'function'){
3		return false;	// return failure if the callback is incomplete (popup won't be shown).
4	};
5	var $ = jQuery,
6		$grayout = $('<div id="settingstree_grayout"></div>').appendTo('body'),
7		$container = $('<div id="settingstree_export_popup_container"></div>').appendTo($grayout),
8		$dialog = $('<div class="settingstree_export_popup_layer"></div>').appendTo($container),
9		$close = $('<div class="settingstree_export_popup_close_button"> x </div>').appendTo($container),
10		$form = $('<form class="settingstree_area" method="get"></form>').appendTo($dialog);
11	$close.on('click', function(e){ $form.trigger('settingstree_close');});
12	opts._export = true;
13	$form.settingsTree(opts);
14	$form.on('settingstree_export_complete',function(e,values,changes){
15		$form.trigger('settingstree_close'); // clean up before calling the on_complete.
16		window[opts.on_complete].call(null,values,changes);	// call the on_complete callback.
17	});
18	$form.on('settingstree_close', function(){$grayout.remove();}); // destroying the root with jQuery automatically removes all registered data from the memory.
19	return true; // return success (popup fill be shown).
20};
21jQuery.fn.settingsTree = function(opts){
22	if (jQuery(this).length !== 1){
23		throw 'There must be exactly one settingsTree instance on a page!';
24	}
25	var $ = jQuery, $root = $(this), opts = $.extend({},opts), pluginname = opts.pluginname, token = opts.token, pending = false, path = ':',_export = opts._export||false,
26		getLang = function (msgid){
27			var str;
28			if ((str = LANG.plugins.settingstree[msgid]) === undefined){
29				str = '{msgid:'+msgid+'}';	// Note: if lang keys needs to be html escaped then there is a conceptual problem about msgids...
30			}
31			return str;
32		},
33		getchanged = function(){
34			var values = {};
35			$root.find('.input_area.changed, .protect_area.changed').each(function(){
36				var $inp = $(this).find('input, textarea, select'), name = $inp.prop('name'), val = $inp.val(), m;
37				if ($inp.is(':checkbox')){
38					val = $inp.prop('checked') ? 1 : 0;
39				}
40				if (!(m = name.match(/^(config|protect)\[(.*)\]$/))){	// we don't know what area is this...
41					return;
42				}
43				if (!values[m[2]]){ values[m[2]] = {};}
44				values[m[2]][m[1]] = val;
45			});
46			return values;
47		},
48		savelevel = function(){	// save: we save all data, and if it's successful, we display the save options by their new values or display invalid data on error.
49			$root.find('.settingstree_error_area').html($("<div class='notify'>"+getLang('saving_changes')+"</div>"));
50			var changes = getchanged();
51			$.post(DOKU_BASE + 'lib/exe/ajax.php',
52				{ call:'plugin_settingstree', operation: 'savelevel', pluginname: pluginname, path: path, sectok: token, data: changes },
53				function(r){
54					if (r.token) token = r.token;
55					if (r.html){ $root.html(r.html);	}
56					if (r.success){
57						$root.find('.settingstree_error_area').html(("<div class='success'>"+(r.msg||"success")+"</div>"));
58						// update the hierarchy, if it was changed by the save, and save was successful.
59						var key = $('.settingstree_left_column').data('current');
60						if (key && changes[key] !== undefined){
61							$('.settingstree_left_column').data('current',null);
62							show_in_hierarchy(key,path);
63						}
64					}
65					else{			$root.find('.settingstree_error_area').html($("<div class='error'>"+(r.msg||"fail")+"</div>"));		}
66				}
67			);
68		},
69		simplify = function(conf){
70			var ret = {};
71			for (var key in conf){
72				if (typeof conf[key] === 'object' && typeof conf[key].config !== undefined)
73					ret[key] = conf[key].config;
74			}
75			return ret;
76		},
77		exportlevel = function(){	// export: we check all data - but not save it, and if it's successful, we are finished. on error we display invalid data an all changes.
78			$root.find('.settingstree_error_area').html($("<div class='notify'>"+getLang('preparing_export')+"</div>"));
79			var changes = getchanged();
80			$.post(DOKU_BASE + 'lib/exe/ajax.php',
81				{ call:'plugin_settingstree', operation: 'exportlevel', pluginname: pluginname, path: path, sectok: token, data: changes, options: opts.options },
82				function(r){
83					if (r.token) token = r.token;
84					if (r.html){ $root.html(r.html);	}
85					if (r.success){		$root.trigger('settingstree_export_complete',[r.values,simplify(changes)]);					}
86					else{			$root.find('.settingstree_error_area').html($("<div class='error'>"+(r.msg||"fail")+"</div>"));		}
87				}
88			);
89
90		},
91		resetlevel = function(){
92			$root.find('.input_area.changed, .protect_area.changed').each(function(){
93				var $inp = $(this).find('input, textarea, select'), val =$inp.val(), def = $(this).data('currentval');
94				if ($inp.is(':checkbox')){
95					$inp.prop('checked',def ? true : false);
96				}else{
97					$inp.val(def);
98				}
99				$(this).removeClass('changed');
100			})
101		},
102		inputchange = function(){
103			var $inp = $(this), $inpa = $inp.parents('.input_area:first, .protect_area:first'), val = $inp.val();
104			if ($inp.is(':checkbox')){
105				val = $inp.prop('checked') ? 1 : 0;
106			}
107			if (val == $inpa.data('currentval')){
108				$inpa.removeClass('changed');
109			}else{
110				$inpa.addClass('changed');
111			}
112		},
113		has_pending = function(){
114			return (pending || $root.has('.input_area.changed, .protect_area.changed').length);
115		},
116		open_hierarchy_level = function(open_level){
117			var $hier = $('.settingstree_left_column');
118			$hier.find('.highlighted_level').removeClass('highlighted_level');
119			$hier.find('[data-path="'+open_level+'"]').addClass('highlighted_level');
120		},
121		selectlevel = function(id){
122			if (has_pending()){
123				$root.find('.settingstree_error_area').html($("<div class='error'><h4>"+getLang('pending_change')+"</h4><p>"+getLang('pending_change_explain')+"</p></div>"));
124				return;
125			}
126			$root.html('<div class="settingstree_error_area"><div class="notify">'+getLang('loading_level')+'</div></div>');
127			$.post(DOKU_BASE + 'lib/exe/ajax.php',
128				{ call:'plugin_settingstree', operation: 'loadlevel', pluginname: pluginname, path: id, sectok: token, showtype: _export? 'export' : 'normal', options: opts.options },
129				function(r){
130					if (r.token) token = r.token;
131					if (r.error) alert(r.msg);
132					if (r.path){ path = r.path;	}
133					if (r.html){
134						$root.html(r.html);
135						var key = $('.settingstree_left_column').data('current');
136						if (key){
137							show_in_hierarchy(key,path);
138						}
139					}
140					else alert('Error: html not loaded!');
141				}
142			);
143		},
144		show_in_hierarchy = function(key,open_level){
145			var $left_col = $('.settingstree_left_column'), current = $left_col.data('current')||null;
146			if (current !== key){
147				$left_col.html('<div class="notify">'+getLang('loading_hierarchy')+'</div>');
148				$.post(DOKU_BASE + 'lib/exe/ajax.php',
149					{ call:'plugin_settingstree', operation: 'show_hierarchy', pluginname: pluginname, key: key, sectok: token },
150					function(r){
151						if (r.token) token = r.token;
152						if (r.error) alert(r.msg);
153						if (r.html){
154							$left_col.html(r.html);
155							$left_col.data('current',key);
156							open_hierarchy_level(open_level);
157						}
158						else alert('Error: html not loaded!');
159					}
160				);
161			}else{
162				open_hierarchy_level(open_level);
163			}
164		}
165		;
166	if (typeof opts.explorertree_id === 'string'){
167		$(document).ready(function(){
168			$('#'+opts.explorertree_id).on('tree_selected',function (event,id){ selectlevel(id); });
169		});
170	}
171	if (!(path = $root.find('#config__manager').data('path'))){ path = ':';}
172	if (typeof opts.path === 'string' && opts.path !== path){
173		selectlevel(opts.path);
174	}
175	// we're delegating the listener to the $root (the container) so events will bubble up to it for any dynamically placed children.
176	$root.on('select_level', function (event,id){ selectlevel(id); });
177	$root.on('show_in_hierarchy',function(event,key,open_level){ show_in_hierarchy(key,open_level); });
178	$root.on('change','input, textarea, select',inputchange);
179	$root.on('settingstree_save',savelevel);
180	$root.on('settingstree_export',exportlevel);
181	$root.on('settingstree_cancel',resetlevel);
182	return this;
183};