From 2a2d6f5abb21eea4011a14227b04248eaa60a6a2 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Sun, 5 Jul 2020 18:18:14 +0800 Subject: [PATCH] Copy and paste html support --- web/deps.edn | 3 +- web/externs.js | 1 + web/src/main/frontend/components/editor.cljs | 18 +- web/src/main/frontend/components/hiccup.cljs | 13 +- web/src/main/frontend/config.cljs | 70 ++++++ .../main/frontend/extensions/html_parser.cljs | 223 ++++++++++++++++++ web/src/main/frontend/regex.cljs | 5 + web/src/main/frontend/state.cljs | 23 +- web/src/main/frontend/util.cljs | 18 +- 9 files changed, 358 insertions(+), 16 deletions(-) create mode 100644 web/src/main/frontend/extensions/html_parser.cljs create mode 100644 web/src/main/frontend/regex.cljs diff --git a/web/deps.edn b/web/deps.edn index 1e46a5c4d..016741fa3 100755 --- a/web/deps.edn +++ b/web/deps.edn @@ -15,7 +15,8 @@ org.clojure/core.match {:mvn/version "1.0.0"} com.andrewmcveigh/cljs-time {:mvn/version "0.5.2"} cljs-drag-n-drop {:mvn/version "0.1.0"} - borkdude/sci {:mvn/version "0.0.13-alpha.27"} + borkdude/sci {:mvn/version "0.0.13-alpha.27"} + hickory {:mvn/version "0.7.1"} } :aliases {:cljs {:extra-paths ["src/dev-cljs/"] diff --git a/web/externs.js b/web/externs.js index c46177d3b..b7fc74fd0 100644 --- a/web/externs.js +++ b/web/externs.js @@ -28,3 +28,4 @@ dummy.fillText = function() {}; dummy.beginPath = function() {}; dummy.arc = function() {}; dummy.fill = function() {}; +dummy.getData = function() {}; diff --git a/web/src/main/frontend/components/editor.cljs b/web/src/main/frontend/components/editor.cljs index 3b056bc8e..68a0cdc08 100644 --- a/web/src/main/frontend/components/editor.cljs +++ b/web/src/main/frontend/components/editor.cljs @@ -27,7 +27,8 @@ [cljs-time.coerce :as tc] [cljs-drag-n-drop.core :as dnd] [frontend.search :as search] - ["/frontend/utils" :as utils])) + ["/frontend/utils" :as utils] + [frontend.extensions.html-parser :as html-parser])) ;; TODO: refactor the state, it is already too complex. (defonce *last-edit-heading (atom nil)) @@ -556,6 +557,14 @@ (set-last-edit-heading! (:heading/uuid heading) value) (handler/save-heading-if-changed! heading new-value))) +(defn- append-paste-doc! + [format event] + (when-let [html (util/get-clipboard-as-html event)] + (let [doc-text (html-parser/parse format html)] + (when-not (string/blank? doc-text) + (util/stop event) + (state/append-current-edit-content! doc-text))))) + (rum/defc box < rum/reactive (mixins/event-mixin (fn [state] @@ -563,6 +572,8 @@ input-id id input (gdom/getElement input-id) repo (:heading/repo heading)] + (.addEventListener input "paste" (fn [event] + (append-paste-doc! format event))) (mixins/hide-when-esc-or-outside state :on-hide @@ -736,9 +747,12 @@ :will-unmount (fn [state] (let [{:keys [id value format heading repo dummy?]} (get-state state)] (when-let [input (gdom/getElement id)] + (.removeEventListener input "paste" (fn [event] + (append-paste-doc! format event))) (dnd/unsubscribe! input - :upload-images)) + :upload-images) + ) (clear-when-saved!)) state)} [content {:keys [on-hide dummy? node format heading] diff --git a/web/src/main/frontend/components/hiccup.cljs b/web/src/main/frontend/components/hiccup.cljs index 6b8b1f7a9..0b2be9e3d 100644 --- a/web/src/main/frontend/components/hiccup.cljs +++ b/web/src/main/frontend/components/hiccup.cljs @@ -282,12 +282,18 @@ (let [{:keys [url label title]} link] (match url ["Search" s] - (case (first s) - \# + (cond + (= \# (first s)) (->elem :a {:href (str "#" (anchor-link (subs s 1)))} (map-inline config label)) ;; FIXME: same headline, see more https://orgmode.org/manual/Internal-Links.html - \* + (= \* (first s)) (->elem :a {:href (str "#" (anchor-link (subs s 1)))} (map-inline config label)) + + (re-find #"^https://" s) + (->elem :a {:href s} + (map-inline config label)) + + :else ;; page reference [:span.page-reference [:span.text-gray-500 "[["] @@ -1110,6 +1116,7 @@ [:div.headings-container {:style {:margin-left -24}} (build-headings headings config)]) +;; headers to hiccup (rum/defc ->hiccup < rum/reactive ;; (mixins/perf-measure-mixin "hiccup") [headings config option] diff --git a/web/src/main/frontend/config.cljs b/web/src/main/frontend/config.cljs index 7eea01ac5..2f5533e10 100644 --- a/web/src/main/frontend/config.cljs +++ b/web/src/main/frontend/config.cljs @@ -84,6 +84,76 @@ ""))) +(defn get-hr + [format] + (let [format (or format (keyword (state/get-preferred-format)))] + (case format + :org + "-----" + :markdown + "---" + ""))) + +(defn get-bold + [format] + (let [format (or format (keyword (state/get-preferred-format)))] + (case format + :org + "*" + :markdown + "**" + ""))) + +(defn get-italic + [format] + (let [format (or format (keyword (state/get-preferred-format)))] + (case format + :org + "/" + :markdown + "__" + ""))) +(defn get-underline + [format] + (let [format (or format (keyword (state/get-preferred-format)))] + (case format + :org + "/" + :markdown + "__" + ""))) +(defn get-strike-through + [format] + (let [format (or format (keyword (state/get-preferred-format)))] + (case format + :org + "+" + :markdown + "~~" + ""))) + +(defn get-highlight + [format] + "^^") + +(defn get-code + [format] + (let [format (or format (keyword (state/get-preferred-format)))] + (case format + :org + "~" + :markdown + "`" + ""))) + +(defn get-subscript + [format] + "_") + +(defn get-superscript + [format] + "^") + (defn default-empty-heading ([format] (default-empty-heading format 2)) diff --git a/web/src/main/frontend/extensions/html_parser.cljs b/web/src/main/frontend/extensions/html_parser.cljs new file mode 100644 index 000000000..a68bf67ec --- /dev/null +++ b/web/src/main/frontend/extensions/html_parser.cljs @@ -0,0 +1,223 @@ +(ns frontend.extensions.html-parser + (:require [goog.object :as gobj] + [hickory.core :as hickory] + [cljs.core.match :refer-macros [match]] + [frontend.config :as config] + [frontend.util :as util] + [clojure.string :as string] + [clojure.walk :as walk])) + +(defonce *inside-pre? (atom false)) +(defn- hiccup-without-style + [hiccup] + (walk/postwalk (fn [f] + (if (map? f) + (dissoc f :style) + f)) hiccup)) + +(defn- export-hiccup + [hiccup] + (util/format "#+BEGIN_EXPORT hiccup\n%s\n#+END_EXPORT" + + (str (hiccup-without-style hiccup)))) + +(defn hiccup->doc-inner + [format hiccup] + (let [transform-fn (fn [hiccup] + (hiccup->doc-inner format hiccup)) + heading-pattern (config/get-heading-pattern format) + map-join (fn [children] (apply str (map transform-fn children))) + heading-transform (fn [level children] + (str (apply str (repeat level heading-pattern)) + " " + (->> (map transform-fn children) + (string/join " ")) + "\n")) + emphasis-transform (fn [tag attrs children] + (let [pattern (cond + (contains? #{:b :strong} tag) + (config/get-bold format) + (contains? #{:i :em} tag) + (config/get-italic format) + (contains? #{:ins} tag) + (config/get-underline format) + (contains? #{:del} tag) + (config/get-strike-through format) + (contains? #{:mark} tag) + (config/get-highlight format) + :else + nil)] + (str pattern (map-join children) pattern))) + wrapper (fn [tag content] + (cond + (contains? #{:p :hr :ul :ol :dl :table :pre :blockquote :aside :canvas + :center :figure :figcaption :fieldset :div :footer + :header} tag) + (str "\n\n" content "\n\n") + + (contains? #{:thead :tr :li :dt :dd :dl} tag) + (str content "\n") + + :else + content)) + single-hiccup-transform + (fn [x] + (do + (cond + (vector? x) + (let [[tag attrs & children] x + result (match tag + :head nil + :h1 (heading-transform 1 children) + :h2 (heading-transform 2 children) + :h3 (heading-transform 3 children) + :h4 (heading-transform 4 children) + :h5 (heading-transform 5 children) + :h6 (heading-transform 6 children) + :a (let [href (:href attrs) + title (:title attrs) + label (map-join children) + has-img-tag? (re-find #"\[:img" (str x))] + (if has-img-tag? + (export-hiccup x) + (case format + :markdown (util/format "[%s](%s)" label href) + :org (util/format "[[%s][%s]]" href label) + nil))) + :img (let [src (:src attrs) + alt (:alt attrs)] + (case format + :markdown (util/format "![%s](%s)" alt src) + :org (util/format "[[%s][%s]]" src alt) + nil)) + :p (util/format "%s" + (map-join children)) + + :hr (config/get-hr format) + + (_ :guard #(contains? #{:b :strong + :i :em + :ins + :del + :mark} %)) + (emphasis-transform tag attrs children) + + :code (if @*inside-pre? + (map-join children) + (let [pattern (config/get-code format)] + (str " " + (str pattern (first children) pattern) + " "))) + + :pre + (do + (reset! *inside-pre? true) + (let [content (string/trim (doall (map-join children)))] + (reset! *inside-pre? false) + (case format + :markdown (if (string/starts-with? content "```") + content + (str "```\n" content "\n```")) + :org (if (string/starts-with? content "#+BEGIN_SRC") + content + (util/format "#+BEGIN_SRC\n%s\n#+END_SRC" content)) + nil))) + + :blockquote + (case format + :markdown (str "> " (map-join children)) + :org (util/format "#+BEGIN_QUOTE\n%s\n#+END_QUOTE" (map-join children)) + nil) + + :li + (str "- " (map-join children)) + + :dt + (case format + :org (str "- " (map-join children) " ") + :markdown (map-join children) + nil) + + :dd + (case format + :markdown (str ": " (map-join children) "\n") + :org (str ":: " (map-join children) "\n") + nil) + + :thead + (case format + :markdown (let [columns (count (last (first children)))] + (str + (map-join children) + (str "| " (string/join " | " + (repeat columns "----")) + " |"))) + :org (let [columns (count (last (first children)))] + (str + (map-join children) + (str "|" (string/join "+" + (repeat columns "----")) + "|"))) + nil) + :tr + (str "| " + (->> (map transform-fn children) + (string/join " | ")) + " |") + + (_ :guard #(contains? #{:aside :center :figure :figcaption :fieldset :footer :header} %)) + (export-hiccup x) + + :else (map-join children) + )] + (wrapper tag result)) + + (string? x) + x + + :else + (println "hiccup->doc error: " x)))) + result (if (vector? (first hiccup)) + (for [x hiccup] + (single-hiccup-transform x)) + (single-hiccup-transform hiccup))] + (apply str result))) + +(defn hiccup->doc + [format hiccup] + (let [s (hiccup->doc-inner format hiccup)] + (if (string/blank? s) + "" + (-> s + (string/trim) + (string/replace "\n\n\n\n" "\n\n") + (string/replace "\n\n\n" "\n\n"))))) + +(defn html-decode-hiccup + [hiccup] + (walk/postwalk (fn [f] + (if (string? f) + (goog.string.unescapeEntities f) + f)) hiccup)) + +(defonce debug-hiccup (atom nil)) +(defn parse + [format html] + (when-not (string/blank? html) + (let [hiccup (hickory/as-hiccup (hickory/parse html)) + decoded-hiccup (html-decode-hiccup hiccup)] + (reset! debug-hiccup decoded-hiccup) + (hiccup->doc format decoded-hiccup)))) + +(def img-link + [:a {:href "https://www.markdownguide.org/book/", :style "box-sizing: border-box; color: rgb(0, 123, 255); text-decoration: none; background-color: transparent;"} [:img {:src "https://d33wubrfki0l68.cloudfront.net/cb41dd8e38b0543a305f9c56db89b46caa802263/25192/assets/images/book-cover.jpg", :class "card-img", :alt "Markdown Guide book cover", :style "box-sizing: border-box; vertical-align: middle; border-style: none; flex-shrink: 0; width: 205.75px; border-radius: calc(0.25rem - 1px);"}]]) + +(comment + ;; | Syntax | Description | Test Text |`` + ;; | :--- | :----: | ---: | + ;; | Header | Title | Here's this | + ;; | Paragraph | Text | And more | + + (def img-link + [:a {:href "https://www.markdownguide.org/book/", :style "box-sizing: border-box; color: rgb(0, 123, 255); text-decoration: none; background-color: transparent;"} [:img {:src "https://d33wubrfki0l68.cloudfront.net/cb41dd8e38b0543a305f9c56db89b46caa802263/25192/assets/images/book-cover.jpg", :class "card-img", :alt "Markdown Guide book cover", :style "box-sizing: border-box; vertical-align: middle; border-style: none; flex-shrink: 0; width: 205.75px; border-radius: calc(0.25rem - 1px);"}]]) + ) diff --git a/web/src/main/frontend/regex.cljs b/web/src/main/frontend/regex.cljs new file mode 100644 index 000000000..3ad81dc15 --- /dev/null +++ b/web/src/main/frontend/regex.cljs @@ -0,0 +1,5 @@ +(ns frontend.regex) + +;; Get this from XRegExp("^\\pL+$") +(def valid-tag-pattern + #"^[0-9A-Za-z_\-@%ªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽͿΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙՠ-ֈא-תׯ-ײؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࡠ-ࡪࢠ-ࢴࢶ-ࢽऄ-हऽॐक़-ॡॱ-ঀঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱৼਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡૹଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-హఽౘ-ౚౠౡಀಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡೱೲഅ-ഌഎ-ഐഒ-ഺഽൎൔ-ൖൟ-ൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาำเ-ๆກຂຄຆ-ຊຌ-ຣລວ-ະາຳຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡸᢀ-ᢄᢇ-ᢨᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᲀ-ᲈᲐ-ᲺᲽ-Ჿᳩ-ᳬᳮ-ᳳᳵᳶᳺᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎↃↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ々〆〱-〵〻〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄯㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿯ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞿꟂ-Ᶎꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꣽꣾꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꩾ-ꪯꪱꪵꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭧꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ]+$") diff --git a/web/src/main/frontend/state.cljs b/web/src/main/frontend/state.cljs index 25812c453..26354e906 100644 --- a/web/src/main/frontend/state.cljs +++ b/web/src/main/frontend/state.cljs @@ -146,6 +146,25 @@ (update-state! :editor/content (fn [m] (assoc m input-id value))))) +(defn get-edit-input-id + [] + (ffirst (:editor/editing? @state))) + +(defn append-current-edit-content! + [append-text] + (when-not (string/blank? append-text) + (when-let [input-id (get-edit-input-id)] + (when-let [input (gdom/getElement input-id)] + (let [value (gobj/get input "value") + new-value (str value append-text) + new-value (if (or (= (last value) " ") + (= (last value) "\n")) + new-value + (str "\n" new-value))] + (js/document.execCommand "insertText" false append-text) + (update-state! :editor/content (fn [m] + (assoc m input-id new-value)))))))) + (defn get-cursor-range [] (:cursor-range @state)) @@ -236,10 +255,6 @@ (fn [m] (and input-id {input-id true})))) -(defn get-edit-input-id - [] - (ffirst (:editor/editing? @state))) - (defn sub-edit-input-id [] (ffirst (util/react (rum/cursor state :editor/editing?)))) diff --git a/web/src/main/frontend/util.cljs b/web/src/main/frontend/util.cljs index cba867530..bbca44fe9 100644 --- a/web/src/main/frontend/util.cljs +++ b/web/src/main/frontend/util.cljs @@ -13,7 +13,8 @@ [dommy.core :as d] [cljs-time.core :as t] [cljs-time.coerce :as tc] - [cljs-time.format :as format])) + [cljs-time.format :as format] + [frontend.regex :as regex])) (defn format [fmt & args] @@ -664,15 +665,20 @@ [] (str (rand-str 6) (rand-str 3))) -;; Get this from XRegExp("^\\pL+$") -(def valid-tag-pattern - #"^[0-9A-Za-z_\-@%ªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽͿΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙՠ-ֈא-תׯ-ײؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࡠ-ࡪࢠ-ࢴࢶ-ࢽऄ-हऽॐक़-ॡॱ-ঀঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱৼਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡૹଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-హఽౘ-ౚౠౡಀಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡೱೲഅ-ഌഎ-ഐഒ-ഺഽൎൔ-ൖൟ-ൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาำเ-ๆກຂຄຆ-ຊຌ-ຣລວ-ະາຳຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡸᢀ-ᢄᢇ-ᢨᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᲀ-ᲈᲐ-ᲺᲽ-Ჿᳩ-ᳬᳮ-ᳳᳵᳶᳺᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎↃↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ々〆〱-〵〻〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄯㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿯ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞿꟂ-Ᶎꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꣽꣾꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꩾ-ꪯꪱꪵꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭧꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ]+$") - (defn tag-valid? [tag-name] - (re-find valid-tag-pattern tag-name)) + (re-find regex/valid-tag-pattern tag-name)) ;; TODO: emoji, unicode alphanum, spaces, _, - ;; (defn page-title-valid? ;; [page-title] ;; ) + +(defn- get-clipboard-as-html + [event] + (if-let [c (gobj/get event "clipboardData")] + (.getData c "text/html") + (if-let [c (gobj/getValueByKeys event "originalEvent" "clipboardData")] + (.getData c "text/html") + (if-let [c (gobj/get js/window "clipboardData")] + (.getData c "Text")))))