Skip to content

Commit

Permalink
Merge pull request #1003 from juhasch/fix/chrome-clipboard2
Browse files Browse the repository at this point in the history
Update chrome-clipboard to 5.x
  • Loading branch information
juhasch authored Jun 15, 2017
2 parents a9f8845 + 5e53823 commit 85770eb
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 100 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
Type: IPython Notebook Extension
Name: Chrome Clipboard
Description: This extension allows using the system-wide clipboard to copy cells and images
Description: This extension allows using the system-wide clipboard to paste images into a notebook
Link: readme.md
Icon: chrome.png
Main: main.js
Compatibility: 4.x
Compatibility: 4.x, 5.x
Parameters:
- name: dragdrop.subdirectory
description: Subdirectory to put images to
input_type: string
default:
153 changes: 63 additions & 90 deletions src/jupyter_contrib_nbextensions/nbextensions/chrome-clipboard/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,56 +5,83 @@ define([
'base/js/namespace',
'jquery',
'base/js/utils',
'services/config',
'base/js/events'
], function(IPython, $, utils, events) {
], function(IPython, $, utils, configmod, events) {
"use strict";
if (window.chrome === undefined) return;

var params = {
subdirectory : '',
};

var base_url = utils.get_body_data("baseUrl");
var config = new configmod.ConfigSection('notebook', {base_url: base_url});

/* http://stackoverflow.com/questions/3231459/create-unique-id-with-javascript */
function uniqueid() {
function uniqueid(){
// always start with a letter (for DOM friendlyness)
var idstr = String.fromCharCode(Math.floor((Math.random() * 25) + 65));
var idstr=String.fromCharCode(Math.floor((Math.random()*25)+65));
do {
// between numbers and characters (48 is 0 and 90 is Z (42-48 = 90)
var ascicode = Math.floor((Math.random() * 42) + 48);
if (ascicode < 58 || ascicode > 64) {
var ascicode=Math.floor((Math.random()*42)+48);
if (ascicode<58 || ascicode>64){
// exclude all chars between : (58) and @ (64)
idstr += String.fromCharCode(ascicode);
idstr+=String.fromCharCode(ascicode);
}
} while (idstr.length < 32);
} while (idstr.length<32);

return (idstr);
}

var send_to_server = function (name, path, msg) {
var create_dir = function(path) {
var options = {type:'directory'};

var data = JSON.stringify({
ext: options.ext,
type: options.type
});

var settings = {
processData : false,
type : "PUT",
data: data,
contentType: 'application/json',
dataType : "json"
};
utils.promising_ajax(IPython.contents.api_url(path), settings);
};

var send_to_server = function(name, msg) {
var path = utils.url_path_join(utils.url_path_split(IPython.notebook.notebook_path)[0], params.subdirectory);
if (name == '') {
name = uniqueid() + '.' + msg.match(/data:image\/(\S+);/)[1];
}
var path = path.substring(0, path.lastIndexOf('/')) + '/';
if (path === '/') path = '';
var url = '//' + location.host + '/api/contents/' + path + name;
}
create_dir(path);
var url = '//' + location.host + utils.url_path_join(base_url, 'api/contents', path, name);

var img = msg.replace(/(^\S+,)/, ''); // strip header
var data = {'name': name, 'format': 'base64', 'content': img, 'type': 'file'};
//console.log("send_to_server:", url, img);
var data = {'name': name, 'format':'base64', 'content': img, 'type': 'file'};
var settings = {
processData: false,
cache: false,
type: "PUT",
dataType: "json",
data: JSON.stringify(data),
headers: {'Content-Type': 'text/plain'},
async: false,
error : function() {console.log('Data transfer for copy-paste has failed.');
}
processData : false,
cache : false,
type : "PUT",
dataType : "json",
data : JSON.stringify(data),
headers : {'Content-Type': 'text/plain'},
async : false,
error : function() {console.log('Data transfer for clipboard paste failed.'); }
};
utils.promising_ajax(url, settings).then(
utils.promising_ajax(url, settings).then(
function on_success (data, status, xhr) {
var new_cell = IPython.notebook.insert_cell_below('markdown');
var str = '<img src="' + name + '"/>';
var str = '![](' + utils.url_path_join(params.subdirectory, name) + ')';
new_cell.set_text(str);
new_cell.execute();
},
function on_error (reason) {
console.log('Data transfer for copy-paste has failed.');
console.log('Data transfer for clipboard paste has failed.');
});
};

Expand All @@ -63,6 +90,15 @@ define([
*/
var load_ipython_extension = function() {

config.load();
config.loaded
.then(function () {
$.extend(true, params, config.data.dragdrop); // update params
if (params.subdirectory) {
console.log('subdir:', params.subdirectory)
}
})

/*
* override clipboard 'paste' and insert new cell from json data in clipboard
*/
Expand All @@ -71,84 +107,21 @@ define([
if (cell.mode == "command") {
var items = event.clipboardData.items;
for (var i = 0; i < items.length; i++) {
if (items[i].type == 'notebook-cell/json') {
event.preventDefault();
/* json data adds a new notebook cell */
var data = event.clipboardData.getData('notebook-cell/json').split("\n").filter(Boolean);
for (var i = 0; i < data.length; i++) {
var ix = data.length - 1 - i;
var new_cell_data = JSON.parse(data[ix]);
var new_cell = IPython.notebook.insert_cell_below(new_cell_data.cell_type);
new_cell.fromJSON(new_cell_data);
if (new_cell.cell_type === "markdown") {
new_cell.execute();
};
}
} else if (items[i].type.indexOf('image/') !== -1) {
if (items[i].type.indexOf('image/') !== -1) {
event.preventDefault();
/* images are transferred to the server as file and linked to */
var blob = items[i].getAsFile();
var reader = new FileReader();
reader.onload = ( function (evt) {
var filename = '';
send_to_server(filename, IPython.notebook.notebook_path, evt.target.result);
send_to_server(filename, evt.target.result);
event.preventDefault();
} );
reader.readAsDataURL(blob);
}
}
}
});

/*
* override clipboard 'copy' and copy current cell as json and text to clipboard
*/
window.addEventListener('copy', function (event) {
var cell = IPython.notebook.get_selected_cell();
if (cell.mode == "command") {
var sel = window.getSelection();
if (sel.type == "Range") return;
/* default: copy marked text */
event.preventDefault();
var json = "";
var text = "";
var selected_cells = IPython.notebook.get_selected_cells();
for (var i in selected_cells) {
var j = selected_cells[i].toJSON();
json += JSON.stringify(j) + '\n';
text += selected_cells[i].code_mirror.getValue() + '\n';
}
/* copy cell as json and cell contents as text */
event.clipboardData.setData('notebook-cell/json', json);
event.clipboardData.setData("Text", text);
}
});

/*
* override clipboard 'cut' and copy current cell as json and text to clipboard
*/
window.addEventListener('cut', function (event) {
var cell = IPython.notebook.get_selected_cell();
if (cell.mode == "command") {
var sel = window.getSelection();
if (sel.type == "Range") return;
/* default: copy marked text */
event.preventDefault();
var json = "";
var text = "";
var selected_cells = IPython.notebook.get_selected_cells();
for (var i in selected_cells) {
var j = selected_cells[i].toJSON();
json += JSON.stringify(j) + '\n';
text += selected_cells[i].code_mirror.getValue() + '\n';
IPython.notebook.delete_cell(IPython.notebook.find_cell_index(selected_cells[i]));
}
/* copy cell as json and cell contents as text */
event.clipboardData.setData('notebook-cell/json', json);
event.clipboardData.setData("Text", text);
}
});

};

var extension = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ Chrome Clipboard

**Note**: Improved copy&paste functionality is now integrated in the main Jupyter notebook

This notebook extension adds system clipboard actions for single or multiple cells.
It allows cut/copy/paste operation of notebook cells and images. Images will be saved to the directory where the
current notebook sits. There is currently no way to embed images in markdown cells, due to the google-caja sanitizer
used to prevent malicous code execution. Multi-cell operation is possible with the latest Jupyter version, or using the `rubberband` extension in this repository.
This notebook extension adds system clipboard actions pasting images.

A demo showing single-cell copy & paste operating in Chrome is available on youtube:
[youtu.be/iU9dNe4vMUY](http://youtu.be/iU9dNe4vMUY)
Expand All @@ -18,12 +15,14 @@ A demo showing single-cell copy & paste operating in Chrome is available on yout

Hotkeys:

* `CTRL+C` - Copy cell to system clipboard
* `CTRL+X` - Cut cell and copy to system clipboard
* `CTRL+V` - Paste cell or image from system clipboard
* `CTRL+V` - Paste image from system clipboard

You can specify a target subdirectory using the `dragdrop.subdirectory`
parameter in the notebook configurator. This is the same subdirectory
as used in the `dragdrop` extension.

Internals
---------

Regarding copying notebook cells over the clipboard, they are stored as mime-type `notebook-cell/json`.
The image pasted from clipboard will be uploaded to the notebook
directory. A unique ID will be generated as image filename.

0 comments on commit 85770eb

Please sign in to comment.