c9-core/plugins/c9.ide.clipboard/clipboard.js

492 wiersze
18 KiB
JavaScript

define(function(require, exports, module) {
main.consumes = [
"Plugin", "c9", "menus", "commands", "clipboard.provider", "ui",
"dialog.alert", "settings"
];
main.provides = ["clipboard"];
return main;
function main(options, imports, register) {
var c9 = imports.c9;
var ui = imports.ui;
var Plugin = imports.Plugin;
var menus = imports.menus;
var settings = imports.settings;
var commands = imports.commands;
var provider = imports["clipboard.provider"];
var alert = imports["dialog.alert"];
/***** Initialization *****/
var plugin = new Plugin("Ajax.org", main.consumes);
var emit = plugin.getEmitter();
var input = { "input":1, "textarea":1, "iframe":1 };
var loaded = false;
function load(){
if (loaded) return false;
loaded = true;
// return !(event instanceof KeyboardEvent) && !c9.readonly;
function checkAvailable(editor, args, e) {
var handler = findClipboardHandler();
var fromKeyboard = e instanceof KeyboardEvent;
if (handler && handler.isClipboardAvailable) {
return handler.isClipboardAvailable({
type: this.name,
fromKeyboard: fromKeyboard,
sourceEvent: e,
editor: editor,
args: args
});
}
else if (options.local) {
var name = document.activeElement.tagName.toLowerCase();
if (input[name] || document.activeElement.contentEditable)
return true;
}
return !!handler;
}
commands.addCommand({
name: "cut",
bindKey: { mac: "Command-X", win: "Ctrl-X" },
isAvailable: checkAvailable,
exec: function(){ cut(); },
passEvent: true,
readOnly: true
}, plugin);
commands.addCommand({
name: "copy",
bindKey: { mac: "Command-C", win: "Ctrl-C" },
isAvailable: checkAvailable,
exec: function(editor, args){ copy(null, args.data); },
passEvent: true
}, plugin);
commands.addCommand({
name: "paste",
bindKey: { mac: "Command-V", win: "Ctrl-V" },
isAvailable: checkAvailable,
exec: function(){ paste(); },
passEvent: true,
readOnly: true
}, plugin);
commands.addCommand({
name: "clearcut",
bindKey: { mac: "ESC", win: "ESC" },
isAvailable: checkAvailable,
exec: clearcut,
passEvent: true
}, plugin);
menus.addItemByPath("Edit/~", new ui.divider(), 300, plugin);
menus.addItemByPath("Edit/Cut",
new ui.item({ command : "cut" }), 400, plugin);
menus.addItemByPath("Edit/Copy",
new ui.item({ command : "copy" }), 500, plugin);
menus.addItemByPath("Edit/Paste",
new ui.item({ command : "paste" }), 600, plugin);
}
var clipboardData = {
items: [],
get types(){
return clipboardData.items.map(function(item) {
return item.type;
});
},
wrap: function(nativeObject){ provider.wrap(nativeObject); },
unwrap: function(){ provider.unwrap(); },
setData: function(type, data) {
if (provider.set(type, data) === false)
showAlert(data);
var found;
clipboardData.items.every(function(item) {
if (item.type == type) {
found = item;
return false;
}
return true;
});
if (found) found.value = data;
else clipboardData.items.push({ type: type, value: data });
},
getData: function(type, callback) {
var data = provider.get(type);
// If `data` is false the provider was not able to fetch data from the clipboard.
// This is usually because the browser does not allow this for security reasons.
// The browser (chrome) does allow this when the cloud9 plugin is installed.
if (data === false)
showAlert(null, true);
// If `data` is undefined the provider did not support the mime type. This usually
// happens when the mime type is custom, such as when copying tree nodes or other
// internal to Cloud9 data.
// In either case we try to find the data in the local (in memory) clipboard.
if (data === false || data === undefined) {
var found;
clipboardData.items.every(function(item) {
if (item.type == type) {
found = item;
return false;
}
return true;
});
return found && found.value || false;
}
return data;
},
clearData: function(type) {
provider.clear();
clipboardData.items = [];
clipboardData.types = [];
}
};
function findClipboardHandler() {
var el = apf.activeElement || apf.menu.lastFocussed;
if (!el)
return;
if (el.localName == "menu")
el = apf.menu.lastFocussed || el;
while (el) {
var handler = el.clipboardHandler || el.editor;
if (handler)
return handler;
el = el.parentNode;
}
}
/***** Methods *****/
function cut(native) {
var handler = findClipboardHandler();
var event = {
clipboardData: clipboardData,
native: native,
handler: handler
};
if (emit("beforeCut", event) === false)
return;
if (!native) clipboardData.unwrap();
emit("cut", event);
if (handler && handler.cut)
handler.cut(event);
}
function copy(native, data) {
var handler = findClipboardHandler();
var event = {
clipboardData: clipboardData,
native: native,
handler: handler
};
if (emit("beforeCopy", event) === false)
return;
if (!native) clipboardData.unwrap();
if (data)
clipboardData.setData("text/plain", data);
else {
emit("copy", event);
if (handler && handler.copy)
handler.copy(event);
}
}
function paste(native) {
var handler = findClipboardHandler();
var event = {
clipboardData: clipboardData,
native: native,
handler: handler
};
if (emit("beforePaste", event) === false)
return;
if (!native) clipboardData.unwrap();
emit("paste", event);
if (handler && handler.paste)
handler.paste(event);
}
function clearcut() {
var handler = findClipboardHandler();
if (handler && handler.clearcut)
return handler.clearcut();
return false;
}
function registerHandler(amlNode, clipboardHandler) {
amlNode.clipboardHandler = clipboardHandler;
}
function showAlert(data, isPaste) {
if (settings.getBool("user/clipboard/@dontshow"))
return;
var emitter = alert.show("Native Clipboard Unavailable",
"Important information regarding the Native Clipboard",
"By default the browser doesn't allow you to use the native "
+ "clipboard via menus. This means that whatever you copy via a "
+ "menu needs to be pasted by the menu as well and only works "
+ "in Cloud9. <br /><br />"
+ (isPaste ? "" : "You can copy the string from the "
+ "textarea below. <textarea style='width:100%;' rows='4'>"
+ data
+ "</textarea><br /><br />")
+ "To enable the native keyboard either use "
+ "Command-C on Mac or Ctrl-C on Windows or for Chrome install "
+ "the Cloud9 App at <a target='_blank' href='http://bit.ly/K5XNzK'>the Chrome store</a>.",
function(){
if (alert.dontShow)
settings.set("user/clipboard/@dontshow", true);
}, { isHTML: true, showDontShow: true });
emitter.on("show", function(){
var textarea = alert.aml.$ext.querySelector("textarea");
if (!textarea) return;
textarea.select();
textarea.focus();
function hide() {
setTimeout(function() {
alert.hide();
});
}
textarea.addEventListener("copy", hide);
textarea.addEventListener("cut", hide);
});
}
/***** Lifecycle *****/
plugin.on("load", function(){
load();
});
plugin.on("enable", function(){
});
plugin.on("disable", function(){
});
plugin.on("unload", function(){
loaded = false;
});
/***** Register and define API *****/
/**
* Object that represents the clipboard state and provides methods to
* access the clipboard.
* @class clipboard.ClipboardData
*/
/**
* @property {String[]} types The content types that currently hold
* data in the clipboard.
* @readonly
*/
/**
* Adds an item to the clipboard, replacing any item that was there
* with the same `type`.
*
* This example shows how to store a selection of tree nodes on the
* clipboard:
*
* var nodes = tree.getSelection();
* clipboardData.setData("tree-nodes", nodes);
*
* @method setData
* @param {String} type The content type for this data. To be
* compatible with the native clipboard for all platforms use "text".
* @param {Mixed} data The actual data. This can be a string
* or a more complex object. Complex objects cannot be stored using
* the native clipboard.
*/
/**
* Retrieves the data of an item on the clipboard.
*
* This example shows how to retrieve a set of tree nodes from the
* clipboard:
*
* var nodes = clipboardData.getData("tree-nodes");
*
* @method getData
* @param {String} type The content type for this data. To be
* compatible with the native clipboard for all platforms use "text".
*/
/**
* Clears the data from the clipboard for a certain `type`.
* @method clearData
* @param {String} type The content type for this data. To be
* compatible with the native clipboard for all platforms use "text".
*/
/**
* Providers Clipboard access for Cloud9 plugins. The Cloud9 Clipboard
* abstracts away the implementation details of the different
* environments (e.g. Browser, Node-Webkit). The Clipboard API is
* loosely based on the [HTML5 Clipboard specification](http://dev.w3.org/2006/webapi/clipops/).
*
* *N.B.: Usually these events are not listened to directly. Instead
* each editor has an {@link Editor#event-cut}, {@link Editor#event-copy} and
* {@link Editor#event-paste} event to implement. However if you are
* implementing a pane that is not an editor, use the events of this
* plugin.*
*
* Example (copy):
*
* clipboard.on("copy", function(e) {
* if (e.native) return;
*
* var data = "Example Data";
* e.clipboardData.setData("text/plain", data);
* });
*
* Example (paste):
*
* clipboard.on("paste", function(e) {
* if (e.native) return;
*
* var data = e.clipboardData.getData("text/plain");
*
* });
*
* @singleton
*/
plugin.freezePublicAPI({
_events: [
/**
* Fires when a cut, copy or paste command is executed. In order
* for the clipboard to work for your plugin, you will need to
* hook this event and return true when and only if your plugin
* has the focus and is capable of handling the specific action.
*
* @event available
* @param {Object} e
* @param {String} e.type The name of the command that is
* executed. This can be "cut", "copy" or "paste".
*/
"available",
/**
* Fires prior to the "cut" operation.
* @event beforeCut
* @cancellable
* @param {Object} e
* @param {clipboard.ClipboardData} e.clipboardData Gives
* access to the clipboard. See {@link #clipboardData}
*/
"beforeCut",
/**
* Fires when the cut operation is executed.
* @event cut
* @param {Object} e
* @param {clipboard.ClipboardData} e.clipboardData Gives
* access to the clipboard. See {@link #clipboardData}
*/
"cut",
/**
* Fires prior to the "paste" operation.
* @event beforePaste
* @cancellable
* @param {Object} e
* @param {clipboard.ClipboardData} e.clipboardData Gives
* access to the clipboard. See {@link #clipboardData}
*/
"beforePaste",
/**
* Fires when the paste operation is executed.
* @event paste
* @param {Object} e
* @param {clipboard.ClipboardData} e.clipboardData Gives
* access to the clipboard. See {@link #clipboardData}
*/
"paste",
/**
* Fires prior to the "copy" operation.
* @event beforeCopy
* @cancellable
* @param {Object} e
* @param {clipboard.ClipboardData} e.clipboardData Gives
* access to the clipboard. See {@link #clipboardData}
*/
"beforeCopy",
/**
* Fires when the copy operation is executed.
* @event copy
* @param {Object} e
* @param {clipboard.ClipboardData} e.clipboardData Gives
* access to the clipboard. See {@link #clipboardData}
*/
"copy",
/**
* Fires when the user has requested to clear the cut buffer.
* @event clearcut
* @param {Object} e
* @param {clipboard.ClipboardData} e.clipboardData Gives
* access to the clipboard. See {@link #clipboardData}
*/
"clearcut"
],
/**
* Object that represents the clipboard state
* @param {AmlNode} target
* @param {Object} handler {cut, copy, paste, clearcut, isClipboardAvailable}
*/
registerHandler: registerHandler,
/**
* Object that represents the clipboard state
* @property {clipboard.ClipboardData}
*/
clipboardData: clipboardData,
/**
* Triggers a cut by the currently focused component.
* @fires beforeCut
* @fires cut
*/
cut: cut,
/**
* Triggers a copy by the currently focused component.
* @fires beforeCopy
* @fires copy
*/
copy: copy,
/**
* Triggers a paste by the currently focused component.
* @fires beforePaste
* @fires paste
*/
paste: paste
});
register(null, {
clipboard: plugin
});
}
});