docs: function(protocol, name, host, opt) {
  		var noOffline = typeof opt === 'boolean'? opt : null, // for backward compat
  			opts = Object.assign({
  				noOffline : false,
  				root      : 'root',
  				pathI18n  : 'folderId',
  				folders   : true
  			}, (noOffline === null? (opt || {}) : {noOffline : noOffline})),
  			addFolders = function(fm, bro, folders) {
  				var self = this,
  					cnt  = Object.keys($.isPlainObject(folders)? folders : {}).length,
  					select;
  				
  				bro.next().remove();
  				if (cnt) {
  					select = $('<select class="ui-corner-all elfinder-tabstop" style="max-width:200px;">').append(
  						$($.map(folders, function(n,i){return '<option value="'+fm.escape((i+'').trim())+'">'+fm.escape(n)+'</option>';}).join(''))
  					).on('change click', function(e){
  						var node = $(this),
  							path = node.val(),
  							spn;
  						self.inputs.path.val(path);
  						if (opts.folders && (e.type === 'change' || node.data('current') !== path)) {
  							node.next().remove();
  							node.data('current', path);
  							if (path != opts.root) {
  								spn = spinner();
  								if (xhr && xhr.state() === 'pending') {
  									fm.abortXHR(xhr, { quiet: true , abort: true });
  								}
  								node.after(spn);
  								xhr = fm.request({
  									data : {cmd : 'netmount', protocol: protocol, host: host, user: 'init', path: path, pass: 'folders'},
  									preventDefault : true
  								}).done(function(data){
  									addFolders.call(self, fm, node, data.folders);
  								}).always(function() {
  									fm.abortXHR(xhr, { quiet: true });
  									spn.remove();
  								}).xhr;
  							}
  						}
  					});
  					bro.after($('<div/>').append(select))
  						.closest('.ui-dialog').trigger('tabstopsInit');
  					select.trigger('focus');
  				}
  			},
  			spinner = function() {
  				return $('<div class="elfinder-netmount-spinner"/>').append('<span class="elfinder-info-spinner"/>');
  			},
  			xhr;
  		return {
  			vars : {},
  			name : name,
  			inputs: {
  				offline  : $('<input type="checkbox"/>').on('change', function() {
  					$(this).parents('table.elfinder-netmount-tb').find('select:first').trigger('change', 'reset');
  				}),
  				host     : $('<span><span class="elfinder-info-spinner"/></span><input type="hidden"/>'),
  				path     : $('<input type="text" value="'+opts.root+'"/>'),
  				user     : $('<input type="hidden"/>'),
  				pass     : $('<input type="hidden"/>')
  			},
  			select: function(fm, ev, d){
  				var f = this.inputs,
  					oline = f.offline,
  					f0 = $(f.host[0]),
  					data = d || null;
  				this.vars.mbtn = f.host.closest('.ui-dialog').children('.ui-dialog-buttonpane:first').find('button.elfinder-btncnt-0');
  				if (! f0.data('inrequest')
  						&& (f0.find('span.elfinder-info-spinner').length
  							|| data === 'reset'
  							|| (data === 'winfocus' && ! f0.siblings('span.elfinder-button-icon-reload').length))
  							)
  				{
  					if (oline.parent().children().length === 1) {
  						f.path.parent().prev().html(fm.i18n(opts.pathI18n));
  						oline.attr('title', fm.i18n('offlineAccess'));
  						oline.uniqueId().after($('<label/>').attr('for', oline.attr('id')).html(' '+fm.i18n('offlineAccess')));
  					}
  					f0.data('inrequest', true).empty().addClass('elfinder-info-spinner')
  						.parent().find('span.elfinder-button-icon').remove();
  					fm.request({
  						data : {cmd : 'netmount', protocol: protocol, host: host, user: 'init', options: {id: fm.id, offline: oline.prop('checked')? 1:0, pass: f.host[1].value}},
  						preventDefault : true
  					}).done(function(data){
  						f0.removeClass("elfinder-info-spinner").html(data.body.replace(/\{msg:([^}]+)\}/g, function(whole,s1){return fm.i18n(s1, host);}));
  					});
  					opts.noOffline && oline.closest('tr').hide();
  				} else {
  					oline.closest('tr')[(opts.noOffline || f.user.val())? 'hide':'show']();
  					f0.data('funcexpup') && f0.data('funcexpup')();
  				}
  				this.vars.mbtn[$(f.host[1]).val()? 'show':'hide']();
  			},
  			done: function(fm, data){
  				var f = this.inputs,
  					p = this.protocol,
  					f0 = $(f.host[0]),
  					f1 = $(f.host[1]),
  					expires = '&nbsp;';
  				
  				opts.noOffline && f.offline.closest('tr').hide();
  				if (data.mode == 'makebtn') {
  					f0.removeClass('elfinder-info-spinner').removeData('expires').removeData('funcexpup');
  					f.host.find('input').on('mouseenter mouseleave', function(){$(this).toggleClass('ui-state-hover');});
  					f1.val('');
  					f.path.val(opts.root).next().remove();
  					f.user.val('');
  					f.pass.val('');
  					! opts.noOffline && f.offline.closest('tr').show();
  					this.vars.mbtn.hide();
  				} else if (data.mode == 'folders') {
  					if (data.folders) {
  						addFolders.call(this, fm, f.path.nextAll(':last'), data.folders);
  					}
  				} else {
  					if (data.expires) {
  						expires = '()';
  						f0.data('expires', data.expires);
  					}
  					f0.html(host + expires).removeClass('elfinder-info-spinner');
  					if (data.expires) {
  						f0.data('funcexpup', function() {
  							var rem = Math.floor((f0.data('expires') - (+new Date()) / 1000) / 60);
  							if (rem < 3) {
  								f0.parent().children('.elfinder-button-icon-reload').click();
  							} else {
  								f0.text(f0.text().replace(/\(.*\)/, '('+fm.i18n(['minsLeft', rem])+')'));
  								setTimeout(function() {
  									if (f0.is(':visible')) {
  										f0.data('funcexpup')();
  									}
  								}, 60000);
  							}
  						});
  						f0.data('funcexpup')();
  					}
  					if (data.reset) {
  						p.trigger('change', 'reset');
  						return;
  					}
  					f0.parent().append($('<span class="elfinder-button-icon elfinder-button-icon-reload" title="'+fm.i18n('reAuth')+'">')
  						.on('click', function() {
  							f1.val('reauth');
  							p.trigger('change', 'reset');
  						}));
  					f1.val(protocol);
  					this.vars.mbtn.show();
  					if (data.folders) {
  						addFolders.call(this, fm, f.path, data.folders);
  					}
  					f.user.val('done');
  					f.pass.val('done');
  					f.offline.closest('tr').hide();
  				}
  				f0.removeData('inrequest');
  			},
  			fail: function(fm, err){
  				$(this.inputs.host[0]).removeData('inrequest');
  				this.protocol.trigger('change', 'reset');
  			},
  			integrateInfo: opts.integrate
  		};
  	}