#2 日本語の文中改行を削除

Merged
senooken merged 6 commits from tak4/main into gnusocialjp/main 10 months ago
1 changed files with 150 additions and 1 deletions
  1. 150 1
      index.xhtml

+ 150 - 1
index.xhtml

@@ -20,6 +20,7 @@
         var value = document.getElementById("input").value;
         var view  = document.getElementById("view");
         view.innerHTML = value;
+        cleanup_element(view);
 
         // Generate TOC
         var root = document.getElementById("view");
@@ -80,6 +81,154 @@
         return toc;
       }
 
+      // 改行除去処理でスキップする要素 (子孫含めスキップされる)
+      const element_skip_list = {
+        pre: true,
+      };
+
+      // 改行除去処理の対象要素 (子孫含め処理される)
+      const element_target_flags = {
+        a: true,
+        em: true,
+        strong: true,
+        p: true,
+        li: true,
+        td: true,
+        h1: true,
+        h2: true,
+        h3: true,
+        h4: true,
+        h5: true,
+        h6: true,
+      };
+
+      // DOMツリーの各要素から不要な改行/空白を除去
+      // in_target: 処理対象の要素に入るとtrueになる
+      // queue: 要素を跨いだ処理の空白で使用する作業用配列
+      function cleanup_element(element, in_target, queue) {
+        in_target = in_target || false; // 未指定ならfalse
+        queue = queue || []; // 未指定なら空配列
+
+        // 処理対象要素から出た時にqueueを空にする
+        if (!in_target) {
+          queue.length = '';
+        }
+
+        // 子要素のリストを持たなければスキップ
+        if (!element.childNodes) { return; }
+
+        // 直下の子要素を処理
+        let list = element.childNodes;
+        for (let i = 0, length = list.length; i < length; i++) {
+          let child = list[i];
+          let nodename = child.nodeName.toLowerCase();
+
+          // 特定の要素(主にpre)をスキップ
+          if (element_skip_list[nodename] === true) { continue; }
+
+          // 対象要素又はその子孫なら改行/空白を除去
+          let next_in_target = (in_target || element_target_flags[nodename] === true);
+          if  (next_in_target) {
+            cleanup_node(child, queue);
+          }
+
+          // 子孫を処理するため再帰呼び出し
+          cleanup_element(child, next_in_target, queue);
+
+          // 子孫要素の最後の「日本語文字」以降の改行/空白を除去
+          if (!in_target && next_in_target) {
+            cleanup_queue(queue);
+          }
+        }
+      }
+
+      // テキストノードから不要な改行/空白を除去
+      function cleanup_node(node, queue) {
+
+        // 正規表現のメモ:
+        // (?:[^\s\d\w]| )←日本語 (記号含む)
+        // [^\S ]←改行/空白 (\sから全角スペースを除いた物)
+        // [^\S\n ]←空白 (\sから全角スペース/改行文字を除いた物)
+        // [\[\]\(\)\{\}\<\>\|] ←括弧と「|」
+
+        // テキストノード以外はスキップ
+        if (node.nodeType !== document.TEXT_NODE) { return; }
+
+        let text = node.data;
+
+        // 前要素の末尾が日本語だった場合
+        if (queue.length > 0) {
+
+          // 改行/空白だけのテキストノードはqueueに追加 (次に日本語が出現したらまとめて除去)
+          if (text === '' || /^[^\S ]*$/.test(text)) {
+            queue.push(node);
+            return;
+          }
+
+          // 日本語又は開き括弧以外の記号が出現したらtrue
+          let jp_or_paren = /^[^\S ]*(?:[^\s\d\w\[\(\{\<\|]| )/.test(text);
+
+          // 前の要素の末尾が閉じ括弧ならtrue
+          let last_paren = (/[^\S ]*[\]\)\}\>\|][^\S ]*$/.test(queue[0].data));
+
+          // 日本語又は開き括弧以外の記号が出現したらqueueを使用して最後の日本語より後の改行/空白をまとめて除外
+          // 閉じ括弧の後に、。が来た場合も除外
+          if (jp_or_paren && (!last_paren || /^[^\S ]*[、。]/.test(text))) {
+            cleanup_queue(queue);
+            text = text.replace(/^[^\S ]*/, "");
+          } else {
+            // 上記以外ならqueueを破棄
+            queue.length = 0;
+          }
+        }
+
+        // 日本語と閉じ括弧以外の記号+改行+後続の空白+開き括弧以外
+        text = text.replace(/((?:[^\s\d\w\]\)\}\>\|]| ))\n[^\S ]*([^\[\(\{\<\|])/g, "$1$2");
+
+        // 閉じ括弧以外+改行+後続の空白+日本語と開き括弧以外の記号
+        text = text.replace(/([^\]\)\}\>\|])\n[^\S ]*((?:[^\s\d\w\[\(\{\<\|]| ))/g, "$1$2");
+
+        // 括弧+改行+後続の空白+、。
+        text = text.replace(/([\[\]\(\)\{\}\<\>\|])\n[^\S ]*([、。])/g, "$1$2");
+
+        if (/(?:[^\s\d\w]| )[^\S ]*$/.test(text)) {
+          // 末尾が日本語ならqueueに追加
+          queue.push(node);
+        } else {
+          // 上記以外ならqueueを使用して最後の日本語より後の改行/空白を除去
+          cleanup_queue(queue);
+        }
+
+        node.data = text;
+      }
+
+      // queueに溜まったテキストノードから末尾の改行/空白を削除
+      function cleanup_queue(queue) {
+
+          // 途中に改行文字を含むか?
+          let has_break = false;
+          for (let i = 1, length = queue.length; i < length; i++) { // 0番目は日本語が末尾なので1番目以降を調べる
+            let text = queue[i].data;
+            if (/\n/g.test(text)) {
+              has_break = true;
+              break;
+            }
+          }
+
+          // 途中に改行を含まないなら削除しない
+          if (!has_break) {
+            queue.length = 0;
+            return;
+          }
+
+          for (let i = 0, length = queue.length; i < length; i++) {
+            let text = queue[i].data;
+            text = text.replace(/[^\S ]*$/, "");
+            queue[i].data = text;
+          }
+          queue.length = 0;
+      }
+
 function escape_html(unsafeText) {
   if(typeof unsafeText !== 'string') {
     return unsafeText;
@@ -816,4 +965,4 @@ href="#20170505T0118_Heading-1-2">Heading 1-2</a></li></ol>
       <p> <small>Author: SENOO, Ken (<a href="https://twitter.com/senopen">@senopen</a>). License: CC0. Created date: <time>2017-05-06.</time></small></p>
     </footer>
 </body>
-</html>
+</html>