From 1d3e5787a26b708195635a434a045240a726e204 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Wed, 25 Oct 2017 20:20:28 +1000 Subject: [PATCH] separate basic and extended paste modes - basic mode is the default, and includes only HTML elements that can be added/edited easily with the default editor - extended mode is enabled by holding down shift and includes a bunch of other HTML elements --- aqt/editor.py | 9 +++++++-- aqt/webview.py | 2 ++ web/editor.js | 52 +++++++++++++++++++++++++++++++++----------------- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/aqt/editor.py b/aqt/editor.py index a2ae152e4..6294e7ce9 100644 --- a/aqt/editor.py +++ b/aqt/editor.py @@ -646,8 +646,13 @@ to a cloze type first, via Edit>Change Note Type.""")) def doPaste(self, html, internal): if not internal: html = self._pastePreFilter(html) - self.web.eval("pasteHTML(%s, %s);" % ( - json.dumps(html), json.dumps(internal))) + extended = self.mw.app.queryKeyboardModifiers() & Qt.ShiftModifier + if extended: + extended = "true" + else: + extended = "false" + self.web.eval("pasteHTML(%s, %s, %s);" % ( + json.dumps(html), json.dumps(internal), extended)) def doDrop(self, html, internal): self.web.evalWithCallback("makeDropTargetCurrent();", diff --git a/aqt/webview.py b/aqt/webview.py index 964774397..5cc70cdb9 100644 --- a/aqt/webview.py +++ b/aqt/webview.py @@ -99,6 +99,8 @@ class AnkiWebView(QWebEngineView): QShortcut(key, self, context=Qt.WidgetWithChildrenShortcut, activated=fn) + QShortcut(QKeySequence("ctrl+shift+v"), self, + context=Qt.WidgetWithChildrenShortcut, activated=self.onPaste) self.focusProxy().installEventFilter(self) diff --git a/web/editor.js b/web/editor.js index 400599775..7f119746c 100644 --- a/web/editor.js +++ b/web/editor.js @@ -300,18 +300,18 @@ function hideDupes() { $("#dupes").hide(); } -var pasteHTML = function (html, internal) { - html = filterHTML(html, internal); +var pasteHTML = function (html, internal, allowedTags) { + html = filterHTML(html, internal, allowedTags); setFormat("inserthtml", html); }; -var filterHTML = function (html, internal) { +var filterHTML = function (html, internal, extendedMode) { // wrap it in as we aren't allowed to change top level elements var top = $.parseHTML("" + html + "")[0]; if (internal) { filterInternalNode(top); } else { - filterNode(top); + filterNode(top, extendedMode); } var outHtml = top.innerHTML; //console.log(`input html: ${html}`); @@ -319,20 +319,31 @@ var filterHTML = function (html, internal) { return outHtml; }; -var allowedTags = {}; +var allowedTagsBasic = {}; +var allowedTagsExtended = {}; -var TAGS_WITHOUT_ATTRS = ["H1", "H2", "H3", "P", "DIV", "BR", "LI", "UL", - "OL", "B", "I", "U", "BLOCKQUOTE", "CODE", "EM", - "STRONG", "PRE", "SUB", "SUP", "TABLE", "DD", "DT", "DL"]; -for (var i = 0; i < TAGS_WITHOUT_ATTRS.length; i++) { - allowedTags[TAGS_WITHOUT_ATTRS[i]] = {"attrs": []}; +var TAGS_WITHOUT_ATTRS = ["P", "DIV", "BR", + "B", "I", "U", "EM", "STRONG", "SUB", "SUP"]; +var i; +for (i = 0; i < TAGS_WITHOUT_ATTRS.length; i++) { + allowedTagsBasic[TAGS_WITHOUT_ATTRS[i]] = {"attrs": []}; } -allowedTags["A"] = {"attrs": ["HREF"]}; -allowedTags["TR"] = {"attrs": ["ROWSPAN"]}; -allowedTags["TD"] = {"attrs": ["COLSPAN", "ROWSPAN"]}; -allowedTags["TH"] = {"attrs": ["COLSPAN", "ROWSPAN"]}; -allowedTags["IMG"] = {"attrs": ["SRC"]}; +TAGS_WITHOUT_ATTRS = ["H1", "H2", "H3", "LI", "UL", "BLOCKQUOTE", "CODE", + "PRE", "TABLE", "DD", "DT", "DL"]; +for (i = 0; i < TAGS_WITHOUT_ATTRS.length; i++) { + allowedTagsExtended[TAGS_WITHOUT_ATTRS[i]] = {"attrs": []}; +} + +allowedTagsBasic["IMG"] = {"attrs": ["SRC"]}; + +allowedTagsExtended["A"] = {"attrs": ["HREF"]}; +allowedTagsExtended["TR"] = {"attrs": ["ROWSPAN"]}; +allowedTagsExtended["TD"] = {"attrs": ["COLSPAN", "ROWSPAN"]}; +allowedTagsExtended["TH"] = {"attrs": ["COLSPAN", "ROWSPAN"]}; + +// add basic tags to extended +Object.assign(allowedTagsExtended, allowedTagsBasic); // filtering from another field var filterInternalNode = function (node) { @@ -348,7 +359,7 @@ var filterInternalNode = function (node) { }; // filtering from external sources -var filterNode = function (node) { +var filterNode = function (node, extendedMode) { // text node? if (node.nodeType === 3) { return; @@ -363,14 +374,19 @@ var filterNode = function (node) { nodes.push(node.childNodes[i]); } for (i = 0; i < nodes.length; i++) { - filterNode(nodes[i]); + filterNode(nodes[i], extendedMode); } if (node.tagName === "ANKITOP") { return; } - var tag = allowedTags[node.tagName]; + var tag; + if (extendedMode) { + tag = allowedTagsExtended[node.tagName]; + } else { + tag = allowedTagsBasic[node.tagName]; + } if (!tag) { if (!node.innerHTML) { node.parentNode.removeChild(node);