• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JavaScriptでpreタグで囲まれたソースコードをtextareaにコピーする
#navi(../)
RIGHT:Posted by [[aterai]] at 2012-07-25
* JavaScriptでpreタグで囲まれたソースコードをtextareaにコピーする [#c270c144]

#contents

** 概要 [#f8b8ce13]
以下、Clipboard API を使い、preタグで囲まれたソースコードを簡単にコピーする方法のテスト。
以下、preタグで囲まれたソースコードを簡単にコピーする方法をテスト
- オリジナルは[http://userscripts.org/scripts/show/11438 pre to textarea for Greasemonkey]
-- preタグをtextareaにしてコピーするという方法を引用
-- preタグをtextareaにして簡単にコピーできるようにする

-- [http://d.hatena.ne.jp/aterai/20080725/1216954636 preタグで囲まれたソースコードを簡単にコピーする方法 - てんぷらメモ@はてな]から移動の予定
- ユーザーJavaScriptフォルダにコピー [http://terai.xrea.jp/data/javascript/pre2textarea.user.js pre2textarea.user.js]
- pre要素をダブルクリックすると、内容がtextareaに変換される
- Clipboard API が使用できる ChromeやOpera 12.50 の場合、コピー、カットすると元のpreに戻る

** ソースコード [#v391264a]
#code{{
// ==UserScript==
// @name        <pre> to <textarea>
// @namespace   http://terai.xrea.jp/
// @include     http://*
// @exclude     http://*.google.*
// @description pre <-> textarea
// @version     1.0.4
// @version     1.0.6
// ==/UserScript==
// Double Click <PRE>, open <TEXTAREA>
// Test: Clipboard API and events

// Fork of <pre> to <textarea> http://userscripts.org/scripts/show/11438
// original author is Sybian http://d.hatena.ne.jp/Sybian/

// _@name        <pre> to <textarea>
// _@namespace   http://d.hatena.ne.jp/Sybian/
// _@include     http://*
// _@description pre <-> textarea
// _@version     1.0.2
// Ctrl+Click <PRE>, become <TEXTAREA>
// ESC or Ctrl+[ or Ctrl+Shift+Click in <TEXTAREA>, restore <PRE>

(function() {
  function pre2text(pre) {
    var div = document.createElement('div'); //dummy div
    div.innerHTML = pre.innerHTML.replace(/<br[ \/]*>/ig, '\n').replace(/<.*?>/mg, '');
    return div.childNodes[0].nodeValue.replace(/\xA0/g, ' '); //replace &nbsp;
  }
  function cleanup(tx) {
    tx.parentNode.replaceChild(tx.originalPre, tx);
  }
  var listener = function(e) {
    var tx = document.createElement("textarea"),
      rect = this.getBoundingClientRect(),
        div = document.createElement("div"),
    closeListener = function(e) {
      switch(e.type) {
      case 'copy':
      case 'cut':
        var dataTransfer = e.clipboardData;
        dataTransfer.items.add(this.innerText, 'text/plain');
        this.parentNode.replaceChild(this.originalPre, this);
        e.preventDefault();
      case 'keydown':
        if(e.keyCode=="27") { // ESC
          cleanup(this);
        }
        break;
      case 'dblclick':
        cleanup(this);
      }
    };
    tx.addEventListener("keydown",  closeListener, false);
    tx.addEventListener("dblclick", closeListener, false);

    //tx.style.width  = parseInt(getComputedStyle(this,"").width).toString()+"px";
    tx.style.width  = rect.width+"px";
    tx.style.height = (rect.height>240 ? 240 : rect.height) + "px";

    tx.originalPre  = this;

// ctrl-c 無しでコピーできるかもと思ったけど…
//       var event = new ClipboardEvent('copy', { bubbles: true, cancelable: true } );
//       var dataTransfer = event.clipboardData;
//       dataTransfer.items.add("aaaaaaaaaaaaaaaaaaaaaaa", "text/plain");
//       //this.parentNode.replaceChild(this.originalPre, this);
//       document.dispatchEvent(event);
//       //event.preventDefault();

    tx.addEventListener("keydown", function(e) {
      if(e.keyCode=="27") { // ESC
        this.parentNode.replaceChild(this.originalPre, this);
      }
    }, false);
    tx.addEventListener("dblclick", function(e) {
      this.parentNode.replaceChild(this.originalPre, this);
    }, false);
    div.innerHTML = this.innerHTML.replace(/<br[ \/]*>/ig, "\n").replace(/<.*?>/mg, "");
    tx.value = div.childNodes[0].nodeValue.replace(/\xA0/g, ' ');
    tx.value = pre2text(this);
    this.tx = tx;
    this.parentNode.replaceChild(this.tx, this);
    this.tx.focus();
    this.tx.select();
    //document.execCommand("copy", false, null); 
    //document.execCommand("SaveAs", false, getFileName(tx.value));

    tx.addEventListener('copy', closeListener, false);
    tx.addEventListener('cut',  closeListener, false);
  },
  pre = document.getElementsByTagName("pre"),
  i = 0, len = pre.length;
  for(; i<len; i++) {
    pre[i].addEventListener("dblclick", listener, false);
  }
}());
}}

** Bookmarklet版 [#rfe513cb]
- 名前
-- pre2textarea
- アドレス(YUI Compressor で圧縮)
 javascript:(function(){var d=document.getElementsByTagName("pre"),b=0,a=d.length,c=function(g){var f=document.createElement("textarea"),h=document.createElement("div");f.style.width=parseInt(getComputedStyle(this,"").width).toString()+"px";f.style.height="240px";f.originalPre=this;f.addEventListener("keydown",function(i){if(i.keyCode=="27"){this.parentNode.replaceChild(this.originalPre,this)}},false);f.addEventListener("dblclick",function(i){this.parentNode.replaceChild(this.originalPre,this)},false);h.innerHTML=this.innerHTML.replace(/<br[ \/]*>/ig,"\n").replace(/<.*?>/mg,"");f.value=h.childNodes[0].nodeValue.replace(/\xA0/g," ");this.tx=f;this.parentNode.replaceChild(this.tx,this);this.tx.focus();this.tx.select()};for(;b<a;b++){d[b].addEventListener("dblclick",c,false)}}());
 javascript:(function(){function d(g){var h=document.createElement("div");h.innerHTML=g.innerHTML.replace(/<br[ \/]*>/ig,"\n").replace(/<.*?>/mg,"");return h.childNodes[0].nodeValue.replace(/\xA0/g," ")}function c(g){g.parentNode.replaceChild(g.originalPre,g)}var e=function(j){var g=document.createElement("textarea"),h=this.getBoundingClientRect(),i=function(k){switch(k.type){case"keydown":if(k.keyCode=="27"){c(this)}break;case"dblclick":c(this)}};g.addEventListener("keydown",i,false);g.addEventListener("dblclick",i,false);g.style.width=h.width+"px";g.style.height=(h.height>240?240:h.height)+"px";g.originalPre=this;g.value=d(this);this.tx=g;this.parentNode.replaceChild(this.tx,this);this.tx.focus();this.tx.select()},f=document.getElementsByTagName("pre"),b=0,a=f.length;for(;b<a;b++){f[b].addEventListener("dblclick",e,false)}}());

** メモ: ローカルにドロップして保存(ファイル名付き) [#ua1c0148]
- [http://blog.futuresoftware.jp/?p=101 FutureSoftware開発日誌 Ver2.0 » HTML5のドラッグ&ドロップによるファイル保存]
-- Chromeでのみ動作

#code{{
function makeDraggableDownloadLink(text, mimeType, fileName) {
  var blob = new Blob([text], {type: mimeType}),
         a = document.createElement('a');
  a.appendChild(document.createTextNode("download"));
  a.setAttribute('download', fileName);
  if(window.createObjectURL) {
    a.href = window.createObjectURL(blob);
  }else if(window.createBlobURL) {
    a.href = window.createBlobURL(blob);
  }else if(window.URL && window.URL.createObjectURL) {
    a.href = window.URL.createObjectURL(blob);
  }else if(window.webkitURL && window.webkitURL.createObjectURL) {
    a.href = window.webkitURL.createObjectURL(blob);
  }
  window.URL = window.URL || window.webkitURL;
  a.href = window.URL.createObjectURL(blob);
  a.addEventListener("dragstart", function(e) {
    e.dataTransfer.setData("DownloadURL", [mimeType,fileName,this.href].join(':'));
  }, false);
  return a;
}
}}

** メモ: 直接クリップボードにコピー [#baa5f2ba]
- [http://javascripter.hatenablog.com/entry/20091230/1262191418 クリップボードにコピー - 素人がプログラミングを勉強していたブログ]
- [http://fukayatsu.github.com/blog/2012/07/22/markdown-linker-for-chrome/ markdown形式でリンクをクリップボードにコピーするchrome-extension作った - fukayatsu.dev()]

 //ie, chrome-extension???
 document.execCommand("copy");

** メモ: Clipboard API [#q55b37f8]
- Clipboard API が使用できる ChromeやOpera 12.50 の場合、コピー、カットすると自動的に元のpreに戻すことが可能

#code{{
var listener = function(e) {
  var tx = document.createElement("textarea"),
    rect = this.getBoundingClientRect(),
    name, ret,
  closeListener = function(e) {
    switch(e.type) {
    case 'copy':
    case 'cut':
      var dataTransfer = e.clipboardData;
      dataTransfer.items.add(this.innerText, 'text/plain');
      cleanup(this);
      e.preventDefault();
      break;
    case 'keydown':
      if(e.keyCode=="27") { // ESC
        cleanup(this);
      }
      break;
    case 'dblclick':
      cleanup(this);
    }
  };
  tx.addEventListener('copy',     closeListener, false);
  tx.addEventListener('cut',      closeListener, false);
  tx.addEventListener("keydown",  closeListener, false);
  tx.addEventListener("dblclick", closeListener, false);
//......
}}

** 参考リンク [#led8cfb3]
- [http://userscripts.org/scripts/show/11438 pre to textarea for Greasemonkey]
-- http://sybian99.googlepages.com/preToTextarea.user.js
-- %%[http://d.hatena.ne.jp/Sybian/20070815/p2 preをtextareaに - Sybianの日記]%%
-- %%via: [http://kawatarou.info/note/opera/opera_userjs.htm Shishimushi - User JavaScript]%%
- [http://userscripts.org/scripts/review/73497 Source for "pre select" - Userscripts.org]
- [http://www.w3.org/TR/clipboard-apis/ Clipboard API and events]
- [http://my.opera.com/desktopteam/blog/2012/07/06/marlin-1250-swim Opera Desktop Team - First bite of 12.50 ‘Marlin’: Clipboard API, redesigned key event handling, -webkit- CSS, and Notification Center]
- [http://domes.lingua.heliohost.org/webapi/intro-webplatform1.html#section-1-3 クリップボード API - Introduction to the Web Platform - DOM ECMAScripting]

** コメント [#vf0fac74]
#comment