Permalink
Browse files

Allow copy and pasting of Perseus widgets using localStorage

Summary:
Before, copying widgets between sections would create entirely new widgets on a
paste. For example, if I had an image widget with a image URL and caption when
I copied the `[[☃ image 1]]` and pasted it the image URL and caption would be
erased.

Now widgets can be directly cut/copied/pasted between sections and even
in between different Perseus exercises and articles on different pages!

This is done by:

- Listening for the cut/copy/paste events in jQuery in `componentDidMount`.
- If widgets are being cut/copied, grab the JSON of those widgets and throw
  them in `localStorage.perseusLastCopiedWidgets`
- On a paste, grab the widget data from `localStorage` and use it to update the
  state; if there's a widget name collision, don't override widgets already in
  the section we're pasting into.

Test Plan:
Visit [this Perseus link](http://localhost:9000/article.html#content=%5B%7B%22content%22%3A%22%5B%5B%E2%98%83%20image%201%5D%5D%5Cn%5CnTesting123%5Cn%5Cn%5B%5B%E2%98%83%20number-line%201%5D%5D%5Cn%22%2C%22images%22%3A%7B%22https%3A%2F%2Fka-perseus-graphie.s3.amazonaws.com%2Fda8df81c78b22f5c69d477d8eabfb583968eaf84.png%22%3A%7B%22width%22%3A400%2C%22height%22%3A70%7D%2C%22https%3A%2F%2Fka-perseus-graphie.s3.amazonaws.com%2Fb59fc02ca1aae800977b8793ed22f647a1aa75ee.png%22%3A%7B%22width%22%3A425%2C%22height%22%3A150%7D%7D%2C%22widgets%22%3A%7B%22image%201%22%3A%7B%22type%22%3A%22image%22%2C%22alignment%22%3A%22block%22%2C%22graded%22%3Atrue%2C%22options%22%3A%7B%22title%22%3A%22%22%2C%22range%22%3A%5B%5B0%2C10%5D%2C%5B0%2C10%5D%5D%2C%22box%22%3A%5B460%2C276%5D%2C%22backgroundImage%22%3A%7B%22url%22%3A%22http%3A%2F%2Fwww.easybranches.eu%2Fwp-content%2Fuploads%2F2014%2F02%2F80254dea-a664-4c53-be6e-d53fe7c074de-460x276.jpeg.jpg%22%2C%22width%22%3A460%2C%22height%22%3A276%7D%2C%22labels%22%3A%5B%5D%2C%22alt%22%3A%22%22%2C%22caption%22%3A%22%22%7D%2C%22version%22%3A%7B%22major%22%3A0%2C%22minor%22%3A0%7D%7D%2C%22number-line%201%22%3A%7B%22type%22%3A%22number-line%22%2C%22alignment%22%3A%22default%22%2C%22graded%22%3Atrue%2C%22options%22%3A%7B%22range%22%3A%5B-10%2C10%5D%2C%22labelRange%22%3A%5B-10%2C10%5D%2C%22divisionRange%22%3A%5B1%2C12%5D%2C%22labelStyle%22%3A%22improper%22%2C%22labelTicks%22%3Atrue%2C%22numDivisions%22%3A10%2C%22tickStep%22%3Anull%2C%22snapDivisions%22%3A2%2C%22correctRel%22%3A%22eq%22%2C%22correctX%22%3A4%2C%22initialX%22%3A3%2C%22isTickCtrl%22%3Afalse%7D%2C%22version%22%3A%7B%22major%22%3A0%2C%22minor%22%3A0%7D%7D%7D%7D%2C%7B%22content%22%3A%22%22%2C%22images%22%3A%7B%7D%2C%22widgets%22%3A%7B%7D%7D%5D) and verify that copying and pasting of widgets works when:

- There are no widgets being pasted
- There is one widget being pasted
- There are multiple widgets being pasted

Also try opening a new Perseus tab and check that copying and pasting
in between Perseus editors works.

Reviewers: alex

Subscribers: aria

Differential Revision: https://phabricator.khanacademy.org/D19447
  • Loading branch information...
SamLau95 committed Jul 15, 2015
1 parent 0ecb6ee commit e693b679fd799845da47ed8d6d5b04c6e2e4a0b2
Showing with 42 additions and 0 deletions.
  1. +42 −0 src/editor.jsx
View
@@ -1,4 +1,5 @@
var React = require('react');
var $ = require('jquery');
var _ = require("underscore");
var ApiOptions = require("./perseus-api.jsx").Options;
@@ -495,6 +496,10 @@ var Editor = React.createClass({
// this.props.onChange during that, since it calls our parent's
// setState
this._sizeImages(this.props);
$(React.findDOMNode(this.refs.textarea))
.on('copy cut', this._maybeCopyWidgets)
.on('paste', this._maybePasteWidgets);
},
componentDidUpdate: function(prevProps) {
@@ -579,6 +584,8 @@ var Editor = React.createClass({
},
_handleKeyDown: function(e) {
// Tab-completion of widgets. For example, to insert an image:
// type `[[im`, then tab.
if (e.key === "Tab") {
var textarea = this.refs.textarea.getDOMNode();
@@ -607,6 +614,41 @@ var Editor = React.createClass({
}
},
_maybeCopyWidgets: function(e) {
// If there are widgets being cut/copied, put the widget JSON in
// localStorage.perseusLastCopiedWidgets to allow copy-pasting of
// widgets between Editors.
var textarea = e.target;
var selectedText = textarea.value.substring(
textarea.selectionStart,
textarea.selectionEnd
);
var widgetNames = _.map(selectedText.match(rWidgetSplit), (syntax) => {
return Util.rWidgetParts.exec(syntax)[1];
});
var widgetData = _.pick(this.props.widgets, widgetNames);
localStorage.perseusLastCopiedWidgets = JSON.stringify(widgetData);
console.log(
`Widgets copied: ${localStorage.perseusLastCopiedWidgets}`);
},
_maybePasteWidgets: function() {
// Use the data from localStorage to paste any widgets we copied
// before. If there is a widget name conflict, don't override the
// widgets in the Editor we're pasting into.
var widgetJSON = localStorage.perseusLastCopiedWidgets;
if (widgetJSON) {
var widgetData = JSON.parse(widgetJSON);
var newWidgets = _.extend(widgetData, this.props.widgets);
this.props.onChange({widgets: newWidgets});
}
},
_addWidgetToContent: function(oldContent, cursorRange, widgetType) {
var textarea = this.refs.textarea.getDOMNode();

0 comments on commit e693b67

Please sign in to comment.