define(function(require, exports, module) { "use strict"; main.consumes = [ "Plugin", "util", "ui", "layout", "menus", "fs", "tree", "fs.cache", "upload.manager", "apf", "dialog.fileoverwrite", "dialog.alert", "tabManager" ]; main.provides = ["upload"]; return main; function main(options, imports, register) { var util = imports.util; var Plugin = imports.Plugin; var ui = imports.ui; var menus = imports.menus; var fsCache = imports["fs.cache"]; var tree = imports.tree; var uploadManager = imports["upload.manager"]; var question = imports["dialog.fileoverwrite"].show; var alert = imports["dialog.alert"].show; var apf = imports.apf; var tabManager = imports.tabManager; var path = require("path"); var css = require("text!./upload.css"); var winUploadFilesMarkup = require("text!./markup/win_upload_files.xml"); /***** Initialization *****/ var MAX_OPEN_COUNT = options.maxOpenCount || 10; var MAX_FILE_COUNT = options.maxFileCount || 100000; var MAX_UPLOAD_SIZE = options.maxUploadSize || Infinity; var plugin = new Plugin("Ajax.org", main.consumes); var emit = plugin.getEmitter(); var winUploadFiles; var loaded = false; function load() { if (loaded) return false; loaded = true; // Menus menus.addItemByPath("File/~", new ui.divider(), 1200, plugin); menus.addItemByPath("File/Upload Local Files...", new ui.item({ onclick: showUploadWindow }), 1300, plugin); uploadManager.on("addJob", onAddUploadJob); } /***** Methods *****/ function onAddUploadJob(e) { var job = e.job; var dir = path.dirname(job.fullPath); var cleanup = function() {}; function onExpand(e) { // console.log("expand", e.path, dir); if (e.path == dir) { cleanup(); updateNode(fsCache.findNode(dir)); // console.log("match!!"); } } function updateNode(parent) { var node = fsCache.findNode(job.fullPath); if (!node) { node = fsCache.createNode(job.fullPath, { mime: job.file.type, size: job.file.size || 0 }); } node.status = "predicted"; // console.log("new node", node); } job.on("changeState", function(state) { switch (state.state) { case "uploading": // add to tree fsCache.on("readdir", onExpand); cleanup = function() { fsCache.off("readdir", onExpand); }; var parent = fsCache.findNode(dir); if (parent) { updateNode(parent); } // console.log("up", job) break; case "done": // remove uploading state var node = fsCache.findNode(job.fullPath); if (node) node.status = "loaded"; cleanup(); // console.log("done", job) break; case "error": // remove node from tree var node = fsCache.findNode(job.fullPath); if (node) fsCache.removeNode(node); cleanup(); break; } }); } function uploadFromInput(inputEl) { uploadManager.batchFromInput(inputEl, function(err, batch) { if (err) return onUploadError(err); uploadBatch(batch); }); } function getEntries(e) { // items is undefined on firefox 30 if (!e.dataTransfer.items) return []; var first = e.dataTransfer.items[0]; var getAsEntry = first.getAsEntry || first.webkitGetAsEntry; return [].map.call(e.dataTransfer.items, function(item) { return getAsEntry.call(item); }); } function uploadFromDrop(dropEvent, targetPath, type) { if (emit("upload.drop", { files: dropEvent.dataTransfer.files, entries: getEntries(dropEvent), path: targetPath, type: type }) === false) return; uploadManager.batchFromDrop(dropEvent, function(err, batch, skipped) { if (err) return onUploadError(err); if (skipped && Object.keys(skipped).length) { alert( "File upload", "Not all files can be uploaded:", Object.keys(skipped).map(util.escapeXml).join("
") ); } uploadBatch(batch, targetPath.path || targetPath); }); } function onUploadError(err) { // TODO console.error(err); } function uploadBatch(batch, targetPath) { // 1. has directories if (batch.hasDirectories()) { alert( "Folder Upload", "Folder uploads are currently only supported by Google Chrome.", "If you want to upload folders you need to run a current version of Google Chrome." ); return; } // 2. filter DS_Store batch.ignoreFiles({ ".DS_Store": 1, "Thumbs.db": 1 }); var sizes = batch.getSizes(); // 3. check file count if (sizes.count > MAX_FILE_COUNT) { alert( "Too many files", "File uploads are limited to " + MAX_FILE_COUNT + " files per upload.", "Please upload files in smaller batches" ); return; } // 4. check total size quota if (sizes.sum > MAX_UPLOAD_SIZE) { alert( "Maximum upload-size exceeded", "File uploads are limited to " + Math.floor(MAX_UPLOAD_SIZE / 1000 / 1000) + "MB in total.", "" ); return; } // 6. start upload if still files in batch if (!batch.files.length) return; var targetFolder; if (targetPath && typeof targetPath === "object") { if (!window.FileReader) return alert( "Unable to open files", "Drop files on the tree to upload", "" ); if (batch.files.length > MAX_OPEN_COUNT) return alert( "Maximum open count exceeded (" + batch.files.length + ")", "Drop files on the tree to upload", "" ); var hasImage = false; batch.files.forEach(function(file, i) { if (/image/i.test(file.type)) return hasImage = true; var reader = new FileReader(); reader.onload = function() { tabManager.open({ path: "/" + file.name, value: reader.result, document: { meta: { newfile: true }}, active: i === 0, pane: targetPath }, function(err, tab) {}); }; reader.readAsText(file); }); if (hasImage) alert( "Can't open an image", "Drop files on the tree to upload", "" ); return; } else if (targetPath) { targetFolder = fsCache.findNode(targetPath); } else { targetFolder = getTargetFolder(); targetPath = targetFolder.path; } tree.expand(targetFolder, function() { uploadManager.upload(targetPath, batch, fileExistsDialog, function(err) { // TODO handle error }); }); var initialSelection = JSON.stringify(tree.selection); uploadManager.on("batchDone", function onBatchDone(e) { var b = e.batch; if (b != batch) return; uploadManager.off("batchDone", onBatchDone); if (initialSelection == JSON.stringify(tree.selection)) { tree.selectList(batch.getRoots().map(function(p) { return path.join(targetPath, p); })); } }); } function showUploadWindow() { function handleFileSelect(e) { uploadFromInput(e.target); e.target.value = ""; } if (!winUploadFiles) { ui.insertCss(css, options.staticPrefix, plugin); ui.insertMarkup(null, winUploadFilesMarkup, plugin); winUploadFiles = plugin.getElement("winUploadFiles"); winUploadFiles.on("show", function(e) { onShow(); }); winUploadFiles.on("close", function(e) { onClose(); }); winUploadFiles.selectNodes(".//a:button").pop().on("click", function() { winUploadFiles.hide(); }); var fileUploadSelect = plugin.getElement("fileUploadSelect"); var folderUploadSelect = plugin.getElement("folderUploadSelect"); var hboxUploadNoFolders = plugin.getElement("hboxUploadNoFolders"); var hboxUploadWithFolders = plugin.getElement("hboxUploadWithFolders"); var filebrowser = fileUploadSelect.$ext; filebrowser.addEventListener("change", handleFileSelect, false); // enable folder upload if (uploadManager.SUPPORT_FOLDER_UPLOAD) { hboxUploadNoFolders.hide(); hboxUploadWithFolders.show(); apf.setStyleClass(filebrowser, "uploadWithFolders"); var folderbrowser = folderUploadSelect.$ext; folderbrowser.style.display = "block"; folderbrowser.addEventListener("change", handleFileSelect, false); } emit("drawUploadWindow", plugin.getElement("uploadDropArea")); } winUploadFiles.show(); } function hideUploadWindow() { winUploadFiles && winUploadFiles.hide(); } function fileExistsDialog(batch, path, root, callback) { question( "File already exists", "File already exists", '"' + root + '" already exists, do you want to replace it? ' + "Replacing it will overwrite its current contents.", function(all) { // Overwrite callback("replace", all); }, function(all) { // Skip if (all) callback("stop", true); else callback("no-replace", false); }, { all: batch.files.length > 1 } ); } function onShow () { if (!(window.File && window.FileReader && window.FileList && window.Blob)) { alert("The File APIs are not fully supported in this browser."); return hideUploadWindow(); } updateTargetFolder(); tree.on("select", updateTargetFolder); } function onClose () { tree.off("select", updateTargetFolder); } function getTargetFolder() { return tree.getSelectedFolder(); } function updateTargetFolder() { plugin.getElement("uplTargetFolder").$ext.textContent = getTargetFolder().path; } /***** Lifecycle *****/ plugin.on("load", function() { load(); }); plugin.on("enable", function() { }); plugin.on("disable", function() { }); plugin.on("unload", function() { loaded = false; }); /***** Register and define API *****/ /** * Implements the file upload UI for Cloud9 * @singleton **/ plugin.freezePublicAPI({ /** * Display the file upload window */ showUploadWindow: showUploadWindow, /** * Display the file exists dialog. * Only used for unit testing * @ignore */ fileExistsDialog: fileExistsDialog, /** * Upload files from a native drag and drop operation * * @param {DragEvent} dropEvent native DOM drop event * @param {String} targetPath path where to upload the files */ uploadFromDrop: uploadFromDrop, /** * Upload files from an file upload input element * * @param {HTMLInputElement} inputElement the upload input DOM * element */ uploadFromInput: uploadFromInput, /** * */ getTargetFolder: getTargetFolder }); register(null, { upload: plugin }); } });