Rev 1 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
// YUI3 File Picker module for moodle// Author: Dongsheng Cai <dongsheng@moodle.com>/**** File Picker UI* =====* this.fpnode, contains reference to filepicker Node, non-empty if and only if rendered* this.api, stores the URL to make ajax request* this.mainui, YUI Panel* this.selectnode, contains reference to select-file Node* this.selectui, YUI Panel for selecting particular file* this.msg_dlg, YUI Panel for error or info message* this.process_dlg, YUI Panel for processing existing filename* this.treeview, YUI Treeview* this.viewmode, store current view mode* this.pathbar, reference to the Node with path bar* this.pathnode, a Node element representing one folder in a path bar (not attached anywhere, just used for template)* this.currentpath, the current path in the repository (or last requested path)** Filepicker options:* =====* this.options.client_id, the instance id* this.options.contextid* this.options.itemid* this.options.repositories, stores all repositories displayed in file picker* this.options.formcallback** Active repository options* =====* this.active_repo.id* this.active_repo.defaultreturntype* this.active_repo.nosearch* this.active_repo.norefresh* this.active_repo.nologin* this.active_repo.help* this.active_repo.manage** Server responses* =====* this.filelist, cached filelist* this.pages* this.page* this.filepath, current path (each element of the array is a part of the breadcrumb)* this.logindata, cached login form*/YUI.add('moodle-core_filepicker', function(Y) {/** help function to extract width/height style as a number, not as a string */Y.Node.prototype.getStylePx = function(attr) {var style = this.getStyle(attr);if (''+style == '0' || ''+style == '0px') {return 0;}var matches = style.match(/^([\d\.]+)px$/)if (matches && parseFloat(matches[1])) {return parseFloat(matches[1]);}return null;}/** if condition is met, the class is added to the node, otherwise - removed */Y.Node.prototype.addClassIf = function(className, condition) {if (condition) {this.addClass(className);} else {this.removeClass(className);}return this;}/** sets the width(height) of the node considering existing minWidth(minHeight) */Y.Node.prototype.setStyleAdv = function(stylename, value) {var stylenameCap = stylename.substr(0,1).toUpperCase() + stylename.substr(1, stylename.length-1).toLowerCase();this.setStyle(stylename, '' + Math.max(value, this.getStylePx('min'+stylenameCap)) + 'px')return this;}/** set image source to src, if there is preview, remember it in lazyloading.* If there is a preview and it was already loaded, use it. */Y.Node.prototype.setImgSrc = function(src, realsrc, lazyloading) {if (realsrc) {if (M.core_filepicker.loadedpreviews[realsrc]) {this.set('src', realsrc).addClass('realpreview');return this;} else {if (!this.get('id')) {this.generateID();}lazyloading[this.get('id')] = realsrc;}}this.set('src', src);return this;}/*** Replaces the image source with preview. If the image is inside the treeview, we need* also to update the html property of corresponding YAHOO.widget.HTMLNode* @param array lazyloading array containing associations of imgnodeid->realsrc*/Y.Node.prototype.setImgRealSrc = function(lazyloading) {if (this.get('id') && lazyloading[this.get('id')]) {var newsrc = lazyloading[this.get('id')];M.core_filepicker.loadedpreviews[newsrc] = true;this.set('src', newsrc).addClass('realpreview');delete lazyloading[this.get('id')];var treenode = this.ancestor('.fp-treeview')if (treenode && treenode.get('parentNode').treeview) {treenode.get('parentNode').treeview.getRoot().refreshPreviews(this.get('id'), newsrc);}}return this;}/** scan TreeView to find which node contains image with id=imgid and replace it's html* with the new image source. */Y.YUI2.widget.Node.prototype.refreshPreviews = function(imgid, newsrc, regex) {if (!regex) {regex = new RegExp("<img\\s[^>]*id=\""+imgid+"\"[^>]*?(/?)>", "im");}if (this.expanded || this.isLeaf) {var html = this.getContentHtml();if (html && this.setHtml && regex.test(html)) {var newhtml = this.html.replace(regex, "<img id=\""+imgid+"\" src=\""+newsrc+"\" class=\"realpreview\"$1>", html);this.setHtml(newhtml);return true;}if (!this.isLeaf && this.children) {for(var c in this.children) {if (this.children[c].refreshPreviews(imgid, newsrc, regex)) {return true;}}}}return false;}/*** Displays a list of files (used by filepicker, filemanager) inside the Node** @param array options* viewmode : 1 - icons, 2 - tree, 3 - table* appendonly : whether fileslist need to be appended instead of replacing the existing content* filenode : Node element that contains template for displaying one file* callback : On click callback. The element of the fileslist array will be passed as argument* rightclickcallback : On right click callback (optional).* callbackcontext : context where callbacks are executed* sortable : whether content may be sortable (in table mode)* dynload : allow dynamic load for tree view* filepath : for pre-building of tree view - the path to the current directory in filepicker format* treeview_dynload : callback to function to dynamically load the folder in tree view* classnamecallback : callback to function that returns the class name for an element* @param array fileslist array of files to show, each array element may have attributes:* title or fullname : file name* shorttitle (optional) : display file name* thumbnail : url of image* icon : url of icon image* thumbnail_width : width of thumbnail, default 90* thumbnail_height : height of thumbnail, default 90* thumbnail_alt : TODO not needed!* description or thumbnail_title : alt text* @param array lazyloading : reference to the array with lazy loading images*/Y.Node.prototype.fp_display_filelist = function(options, fileslist, lazyloading) {var viewmodeclassnames = {1:'fp-iconview', 2:'fp-treeview', 3:'fp-tableview'};var classname = viewmodeclassnames[options.viewmode];var scope = this;/** return whether file is a folder (different attributes in FileManager and FilePicker) */var file_is_folder = function(node) {if (node.children) {return true;}if (node.type && node.type == 'folder') {return true;}return false;};/** return the name of the file (different attributes in FileManager and FilePicker) */var file_get_filename = function(node) {return node.title ? node.title : node.fullname;};/** return display name of the file (different attributes in FileManager and FilePicker) */var file_get_displayname = function(node) {var displayname = node.shorttitle ? node.shorttitle : file_get_filename(node);return Y.Escape.html(displayname);};/** return file description (different attributes in FileManager and FilePicker) */var file_get_description = function(node) {var description = '';if (node.description) {description = node.description;} else if (node.thumbnail_title) {description = node.thumbnail_title;} else {description = file_get_filename(node);}return Y.Escape.html(description);};/** help funciton for tree view */var build_tree = function(node, level) {// prepare file name with iconvar el = Y.Node.create('<div/>');el.appendChild(options.filenode.cloneNode(true));el.one('.fp-filename').setContent(file_get_displayname(node));// TODO add tooltip with node.title or node.thumbnail_titlevar tmpnodedata = {className:options.classnamecallback(node)};el.get('children').addClass(tmpnodedata.className);if (node.icon) {el.one('.fp-icon').appendChild(Y.Node.create('<img/>'));el.one('.fp-icon img').setImgSrc(node.icon, node.realicon, lazyloading);}// create nodetmpnodedata.html = el.getContent();var tmpNode = new Y.YUI2.widget.HTMLNode(tmpnodedata, level, false);if (node.dynamicLoadComplete) {tmpNode.dynamicLoadComplete = true;}tmpNode.fileinfo = node;tmpNode.isLeaf = !file_is_folder(node);if (!tmpNode.isLeaf) {if(node.expanded) {tmpNode.expand();}tmpNode.path = node.path ? node.path : (node.filepath ? node.filepath : '');for(var c in node.children) {build_tree(node.children[c], tmpNode);}}};/** initialize tree view */var initialize_tree_view = function() {var parentid = scope.one('.'+classname).get('id');// TODO MDL-32736 use YUI3 gallery TreeViewscope.treeview = new Y.YUI2.widget.TreeView(parentid);if (options.dynload) {scope.treeview.setDynamicLoad(Y.bind(options.treeview_dynload, options.callbackcontext), 1);}scope.treeview.singleNodeHighlight = true;if (options.filepath && options.filepath.length) {// we just jumped from icon/details view, we need to show all parents// we extract as much information as possible from filepath and filelist// and send additional requests to retrieve siblings for parent foldersvar mytree = {};var mytreeel = null;for (var i in options.filepath) {if (mytreeel == null) {mytreeel = mytree;} else {mytreeel.children = [{}];mytreeel = mytreeel.children[0];}var pathelement = options.filepath[i];mytreeel.path = pathelement.path;mytreeel.title = pathelement.name;mytreeel.icon = pathelement.icon;mytreeel.dynamicLoadComplete = true; // we will call it manuallymytreeel.expanded = true;}mytreeel.children = fileslist;build_tree(mytree, scope.treeview.getRoot());// manually call dynload for parent elements in the tree so we can load other siblingsif (options.dynload) {var root = scope.treeview.getRoot();// Whether search results are currently displayed in the active repository in the filepicker.// We do not want to load siblings of parent elements when displaying search tree results.var isSearchResult = typeof options.callbackcontext.active_repo !== 'undefined' &&options.callbackcontext.active_repo.issearchresult;while (root && root.children && root.children.length) {root = root.children[0];if (root.path == mytreeel.path) {root.origpath = options.filepath;root.origlist = fileslist;} else if (!root.isLeaf && root.expanded && !isSearchResult) {Y.bind(options.treeview_dynload, options.callbackcontext)(root, null);}}}} else {// there is no path information, just display all elements as a list, without hierarchyfor(k in fileslist) {build_tree(fileslist[k], scope.treeview.getRoot());}}scope.treeview.subscribe('clickEvent', function(e){e.node.highlight(false);var callback = options.callback;if (options.rightclickcallback && e.event.target &&Y.Node(e.event.target).ancestor('.fp-treeview .fp-contextmenu', true)) {callback = options.rightclickcallback;}Y.bind(callback, options.callbackcontext)(e, e.node.fileinfo);Y.YUI2.util.Event.stopEvent(e.event)});// Simulate click on file not folder.scope.treeview.subscribe('enterKeyPressed', function(node) {if (node.children.length === 0) {Y.one(node.getContentEl()).one('a').simulate('click');}});// TODO MDL-32736 support right click/*if (options.rightclickcallback) {scope.treeview.subscribe('dblClickEvent', function(e){e.node.highlight(false);Y.bind(options.rightclickcallback, options.callbackcontext)(e, e.node.fileinfo);});}*/scope.treeview.draw();};/** formatting function for table view */var formatValue = function (o){if (o.data[''+o.column.key+'_f_s']) {return o.data[''+o.column.key+'_f_s'];}else if (o.data[''+o.column.key+'_f']) {return o.data[''+o.column.key+'_f'];}else if (o.value) {return o.value;}else {return '';}};/** formatting function for table view */var formatTitle = function(o) {var el = Y.Node.create('<div/>');el.appendChild(options.filenode.cloneNode(true)); // TODO not node but string!el.get('children').addClass(o.data['classname']);el.one('.fp-filename').setContent(o.value);if (o.data['icon']) {el.one('.fp-icon').appendChild(Y.Node.create('<img/>'));el.one('.fp-icon img').setImgSrc(o.data['icon'], o.data['realicon'], lazyloading);}if (options.rightclickcallback) {el.get('children').addClass('fp-hascontextmenu');}// TODO add tooltip with o.data['title'] (o.value) or o.data['thumbnail_title']return el.getContent();}/*** Generate slave checkboxes based on toggleall's specification* @param {object} o An object reprsenting the record for the current row.* @return {html} The checkbox html*/var formatCheckbox = function(o) {var el = Y.Node.create('<div/>');var parentid = scope.one('.' + classname).get('id');var checkbox = Y.Node.create('<input/>').setAttribute('type', 'checkbox').setAttribute('data-fieldtype', 'checkbox').setAttribute('data-fullname', o.data.fullname).setAttribute('data-action', 'toggle').setAttribute('data-toggle', 'slave').setAttribute('data-togglegroup', 'file-selections-' + parentid);var checkboxLabel = Y.Node.create('<label>').setHTML("Select file '" + o.data.fullname + "'").addClass('sr-only').setAttrs({for: checkbox.generateID(),});el.appendChild(checkbox);el.appendChild(checkboxLabel);return el.getContent();};/** sorting function for table view */var sortFoldersFirst = function(a, b, desc) {if (a.get('isfolder') && !b.get('isfolder')) {return -1;}if (!a.get('isfolder') && b.get('isfolder')) {return 1;}var aa = a.get(this.key), bb = b.get(this.key), dir = desc ? -1 : 1;return (aa > bb) ? dir : ((aa < bb) ? -dir : 0);}/** initialize table view */var initialize_table_view = function() {var cols = [{key: "displayname", label: M.util.get_string('name', 'moodle'), allowHTML: true, formatter: formatTitle,sortable: true, sortFn: sortFoldersFirst},{key: "datemodified", label: M.util.get_string('lastmodified', 'moodle'), allowHTML: true, formatter: formatValue,sortable: true, sortFn: sortFoldersFirst},{key: "size", label: M.util.get_string('size', 'repository'), allowHTML: true, formatter: formatValue,sortable: true, sortFn: sortFoldersFirst},{key: "mimetype", label: M.util.get_string('type', 'repository'), allowHTML: true,sortable: true, sortFn: sortFoldersFirst}];// Generate a checkbox based on toggleall's specificationvar div = Y.Node.create('<div/>');var parentid = scope.one('.' + classname).get('id');var checkbox = Y.Node.create('<input/>').setAttribute('type', 'checkbox')// .setAttribute('title', M.util.get_string('selectallornone', 'form')).setAttribute('data-action', 'toggle').setAttribute('data-toggle', 'master').setAttribute('data-togglegroup', 'file-selections-' + parentid);var checkboxLabel = Y.Node.create('<label>').setHTML(M.util.get_string('selectallornone', 'form')).addClass('sr-only').setAttrs({for: checkbox.generateID(),});div.appendChild(checkboxLabel);div.appendChild(checkbox);// Define the selector for the click event handler.var clickEventSelector = 'tr';// Enable the selectable checkboxesif (options.disablecheckboxes != undefined && !options.disablecheckboxes) {clickEventSelector = 'tr td:not(:first-child)';cols.unshift({key: "",label: div.getContent(),allowHTML: true,formatter: formatCheckbox,sortable: false});}scope.tableview = new Y.DataTable({columns: cols, data: fileslist});scope.tableview.delegate('click', function (e, tableview) {var record = tableview.getRecord(e.currentTarget.get('id'));if (record) {var callback = options.callback;if (options.rightclickcallback && e.target.ancestor('.fp-tableview .fp-contextmenu', true)) {callback = options.rightclickcallback;}Y.bind(callback, this)(e, record.getAttrs());}}, clickEventSelector, options.callbackcontext, scope.tableview);if (options.rightclickcallback) {scope.tableview.delegate('contextmenu', function (e, tableview) {var record = tableview.getRecord(e.currentTarget.get('id'));if (record) { Y.bind(options.rightclickcallback, this)(e, record.getAttrs()); }}, 'tr', options.callbackcontext, scope.tableview);}}/** append items in table view mode */var append_files_table = function() {if (options.appendonly) {fileslist.forEach(function(el) {this.tableview.data.add(el);},scope);}scope.tableview.render(scope.one('.'+classname));scope.tableview.sortable = options.sortable ? true : false;};/** append items in tree view mode */var append_files_tree = function() {if (options.appendonly) {var parentnode = scope.treeview.getRoot();if (scope.treeview.getHighlightedNode()) {parentnode = scope.treeview.getHighlightedNode();if (parentnode.isLeaf) {parentnode = parentnode.parent;}}for (var k in fileslist) {build_tree(fileslist[k], parentnode);}scope.treeview.draw();} else {// otherwise files were already added in initialize_tree_view()}}/** append items in icon view mode */var append_files_icons = function() {parent = scope.one('.'+classname);for (var k in fileslist) {var node = fileslist[k];var element = options.filenode.cloneNode(true);parent.appendChild(element);element.addClass(options.classnamecallback(node));var filenamediv = element.one('.fp-filename');filenamediv.setContent(file_get_displayname(node));var imgdiv = element.one('.fp-thumbnail'), width, height, src;if (node.thumbnail) {width = node.thumbnail_width ? node.thumbnail_width : 90;height = node.thumbnail_height ? node.thumbnail_height : 90;src = node.thumbnail;} else {width = 16;height = 16;src = node.icon;}filenamediv.setStyleAdv('width', width);imgdiv.setStyleAdv('width', width).setStyleAdv('height', height);var img = Y.Node.create('<img/>').setAttrs({title: file_get_description(node),alt: Y.Escape.html(node.thumbnail_alt ? node.thumbnail_alt : file_get_filename(node))}).setStyle('maxWidth', ''+width+'px').setStyle('maxHeight', ''+height+'px');img.setImgSrc(src, node.realthumbnail, lazyloading);imgdiv.appendChild(img);element.on('click', function(e, nd) {if (options.rightclickcallback && e.target.ancestor('.fp-iconview .fp-contextmenu', true)) {Y.bind(options.rightclickcallback, this)(e, nd);} else {Y.bind(options.callback, this)(e, nd);}}, options.callbackcontext, node);if (options.rightclickcallback) {element.on('contextmenu', options.rightclickcallback, options.callbackcontext, node);}}}// Notify the user if any of the files has a problem status.var problemFiles = [];fileslist.forEach(function(file) {if (!file_is_folder(file) && file.hasOwnProperty('status') && file.status != 0) {problemFiles.push(file);}});if (problemFiles.length > 0) {require(["core/notification", "core/str"], function(Notification, Str) {problemFiles.forEach(function(problemFile) {Str.get_string('storedfilecannotreadfile', 'error', problemFile.fullname).then(function(string) {Notification.addNotification({message: string,type: "error"});return;}).catch(Notification.exception);});});}// If table view, need some additional properties// before passing fileslist to the YUI tableviewif (options.viewmode == 3) {fileslist.forEach(function(el) {el.displayname = file_get_displayname(el);el.isfolder = file_is_folder(el);el.classname = options.classnamecallback(el);}, scope);}// initialize files viewif (!options.appendonly) {var parent = Y.Node.create('<div/>').addClass(classname);this.setContent('').appendChild(parent);parent.generateID();if (options.viewmode == 2) {initialize_tree_view();} else if (options.viewmode == 3) {initialize_table_view();} else {// nothing to initialize for icon view}}// append files to the listif (options.viewmode == 2) {append_files_tree();} else if (options.viewmode == 3) {append_files_table();} else {append_files_icons();}}}, '@VERSION@', {requires:['base', 'node', 'yui2-treeview', 'panel', 'cookie', 'datatable', 'datatable-sort']});M.core_filepicker = M.core_filepicker || {};/*** instances of file pickers used on page*/M.core_filepicker.instances = M.core_filepicker.instances || {};M.core_filepicker.active_filepicker = null;/*** HTML Templates to use in FilePicker*/M.core_filepicker.templates = M.core_filepicker.templates || {};/*** Array of image sources for real previews (realicon or realthumbnail) that are already loaded*/M.core_filepicker.loadedpreviews = M.core_filepicker.loadedpreviews || {};/*** Set selected file info** @param object file info*/M.core_filepicker.select_file = function(file) {M.core_filepicker.active_filepicker.select_file(file);}/*** Init and show file picker*/M.core_filepicker.show = function(Y, options) {if (!M.core_filepicker.instances[options.client_id]) {M.core_filepicker.init(Y, options);}M.core_filepicker.instances[options.client_id].options.formcallback = options.formcallback;M.core_filepicker.instances[options.client_id].show();};M.core_filepicker.set_templates = function(Y, templates) {for (var templid in templates) {M.core_filepicker.templates[templid] = templates[templid];}}/*** Add new file picker to current instances*/M.core_filepicker.init = function(Y, options) {var FilePickerHelper = function(options) {FilePickerHelper.superclass.constructor.apply(this, arguments);};FilePickerHelper.NAME = "FilePickerHelper";FilePickerHelper.ATTRS = {options: {},lang: {}};Y.extend(FilePickerHelper, Y.Base, {api: M.cfg.wwwroot+'/repository/repository_ajax.php',cached_responses: {},waitinterval : null, // When the loading template is being displayed and its animation is running this will be an interval instance.initializer: function(options) {this.options = options;if (!this.options.savepath) {this.options.savepath = '/';}},destructor: function() {},request: function(args, redraw) {var api = (args.api ? args.api : this.api) + '?action='+args.action;var params = {};var scope = args['scope'] ? args['scope'] : this;params['repo_id']=args.repository_id;params['p'] = args.path?args.path:'';params['page'] = args.page?args.page:'';params['env']=this.options.env;// the form element only accept certain file typesparams['accepted_types']=this.options.accepted_types;params['sesskey'] = M.cfg.sesskey;params['client_id'] = args.client_id;params['itemid'] = this.options.itemid?this.options.itemid:0;params['maxbytes'] = this.options.maxbytes?this.options.maxbytes:-1;// The unlimited value of areamaxbytes is -1, it is defined by FILE_AREA_MAX_BYTES_UNLIMITED.params['areamaxbytes'] = this.options.areamaxbytes ? this.options.areamaxbytes : -1;if (this.options.context && this.options.context.id) {params['ctx_id'] = this.options.context.id;}if (args['params']) {for (i in args['params']) {params[i] = args['params'][i];}}if (args.action == 'upload') {var list = [];for(var k in params) {var value = params[k];if(value instanceof Array) {for(var i in value) {list.push(k+'[]='+value[i]);}} else {list.push(k+'='+value);}}params = list.join('&');} else {params = build_querystring(params);}var cfg = {method: 'POST',on: {complete: function(id,o,p) {var data = null;try {data = Y.JSON.parse(o.responseText);} catch(e) {if (o && o.status && o.status > 0) {Y.use('moodle-core-notification-exception', function() {return new M.core.exception(e);});return;}}// error checkingif (data && data.error) {Y.use('moodle-core-notification-ajaxexception', function () {return new M.core.ajaxException(data);});this.fpnode.one('.fp-content').setContent('');return;} else {if (data.msg) {scope.print_msg(data.msg, 'info');}// cache result if applicableif (args.action != 'upload' && data.allowcaching) {scope.cached_responses[params] = data;}// invoke callbackargs.callback(id,data,p);}}},arguments: {scope: scope},headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},data: params,context: this};if (args.form) {cfg.form = args.form;}// check if result of the same request has been already cached. If not, request it// (never applicable in case of form submission and/or upload action):if (!args.form && args.action != 'upload' && scope.cached_responses[params]) {args.callback(null, scope.cached_responses[params], {scope: scope})} else {Y.io(api, cfg);if (redraw) {this.wait();}}},/** displays the dialog and processes rename/overwrite if there is a file with the same name in the same filearea*/process_existing_file: function(data) {var scope = this;var handleOverwrite = function(e) {// overwritee.preventDefault();var data = this.process_dlg.dialogdata;var params = {}params['existingfilename'] = data.existingfile.filename;params['existingfilepath'] = data.existingfile.filepath;params['newfilename'] = data.newfile.filename;params['newfilepath'] = data.newfile.filepath;this.hide_header();this.request({'params': params,'scope': this,'action':'overwrite','path': '','client_id': this.options.client_id,'repository_id': this.active_repo.id,'callback': function(id, o, args) {scope.hide();// Add an arbitrary parameter to the URL to force browsers to re-load the new image even// if the file name has not changed.var urlimage = data.existingfile.url + "?time=" + (new Date()).getTime();if (scope.options.editor_target && scope.options.env == 'editor') {// editor needs to update urlscope.options.editor_target.value = urlimage;scope.options.editor_target.dispatchEvent(new Event('change'), {'bubbles': true});}var fileinfo = {'client_id':scope.options.client_id,'url': urlimage,'file': data.existingfile.filename};var formcallback_scope = scope.options.magicscope ? scope.options.magicscope : scope;scope.options.formcallback.apply(formcallback_scope, [fileinfo]);}}, true);}var handleRename = function(e) {// inserts file with the new namee.preventDefault();var scope = this;var data = this.process_dlg.dialogdata;if (scope.options.editor_target && scope.options.env == 'editor') {scope.options.editor_target.value = data.newfile.url;scope.options.editor_target.dispatchEvent(new Event('change'), {'bubbles': true});}scope.hide();var formcallback_scope = scope.options.magicscope ? scope.options.magicscope : scope;var fileinfo = {'client_id':scope.options.client_id,'url':data.newfile.url,'file':data.newfile.filename};scope.options.formcallback.apply(formcallback_scope, [fileinfo]);}var handleCancel = function(e) {// Delete tmp filee.preventDefault();var params = {};params['newfilename'] = this.process_dlg.dialogdata.newfile.filename;params['newfilepath'] = this.process_dlg.dialogdata.newfile.filepath;this.request({'params': params,'scope': this,'action':'deletetmpfile','path': '','client_id': this.options.client_id,'repository_id': this.active_repo.id,'callback': function(id, o, args) {// let it be in background, from user point of view nothing is happenning}}, false);this.process_dlg.hide();this.selectui.hide();}if (!this.process_dlg) {this.process_dlg_node = Y.Node.create(M.core_filepicker.templates.processexistingfile);var node = this.process_dlg_node;node.generateID();this.process_dlg = new M.core.dialogue({draggable : true,bodyContent : node,headerContent: M.util.get_string('fileexistsdialogheader', 'repository'),centered : true,modal : true,visible : false,zIndex : this.options.zIndex});node.one('.fp-dlg-butoverwrite').on('click', handleOverwrite, this);node.one('.fp-dlg-butrename').on('click', handleRename, this);node.one('.fp-dlg-butcancel').on('click', handleCancel, this);if (this.options.env == 'editor') {node.one('.fp-dlg-text').setContent(M.util.get_string('fileexistsdialog_editor', 'repository'));} else {node.one('.fp-dlg-text').setContent(M.util.get_string('fileexistsdialog_filemanager', 'repository'));}}this.selectnode.removeClass('loading');this.process_dlg.dialogdata = data;this.process_dlg_node.one('.fp-dlg-butrename').setContent(M.util.get_string('renameto', 'repository', data.newfile.filename));this.process_dlg.show();},/** displays error instead of filepicker contents */display_error: function(errortext, errorcode) {this.fpnode.one('.fp-content').setContent(M.core_filepicker.templates.error);this.fpnode.one('.fp-content .fp-error').addClass(errorcode).setContent(Y.Escape.html(errortext));},/** displays message in a popup */print_msg: function(msg, type) {var header = M.util.get_string('error', 'moodle');if (type != 'error') {type = 'info'; // one of only two types exceptedheader = M.util.get_string('info', 'moodle');}if (!this.msg_dlg) {this.msg_dlg_node = Y.Node.create(M.core_filepicker.templates.message);this.msg_dlg_node.generateID();this.msg_dlg = new M.core.dialogue({draggable : true,bodyContent : this.msg_dlg_node,centered : true,modal : true,visible : false,zIndex : this.options.zIndex});this.msg_dlg_node.one('.fp-msg-butok').on('click', function(e) {e.preventDefault();this.msg_dlg.hide();}, this);}this.msg_dlg.set('headerContent', header);this.msg_dlg_node.removeClass('fp-msg-info').removeClass('fp-msg-error').addClass('fp-msg-'+type)this.msg_dlg_node.one('.fp-msg-text').setContent(Y.Escape.html(msg));this.msg_dlg.show();},view_files: function(appenditems) {this.viewbar_set_enabled(true);this.print_path();/*if ((appenditems == null) && (!this.filelist || !this.filelist.length) && !this.active_repo.hasmorepages) {// TODO do it via classes and adjust for each view mode!// If there are no items and no next page, just display status message and quitthis.display_error(M.util.get_string('nofilesavailable', 'repository'), 'nofilesavailable');return;}*/if (this.viewmode == 2) {this.view_as_list(appenditems);} else if (this.viewmode == 3) {this.view_as_table(appenditems);} else {this.view_as_icons(appenditems);}this.fpnode.one('.fp-content').setAttribute('tabindex', '0');this.fpnode.one('.fp-content').focus();// display/hide the link for requesting next pageif (!appenditems && this.active_repo.hasmorepages) {if (!this.fpnode.one('.fp-content .fp-nextpage')) {this.fpnode.one('.fp-content').append(M.core_filepicker.templates.nextpage);}this.fpnode.one('.fp-content .fp-nextpage').one('a,button').on('click', function(e) {e.preventDefault();this.fpnode.one('.fp-content .fp-nextpage').addClass('loading');this.request_next_page();}, this);}if (!this.active_repo.hasmorepages && this.fpnode.one('.fp-content .fp-nextpage')) {this.fpnode.one('.fp-content .fp-nextpage').remove();}if (this.fpnode.one('.fp-content .fp-nextpage')) {this.fpnode.one('.fp-content .fp-nextpage').removeClass('loading');}this.content_scrolled();},content_scrolled: function(e) {setTimeout(Y.bind(function() {if (this.processingimages) {return;}this.processingimages = true;var scope = this,fpcontent = this.fpnode.one('.fp-content'),fpcontenty = fpcontent.getY(),fpcontentheight = fpcontent.getStylePx('height'),nextpage = fpcontent.one('.fp-nextpage'),is_node_visible = function(node) {var offset = node.getY()-fpcontenty;if (offset <= fpcontentheight && (offset >=0 || offset+node.getStylePx('height')>=0)) {return true;}return false;};// automatically load next page when 'more' link becomes visibleif (nextpage && !nextpage.hasClass('loading') && is_node_visible(nextpage)) {nextpage.one('a,button').simulate('click');}// replace src for visible images that need to be lazy-loadedif (scope.lazyloading) {fpcontent.all('img').each( function(node) {if (node.get('id') && scope.lazyloading[node.get('id')] && is_node_visible(node)) {node.setImgRealSrc(scope.lazyloading);}});}this.processingimages = false;}, this), 200)},treeview_dynload: function(node, cb) {var retrieved_children = {};if (node.children) {for (var i in node.children) {retrieved_children[node.children[i].path] = node.children[i];}}this.request({action:'list',client_id: this.options.client_id,repository_id: this.active_repo.id,path:node.path?node.path:'',page:node.page?args.page:'',scope:this,callback: function(id, obj, args) {var list = obj.list;var scope = args.scope;// check that user did not leave the view mode before recieving this responseif (!(scope.active_repo.id == obj.repo_id && scope.viewmode == 2 && node && node.getChildrenEl())) {return;}if (cb != null) { // (in manual mode do not update current path)scope.viewbar_set_enabled(true);scope.parse_repository_options(obj);}node.highlight(false);node.origlist = obj.list ? obj.list : null;node.origpath = obj.path ? obj.path : null;node.children = [];for(k in list) {if (list[k].children && retrieved_children[list[k].path]) {// if this child is a folder and has already been retrievednode.children[node.children.length] = retrieved_children[list[k].path];} else {// append new file to the listscope.view_as_list([list[k]]);}}if (cb == null) {node.refresh();} else {// invoke callback requested by TreeView componentcb();}scope.content_scrolled();}}, false);},classnamecallback : function(node) {var classname = '';if (node.children) {classname = classname + ' fp-folder';}if (node.isref) {classname = classname + ' fp-isreference';}if (node.iscontrolledlink) {classname = classname + ' fp-iscontrolledlink';}if (node.refcount) {classname = classname + ' fp-hasreferences';}if (node.originalmissing) {classname = classname + ' fp-originalmissing';}return Y.Lang.trim(classname);},/** displays list of files in tree (list) view mode. If param appenditems is specified,* appends those items to the end of the list. Otherwise (default behaviour)* clears the contents and displays the items from this.filelist */view_as_list: function(appenditems) {var list = (appenditems != null) ? appenditems : this.filelist;this.viewmode = 2;if (!this.filelist || this.filelist.length==0 && (!this.filepath || !this.filepath.length)) {this.display_error(M.util.get_string('nofilesavailable', 'repository'), 'nofilesavailable');return;}var element_template = Y.Node.create(M.core_filepicker.templates.listfilename);var options = {viewmode : this.viewmode,appendonly : (appenditems != null),filenode : element_template,callbackcontext : this,callback : function(e, node) {// TODO MDL-32736 e is not an event here but an object with properties 'event' and 'node'if (!node.children) {if (e.node.parent && e.node.parent.origpath) {// set the current paththis.filepath = e.node.parent.origpath;this.filelist = e.node.parent.origlist;this.print_path();}this.select_file(node);} else {// save current path and filelist (in case we want to jump to other viewmode)this.filepath = e.node.origpath;this.filelist = e.node.origlist;this.currentpath = e.node.path;this.print_path();this.content_scrolled();}},classnamecallback : this.classnamecallback,dynload : this.active_repo.dynload,filepath : this.filepath,treeview_dynload : this.treeview_dynload};this.fpnode.one('.fp-content').fp_display_filelist(options, list, this.lazyloading);},/** displays list of files in icon view mode. If param appenditems is specified,* appends those items to the end of the list. Otherwise (default behaviour)* clears the contents and displays the items from this.filelist */view_as_icons: function(appenditems) {this.viewmode = 1;var list = (appenditems != null) ? appenditems : this.filelist;var element_template = Y.Node.create(M.core_filepicker.templates.iconfilename);if ((appenditems == null) && (!this.filelist || !this.filelist.length)) {this.display_error(M.util.get_string('nofilesavailable', 'repository'), 'nofilesavailable');return;}var options = {viewmode : this.viewmode,appendonly : (appenditems != null),filenode : element_template,callbackcontext : this,callback : function(e, node) {if (e.preventDefault) {e.preventDefault();}if(node.children) {if (this.active_repo.dynload) {this.list({'path':node.path});} else {this.filelist = node.children;this.view_files();}} else {this.select_file(node);}},classnamecallback : this.classnamecallback};this.fpnode.one('.fp-content').fp_display_filelist(options, list, this.lazyloading);},/** displays list of files in table view mode. If param appenditems is specified,* appends those items to the end of the list. Otherwise (default behaviour)* clears the contents and displays the items from this.filelist */view_as_table: function(appenditems) {this.viewmode = 3;var list = (appenditems != null) ? appenditems : this.filelist;if (!appenditems && (!this.filelist || this.filelist.length==0) && !this.active_repo.hasmorepages) {this.display_error(M.util.get_string('nofilesavailable', 'repository'), 'nofilesavailable');return;}var element_template = Y.Node.create(M.core_filepicker.templates.listfilename);var options = {viewmode : this.viewmode,appendonly : (appenditems != null),filenode : element_template,callbackcontext : this,sortable : !this.active_repo.hasmorepages,callback : function(e, node) {if (e.preventDefault) {e.preventDefault();}if (node.children) {if (this.active_repo.dynload) {this.list({'path':node.path});} else {this.filelist = node.children;this.view_files();}} else {this.select_file(node);}},classnamecallback : this.classnamecallback};this.fpnode.one('.fp-content').fp_display_filelist(options, list, this.lazyloading);},/** If more than one page available, requests and displays the files from the next page */request_next_page: function() {if (!this.active_repo.hasmorepages || this.active_repo.nextpagerequested) {// nothing to loadreturn;}this.active_repo.nextpagerequested = true;var nextpage = this.active_repo.page+1;var args = {page: nextpage,repo_id: this.active_repo.id};var action = this.active_repo.issearchresult ? 'search' : 'list';this.request({path: this.currentpath,scope: this,action: action,client_id: this.options.client_id,repository_id: args.repo_id,params: args,callback: function(id, obj, args) {var scope = args.scope;// Check that we are still in the same repository and are expecting this page. We have no way// to compare the requested page and the one returned, so we assume that if the last chunk// of the breadcrumb is similar, then we probably are on the same page.var samepage = true;if (obj.path && scope.filepath) {var pathbefore = scope.filepath[scope.filepath.length-1];var pathafter = obj.path[obj.path.length-1];if (pathbefore.path != pathafter.path) {samepage = false;}}if (scope.active_repo.hasmorepages && obj.list && obj.page &&obj.repo_id == scope.active_repo.id &&obj.page == scope.active_repo.page+1 && samepage) {scope.parse_repository_options(obj, true);scope.view_files(obj.list)}}}, false);},select_file: function(args) {var argstitle = args.shorttitle ? args.shorttitle : args.title;// Limit the string length so it fits nicely on mobile devicesvar titlelength = 30;if (argstitle.length > titlelength) {argstitle = argstitle.substring(0, titlelength) + '...';}Y.one('#fp-file_label_'+this.options.client_id).setContent(Y.Escape.html(M.util.get_string('select', 'repository')+' '+argstitle));this.selectui.show();Y.one('#'+this.selectnode.get('id')).focus();var client_id = this.options.client_id;var selectnode = this.selectnode;var return_types = this.options.repositories[this.active_repo.id].return_types;selectnode.removeClass('loading');selectnode.one('.fp-saveas input').set('value', args.title);var imgnode = Y.Node.create('<img/>').set('src', args.realthumbnail ? args.realthumbnail : args.thumbnail).setStyle('maxHeight', ''+(args.thumbnail_height ? args.thumbnail_height : 90)+'px').setStyle('maxWidth', ''+(args.thumbnail_width ? args.thumbnail_width : 90)+'px');selectnode.one('.fp-thumbnail').setContent('').appendChild(imgnode);// filelink is the array of file-link-types available for this repository in this envvar filelinktypes = [2/*FILE_INTERNAL*/,1/*FILE_EXTERNAL*/,4/*FILE_REFERENCE*/,8/*FILE_CONTROLLED_LINK*/];var filelink = {}, firstfilelink = null, filelinkcount = 0;for (var i in filelinktypes) {var allowed = (return_types & filelinktypes[i]) &&(this.options.return_types & filelinktypes[i]);if (filelinktypes[i] == 1/*FILE_EXTERNAL*/ && !this.options.externallink && this.options.env == 'editor') {// special configuration setting 'repositoryallowexternallinks' may prevent// using external links in editor environmentallowed = false;}filelink[filelinktypes[i]] = allowed;firstfilelink = (firstfilelink==null && allowed) ? filelinktypes[i] : firstfilelink;filelinkcount += allowed ? 1 : 0;}var defaultreturntype = this.options.repositories[this.active_repo.id].defaultreturntype;if (defaultreturntype) {if (filelink[defaultreturntype]) {firstfilelink = defaultreturntype;}}// make radio buttons enabled if this file-link-type is available and only if there are more than one file-link-type option// check the first available file-link-type optionfor (var linktype in filelink) {var el = selectnode.one('.fp-linktype-'+linktype);el.addClassIf('uneditable', !(filelink[linktype] && filelinkcount>1));el.one('input').set('checked', (firstfilelink == linktype) ? 'checked' : '').simulate('change');}// TODO MDL-32532: attributes 'hasauthor' and 'haslicense' need to be obsolete,selectnode.one('.fp-setauthor input').set('value', args.author ? args.author : this.options.author);this.populateLicensesSelect(selectnode.one('.fp-setlicense select'), args);selectnode.one('form #filesource-'+client_id).set('value', args.source);selectnode.one('form #filesourcekey-'+client_id).set('value', args.sourcekey);// display static information about a file (when known)var attrs = ['datemodified','datecreated','size','license','author','dimensions'];for (var i in attrs) {if (selectnode.one('.fp-'+attrs[i])) {var value = (args[attrs[i]+'_f']) ? args[attrs[i]+'_f'] : (args[attrs[i]] ? args[attrs[i]] : '');selectnode.one('.fp-'+attrs[i]).addClassIf('fp-unknown', ''+value == '').one('.fp-value').setContent(Y.Escape.html(value));}}},setup_select_file: function() {var client_id = this.options.client_id;var selectnode = this.selectnode;var getfile = selectnode.one('.fp-select-confirm');var filePickerHelper = this;// bind labels with corresponding inputsselectnode.all('.fp-saveas,.fp-linktype-2,.fp-linktype-1,.fp-linktype-4,fp-linktype-8,.fp-setauthor,.fp-setlicense').each(function (node) {node.all('label').set('for', node.one('input,select').generateID());});selectnode.one('.fp-linktype-2 input').setAttrs({value: 2, name: 'linktype'});selectnode.one('.fp-linktype-1 input').setAttrs({value: 1, name: 'linktype'});selectnode.one('.fp-linktype-4 input').setAttrs({value: 4, name: 'linktype'});selectnode.one('.fp-linktype-8 input').setAttrs({value: 8, name: 'linktype'});var changelinktype = function(e) {if (e.currentTarget.get('checked')) {var allowinputs = e.currentTarget.get('value') != 1/*FILE_EXTERNAL*/;selectnode.all('.fp-setauthor,.fp-setlicense,.fp-saveas').each(function(node){node.addClassIf('uneditable', !allowinputs);node.all('input,select').set('disabled', allowinputs?'':'disabled');});// If the link to the file is selected, only then.// Remember: this is not to be done for all repos.// Only for those repos where the filereferencewarning is set.// The value 4 represents FILE_REFERENCE here.if (e.currentTarget.get('value') === '4') {var filereferencewarning = filePickerHelper.active_repo.filereferencewarning;if (filereferencewarning) {var fileReferenceNode = e.currentTarget.ancestor('.fp-linktype-4');var fileReferenceWarningNode = Y.Node.create('<div/>').addClass('alert alert-warning px-3 py-1 my-1 small').setAttrs({role: 'alert'}).setContent(filereferencewarning);fileReferenceNode.append(fileReferenceWarningNode);}} else {var fileReferenceInput = selectnode.one('.fp-linktype-4 input');var fileReferenceWarningNode = fileReferenceInput.ancestor('.fp-linktype-4').one('.alert-warning');if (fileReferenceWarningNode) {fileReferenceWarningNode.remove();}}}};selectnode.all('.fp-linktype-2,.fp-linktype-1,.fp-linktype-4,.fp-linktype-8').each(function (node) {node.one('input').on('change', changelinktype, this);});// register event on clicking submit buttongetfile.on('click', function(e) {e.preventDefault();var client_id = this.options.client_id;var scope = this;var repository_id = this.active_repo.id;var title = selectnode.one('.fp-saveas input').get('value');var filesource = selectnode.one('form #filesource-'+client_id).get('value');var filesourcekey = selectnode.one('form #filesourcekey-'+client_id).get('value');var params = {'title': title,'source': filesource,'savepath': this.options.savepath || '/','sourcekey': filesourcekey,};var license = selectnode.one('.fp-setlicense select');if (license) {params['license'] = license.get('value');var origlicense = selectnode.one('.fp-license .fp-value');if (origlicense) {origlicense = origlicense.getContent();}if (this.options.rememberuserlicensepref) {this.set_preference('recentlicense', license.get('value'));}}params['author'] = selectnode.one('.fp-setauthor input').get('value');var return_types = this.options.repositories[this.active_repo.id].return_types;if (this.options.env == 'editor') {// in editor, images are stored in '/' onlyparams.savepath = '/';}if ((this.options.externallink || this.options.env != 'editor') &&(return_types & 1/*FILE_EXTERNAL*/) &&(this.options.return_types & 1/*FILE_EXTERNAL*/) &&selectnode.one('.fp-linktype-1 input').get('checked')) {params['linkexternal'] = 'yes';} else if ((return_types & 4/*FILE_REFERENCE*/) &&(this.options.return_types & 4/*FILE_REFERENCE*/) &&selectnode.one('.fp-linktype-4 input').get('checked')) {params['usefilereference'] = '1';} else if ((return_types & 8/*FILE_CONTROLLED_LINK*/) &&(this.options.return_types & 8/*FILE_CONTROLLED_LINK*/) &&selectnode.one('.fp-linktype-8 input').get('checked')) {params['usecontrolledlink'] = '1';}selectnode.addClass('loading');this.request({action:'download',client_id: client_id,repository_id: repository_id,'params': params,onerror: function(id, obj, args) {selectnode.removeClass('loading');scope.selectui.hide();},callback: function(id, obj, args) {selectnode.removeClass('loading');if (obj.event == 'fileexists') {scope.process_existing_file(obj);return;}if (scope.options.editor_target && scope.options.env=='editor') {scope.options.editor_target.value=obj.url;scope.options.editor_target.dispatchEvent(new Event('change'), {'bubbles': true});}scope.hide();obj.client_id = client_id;var formcallback_scope = args.scope.options.magicscope ? args.scope.options.magicscope : args.scope;scope.options.formcallback.apply(formcallback_scope, [obj]);}}, false);}, this);var elform = selectnode.one('form');elform.appendChild(Y.Node.create('<input/>').setAttrs({type:'hidden',id:'filesource-'+client_id}));elform.appendChild(Y.Node.create('<input/>').setAttrs({type:'hidden',id:'filesourcekey-'+client_id}));elform.on('keydown', function(e) {if (e.keyCode == 13) {getfile.simulate('click');e.preventDefault();}}, this);var cancel = selectnode.one('.fp-select-cancel');cancel.on('click', function(e) {e.preventDefault();this.selectui.hide();}, this);},wait: function() {// First check there isn't already an interval in play, and if there is kill it now.if (this.waitinterval != null) {clearInterval(this.waitinterval);}// Prepare the root node we will set content for and the loading template we want to display as a YUI node.var root = this.fpnode.one('.fp-content');var content = Y.Node.create(M.core_filepicker.templates.loading).addClass('fp-content-hidden').setStyle('opacity', 0);var count = 0;// Initiate an interval, we will have a count which will increment every 100 milliseconds.// Count 0 - the loading icon will have visibility set to hidden (invisible) and have an opacity of 0 (invisible also)// Count 5 - the visiblity will be switched to visible but opacity will still be at 0 (inivisible)// Counts 6 - 15 opacity will be increased by 0.1 making the loading icon visible over the period of a second// Count 16 - The interval will be cancelled.var interval = setInterval(function(){if (!content || !root.contains(content) || count >= 15) {clearInterval(interval);return true;}if (count == 5) {content.removeClass('fp-content-hidden');} else if (count > 5) {var opacity = parseFloat(content.getStyle('opacity'));content.setStyle('opacity', opacity + 0.1);}count++;return false;}, 100);// Store the wait interval so that we can check it in the future.this.waitinterval = interval;// Set the content to the loading template.root.setContent(content);},viewbar_set_enabled: function(mode) {var viewbar = this.fpnode.one('.fp-viewbar')if (viewbar) {if (mode) {viewbar.addClass('enabled').removeClass('disabled');this.fpnode.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').setAttribute("aria-disabled", "false");this.fpnode.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').setAttribute("tabindex", "");} else {viewbar.removeClass('enabled').addClass('disabled');this.fpnode.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').setAttribute("aria-disabled", "true");this.fpnode.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').setAttribute("tabindex", "-1");}}this.fpnode.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').removeClass('checked');var modes = {1:'icons', 2:'tree', 3:'details'};this.fpnode.all('.fp-vb-'+modes[this.viewmode]).addClass('checked');},viewbar_clicked: function(e) {e.preventDefault();var viewbar = this.fpnode.one('.fp-viewbar')if (!viewbar || !viewbar.hasClass('disabled')) {if (e.currentTarget.hasClass('fp-vb-tree')) {this.viewmode = 2;} else if (e.currentTarget.hasClass('fp-vb-details')) {this.viewmode = 3;} else {this.viewmode = 1;}this.viewbar_set_enabled(true)this.view_files();this.set_preference('recentviewmode', this.viewmode);}},render: function() {var client_id = this.options.client_id;var fpid = "filepicker-"+ client_id;var labelid = 'fp-dialog-label_'+ client_id;var width = 873;var draggable = true;this.fpnode = Y.Node.create(M.core_filepicker.templates.generallayout).set('id', 'filepicker-'+client_id).set('aria-labelledby', labelid);if (this.in_iframe()) {width = Math.floor(window.innerWidth * 0.95);draggable = false;}this.mainui = new M.core.dialogue({extraClasses : ['filepicker'],draggable : draggable,bodyContent : this.fpnode,headerContent: '<h3 id="'+ labelid +'">'+ M.util.get_string('filepicker', 'repository') +'</h3>',centered : true,modal : true,visible : false,width : width+'px',responsiveWidth : 768,height : '558px',zIndex : this.options.zIndex,focusOnPreviousTargetAfterHide: true,focusAfterHide: this.options.previousActiveElement});// create panel for selecting a file (initially hidden)this.selectnode = Y.Node.create(M.core_filepicker.templates.selectlayout).set('id', 'filepicker-select-'+client_id).set('aria-live', 'assertive').set('role', 'dialog');var fplabel = 'fp-file_label_'+ client_id;this.selectui = new M.core.dialogue({headerContent: '<h3 id="' + fplabel +'">'+M.util.get_string('select', 'repository')+'</h3>',draggable : true,width : '450px',bodyContent : this.selectnode,centered : true,modal : true,visible : false,zIndex : this.options.zIndex});Y.one('#'+this.selectnode.get('id')).setAttribute('aria-labelledby', fplabel);// event handler for lazy loading of thumbnails and next pagethis.fpnode.one('.fp-content').on(['scroll','resize'], this.content_scrolled, this);// save template for one path element and location of path barif (this.fpnode.one('.fp-path-folder')) {this.pathnode = this.fpnode.one('.fp-path-folder');this.pathbar = this.pathnode.get('parentNode');this.pathbar.removeChild(this.pathnode);}// assign callbacks for view mode switch buttonsthis.fpnode.one('.fp-vb-icons').on('click', this.viewbar_clicked, this);this.fpnode.one('.fp-vb-tree').on('click', this.viewbar_clicked, this);this.fpnode.one('.fp-vb-details').on('click', this.viewbar_clicked, this);// assign callbacks for toolbar linksthis.setup_toolbar();this.setup_select_file();this.hide_header();// processing repository listing// Resort the repositories by sortordervar sorted_repositories = [];var i;for (i in this.options.repositories) {sorted_repositories[i] = this.options.repositories[i];}sorted_repositories.sort(function(a,b){return a.sortorder-b.sortorder});// extract one repository template and repeat it for all repositories available,// set name and icon and assign callbacksvar reponode = this.fpnode.one('.fp-repo');if (reponode) {var list = reponode.get('parentNode');list.removeChild(reponode);for (i in sorted_repositories) {var repository = sorted_repositories[i];var h = (parseInt(i) == 0) ? parseInt(i) : parseInt(i) - 1,j = (parseInt(i) == Object.keys(sorted_repositories).length - 1) ? parseInt(i) : parseInt(i) + 1;var previousrepository = sorted_repositories[h];var nextrepository = sorted_repositories[j];var node = reponode.cloneNode(true);list.appendChild(node);node.set('id', 'fp-repo-'+client_id+'-'+repository.id).on('click', function(e, repository_id) {e.preventDefault();this.set_preference('recentrepository', repository_id);this.hide_header();this.list({'repo_id':repository_id});}, this /*handler running scope*/, repository.id/*second argument of handler*/);node.on('key', function(e, previousrepositoryid, nextrepositoryid, clientid, repositoryid) {this.changeHighlightedRepository(e, clientid, repositoryid, previousrepositoryid, nextrepositoryid);}, 'down:38,40', this, previousrepository.id, nextrepository.id, client_id, repository.id);node.on('key', function(e, repositoryid) {e.preventDefault();this.set_preference('recentrepository', repositoryid);this.hide_header();this.list({'repo_id': repositoryid});}, 'enter', this, repository.id);node.one('.fp-repo-name').setContent(Y.Escape.html(repository.name));node.one('.fp-repo-icon').set('src', repository.icon);if (i==0) {node.addClass('first');}if (i==sorted_repositories.length-1) {node.addClass('last');}if (i%2) {node.addClass('even');} else {node.addClass('odd');}}}// display error if no repositories foundif (sorted_repositories.length==0) {this.display_error(M.util.get_string('norepositoriesavailable', 'repository'), 'norepositoriesavailable')}// display repository that was used last timethis.mainui.show();this.show_recent_repository();},/*** Change the highlighted repository to a new one.** @param {object} event The key event* @param {integer} clientid The client id to identify the repo class.* @param {integer} oldrepositoryid The repository id that we are removing the highlight for* @param {integer} previousrepositoryid The previous repository id.* @param {integer} nextrepositoryid The next repository id.*/changeHighlightedRepository: function(event, clientid, oldrepositoryid, previousrepositoryid, nextrepositoryid) {event.preventDefault();var newrepositoryid = (event.keyCode == '40') ? nextrepositoryid : previousrepositoryid;this.fpnode.one('#fp-repo-' + clientid + '-' + oldrepositoryid).setAttribute('tabindex', '-1');this.fpnode.one('#fp-repo-' + clientid + '-' + newrepositoryid).setAttribute('tabindex', '0').focus();},parse_repository_options: function(data, appendtolist) {if (appendtolist) {if (data.list) {if (!this.filelist) {this.filelist = [];}for (var i in data.list) {this.filelist[this.filelist.length] = data.list[i];}}} else {this.filelist = data.list?data.list:null;this.lazyloading = {};}this.filepath = data.path?data.path:null;this.objecttag = data.object?data.object:null;this.active_repo = {};this.active_repo.issearchresult = data.issearchresult ? true : false;this.active_repo.defaultreturntype = data.defaultreturntype?data.defaultreturntype:null;this.active_repo.dynload = data.dynload?data.dynload:false;this.active_repo.pages = Number(data.pages?data.pages:null);this.active_repo.page = Number(data.page?data.page:null);this.active_repo.hasmorepages = (this.active_repo.pages && this.active_repo.page && (this.active_repo.page < this.active_repo.pages || this.active_repo.pages == -1))this.active_repo.id = data.repo_id?data.repo_id:null;this.active_repo.nosearch = (data.login || data.nosearch); // this is either login form or 'nosearch' attribute setthis.active_repo.norefresh = (data.login || data.norefresh); // this is either login form or 'norefresh' attribute setthis.active_repo.nologin = (data.login || data.nologin); // this is either login form or 'nologin' attribute is setthis.active_repo.logouttext = data.logouttext?data.logouttext:null;this.active_repo.logouturl = (data.logouturl || '');this.active_repo.message = (data.message || '');this.active_repo.help = data.help?data.help:null;this.active_repo.manage = data.manage?data.manage:null;// Warning message related to the file reference option, if applicable to the given repository.this.active_repo.filereferencewarning = data.filereferencewarning ? data.filereferencewarning : null;this.print_header();},print_login: function(data) {this.parse_repository_options(data);var client_id = this.options.client_id;var repository_id = data.repo_id;var l = this.logindata = data.login;var loginurl = '';var action = data['login_btn_action'] ? data['login_btn_action'] : 'login';var form_id = 'fp-form-'+client_id;var loginform_node = Y.Node.create(M.core_filepicker.templates.loginform);loginform_node.one('form').set('id', form_id);this.fpnode.one('.fp-content').setContent('').appendChild(loginform_node);var templates = {'popup' : loginform_node.one('.fp-login-popup'),'textarea' : loginform_node.one('.fp-login-textarea'),'select' : loginform_node.one('.fp-login-select'),'text' : loginform_node.one('.fp-login-text'),'radio' : loginform_node.one('.fp-login-radiogroup'),'checkbox' : loginform_node.one('.fp-login-checkbox'),'input' : loginform_node.one('.fp-login-input')};var container;for (var i in templates) {if (templates[i]) {container = templates[i].get('parentNode');container.removeChild(templates[i]);}}for(var k in l) {if (templates[l[k].type]) {var node = templates[l[k].type].cloneNode(true);} else {node = templates['input'].cloneNode(true);}if (l[k].type == 'popup') {// submit buttonloginurl = l[k].url;var popupbutton = node.one('button');popupbutton.on('click', function(e){M.core_filepicker.active_filepicker = this;window.open(loginurl, 'repo_auth', 'location=0,status=0,width=500,height=300,scrollbars=yes');e.preventDefault();}, this);loginform_node.one('form').on('keydown', function(e) {if (e.keyCode == 13) {popupbutton.simulate('click');e.preventDefault();}}, this);loginform_node.all('.fp-login-submit').remove();action = 'popup';} else if(l[k].type=='textarea') {// textarea elementif (node.one('label')) {node.one('label').set('for', l[k].id).setContent(l[k].label);}node.one('textarea').setAttrs({id:l[k].id, name:l[k].name});} else if(l[k].type=='select') {// select elementif (node.one('label')) {node.one('label').set('for', l[k].id).setContent(l[k].label);}node.one('select').setAttrs({id:l[k].id, name:l[k].name}).setContent('');for (i in l[k].options) {node.one('select').appendChild(Y.Node.create('<option/>').set('value', l[k].options[i].value).setContent(l[k].options[i].label));}} else if(l[k].type=='radio') {// radio input elementnode.all('label').setContent(l[k].label);var list = l[k].value.split('|');var labels = l[k].value_label.split('|');var radionode = null;for(var item in list) {if (radionode == null) {radionode = node.one('.fp-login-radio');radionode.one('input').set('checked', 'checked');} else {var x = radionode.cloneNode(true);radionode.insert(x, 'after');radionode = x;radionode.one('input').set('checked', '');}radionode.one('input').setAttrs({id:''+l[k].id+item, name:l[k].name,type:l[k].type, value:list[item]});radionode.all('label').setContent(labels[item]).set('for', ''+l[k].id+item)}if (radionode == null) {node.one('.fp-login-radio').remove();}} else {// input elementif (node.one('label')) { node.one('label').set('for', l[k].id).setContent(l[k].label) }node.one('input').set('type', l[k].type).set('id', l[k].id).set('name', l[k].name).set('value', l[k].value?l[k].value:'')}container.appendChild(node);}// custom label text for submit buttonif (data['login_btn_label']) {loginform_node.all('.fp-login-submit').setContent(data['login_btn_label'])}// register button action for login and searchif (action == 'login' || action == 'search') {loginform_node.one('.fp-login-submit').on('click', function(e){e.preventDefault();this.hide_header();this.request({'scope': this,'action':(action == 'search') ? 'search' : 'signin','path': '','client_id': client_id,'repository_id': repository_id,'form': {id:form_id, upload:false, useDisabled:true},'callback': this.display_response}, true);}, this);}// if 'Enter' is pressed in the form, simulate the button clickif (loginform_node.one('.fp-login-submit')) {loginform_node.one('form').on('keydown', function(e) {if (e.keyCode == 13) {loginform_node.one('.fp-login-submit').simulate('click')e.preventDefault();}}, this);}},display_response: function(id, obj, args) {var scope = args.scope;// highlight the current repository in repositories listscope.fpnode.all('.fp-repo.active').removeClass('active').setAttribute('aria-selected', 'false').setAttribute('tabindex', '-1');scope.fpnode.all('.nav-link').removeClass('active').setAttribute('aria-selected', 'false').setAttribute('tabindex', '-1');var activenode = scope.fpnode.one('#fp-repo-' + scope.options.client_id + '-' + obj.repo_id);activenode.addClass('active').setAttribute('aria-selected', 'true').setAttribute('tabindex', '0');activenode.all('.nav-link').addClass('active');// add class repository_REPTYPE to the filepicker (for repository-specific styles)for (var i in scope.options.repositories) {scope.fpnode.removeClass('repository_'+scope.options.repositories[i].type)}if (obj.repo_id && scope.options.repositories[obj.repo_id]) {scope.fpnode.addClass('repository_'+scope.options.repositories[obj.repo_id].type)}Y.one('.file-picker .fp-repo-items').focus();// display responseif (obj.login) {scope.viewbar_set_enabled(false);scope.print_login(obj);} else if (obj.upload) {scope.viewbar_set_enabled(false);scope.parse_repository_options(obj);scope.create_upload_form(obj);} else if (obj.object) {M.core_filepicker.active_filepicker = scope;scope.viewbar_set_enabled(false);scope.parse_repository_options(obj);scope.create_object_container(obj.object);} else if (obj.list) {scope.viewbar_set_enabled(true);scope.parse_repository_options(obj);scope.view_files();}},list: function(args) {if (!args) {args = {};}if (!args.repo_id) {args.repo_id = this.active_repo.id;}if (!args.path) {args.path = '';}this.currentpath = args.path;this.request({action: 'list',client_id: this.options.client_id,repository_id: args.repo_id,path: args.path,page: args.page,scope: this,callback: this.display_response}, true);},populateLicensesSelect: function(licensenode, filenode) {if (!licensenode) {return;}licensenode.setContent('');var selectedlicense = this.options.defaultlicense;if (filenode) {// File has a license already, use it.selectedlicense = filenode.license;} else if (this.options.rememberuserlicensepref && this.get_preference('recentlicense')) {// When 'Remember user licence preference' is enabled use the last license selected by the user, if any.selectedlicense = this.get_preference('recentlicense');}var licenses = this.options.licenses;for (var i in licenses) {// Include the file's current license, even if not enabled, to prevent displaying// misleading information about which license the file currently has assigned to it.if (licenses[i].enabled == true || (filenode !== undefined && licenses[i].shortname === filenode.license)) {var option = Y.Node.create('<option/>').set('selected', (licenses[i].shortname == selectedlicense)).set('value', licenses[i].shortname).setContent(Y.Escape.html(licenses[i].fullname));licensenode.appendChild(option);}}},create_object_container: function(data) {var content = this.fpnode.one('.fp-content');content.setContent('');//var str = '<object data="'+data.src+'" type="'+data.type+'" width="98%" height="98%" id="container_object" class="fp-object-container mdl-align"></object>';var container = Y.Node.create('<object/>').setAttrs({data:data.src, type:data.type, id:'container_object'}).addClass('fp-object-container');content.setContent('').appendChild(container);},create_upload_form: function(data) {var client_id = this.options.client_id;var id = data.upload.id+'_'+client_id;var content = this.fpnode.one('.fp-content');var template_name = 'uploadform_'+this.options.repositories[data.repo_id].type;var template = M.core_filepicker.templates[template_name] || M.core_filepicker.templates['uploadform'];content.setContent(template);content.all('.fp-file,.fp-saveas,.fp-setauthor,.fp-setlicense').each(function (node) {node.all('label').set('for', node.one('input,select').generateID());});content.one('form').set('id', id);content.one('.fp-file input').set('name', 'repo_upload_file');if (data.upload.label && content.one('.fp-file label')) {content.one('.fp-file label').setContent(data.upload.label);}content.one('.fp-saveas input').set('name', 'title');content.one('.fp-setauthor input').setAttrs({name:'author', value:this.options.author});content.one('.fp-setlicense select').set('name', 'license');this.populateLicensesSelect(content.one('.fp-setlicense select'));// append hidden inputs to the upload formcontent.one('form').appendChild(Y.Node.create('<input/>').setAttrs({type:'hidden',name:'itemid',value:this.options.itemid}));var types = this.options.accepted_types;for (var i in types) {content.one('form').appendChild(Y.Node.create('<input/>').setAttrs({type:'hidden',name:'accepted_types[]',value:types[i]}));}var scope = this;content.one('.fp-upload-btn').on('click', function(e) {e.preventDefault();var license = content.one('.fp-setlicense select');if (this.options.rememberuserlicensepref) {this.set_preference('recentlicense', license.get('value'));}if (!content.one('.fp-file input').get('value')) {scope.print_msg(M.util.get_string('nofilesattached', 'repository'), 'error');return false;}this.hide_header();scope.request({scope: scope,action:'upload',client_id: client_id,params: {'savepath': scope.options.savepath || '/'},repository_id: scope.active_repo.id,form: {id: id, upload:true},onerror: function(id, o, args) {scope.create_upload_form(data);},callback: function(id, o, args) {if (o.event == 'fileexists') {scope.create_upload_form(data);scope.process_existing_file(o);return;}if (scope.options.editor_target&&scope.options.env=='editor') {scope.options.editor_target.value=o.url;scope.options.editor_target.dispatchEvent(new Event('change'), {'bubbles': true});}scope.hide();o.client_id = client_id;var formcallback_scope = args.scope.options.magicscope ? args.scope.options.magicscope : args.scope;scope.options.formcallback.apply(formcallback_scope, [o]);}}, true);}, this);},/** setting handlers and labels for elements in toolbar. Called once during the initial render of filepicker */setup_toolbar: function() {var client_id = this.options.client_id;var toolbar = this.fpnode.one('.fp-toolbar');toolbar.one('.fp-tb-logout').one('a,button').on('click', function(e) {e.preventDefault();if (!this.active_repo.nologin) {this.hide_header();this.request({action:'logout',client_id: this.options.client_id,repository_id: this.active_repo.id,path:'',callback: this.display_response}, true);}if (this.active_repo.logouturl) {window.open(this.active_repo.logouturl, 'repo_auth', 'location=0,status=0,width=500,height=300,scrollbars=yes');}}, this);toolbar.one('.fp-tb-refresh').one('a,button').on('click', function(e) {e.preventDefault();if (!this.active_repo.norefresh) {this.list({ path: this.currentpath });}}, this);toolbar.one('.fp-tb-search form').set('method', 'POST').set('id', 'fp-tb-search-'+client_id).on('submit', function(e) {e.preventDefault();if (!this.active_repo.nosearch) {this.request({scope: this,action:'search',client_id: this.options.client_id,repository_id: this.active_repo.id,form: {id: 'fp-tb-search-'+client_id, upload:false, useDisabled:true},callback: this.display_response}, true);}}, this);// it does not matter what kind of element is .fp-tb-manage, we create a dummy <a>// element and use it to open url on click eventvar managelnk = Y.Node.create('<a/>').setAttrs({id:'fp-tb-manage-'+client_id+'-link', target:'_blank'}).setStyle('display', 'none');toolbar.append(managelnk);toolbar.one('.fp-tb-manage').one('a,button').on('click', function(e) {e.preventDefault();managelnk.simulate('click')});// same with .fp-tb-helpvar helplnk = Y.Node.create('<a/>').setAttrs({id:'fp-tb-help-'+client_id+'-link', target:'_blank'}).setStyle('display', 'none');toolbar.append(helplnk);toolbar.one('.fp-tb-help').one('a,button').on('click', function(e) {e.preventDefault();helplnk.simulate('click')});},hide_header: function() {if (this.fpnode.one('.fp-toolbar')) {this.fpnode.one('.fp-toolbar').addClass('empty');}if (this.pathbar) {this.pathbar.setContent('').addClass('empty');}},print_header: function() {var r = this.active_repo;var scope = this;var client_id = this.options.client_id;this.hide_header();this.print_path();var toolbar = this.fpnode.one('.fp-toolbar');if (!toolbar) { return; }var enable_tb_control = function(node, enabled) {if (!node) { return; }node.addClassIf('disabled', !enabled).addClassIf('enabled', enabled)if (enabled) {toolbar.removeClass('empty');}}// TODO 'back' permanently disabled for now. Note, flickr_public uses 'Logout' for it!enable_tb_control(toolbar.one('.fp-tb-back'), false);// search formenable_tb_control(toolbar.one('.fp-tb-search'), !r.nosearch);if(!r.nosearch) {var searchform = toolbar.one('.fp-tb-search form');searchform.setContent('');this.request({scope: this,action:'searchform',repository_id: this.active_repo.id,callback: function(id, obj, args) {if (obj.repo_id == scope.active_repo.id && obj.form) {// if we did not jump to another repository meanwhilesearchform.setContent(obj.form);// Highlight search text when user click for search.var searchnode = searchform.one('input[name="s"]');if (searchnode) {searchnode.once('click', function(e) {e.preventDefault();this.select();});}}}}, false);}// refresh button// weather we use cache for this instance, this button will reload listing anywayenable_tb_control(toolbar.one('.fp-tb-refresh'), !r.norefresh);// login buttonenable_tb_control(toolbar.one('.fp-tb-logout'), !r.nologin);// manage urlenable_tb_control(toolbar.one('.fp-tb-manage'), r.manage);Y.one('#fp-tb-manage-'+client_id+'-link').set('href', r.manage);// help urlenable_tb_control(toolbar.one('.fp-tb-help'), r.help);Y.one('#fp-tb-help-'+client_id+'-link').set('href', r.help);// messageenable_tb_control(toolbar.one('.fp-tb-message'), r.message);toolbar.one('.fp-tb-message').setContent(r.message);},print_path: function() {if (!this.pathbar) {return;}this.pathbar.setContent('').addClass('empty');var p = this.filepath;if (p && p.length!=0 && this.viewmode != 2) {for(var i = 0; i < p.length; i++) {var el = this.pathnode.cloneNode(true);this.pathbar.appendChild(el);if (i == 0) {el.addClass('first');}if (i == p.length-1) {el.addClass('last');}if (i%2) {el.addClass('even');} else {el.addClass('odd');}el.all('.fp-path-folder-name').setContent(Y.Escape.html(p[i].name));el.on('click',function(e, path) {e.preventDefault();this.list({'path':path});},this, p[i].path);}this.pathbar.removeClass('empty');}},hide: function() {this.selectui.hide();if (this.process_dlg) {this.process_dlg.hide();}if (this.msg_dlg) {this.msg_dlg.hide();}this.mainui.hide();},show: function() {if (this.fpnode) {this.hide();this.mainui.show();this.show_recent_repository();} else {this.launch();}},launch: function() {this.render();},show_recent_repository: function() {this.hide_header();this.viewbar_set_enabled(false);var repository_id = this.get_preference('recentrepository');this.viewmode = this.get_preference('recentviewmode');if (this.viewmode != 2 && this.viewmode != 3) {this.viewmode = 1;}if (this.options.repositories[repository_id]) {this.list({'repo_id':repository_id});}},get_preference: function (name) {if (this.options.userprefs[name]) {return this.options.userprefs[name];} else {return false;}},set_preference: function(name, value) {if (this.options.userprefs[name] != value) {require(['core_user/repository'], function(UserRepository) {UserRepository.setUserPreference('filepicker_' + name, value);this.options.userprefs[name] = value;}.bind(this));}},in_iframe: function () {// If we're not the top window then we're in an iFramereturn window.self !== window.top;}});var loading = Y.one('#filepicker-loading-'+options.client_id);if (loading) {loading.setStyle('display', 'none');}M.core_filepicker.instances[options.client_id] = new FilePickerHelper(options);};