読者です 読者をやめる 読者になる 読者になる

@wikiに貼れる、CSVをWikiの表組み書式に変換するJavaScript

10/07/25更新

連続するダブルクオーテーションがダブルクオーテーションを表すのはエスケープされているときのみにした

今取り掛かってるWikiが大きな表をバンバン作るものなんですが、いちいち手動で打ち込むのは大変なので、表計算ソフトとかで作った表を変換できればいいなと考えてCSVから表組み書式に変換するJavaScriptを作ろうと思いました。*1
で、都合よく、借りてる@wikiではJavaScriptが使えたので、Wikiに埋め込めるようなスクリプトを作りました。

使用例http://www23.atwiki.jp/freegamemusic/pages/27.html

JavaScriptは初心者同然なので、非効率な書き方やバグなどあると思いますが、もし何か気になるところがありましたら指摘してくださるとありがたいです。

仕様

  • テキストエリアにCSVを貼りつけて"変換"ボタンを押す
  • "元に戻す"ボタンで変換前に戻せる
  • 正しい書式のCSVなら読み込めるはず
  • 間違っていても特にエラーは出さない
  • セル内での改行は改行プラグイン'&br()'に変換する
  • 連続する改行は削除される
  • セル中の'|'は文字参照に変換される(Wikiに貼りつければ'|'と表示される)
  • テキストエリアの行数は内容の行数で変わる

補足

  • 比較演算子を使ってないのは、@wikiに上げたときに実体参照に変換されてしまうからです
  • returnでの戻り値にカッコをしてスペースを入れないようにしているのは、@wikiに上げたとき、returnと戻り値の間に改行が挟まれて意味が変わらないようにするためです
    • 特に意識してませんが、同様の理由で文字列リテラルには半角スペースを入れるべきではないでしょう
  • 匿名関数の終わりに';'を付けているのは、逆に改行が取り除かれてしまうため
  • 長いswitch文はあまりよくないですね
var thisScript = (function (e) { if(e.nodeName.toLowerCase() == 'script') return e; return(arguments.callee(e.lastChild)) })(document);
var thisScriptParent = thisScript.parentNode;

function createButton(id,value) {
  var button = document.createElement('input');
  button.id = id;
  button.type = 'button';
  button.value = value;
  thisScriptParent.appendChild(button);

  return(button);
}

function createTextArea(id) {
  var textarea = document.createElement('textarea');
  textarea.id = id;
  textarea.cols = 10;
  textarea.rows = 10;
  textarea.style.width = '100%';
  thisScriptParent.appendChild(textarea);

  return(textarea);
}

function resizeTextArea(textarea) {
  var limit = 10;/* これ以上は小さくならないようにする */
  var lines = textarea.value.split('\n').length;
  var tmp = lines;
  for (;;) {
    if (tmp == limit) {
      tmp = lines;
      break;
    } else if (tmp == 0) {
      tmp = limit;
      break;
    }
    tmp--;
  }
  textarea.rows = tmp;
}

function csv2table(text) {
  var escape = false;/* エスケープフラグ */
  var bol = true;/* 行頭フラグ */
  var i = 0;

  var textArray = text.split('');
  textArray.push(null);/* 番兵 */

  for (;;) {
    if (bol) {
      textArray.splice(i,0,'|');
      i++;
    }
    var c = textArray[i];
    if (c == null) {/* 番兵が見つかったので終了 */
      textArray.pop();
      break;
    }
    switch (c) {
      case ',':
        if (escape == false) {
          textArray[i] = '|';
        }
        i++;/* gotoでdefaultに飛びたい… */
        bol = false;
        break;
      case '\"':
        textArray.splice(i,1);
        if (escape) {
          if (textArray[i] == '\"') {/* エスケープされたダブルクオート */
            i++;
          } else {
            escape = false;
          }
        } else {
          escape = true;
        }
        bol = false;
        break;
      case '\r\n':
      case '\r':
      case '\n':
        if (escape) {/* セル内改行 */
          textArray[i] = '&br()';
          i++;
        } else {
          if (bol) {
            textArray.splice(i-1,2);
            i--;
          } else {
            textArray.splice(i,0,'|');
            i += 2;
          }
          bol = true;/* エスケープしてないときにのみ行頭フラグオン */
        }
        break;
      case '|':
        textArray[i] = '|';
      default:/* 置換する以外はその他の文字と一緒 */
        i++;
        bol = false;
        break;
    }
  }
  if (bol) {
    textArray.pop();
  } else {
    textArray.push('|');
  }

  return(textArray.join(''));
}

new function () {
var idTextArea = 'my_textarea';
var idConvert = 'my_buttonconvert';
var idUndo = 'my_buttonundo';

var prevText = "";
var isConverted = false;

var buttonConvert = createButton(idConvert,'変換');
var buttonUndo = createButton(idUndo,'元に戻す');
var textArea = createTextArea(idTextArea);

textArea.onkeyup = function () {
  resizeTextArea(textArea);
};

buttonConvert.onclick = function () {
  prevText = textArea.value;
  textArea.value = csv2table(prevText);
  isConverted = true;
  resizeTextArea(textArea);
};

buttonUndo.onclick = function () {
  if (isConverted) {
    textArea.value = prevText;
    resizeTextArea(textArea);
  }
};
};

*1:何でJavaScriptでやろうとしたかというと、ブラウザの別のタブで開いておけば編集時に便利だと思ったから