Skip to content

Commit

Permalink
Desktop: Fixes laurent22#4441: Fixed copying text from Rich Text editor
Browse files Browse the repository at this point in the history
  • Loading branch information
laurent22 committed Jan 30, 2021
1 parent cb2c216 commit 7331444
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 25 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,9 @@ packages/lib/fs-driver-base.js.map
packages/lib/fs-driver-node.d.ts
packages/lib/fs-driver-node.js
packages/lib/fs-driver-node.js.map
packages/lib/htmlUtils.d.ts
packages/lib/htmlUtils.js
packages/lib/htmlUtils.js.map
packages/lib/import-enex-md-gen.d.ts
packages/lib/import-enex-md-gen.js
packages/lib/import-enex-md-gen.js.map
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,9 @@ packages/lib/fs-driver-base.js.map
packages/lib/fs-driver-node.d.ts
packages/lib/fs-driver-node.js
packages/lib/fs-driver-node.js.map
packages/lib/htmlUtils.d.ts
packages/lib/htmlUtils.js
packages/lib/htmlUtils.js.map
packages/lib/import-enex-md-gen.d.ts
packages/lib/import-enex-md-gen.js
packages/lib/import-enex-md-gen.js.map
Expand Down
9 changes: 8 additions & 1 deletion packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const bridge = require('electron').remote.require('./bridge').default;
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
import Resource from '@joplin/lib/models/Resource';
import htmlUtils from '@joplin/lib/htmlUtils';
const fs = require('fs-extra');
const { clipboard } = require('electron');
const { toSystemSlashes } = require('@joplin/lib/path-utils');
Expand Down Expand Up @@ -47,7 +48,13 @@ function handleCopyToClipboard(options: ContextMenuOptions) {
if (options.textToCopy) {
clipboard.writeText(options.textToCopy);
} else if (options.htmlToCopy) {
clipboard.writeHTML(options.htmlToCopy);
// In that case we need to set both HTML and Text context, otherwise it
// won't be possible to paste the text in, for example, a text editor.
// https://github.com/laurent22/joplin/issues/4441
clipboard.write({
text: htmlUtils.stripHtml(options.htmlToCopy),
html: options.htmlToCopy,
});
}
}

Expand Down
36 changes: 17 additions & 19 deletions packages/lib/htmlUtils.js → packages/lib/htmlUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,19 @@ const selfClosingElements = [
];

class HtmlUtils {
headAndBodyHtml(doc) {
headAndBodyHtml(doc: any) {
const output = [];
if (doc.head) output.push(doc.head.innerHTML);
if (doc.body) output.push(doc.body.innerHTML);
return output.join('\n');
}

isSelfClosingTag(tagName) {
isSelfClosingTag(tagName: string) {
return selfClosingElements.includes(tagName.toLowerCase());
}

// Returns the **encoded** URLs, so to be useful they should be decoded again before use.
extractImageUrls(html) {
extractImageUrls(html: string) {
if (!html) return [];

const output = [];
Expand All @@ -55,8 +55,8 @@ class HtmlUtils {
return output.filter(url => !!url);
}

replaceImageUrls(html, callback) {
return this.processImageTags(html, data => {
replaceImageUrls(html: string, callback: Function) {
return this.processImageTags(html, (data: any) => {
const newSrc = callback(data.src);
return {
type: 'replaceSource',
Expand All @@ -65,10 +65,10 @@ class HtmlUtils {
});
}

processImageTags(html, callback) {
processImageTags(html: string, callback: Function) {
if (!html) return '';

return html.replace(imageRegex, (v, before, src, after) => {
return html.replace(imageRegex, (_v: string, before: string, src: string, after: string) => {
const action = callback({ src: src });

if (!action) return `<img${before}src="${src}"${after}>`;
Expand All @@ -90,16 +90,16 @@ class HtmlUtils {
});
}

prependBaseUrl(html, baseUrl) {
prependBaseUrl(html: string, baseUrl: string) {
if (!html) return '';

return html.replace(anchorRegex, (v, before, href, after) => {
return html.replace(anchorRegex, (_v: string, before: string, href: string, after: string) => {
const newHref = urlUtils.prependBaseUrl(href, baseUrl);
return `<a${before}href="${newHref}"${after}>`;
});
}

attributesHtml(attr) {
attributesHtml(attr: any) {
const output = [];

for (const n in attr) {
Expand All @@ -110,10 +110,10 @@ class HtmlUtils {
return output.join(' ');
}

stripHtml(html) {
const output = [];
stripHtml(html: string) {
const output: string[] = [];

const tagStack = [];
const tagStack: any[] = [];

const currentTag = () => {
if (!tagStack.length) return '';
Expand All @@ -124,16 +124,16 @@ class HtmlUtils {

const parser = new htmlparser2.Parser({

onopentag: (name) => {
onopentag: (name: string) => {
tagStack.push(name.toLowerCase());
},

ontext: (decodedText) => {
ontext: (decodedText: string) => {
if (disallowedTags.includes(currentTag())) return;
output.push(decodedText);
},

onclosetag: (name) => {
onclosetag: (name: string) => {
if (currentTag() === name.toLowerCase()) tagStack.pop();
},

Expand All @@ -146,6 +146,4 @@ class HtmlUtils {
}
}

const htmlUtils = new HtmlUtils();

module.exports = htmlUtils;
export default new HtmlUtils();
6 changes: 3 additions & 3 deletions packages/lib/import-enex-html-gen.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const stringToStream = require('string-to-stream');
// const cleanHtml = require('clean-html');
const resourceUtils = require('./resourceUtils.js');
const { isSelfClosingTag } = require('./htmlUtils');
const htmlUtils = require('./htmlUtils').default;
const Entities = require('html-entities').AllHtmlEntities;
const htmlentities = new Entities().encode;

Expand Down Expand Up @@ -126,7 +126,7 @@ function enexXmlToHtml_(stream, resources) {
const nodeAttributes = attributeToLowerCase(node);
const checkedHtml = nodeAttributes.checked && nodeAttributes.checked.toLowerCase() == 'true' ? ' checked="checked" ' : ' ';
section.lines.push(`<input${checkedHtml}type="checkbox" onclick="return false;" />`);
} else if (isSelfClosingTag(tagName)) {
} else if (htmlUtils.isSelfClosingTag(tagName)) {
section.lines.push(`<${tagName}${attributesStr}/>`);
} else {
section.lines.push(`<${tagName}${attributesStr}>`);
Expand All @@ -135,7 +135,7 @@ function enexXmlToHtml_(stream, resources) {

saxStream.on('closetag', function(node) {
const tagName = node ? node.toLowerCase() : node;
if (!isSelfClosingTag(tagName)) section.lines.push(`</${tagName}>`);
if (!htmlUtils.isSelfClosingTag(tagName)) section.lines.push(`</${tagName}>`);
});

saxStream.on('attribute', function() {});
Expand Down
2 changes: 1 addition & 1 deletion packages/lib/markupLanguageUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Setting from './models/Setting';
import shim from './shim';
import MarkupToHtml, { MarkupLanguage } from '@joplin/renderer/MarkupToHtml';

const htmlUtils = require('./htmlUtils');
import htmlUtils from './htmlUtils';
import Resource from './models/Resource';

export class MarkupLanguageUtils {
Expand Down
2 changes: 1 addition & 1 deletion packages/lib/services/rest/routes/notes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Folder from '../../../models/Folder';
import Note from '../../../models/Note';
import Tag from '../../../models/Tag';
import Resource from '../../../models/Resource';
const htmlUtils = require('../../../htmlUtils');
import htmlUtils from '../../../htmlUtils';
import markupLanguageUtils from '../../../markupLanguageUtils';
const mimeUtils = require('../../../mime-utils.js').mime;
const md5 = require('md5');
Expand Down

0 comments on commit 7331444

Please sign in to comment.