/** * @fileOverview * * 网盘的目录访问组件 * * @author: techird * @copyright: Baidu FEX, 2014 */ KityMinder.registerUI('widget/netdiskfinder', function(minder) { var eve = minder.getUI('eve'); var notice = minder.getUI('widget/notice'); var recycleReady = null; var base = '/apps/kityminder'; var recyclePath = base + '/.recycle'; var moveConfirm = true; var instances = []; var Finder = eve.setup({}); Finder.BASE_PATH = base + '/'; Finder.RECYCLE_PATH = recyclePath + '/'; Finder.on('mv', function(from, to, source) { instances.forEach(function(instance) { if (source != instance) instance.refresh(); }); }); /** * 生成一个网盘的目录访问组件 * * @param {JQueryObject} $container 容器 * @param {function} listFilter 一个函数,检查一个文件是否应该被列出 */ function generate($container, listFilter) { var finder = eve.setup({}); instances.push(finder); var currentPath = base; var $finder = $('
').appendTo($container); /* 顶部工具栏 */ var $headbar = $('
').appendTo($finder); /* 控制按钮 */ var $control = $('
').appendTo($headbar); var $mkdir = $('') .text(minder.getLang('ui.mkdir')) .attr('title', minder.getLang('ui.mkdir')) .addClass('button mkdir') .appendTo($control) .click(mkdir); var $recycle = $('') .text(minder.getLang('ui.recycle')) .attr('title', minder.getLang('ui.recycle')) .addClass('button recycle dir') .data('file', { path: recyclePath, filename: minder.getLang('ui.recycle') }) .appendTo($control) .click(recycle); var $recycleClear = $('') .text(minder.getLang('ui.recycle_clear')) .attr('title', minder.getLang('ui.recycle_clear')) .addClass('button recycle-clear') .appendTo($control) .click(clearRecycle); /* 路径导航 */ var $nav = $('').appendTo($headbar); /* 显示当前目录文件列表 */ var $list = $('') .appendTo($finder); var selected = null; minder.on('uiready', function() { var $user = minder.getUI('topbar/user'); $user.requireLogin($container); fio.user.on('login', function() { list(); }); }); handleClick(); handleDrag(); handleNav(); handleRename(); function handleRename() { $list.delegate('.file-list-item a.rename-button', 'click', function(e) { var $li = $(e.target).closest('li'); $li.find('span.filename').remove(); rename($li); $li.addClass('renaming'); e.stopPropagation(); }); function rename($li) { rename.onprogress = true; var file = $li.data('file'); var $input = $('') .attr('type', 'text') .addClass('new-dir-name fui-widget fui-selectable') .val(file.filename) .appendTo($li); $li.removeAttr('draggable'); $input.on('keydown', function (e) { if (e.keyCode == 13) return confirm(); if (e.keyCode == 27) { e.stopPropagation(); return cancel(); } }).on('blur', cancel); $input.on('dragstart mousedown mouseup click dblclick', function(e) { e.stopPropagation(); }); setTimeout(function() { $input[0].select(); }); function reset(filename) { $input.remove(); $li.find('.icon').after('' + filename + ''); $li.removeClass('renaming'); $li.attr('draggable', true); } function cancel() { reset(file.filename); } function confirm() { var newFilename = $input.val(); var newPath = file.parentPath + newFilename; if (file.filename == newFilename) return cancel(); if (fio.file.anlysisPath(newFilename).extension != file.extension) { $input.addClass('invalid-name'); setTimeout(function () { $input.removeClass('invalid-name'); }, 500); return $input.select(); } $container.addClass('loading'); mv(file.path, newPath).then(function () { var oldPath = file.path; file.filename = newFilename; file.path = newPath; reset(newFilename); Finder.fire('mv', oldPath, newPath, finder); notice.info(minder.getLang('ui.rename_success', newFilename)); })['catch'](function(e) { notice.error('err_rename', e); cancel(); }).then(function() { $container.removeClass('loading'); }); } } } function handleClick() { /* 点击目录中的项目时打开项目 */ $list.delegate('.file-list-item', 'dblclick', function(e) { if (currentPath == recyclePath + '/') return; if (mkdir.onprogress) return mkdir.onprogress.select(); var $file = $(e.target).closest('li'), file = $file.data('file'); if (file) open(file); }); $list.delegate('.file-list-item', 'mousedown', function(e) { if (mkdir.onprogress) return mkdir.onprogress.select(); var $file = $(e.target).closest('li'), file = $file.data('file'); if (!file) return; select(file && file.path); }); } function handleNav() { /* 点击导航处,切换路径 */ $nav.delegate('a', 'click', function(e) { if (mkdir.onprogress) return mkdir.onprogress.select(); if ($(e.target).hasClass('dir-back')) { var parts = currentPath.split('/'); parts.pop(); // 有一个无效部分 parts.pop(); return list(parts.join('/')); } list($(e.target).data('path')); }); } function handleDrag() { var fileItemSelector = '.file-list-item'; var dirSelector = '.dir'; var $dragging = null; $list.delegate(fileItemSelector, 'dragstart', itemDragStart) .delegate(fileItemSelector, 'dragend', itemDragEnd) .delegate(dirSelector, 'dragover', dragOver) .delegate(dirSelector, 'dragenter', dirDragEnter) .delegate(dirSelector, 'dragleave', dirDragLeave) .delegate(dirSelector, 'drop', dirDrop); $headbar.delegate(dirSelector, 'dragover', dragOver) .delegate(dirSelector, 'dragenter', dirDragEnter) .delegate(dirSelector, 'dragleave', dirDragLeave) .delegate(dirSelector, 'drop', dirDrop); $list.delegate(fileItemSelector + ' input', 'dragstart', function(e) { e.stopPropagation(); e.preventDefault(); }); function itemDragStart(e) { var $target = $(e.target); if (!$target.hasClass('file-list-item')) { return; } // e.originalEvent.dataTransfer.effectAllowed = "move"; // e.originalEvent.dataTransfer.dropEffect = 'move'; try { var dataType = kity.Browser.ie && kity.Browser.version == 10 ? 'text' : 'text/plain'; e.originalEvent.dataTransfer.setData(dataType, 'FEX'); e.originalEvent.dataTransfer.setDragImage($target.find('.icon').get(0), 12, 12); } catch (ignore) {} $dragging = $target.addClass('dragging'); $finder.addClass('drop-mode'); } function itemDragEnd(e) { $(e.target).removeClass('dragging'); e.originalEvent.dataTransfer.dropEffect = 'move'; e.preventDefault(); $finder.removeClass('drop-mode'); } function dragOver(e) { if ($(e.target).hasClass('filename')) e.preventDefault(); } function dirDragEnter(e) { var $target = $(e.target).closest('.dir'); $target.addClass('drag-enter'); if (e.target != $target[0]) $target.addClass('enter-child'); } function dirDragLeave(e) { if ($(e.target).hasClass('dir')) { if ($(e.target).hasClass('enter-child')) { return $(e.target).removeClass('enter-child'); } $(e.target).removeClass('drag-enter'); } } function dirDrop(e) { e.preventDefault(); var $target = $(e.target).closest('.dir').removeClass('drag-enter'); if (!$target.hasClass('dir')) return; var source = $dragging.data('file'); var destination = $target.data('file'); var destinationPath = destination.path + '/' + source.filename; var sourcePath = source.path; if (destinationPath.indexOf(sourcePath) === 0) return; if (!moveConfirm || window.confirm(minder.getLang('ui.move_file_confirm', source.filename, destination.filename))) { $container.addClass('loading'); recycleReady.then(doMove); moveConfirm = false; } function doMove() { mv(sourcePath, destinationPath).then(function() { $dragging.remove(); Finder.fire('mv', sourcePath, destinationPath, finder); notice.info(minder.getLang('ui.move_success', source.filename, destination.filename)); })['catch'](function(e) { notice.error('err_move_file', e); }).then(function() { $container.removeClass('loading'); }); } } } function recycle() { list(recyclePath); } function createRecycleBin() { return fio.file.mkdir({ path: recyclePath }); } function clearRecycle() { if (!window.confirm(minder.getLang('ui.recycle_clear_confirm'))) return; $container.addClass('loading'); fio.file['delete']({ path: recyclePath }).then(function() { return recycleReady = createRecycleBin(); }).then(function() { renderList([]); $container.removeClass('loading'); }); } function mv(source, destination) { return fio.file.move({ path: source, newPath: destination, ondup: destination.indexOf(recyclePath) === 0 ? fio.file.DUP_RENAME : fio.file.DUP_FAIL }); } function mkdir() { if (mkdir.onprogress) { return mkdir.onprogress.select(); } var $li = $('
  • ').addClass('file-list-item dir').prependTo($list); $li.append(''); var $input = $('') .attr('type', 'text') .addClass('new-dir-name fui-widget fui-selectable') .val(minder.getLang('ui.newdir')) .appendTo($li); mkdir.onprogress = $input[0]; $input[0].select(); $input.on('keydown', function(e) { if (e.keyCode == 13) confirm(); if (e.keyCode == 27) { cancel(); e.stopPropagation(); } }).on('blur', confirm); function cancel() { $li.remove(); mkdir.onprogress = false; } function confirm() { var name = $input.val(); if (name) { $container.addClass('loading'); fio.file.mkdir({ path: currentPath + name }).then(function() { return new Promise(function(resolve) { setTimeout(function() { resolve(refresh()); }, 200); }); }, function(e) { if (e.detail && e.detail.error_code == 31061) { e.message = '已存在同名目录'; } var notice = minder.getUI('widget/notice'); notice.error('err_mkdir', e); $li.remove(); }).then(function() { $container.removeClass('loading'); mkdir.onprogress = false; }); } } } /** * 返回数值的符号: * 正数 => 1 * 0 => 0 * 负数 => -1 */ function sign(num) { return num > 0 ? 1 : (num < 0 ? -1 : 0); } /** * 打开选中的文件或目录 * * @param {fio.file.File} file */ function open(file) { if (file.isDir) return list(file.path); finder.fire('fileclick', file); } function fadeOutList(x) { return new Promise(function(resolve, reject) { $list.transit({ x: x, opacity: 0 }, 100, resolve); }); } function fadeInList() { return new Promise(function(resolve) { $list.css({ x: -parseInt($list.css('x')) }).stop().transit({ x: 0, opacity: 1 }, 100, resolve); }); } function refresh() { return list(currentPath, true); } /** * 列出指定目录的文件 */ function list(path, noAnimate) { path = path || base; var listPromise = fio.file.list({ path: path }); var transitPromise = noAnimate ? Promise.resolve() : fadeOutList(-100 * sign(path.length - currentPath.length)); currentPath = path.charAt(path.length - 1) == '/' ? path : path + '/'; updateNav(); function checkRecycleBin(files) { if (!recycleReady && path == base) { for (var i = 0; i < files.length; i++) { if (files[i].path == recyclePath) { recycleReady = Promise.resolve(true); } break; } recycleReady = recycleReady || createRecycleBin(); } } return Promise.all([listPromise, transitPromise]).then(function(values) { var files = values[0]; checkRecycleBin(files); return renderList(files); }, function(error) { var notice = minder.getUI('widget/notice'); notice.error('err_ls', error); }); } function renderFileList(files) { $list.empty(); if (!files.length) { $list.append('
  • ' + minder.getLang('ui.emptydir') + '
  • '); } else { files.forEach(function(file) { if (!file.isDir && (!listFilter || !listFilter(file))) return; if (file.path == recyclePath) return; $('
  • ') .append('') .append('' + file.filename + '') .append('"' + minder.getLang('ui.rename') + '"') .addClass('file-list-item') .addClass(file.isDir ? 'dir' : 'file') .data('file', file) .attr('draggable', true) .appendTo($list); }); } } finder._renderFileList = renderFileList; function renderList(files) { files.sort(function(a, b) { if (a.isDir > b.isDir) { return -1; } else if (a.isDir == b.isDir) { return a.createTime > b.createTime ? -1 : 1; } else return 1; }); renderFileList(files); // 通知其他 finder 更新 instances.forEach(function(instance) { if (instance == finder) return; if (instance.pwd() == currentPath) instance._renderFileList(files); }); fadeInList(); checkSelect(); finder.fire('cd', currentPath); } function updateNav() { $nav.empty(); if (currentPath != base && currentPath != base + '/') { $nav.append('Back'); } else { $nav.append(''); } var path = currentPath.substr(base.length); var parts = path.split('/'); var processPath = ''; function pathButton(part) { processPath += part + '/'; var $a = $('').addClass('dir'); if (part == base) { $a.text(minder.getLang('ui.mydocument')); } else if (part == '.recycle') { $a.text(minder.getLang('ui.recycle')); $finder.addClass('recycle-bin'); } else { $a.text(part); } return $a.data('path', processPath).data('file', { path: processPath.substr(0, processPath.length - 1), filename: part == base ? minder.getLang('ui.mydocument') : part }); } $finder.removeClass('recycle-bin'); $nav.append(pathButton(base)); parts.forEach(function(part) { if (!part) return; $nav.append(''); $nav.append(pathButton(part)); }); } function select(path) { selected = path; return checkSelect(); } function checkSelect() { var hasSelect = false; $list.find('.file-list-item').removeClass('selected').each(function() { var file = $(this).data('file'); if (file && file.path == selected) { $(this).addClass('selected'); hasSelect = true; $list[0].focus(); finder.fire('select', file, this); } }); if (!hasSelect) selected = false; return hasSelect; } function pwd() { return currentPath; } finder.list = list; finder.select = select; finder.pwd = pwd; finder.refresh = refresh; return finder; } Finder.generate = generate; return Finder; });