rlm@3: /* rlm@3: Script: FileManager.js rlm@3: MooTools FileManager rlm@3: rlm@3: License: rlm@3: MIT-style license. rlm@3: rlm@3: Version: rlm@3: 1.0rc1 rlm@3: rlm@3: Copyright: rlm@3: Copyright (c) 2009 [Christoph Pojer](http://og5.net/christoph). rlm@3: rlm@3: Dependencies: rlm@3: - MooTools Core 1.2.2 rlm@3: - MooTools More 1.2.2.1 or newer: Drag.js, Drag.Move.js, Tips.js, Asset.js rlm@3: - Additions.js rlm@3: rlm@3: Todo: rlm@3: - Add Scroller.js (optional) for Drag&Drop in the Filelist rlm@3: rlm@3: Inspiration: rlm@3: - Loosely based on a Script by [Yannick Croissant](http://dev.k1der.net/dev/brooser-un-browser-de-fichier-pour-mootools/) rlm@3: rlm@3: Options: rlm@3: - url: (string) The base url to the Backend FileManager, without QueryString rlm@3: - assetBasePath: (string) The path to all images and swf files rlm@3: - selectable: (boolean, defaults to *false*) If true, provides a button to select a file rlm@3: - language: (string, defaults to *en*) The language used for the FileManager rlm@3: - hideOnClick: (boolean, defaults to *false*) When true, hides the FileManager when the area outside of it is clicked rlm@3: - directory: (string) Can be used to load a subfolder instead of the base folder rlm@3: rlm@3: Events: rlm@3: - onComplete(path, file): fired when a file gets selected via the "Select file" button rlm@3: - onModify(file): fired when a file gets renamed/deleted or modified in another way rlm@3: - onShow: fired when the FileManager opens rlm@3: - onHide: event fired when FileManager closes rlm@3: */ rlm@3: rlm@3: var FileManager = new Class({ rlm@3: rlm@3: Implements: [Options, Events], rlm@3: rlm@3: Request: null, rlm@3: Directory: null, rlm@3: Current: null, rlm@3: rlm@3: options: { rlm@3: /*onComplete: $empty, rlm@3: onModify: $empty, rlm@3: onShow: $empty, rlm@3: onHide: $empty,*/ rlm@3: directory: '', rlm@3: url: null, rlm@3: assetBasePath: null, rlm@3: selectable: false, rlm@3: hideOnClick: false, rlm@3: language: 'en' rlm@3: }, rlm@3: rlm@3: hooks: { rlm@3: show: {}, rlm@3: cleanup: {} rlm@3: }, rlm@3: rlm@3: initialize: function(options){ rlm@3: this.setOptions(options); rlm@3: this.options.assetBasePath = this.options.assetBasePath.replace(/(\/|\\)*$/, '/'); rlm@3: this.droppables = []; rlm@3: this.Directory = this.options.directory; rlm@3: rlm@3: this.language = FileManager.Language[this.options.language] || FileManager.Language.en; rlm@3: this.container = new Element('div', {'class': 'filemanager-container filemanager-engine-'+Browser.Engine.name+(Browser.Engine.trident ? Browser.Engine.version : '')}); rlm@3: this.el = new Element('div', {'class': 'filemanager'}).inject(this.container); rlm@3: this.menu = new Element('div', {'class': 'filemanager-menu'}).inject(this.el); rlm@3: this.loader = new Element('div', {'class': 'loader', opacity: 0, tween: {duration: 200}}).inject(this.menu); rlm@3: this.browser = new Element('ul', {'class': 'filemanager-browser'}).addEvents({ rlm@3: click: (function(e){ rlm@3: if(e.target.match('ul')) return this.deselect(); rlm@3: rlm@3: if(!e.target || !e.target.getParent('li')) return; rlm@3: var el = e.target.getParent('li').getElement('span'); rlm@3: if(!el) return; rlm@3: rlm@3: e.stop(); rlm@3: var file = el.retrieve('file'); rlm@3: if(el.retrieve('block')){ rlm@3: el.eliminate('block'); rlm@3: return; rlm@3: }else if(file.mime=='text/directory'){ rlm@3: el.addClass('selected'); rlm@3: this.load(this.Directory+'/'+file.name); rlm@3: return; rlm@3: } rlm@3: rlm@3: this.fillInfo(file); rlm@3: if(this.Current) this.Current.removeClass('selected'); rlm@3: this.Current = el.addClass('selected'); rlm@3: rlm@3: this.switchButton(); rlm@3: }).bind(this) rlm@3: }).inject(this.el); rlm@3: rlm@3: rlm@3: if(this.options.selectable) this.addMenuButton('open'); rlm@3: this.addMenuButton('create'); rlm@3: rlm@3: this.info = new Element('div', {'class': 'filemanager-infos', opacity: 0}).inject(this.el); rlm@3: rlm@3: var head = new Element('div', {'class': 'filemanager-head'}).adopt([ rlm@3: new Element('img', {'class': 'filemanager-icon'}), rlm@3: new Element('h1') rlm@3: ]); rlm@3: rlm@3: this.info.adopt([head, new Element('h2', {text: this.language.information})]); rlm@3: rlm@3: var list = new Element('dl').adopt([ rlm@3: new Element('dt', {text: this.language.modified}), rlm@3: new Element('dd', {'class': 'filemanager-modified'}), rlm@3: new Element('dt', {text: this.language.type}), rlm@3: new Element('dd', {'class': 'filemanager-type'}), rlm@3: new Element('dt', {text: this.language.size}), rlm@3: new Element('dd', {'class': 'filemanager-size'}), rlm@3: new Element('dt', {text: this.language.dir}), rlm@3: new Element('dd', {'class': 'filemanager-dir'}) rlm@3: ]).inject(this.info); rlm@3: rlm@3: this.preview = new Element('div', {'class': 'filemanager-preview'}); rlm@3: this.info.adopt([ rlm@3: new Element('h2', {'class': 'filemanager-headline', text: this.language.preview}), rlm@3: this.preview rlm@3: ]); rlm@3: rlm@3: this.closeIcon = new Element('div', { rlm@3: 'class': 'filemanager-close', rlm@3: title: this.language.close, rlm@3: events: {click: this.hide.bind(this)} rlm@3: }).adopt(new Asset.image(this.options.assetBasePath+'destroy.png')).inject(this.el); rlm@3: new FileManager.Tips(this.closeIcon.appearOn(this.closeIcon, [1, 0.8]).appearOn(this.el, 0.8)); rlm@3: rlm@3: this.imageadd = new Asset.image(this.options.assetBasePath+'add.png', { rlm@3: 'class': 'browser-add' rlm@3: }).set('opacity', 0).inject(this.container); rlm@3: rlm@3: this.container.inject(document.body); rlm@3: this.overlay = new Overlay(this.options.hideOnClick ? { rlm@3: events: {click: this.hide.bind(this)} rlm@3: } : null); rlm@3: this.bound = { rlm@3: keydown: (function(e){ rlm@3: if(e.control) this.imageadd.fade(1); rlm@3: }).bind(this), rlm@3: keyup: (function(){ rlm@3: this.imageadd.fade(0); rlm@3: }).bind(this), rlm@3: keyesc: (function(e){ rlm@3: if(e.key=='esc') this.hide(); rlm@3: }).bind(this), rlm@3: scroll: (function(){ rlm@3: this.el.center(this.offsets); rlm@3: this.fireEvent('scroll'); rlm@3: }).bind(this) rlm@3: }; rlm@3: }, rlm@3: rlm@3: show: function(e){ rlm@3: if(e) e.stop(); rlm@3: rlm@3: this.load(this.Directory); rlm@3: this.overlay.show(); rlm@3: rlm@3: this.info.set('opacity', 0); rlm@3: rlm@3: (function(){ rlm@3: this.container.setStyles({ rlm@3: opacity: 0, rlm@3: display: 'block' rlm@3: }); rlm@3: rlm@3: this.el.center(this.offsets); rlm@3: this.fireEvent('show'); rlm@3: this.container.set('opacity', 1); rlm@3: this.fireHooks('show'); rlm@3: rlm@3: window.addEvents({ rlm@3: scroll: this.bound.scroll, rlm@3: resize: this.bound.scroll, rlm@3: keyup: this.bound.keyesc rlm@3: }); rlm@3: }).delay(500, this); rlm@3: }, rlm@3: rlm@3: hide: function(e){ rlm@3: if(e) e.stop(); rlm@3: rlm@3: this.overlay.hide(); rlm@3: this.browser.empty(); rlm@3: this.container.setStyle('display', 'none'); rlm@3: rlm@3: this.fireHooks('cleanup').fireEvent('hide'); rlm@3: window.removeEvent('scroll', this.bound.scroll).removeEvent('resize', this.bound.scroll).removeEvent('keyup', this.bound.keyesc); rlm@3: }, rlm@3: rlm@3: open: function(e){ rlm@3: e.stop(); rlm@3: rlm@3: if(!this.Current) return false; rlm@3: rlm@3: this.fireEvent('complete', [ rlm@3: this.normalize(this.Directory+'/'+this.Current.retrieve('file').name), rlm@3: this.Current.retrieve('file') rlm@3: ]); rlm@3: this.hide(); rlm@3: }, rlm@3: rlm@3: create: function(e){ rlm@3: e.stop(); rlm@3: rlm@3: var self = this; rlm@3: new Dialog(this.language.createdir, { rlm@3: language: { rlm@3: confirm: this.language.create, rlm@3: decline: this.language.cancel rlm@3: }, rlm@3: content: [ rlm@3: new Element('input', {'class': 'createDirectory'}) rlm@3: ], rlm@3: onOpen: this.onDialogOpen.bind(this), rlm@3: onClose: this.onDialogClose.bind(this), rlm@3: onShow: function(){ rlm@3: var self = this; rlm@3: this.el.getElement('input').addEvent('keyup', function(e){ rlm@3: if(e.key=='enter') self.el.getElement('button-confirm').fireEvent('click'); rlm@3: }).focus(); rlm@3: }, rlm@3: onConfirm: function(){ rlm@3: new FileManager.Request({ rlm@3: url: self.options.url+'?event=create', rlm@3: onSuccess: self.fill.bind(self), rlm@3: data: { rlm@3: file: this.el.getElement('input').get('value'), rlm@3: directory: self.Directory rlm@3: } rlm@3: }, self).post(); rlm@3: } rlm@3: }); rlm@3: }, rlm@3: rlm@3: deselect: function(el){ rlm@3: if(el && this.Current!=el) return; rlm@3: rlm@3: if(el) this.fillInfo(); rlm@3: if(this.Current) this.Current.removeClass('selected'); rlm@3: this.Current = null; rlm@3: rlm@3: this.switchButton(); rlm@3: }, rlm@3: rlm@3: load: function(dir, nofade){ rlm@3: this.deselect(); rlm@3: if(!nofade) this.info.fade(0); rlm@3: rlm@3: if(this.Request) this.Request.cancel(); rlm@3: rlm@3: this.Request = new FileManager.Request({ rlm@3: url: this.options.url, rlm@3: onSuccess: (function(j){ rlm@3: this.fill(j, nofade); rlm@3: }).bind(this), rlm@3: data: { rlm@3: directory: dir rlm@3: } rlm@3: }, this).post(); rlm@3: }, rlm@3: rlm@3: destroy: function(e, file){ rlm@3: e.stop(); rlm@3: rlm@3: var self = this; rlm@3: new Dialog(this.language.destroyfile, { rlm@3: language: { rlm@3: confirm: this.language.destroy, rlm@3: decline: this.language.cancel rlm@3: }, rlm@3: onOpen: this.onDialogOpen.bind(this), rlm@3: onClose: this.onDialogClose.bind(this), rlm@3: onConfirm: function(){ rlm@3: new FileManager.Request({ rlm@3: url: self.options.url+'?event=destroy', rlm@3: data: { rlm@3: file: file.name, rlm@3: directory: self.Directory rlm@3: }, rlm@3: onSuccess: function(j){ rlm@3: if(!j || j.content!='destroyed'){ rlm@3: new Dialog(self.language.nodestroy, {language: {confirm: self.language.ok}, buttons: ['confirm']}); rlm@3: return; rlm@3: } rlm@3: rlm@3: self.fireEvent('modify', [$unlink(file)]); rlm@3: file.element.getParent().fade(0).get('tween').chain(function(){ rlm@3: self.deselect(file.element); rlm@3: this.element.destroy(); rlm@3: }); rlm@3: } rlm@3: }, self).post(); rlm@3: } rlm@3: }); rlm@3: rlm@3: }, rlm@3: rlm@3: rename: function(e, file){ rlm@3: e.stop(); rlm@3: rlm@3: var name = file.name; rlm@3: if(file.mime!='text/directory') name = name.replace(/\..*$/, ''); rlm@3: rlm@3: var self = this; rlm@3: new Dialog(this.language.renamefile, { rlm@3: language: { rlm@3: confirm: this.language.rename, rlm@3: decline: this.language.cancel rlm@3: }, rlm@3: content: [ rlm@3: new Element('input', {'class': 'rename', value: name}) rlm@3: ], rlm@3: onOpen: this.onDialogOpen.bind(this), rlm@3: onClose: this.onDialogClose.bind(this), rlm@3: onShow: function(){ rlm@3: var self = this; rlm@3: this.el.getElement('input').addEvent('keyup', function(e){ rlm@3: if(e.key=='enter') self.el.getElement('button-confirm').fireEvent('click'); rlm@3: }).focus(); rlm@3: }, rlm@3: onConfirm: function(){ rlm@3: new FileManager.Request({ rlm@3: url: self.options.url+'?event=move', rlm@3: onSuccess: (function(j){ rlm@3: if(!j || !j.name) return; rlm@3: rlm@3: self.fireEvent('modify', [$unlink(file)]); rlm@3: rlm@3: file.element.getElement('span').set('text', j.name); rlm@3: file.name = j.name; rlm@3: self.fillInfo(file); rlm@3: }).bind(this), rlm@3: data: { rlm@3: file: file.name, rlm@3: name: this.el.getElement('input').get('value'), rlm@3: directory: self.Directory rlm@3: } rlm@3: }, self).post(); rlm@3: } rlm@3: }); rlm@3: }, rlm@3: rlm@3: fill: function(j, nofade){ rlm@3: this.Directory = j.path; rlm@3: this.CurrentDir = j.dir; rlm@3: if(!nofade) this.fillInfo(j.dir); rlm@3: this.browser.empty(); rlm@3: rlm@3: if(!j.files) return; rlm@3: rlm@3: var els = [[], []]; rlm@3: $each(j.files, function(file){ rlm@3: file.dir = j.path; rlm@3: var el = file.element = new Element('span', {'class': 'fi', href: '#'}).adopt( rlm@3: new Asset.image(this.options.assetBasePath+'Icons/'+file.icon+'.png'), rlm@3: new Element('span', {text: file.name}) rlm@3: ).store('file', file); rlm@3: rlm@3: var icons = []; rlm@3: if(file.mime!='text/directory') rlm@3: icons.push(new Asset.image(this.options.assetBasePath+'disk.png', {title: this.language.download}).addClass('browser-icon').addEvent('click', (function(e){ rlm@3: e.stop(); rlm@3: window.open(this.normalize(this.Directory+'/'+file.name)); rlm@3: }).bind(this)).inject(el, 'top')); rlm@3: rlm@3: if(file.name!='..') rlm@3: ['rename', 'destroy'].each(function(v){ rlm@3: icons.push(new Asset.image(this.options.assetBasePath+v+'.png', {title: this.language[v]}).addClass('browser-icon').addEvent('click', this[v].bindWithEvent(this, [file])).injectTop(el)); rlm@3: }, this); rlm@3: rlm@3: els[file.mime=='text/directory' ? 1 : 0].push(el); rlm@3: if(file.name=='..') el.set('opacity', 0.7); rlm@3: el.inject(new Element('li').inject(this.browser)); rlm@3: icons = $$(icons.map(function(icon){ return icon.appearOn(icon, [1, 0.7]); })).appearOn(el.getParent('li'), 0.7); rlm@3: }, this); rlm@3: rlm@3: var self = this; rlm@3: $$(els[0]).makeDraggable({ rlm@3: droppables: $$(this.droppables, els[1]), rlm@3: rlm@3: onDrag: function(el, e){ rlm@3: self.imageadd.setStyles(Hash.getValues(e.page).map(function(v){ return v+15; }).associate(['left', 'top'])); rlm@3: }, rlm@3: rlm@3: onBeforeStart: function(el){ rlm@3: el.setStyles({left: '0', top: '0'}); rlm@3: }, rlm@3: rlm@3: onStart: function(el){ rlm@3: self.onDragStart(el, this); rlm@3: rlm@3: el.set('opacity', 0.7); rlm@3: document.addEvents({ rlm@3: keydown: self.bound.keydown, rlm@3: keyup: self.bound.keyup rlm@3: }); rlm@3: }, rlm@3: rlm@3: onEnter: function(el, droppable){ rlm@3: droppable.addClass('droppable'); rlm@3: }, rlm@3: rlm@3: onLeave: function(el, droppable){ rlm@3: droppable.removeClass('droppable'); rlm@3: }, rlm@3: rlm@3: onDrop: function(el, droppable, e){ rlm@3: document.removeEvents('keydown', self.bound.keydown).removeEvents('keyup', self.bound.keydown); rlm@3: rlm@3: self.imageadd.fade(0); rlm@3: el.set('opacity', 1).store('block', true); rlm@3: if(e.control || !droppable) rlm@3: el.setStyles({left: '0', top: '0'}); rlm@3: rlm@3: if(!droppable && !e.control) rlm@3: return; rlm@3: rlm@3: var dir; rlm@3: if(droppable){ rlm@3: droppable.addClass('selected'); rlm@3: (function(){ droppable.removeClass('droppable').removeClass('selected'); }).delay(300); rlm@3: rlm@3: if(self.onDragComplete(el, droppable)) rlm@3: return; rlm@3: rlm@3: dir = droppable.retrieve('file'); rlm@3: } rlm@3: var file = el.retrieve('file'); rlm@3: rlm@3: new FileManager.Request({ rlm@3: url: self.options.url+'?event=move', rlm@3: data: { rlm@3: file: file.name, rlm@3: directory: self.Directory, rlm@3: newDirectory: dir ? dir.dir+'/'+dir.name : self.Directory, rlm@3: copy: e.control ? 1 : 0 rlm@3: }, rlm@3: onSuccess: function(){ rlm@3: if(!dir) self.load(self.Directory); rlm@3: } rlm@3: }, self).post(); rlm@3: rlm@3: self.fireEvent('modify', [$unlink(file)]); rlm@3: rlm@3: if(!e.control) rlm@3: el.fade(0).get('tween').chain(function(){ rlm@3: self.deselect(el); rlm@3: el.getParent().destroy(); rlm@3: }); rlm@3: } rlm@3: }); rlm@3: $$(els).setStyles({left: '0', top: '0'}); rlm@3: var tips = new FileManager.Tips(this.browser.getElements('img.browser-icon')); rlm@3: rlm@3: tips.tip.removeClass('tip-base'); rlm@3: }, rlm@3: rlm@3: fillInfo: function(file, path){ rlm@3: if(!file) file = this.CurrentDir; rlm@3: if(!path) path = this.Directory; rlm@3: rlm@3: if(!file) return; rlm@3: var size = this.size(file.size); rlm@3: rlm@3: this.info.fade(1).getElement('img').set({ rlm@3: src: this.options.assetBasePath+'Icons/'+file.icon+'.png', rlm@3: alt: file.mime rlm@3: }); rlm@3: rlm@3: this.fireHooks('cleanup'); rlm@3: this.preview.empty(); rlm@3: rlm@3: this.info.getElement('h1').set('text', file.name); rlm@3: this.info.getElement('dd.filemanager-modified').set('text', file.date); rlm@3: this.info.getElement('dd.filemanager-type').set('text', file.mime); rlm@3: this.info.getElement('dd.filemanager-size').set('text', !size[0] && size[1]=='Bytes' ? '-' : (size.join(' ')+(size[1]!='Bytes' ? ' ('+file.size+' Bytes)' : ''))); rlm@3: this.info.getElement('h2.filemanager-headline').setStyle('display', file.mime=='text/directory' ? 'none' : 'block'); rlm@3: rlm@3: var text = [], pre = []; rlm@3: rlm@3: path.split('/').each(function(v){ rlm@3: if(!v) return; rlm@3: rlm@3: pre.push(v); rlm@3: text.push(new Element('a', { rlm@3: 'class': 'icon', rlm@3: href: '#', rlm@3: text: v rlm@3: }).addEvent('click', (function(e, dir){ rlm@3: e.stop(); rlm@3: rlm@3: this.load(dir); rlm@3: }).bindWithEvent(this, [pre.join('/')])) rlm@3: ); rlm@3: text.push(new Element('span', {text: ' / '})); rlm@3: }, this); rlm@3: rlm@3: text.pop(); rlm@3: text[text.length-1].addClass('selected').removeEvents('click').addEvent('click', function(e){ e.stop(); }); rlm@3: rlm@3: this.info.getElement('dd.filemanager-dir').empty().adopt(new Element('span', {text: '/ '}), text); rlm@3: rlm@3: if(file.mime=='text/directory') return; rlm@3: rlm@3: if(this.Request) this.Request.cancel(); rlm@3: rlm@3: this.Request = new FileManager.Request({ rlm@3: url: this.options.url+'?event=detail', rlm@3: onSuccess: (function(j){ rlm@3: var prev = this.preview.removeClass('filemanager-loading').set('html', j && j.content ? j.content.substitute(this.language, /\\?\$\{([^{}]+)\}/g) : '').getElement('img.prev'); rlm@3: if(prev) prev.addEvent('load', function(){ rlm@3: this.setStyle('background', 'none'); rlm@3: }); rlm@3: rlm@3: var els = this.preview.getElements('button'); rlm@3: if(els) els.addEvent('click', function(e){ rlm@3: e.stop(); rlm@3: window.open(this.get('value')); rlm@3: }); rlm@3: }).bind(this), rlm@3: data: { rlm@3: directory: this.Directory, rlm@3: file: file.name rlm@3: } rlm@3: }, this).post(); rlm@3: }, rlm@3: rlm@3: size: function(size){ rlm@3: var tab = ['Bytes', 'KB', 'MB', 'GB', 'TB']; rlm@3: for(var i = 0; size > 1024; i++) rlm@3: size = size/1024; rlm@3: rlm@3: return [Math.round(size), tab[i]]; rlm@3: }, rlm@3: rlm@3: normalize: function(str){ rlm@3: return str.replace(/\/+/g, '/'); rlm@3: }, rlm@3: rlm@3: switchButton: function(){ rlm@3: var chk = !!this.Current; rlm@3: var el = this.menu.getElement('button.filemanager-open'); rlm@3: if(el) el.set('disabled', !chk)[(chk ? 'remove' : 'add')+'Class']('disabled'); rlm@3: }, rlm@3: rlm@3: addMenuButton: function(name){ rlm@3: var el = new Element('button', { rlm@3: 'class': 'filemanager-'+name, rlm@3: text: this.language[name] rlm@3: }).inject(this.menu); rlm@3: if(this[name]) el.addEvent('click', this[name].bind(this)); rlm@3: return el; rlm@3: }, rlm@3: rlm@3: fireHooks: function(hook){ rlm@3: var args = Array.slice(arguments, 1); rlm@3: for(var key in this.hooks[hook]) this.hooks[hook][key].apply(this, args); rlm@3: return this; rlm@3: }, rlm@3: rlm@3: onRequest: function(){ this.loader.set('opacity', 1); }, rlm@3: onComplete: function(){ this.loader.fade(0); }, rlm@3: onDialogOpen: $empty, rlm@3: onDialogClose: $empty, rlm@3: rlm@3: onDragStart: $empty, rlm@3: onDragComplete: $lambda(false) rlm@3: rlm@3: }); rlm@3: rlm@3: FileManager.Language = {};