MDL-29766 Add drag and drop upload to filemanager / filepicker elements
[moodle.git] / lib / form / filemanager.js
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15 /**
16  *
17  * File Manager UI
18  * =====
19  * this.api, stores the URL to make ajax request
20  * this.currentpath
21  * this.filepicker_options
22  * this.movefile_dialog
23  * this.mkdir_dialog
24  * this.rename_dialog
25  * this.client_id
26  * this.filecount, how many files in this filemanager
27  * this.maxfiles
28  * this.maxbytes
29  *
30  * FileManager options:
31  * =====
32  * this.options.currentpath
33  * this.options.itemid
34  */
37 M.form_filemanager = {};
39 /**
40  * This fucntion is called for each file picker on page.
41  */
42 M.form_filemanager.init = function(Y, options) {
43     var FileManagerHelper = function(options) {
44         FileManagerHelper.superclass.constructor.apply(this, arguments);
45     };
46     FileManagerHelper.NAME = "FileManager";
47     FileManagerHelper.ATTRS = {
48         options: {},
49         lang: {}
50     };
52     Y.extend(FileManagerHelper, Y.Base, {
53         api: M.cfg.wwwroot+'/repository/draftfiles_ajax.php',
54         menus: {},
55         initializer: function(options) {
56             this.options = options;
57             if (options.mainfile) {
58                 this.enablemainfile = options.mainfile;
59             }
60             this.client_id = options.client_id;
61             this.currentpath = '/';
62             this.maxfiles = options.maxfiles;
63             this.maxbytes = options.maxbytes;
65             this.filepicker_options = options.filepicker?options.filepicker:{};
66             this.filepicker_options.client_id = this.client_id;
67             this.filepicker_options.context = options.context;
68             this.filepicker_options.maxfiles = this.maxfiles;
69             this.filepicker_options.maxbytes = this.maxbytes;
70             this.filepicker_options.env = 'filemanager';
71             this.filepicker_options.itemid = options.itemid;
73             if (options.filecount) {
74                 this.filecount = options.filecount;
75             } else {
76                 this.filecount = 0;
77             }
78             this.setup_buttons();
79             this.render();
80         },
82         wait: function(client_id) {
83             var container = Y.one('#filemanager-'+client_id);
84             container.set('innerHTML', '');
85             var html = Y.Node.create('<ul id="draftfiles-'+client_id+'"></ul>');
86             container.appendChild(html);
87             var panel = Y.one('#draftfiles-'+client_id);
88             var name = '';
89             var str = '<div style="text-align:center">';
90             str += '<img src="'+M.util.image_url('i/loading_small')+'" />';
91             str += '</div>';
92             try {
93                 panel.set('innerHTML', str);
94             } catch(e) {
95                 alert(e.toString());
96             }
97         },
98         request: function(args, redraw) {
99             var api = this.api + '?action='+args.action;
100             var params = {};
101             var scope = this;
102             if (args['scope']) {
103                 scope = args['scope'];
104             }
105             params['sesskey'] = M.cfg.sesskey;
106             params['client_id'] = this.client_id;
107             params['filepath'] = this.currentpath;
108             params['itemid'] = this.options.itemid?this.options.itemid:0;
109             if (args['params']) {
110                 for (i in args['params']) {
111                     params[i] = args['params'][i];
112                 }
113             }
114             var cfg = {
115                 method: 'POST',
116                 on: {
117                     complete: function(id,o,p) {
118                         if (!o) {
119                             alert('IO FATAL');
120                             return;
121                         }
122                         var data = Y.JSON.parse(o.responseText);
123                         args.callback(id,data,p);
124                     }
125                 },
126                 arguments: {
127                     scope: scope
128                 },
129                 headers: {
130                     'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
131                     'User-Agent': 'MoodleFileManager/3.0'
132                 },
133                 data: build_querystring(params)
134             };
135             if (args.form) {
136                 cfg.form = args.form;
137             }
138             Y.io(api, cfg);
139             if (redraw) {
140                 this.wait(this.client_id);
141             }
142         },
143         filepicker_callback: function(obj) {
144             var button_addfile  = Y.one("#btnadd-"+this.client_id);
145             this.filecount++;
146             if (this.filecount > 0) {
147                 Y.one("#btndwn-"+this.client_id).setStyle('display', 'inline');
148             }
149             if (this.filecount >= this.maxfiles && this.maxfiles!=-1) {
150                 button_addfile.setStyle('display', 'none');
151             }
152             this.refresh(this.currentpath);
153         },
154         refresh: function(filepath) {
155             var scope = this;
156             this.currentpath = filepath;
157             if (!filepath) {
158                 filepath = this.currentpath;
159             } else {
160                 this.currentpath = filepath;
161             }
162             this.request({
163                 action: 'list',
164                 scope: scope,
165                 params: {'filepath':filepath},
166                 callback: function(id, obj, args) {
167                     scope.options = obj;
168                     scope.render(obj);
169                 }
170             }, true);
171         },
172         setup_buttons: function() {
173             var button_download = Y.one("#btndwn-"+this.client_id);
174             var button_create   = Y.one("#btncrt-"+this.client_id);
175             var button_addfile  = Y.one("#btnadd-"+this.client_id);
177             // setup 'add file' button
178             // if maxfiles == -1, the no limit
179             if (this.filecount >= this.maxfiles
180                     && this.maxfiles!=-1) {
181                 button_addfile.setStyle('display', 'none');
182             } else {
183                 button_addfile.on('click', function(e) {
184                     var options = this.filepicker_options;
185                     options.formcallback = this.filepicker_callback;
186                     // XXX: magic here, to let filepicker use filemanager scope
187                     options.magicscope = this;
188                     options.savepath = this.currentpath;
189                     M.core_filepicker.show(Y, options);
190                 }, this);
191             }
193             // setup 'make a folder' button
194             if (this.options.subdirs) {
195                 button_create.on('click',function(e) {
196                     var scope = this;
197                     // a function used to perform an ajax request
198                     function perform_action(e) {
199                         var foldername = Y.one('#fm-newname').get('value');
200                         if (!foldername) {
201                             return;
202                         }
203                         scope.request({
204                             action:'mkdir',
205                             params: {filepath:scope.currentpath, newdirname:foldername},
206                             callback: function(id, obj, args) {
207                                 var filepath = obj.filepath;
208                                 scope.mkdir_dialog.hide();
209                                 scope.refresh(filepath);
210                                 Y.one('#fm-newname').set('value', '');
211                             }
212                         });
213                     }
214                     if (!Y.one('#fm-mkdir-dlg')) {
215                         var dialog = Y.Node.create('<div id="fm-mkdir-dlg"><div class="hd">'+M.str.repository.entername+'</div><div class="bd"><input type="text" id="fm-newname" /></div></div>');
216                         Y.one(document.body).appendChild(dialog);
217                         this.mkdir_dialog = new YAHOO.widget.Dialog("fm-mkdir-dlg", {
218                              width: "300px",
219                              visible: true,
220                              x:e.pageX,
221                              y:e.pageY,
222                              constraintoviewport : true
223                              });
225                     }
226                     var buttons = [ { text:M.str.moodle.ok, handler:perform_action, isDefault:true },
227                                   { text:M.str.moodle.cancel, handler:function(){this.cancel();}}];
229                     this.mkdir_dialog.cfg.queueProperty("buttons", buttons);
230                     this.mkdir_dialog.render();
231                     this.mkdir_dialog.show();
232                 }, this);
233             } else {
234                 button_create.setStyle('display', 'none');
235             }
237             // setup 'download this folder' button
238             // NOTE: popup window must be enabled to perform download process
239             button_download.on('click',function() {
240                 var scope = this;
241                 // perform downloaddir ajax request
242                 this.request({
243                     action: 'downloaddir',
244                     scope: scope,
245                     callback: function(id, obj, args) {
246                         if (obj) {
247                             scope.refresh(obj.filepath);
248                             var win = window.open(obj.fileurl, 'fm-download-folder');
249                             if (!win) {
250                                 alert(M.str.repository.popupblockeddownload);
251                             }
252                         } else {
253                             alert(M.str.repository.draftareanofiles);
254                         }
255                     }
256                 });
257             }, this);
258         },
259         empty_filelist: function(container) {
260             container.set('innerHTML', '<div class="mdl-align">'+M.str.repository.nofilesattached+'</div>');
261         },
262         render: function() {
263             var options = this.options;
264             var path = this.options.path;
265             var list = this.options.list;
266             var breadcrumb = Y.one('#fm-path-'+this.client_id);
267             // build breadcrumb
268             if (path) {
269                 // empty breadcrumb
270                 breadcrumb.set('innerHTML', '');
271                 var count = 0;
272                 for(var p in path) {
273                     var arrow = '';
274                     if (count==0) {
275                         arrow = Y.Node.create('<span>'+M.str.moodle.path + ': </span>');
276                     } else {
277                         arrow = Y.Node.create('<span> ▶ </span>');
278                     }
279                     count++;
281                     var pathid  = 'fm-path-node-'+this.client_id;
282                     pathid += ('-'+count);
284                     var crumb = Y.Node.create('<a href="###" id="'+pathid+'">'+path[p].name+'</a>');
285                     breadcrumb.appendChild(arrow);
286                     breadcrumb.appendChild(crumb);
288                     var args = {};
289                     args.requestpath = path[p].path;
290                     args.client_id = this.client_id;
291                     Y.one('#'+pathid).on('click', function(e, args) {
292                         var scope = this;
293                         var params = {};
294                         params['filepath'] = args.requestpath;
295                         this.currentpath = args.requestpath;
296                         this.request({
297                             action: 'list',
298                             scope: scope,
299                             params: params,
300                             callback: function(id, obj, args) {
301                                 scope.options = obj;
302                                 scope.render(obj);
303                             }
304                         }, true);
305                     }, this, args);
306                 }
307             }
308             var template = Y.one('#fm-template');
309             var container = Y.one('#filemanager-' + this.client_id);
310             var listhtml = '';
312             // folder list items
313             var folder_ids = [];
314             var folder_data = {};
316             // normal file list items
317             var file_ids   = [];
318             var file_data  = {};
320             // archives list items
321             var zip_ids    = [];
322             var zip_data = {};
324             var html_ids = [];
325             var html_data = {};
327             file_data.itemid = folder_data.itemid = zip_data.itemid = options.itemid;
328             file_data.client_id = folder_data.client_id = zip_data.client_id = this.client_id;
330             var foldername_ids = [];
331             if (!list || list.length == 0) {
332                 // hide file browser and breadcrumb
333                 //container.setStyle('display', 'none');
334                 this.empty_filelist(container);
335                 if (!path || path.length <= 1) {
336                     breadcrumb.setStyle('display', 'none');
337                 }
338                 return;
339             } else {
340                 container.setStyle('display', 'block');
341                 breadcrumb.setStyle('display', 'block');
342             }
344             var count = 0;
345             for(var i in list) {
346                 count++;
347                 // the li html element
348                 var htmlid = 'fileitem-'+this.client_id+'-'+count;
349                 // link to file
350                 var fileid = 'filename-'+this.client_id+'-'+count;
351                 // file menu
352                 var action = 'action-'  +this.client_id+'-'+count;
354                 var html = template.get('innerHTML');
356                 html_ids.push('#'+htmlid);
357                 html_data[htmlid] = action;
359                 list[i].htmlid = htmlid;
360                 list[i].fileid = fileid;
361                 list[i].action = action;
363                 var url = "###";
365                 switch (list[i].type) {
366                     case 'folder':
367                         // click folder name
368                         foldername_ids.push('#'+fileid);
369                         // click folder menu
370                         folder_ids.push('#'+action);
371                         folder_data[action] = list[i];
372                         folder_data[fileid] = list[i];
373                         break;
374                     case 'file':
375                         file_ids.push('#'+action);
376                         // click file name
377                         file_ids.push('#'+fileid);
378                         file_data[action] = list[i];
379                         file_data[fileid] = list[i];
380                         if (list[i].url) {
381                             url = list[i].url;
382                         }
383                     break;
384                     case 'zip':
385                         zip_ids.push('#'+action);
386                         zip_ids.push('#'+fileid);
387                         zip_data[action] = list[i];
388                         zip_data[fileid] = list[i];
389                         if (list[i].url) {
390                             url = list[i].url;
391                         }
392                     break;
393                 }
394                 var fullname = list[i].fullname;
396                 if (list[i].sortorder == 1) {
397                     html = html.replace('___fullname___', '<strong><a title="'+fullname+'" href="'+url+'" id="'+fileid+'"><img src="'+list[i].icon+'" /> ' + fullname + '</a></strong>');
398                 } else {
399                     html = html.replace('___fullname___', '<a title="'+fullname+'" href="'+url+'" id="'+fileid+'"><img src="'+list[i].icon+'" /> ' + fullname + '</a>');
400                 }
401                 html = html.replace('___action___', '<span class="fm-menuicon" id="'+action+'"><img alt="▶" src="'+M.util.image_url('i/menu')+'" /></span>');
402                 html = '<li id="'+htmlid+'">'+html+'</li>';
403                 listhtml += html;
404             }
405             if (!Y.one('#draftfiles-'+this.client_id)) {
406                 var filelist = Y.Node.create('<ul id="draftfiles-'+this.client_id+'"></ul>');
407                 container.appendChild(filelist);
408             }
409             Y.one('#draftfiles-'+this.client_id).set('innerHTML', listhtml);
411             // click normal file menu
412             Y.on('click', this.create_filemenu, file_ids, this, file_data);
413             Y.on('contextmenu', this.create_filemenu, file_ids, this, file_data);
414             // click folder menu
415             Y.on('click', this.create_foldermenu, folder_ids, this, folder_data);
416             Y.on('contextmenu', this.create_foldermenu, folder_ids, this, folder_data);
417             Y.on('contextmenu', this.create_foldermenu, foldername_ids, this, folder_data);
418             // click archievs menu
419             Y.on('click', this.create_zipmenu, zip_ids, this, zip_data);
420             Y.on('contextmenu', this.create_zipmenu, zip_ids, this, zip_data);
421             // click folder name
422             Y.on('click', this.enter_folder, foldername_ids, this, folder_data);
423         },
424         enter_folder: function(e, data) {
425             var node = e.currentTarget;
426             var file = data[node.get('id')];
427             this.refresh(file.filepath);
428         },
429         create_filemenu: function(e, data) {
430             e.preventDefault();
431             var options = this.options;
432             var node = e.currentTarget;
433             var file = data[node.get('id')];
434             var scope = this;
436             var menuitems = [
437                 {text: M.str.moodle.download, url:file.url}
438                 ];
439             function setmainfile(type, ev, obj) {
440                 var file = obj[node.get('id')];
441                 //Y.one(mainid).set('value', file.filepath+file.filename);
442                 var params = {};
443                 params['filepath']   = file.filepath;
444                 params['filename']   = file.filename;
445                 this.request({
446                     action: 'setmainfile',
447                     scope: scope,
448                     params: params,
449                     callback: function(id, obj, args) {
450                         scope.refresh(scope.currentpath);
451                     }
452                 });
453             }
454             if (this.enablemainfile && (file.sortorder != 1)) {
455                 var mainid = '#id_'+this.enablemainfile;
456                 var menu = {text: M.str.repository.setmainfile, onclick:{fn: setmainfile, obj:data, scope:this}};
457                 menuitems.push(menu);
458             }
459             this.create_menu(e, 'filemenu', menuitems, file, data);
460         },
461         create_foldermenu: function(e, data) {
462             e.preventDefault();
463             var scope = this;
464             var node = e.currentTarget;
465             var fileinfo = data[node.get('id')];
466             // an extra menu item for folder to zip it
467             function archive_folder(type,ev,obj) {
468                 var params = {};
469                 params['filepath']   = fileinfo.filepath;
470                 params['filename']   = '.';
471                 this.request({
472                     action: 'zip',
473                     scope: scope,
474                     params: params,
475                     callback: function(id, obj, args) {
476                         scope.refresh(obj.filepath);
477                     }
478                 });
479             }
480             var menuitems = [
481                 {text: M.str.editor.zip, onclick: {fn: archive_folder, obj: data, scope: this}},
482                 ];
483             this.create_menu(e, 'foldermenu', menuitems, fileinfo, data);
484         },
485         create_zipmenu: function(e, data) {
486             e.preventDefault();
487             var scope = this;
488             var node = e.currentTarget;
489             var fileinfo = data[node.get('id')];
491             function unzip(type, ev, obj) {
492                 var params = {};
493                 params['filepath'] = fileinfo.filepath;
494                 params['filename'] = fileinfo.fullname;
495                 this.request({
496                     action: 'unzip',
497                     scope: scope,
498                     params: params,
499                     callback: function(id, obj, args) {
500                         scope.refresh(obj.filepath);
501                     }
502                 });
503             }
504             var menuitems = [
505                 {text: M.str.moodle.download, url:fileinfo.url},
506                 {text: M.str.moodle.unzip, onclick: {fn: unzip, obj: data, scope: this}}
507                 ];
508             function setmainfile(type, ev, obj) {
509                 var file = obj[node.get('id')];
510                 //Y.one(mainid).set('value', file.filepath+file.filename);
511                 var params = {};
512                 params['filepath']   = file.filepath;
513                 params['filename']   = file.filename;
514                 this.request({
515                     action: 'setmainfile',
516                     scope: scope,
517                     params: params,
518                     callback: function(id, obj, args) {
519                         scope.refresh(scope.currentpath);
520                     }
521                 });
522             }
523             if (this.enablemainfile && (fileinfo.sortorder != 1)) {
524                 var mainid = '#id_'+this.enablemainfile;
525                 var menu = {text: M.str.repository.setmainfile, onclick:{fn: setmainfile, obj:data, scope:this}};
526                 menuitems.push(menu);
527             }
528             this.create_menu(e, 'zipmenu', menuitems, fileinfo, data);
529         },
530         create_menu: function(ev, menuid, menuitems, fileinfo, options) {
531             var position = [ev.pageX, ev.pageY];
532             var scope = this;
533             function remove(type, ev, obj) {
534                 var dialog_options = {};
535                 var params = {};
536                 dialog_options.message = M.str.repository.confirmdeletefile;
537                 dialog_options.scope = this;
538                 var filename = '';
539                 var filepath = '';
540                 if (fileinfo.type == 'folder') {
541                     params.filename = '.';
542                     params.filepath = fileinfo.filepath;
543                 } else {
544                     params.filename = fileinfo.fullname;
545                 }
546                 dialog_options.callbackargs = [params];
547                 dialog_options.callback = function(params) {
548                     this.request({
549                         action: 'delete',
550                         scope: this,
551                         params: params,
552                         callback: function(id, obj, args) {
553                             scope.filecount--;
554                             scope.refresh(obj.filepath);
555                             if (scope.filecount < scope.maxfiles && scope.maxfiles!=-1) {
556                                 var button_addfile  = Y.one("#btnadd-"+scope.client_id);
557                                 button_addfile.setStyle('display', 'inline');
558                                 button_addfile.on('click', function(e) {
559                                     var options = scope.filepicker_options;
560                                     options.formcallback = scope.filepicker_callback;
561                                     // XXX: magic here, to let filepicker use filemanager scope
562                                     options.magicscope = scope;
563                                     options.savepath = scope.currentpath;
564                                     M.core_filepicker.show(Y, options);
565                                 }, this);
566                             }
567                         }
568                     });
569                 };
570                 M.util.show_confirm_dialog(ev, dialog_options);
571             }
572             function rename (type, ev, obj) {
573                 var scope = this;
574                 var perform = function(e) {
575                     var newfilename = Y.one('#fm-rename-input').get('value');
576                     if (!newfilename) {
577                         return;
578                     }
580                     var action = '';
581                     var params = {};
582                     if (fileinfo.type == 'folder') {
583                         params['filepath']   = fileinfo.filepath;
584                         params['filename']   = '.';
585                         params['newdirname'] = newfilename;
586                         action = 'renamedir';
587                     } else {
588                         params['filepath']   = fileinfo.filepath;
589                         params['filename']   = fileinfo.fullname;
590                         params['newfilename'] = newfilename;
591                         action = 'rename';
592                     }
593                     scope.request({
594                         action: action,
595                         scope: scope,
596                         params: params,
597                         callback: function(id, obj, args) {
598                             if (obj == false) {
599                                 alert(M.str.repository.fileexists);
600                             } else {
601                                 scope.refresh(obj.filepath);
602                             }
603                             Y.one('#fm-rename-input').set('value', '');
604                             scope.rename_dialog.hide();
605                         }
606                     });
607                 };
609                 var dialog = Y.one('#fm-rename-dlg');
610                 if (!dialog) {
611                     dialog = Y.Node.create('<div id="fm-rename-dlg"><div class="hd">'+M.str.repository.enternewname+'</div><div class="bd"><input type="text" id="fm-rename-input" /></div></div>');
612                     Y.one(document.body).appendChild(dialog);
613                     this.rename_dialog = new YAHOO.widget.Dialog("fm-rename-dlg", {
614                          width: "300px",
615                          fixedcenter: true,
616                          visible: true,
617                          constraintoviewport : true
618                          });
620                 }
621                 var buttons = [ { text:M.str.moodle.rename, handler:perform, isDefault:true},
622                                   { text:M.str.moodle.cancel, handler:function(){this.cancel();}}];
624                 this.rename_dialog.cfg.queueProperty('buttons', buttons);
625                 this.rename_dialog.render();
626                 this.rename_dialog.show();
627                 //var k1 = new YAHOO.util.KeyListener(scope, {keys:13}, {fn:function(){perform();}, correctScope: true});
628                 //k1.enable();
629                 Y.one('#fm-rename-input').set('value', fileinfo.fullname);
630             }
631             function move(type, ev, obj) {
632                 var scope = this;
633                 var itemid = this.options.itemid;
634                 // setup move file dialog
635                 var dialog = null;
636                 if (!Y.one('#fm-move-dlg')) {
637                     dialog = Y.Node.create('<div id="fm-move-dlg"></div>');
638                     Y.one(document.body).appendChild(dialog);
639                 } else {
640                     dialog = Y.one('#fm-move-dlg');
641                 }
643                 dialog.set('innerHTML', '<div class="hd">'+M.str.repository.moving+'</div><div class="bd"><div id="fm-move-div">'+M.str.repository.nopathselected+'</div><div id="fm-tree"></div></div>');
645                 this.movefile_dialog = new YAHOO.widget.Dialog("fm-move-dlg", {
646                      width : "600px",
647                      fixedcenter : true,
648                      visible : false,
649                      constraintoviewport : true
650                      });
652                 var treeview = new YAHOO.widget.TreeView("fm-tree");
654                 var dialog = this.movefile_dialog;
655                 function _move(e) {
656                     if (!treeview.targetpath) {
657                         return;
658                     }
659                     var params = {};
660                     if (fileinfo.type == 'folder') {
661                         action = 'movedir';
662                     } else {
663                         action = 'movefile';
664                     }
665                     params['filepath'] = fileinfo.filepath;
666                     params['filename'] = fileinfo.fullname;
667                     params['newfilepath'] = treeview.targetpath;
668                     scope.request({
669                         action: action,
670                         scope: scope,
671                         params: params,
672                         callback: function(id, obj, args) {
673                             var p = '/';
674                             if (obj) {
675                                 p = obj.filepath;
676                             }
677                             dialog.cancel();
678                             scope.refresh(p);
679                         }
680                     });
681                 }
683                 var buttons = [ { text:M.str.moodle.move, handler:_move, isDefault:true },
684                                   { text:M.str.moodle.cancel, handler:function(){this.cancel();}}];
686                 this.movefile_dialog.cfg.queueProperty("buttons", buttons);
687                 this.movefile_dialog.render();
689                 treeview.subscribe("dblClickEvent", function(e) {
690                     // update destidatoin folder
691                     this.targetpath = e.node.data.path;
692                     var title = Y.one('#fm-move-div');
693                     title.set('innerHTML', '<strong>"' + this.targetpath + '"</strong> has been selected.');
694                 });
696                 function loadDataForNode(node, onCompleteCallback) {
697                     var params = {};
698                     params['filepath'] = node.data.path;
699                     var obj = {
700                         action: 'dir',
701                         scope: scope,
702                         params: params,
703                         callback: function(id, obj, args) {
704                             data = obj.children;
705                             if (data.length == 0) {
706                                 // so it is empty
707                             } else {
708                                 for (var i in data) {
709                                     var textnode = {label: data[i].fullname, path: data[i].filepath, itemid: this.itemid};
710                                     var tmpNode = new YAHOO.widget.TextNode(textnode, node, false);
711                                 }
712                             }
713                             this.oncomplete();
714                         }
715                     };
716                     obj.oncomplete = onCompleteCallback;
717                     scope.request(obj);
718                 }
720                 this.movefile_dialog.subscribe('show', function(){
721                     var rootNode = treeview.getRoot();
722                     treeview.setDynamicLoad(loadDataForNode);
723                     treeview.removeChildren(rootNode);
724                     var textnode = {label: M.str.moodle.files, path: '/'};
725                     var tmpNode = new YAHOO.widget.TextNode(textnode, rootNode, true);
726                     treeview.draw();
727                 }, this, true);
729                 this.movefile_dialog.show();
730             }
731             var shared_items = [
732                 {text: M.str.moodle.rename+'...', onclick: {fn: rename, obj: options, scope: this}},
733                 {text: M.str.moodle.move+'...', onclick: {fn: move, obj: options, scope: this}}
734             ];
735             // delete is reserve word in Javascript
736             shared_items.push({text: M.str.moodle['delete']+'...', onclick: {fn: remove, obj: options, scope: this}});
737             var menu = new YAHOO.widget.Menu(menuid, {xy:position, clicktohide:true});
738             menu.clearContent();
739             menu.addItems(menuitems);
740             menu.addItems(shared_items);
741             menu.render(document.body);
742             menu.subscribe('hide', function(){
743                 this.fireEvent('destroy');
744             });
745             menu.show();
746         }
747     });
749     // finally init everything needed
750     // kill nonjs filemanager
751     var item = document.getElementById('nonjs-filemanager-'+options.client_id);
752     if (item && !options.usenonjs) {
753         item.parentNode.removeChild(item);
754     }
755     // hide loading picture
756     item = document.getElementById('filemanager-loading-'+options.client_id);
757     if (item) {
758         item.parentNode.removeChild(item);
759     }
760     // display filemanager interface
761     item = document.getElementById('filemanager-wrapper-'+options.client_id);
762     if (item) {
763         item.style.display = '';
764     }
766     var manager = new FileManagerHelper(options);
767     var dndoptions = {
768         filemanager: manager,
769         acceptedtypes: options.accepted_types,
770         clientid: options.client_id,
771         maxfiles: options.maxfiles,
772         maxbytes: options.maxbytes,
773         itemid: options.itemid,
774         repositories: manager.filepicker_options.repositories,
775         containerprefix: '#filemanager-',
776     };
777     M.form_dndupload.init(Y, dndoptions);
778 };