リンク作成するTextwellアクションをMarkdown対応にしました ReView

まだ実験運用中。


TextwellのBlogQuoteをYouTubeやTwitterに対応させてみた(森杉版)
文脈に応じて変化する。Textwellの内蔵ブラウザを使い、ブログやサイトの紹介をするアクション。検索エンジンをYahooに替えてみました。Yahooだとリアルタイム検索のときTwitterの検索結果...
BlogQuote。Textwellで一番使っているアクションかもしれません。モブログのベースにあるのがリンクタグの作成。キーワードでインターネットを検索し、そこへのHTMLタグを生成する。気になるサイトを見つけたら、それについての記事がすぐ書けます。


Textwell 1.3.7
分類: 仕事効率化,ユーティリティ
価格: \300 (Sociomedia)

今まではHTMLで書くことを前提としていたのですが、リストや表にMarkdownを使うようになって、従来のままでは使いづらいので新しいアクションを組んでみました。
Import Textwell ActionReView



カーソル行をキーワードと見なし、Googleで検索します。キーワードがhttpで始まる場合は、直接そのサイトを内蔵ブラウザで開きます。Textwellのブラウザは一見「戻る」が無いように見えますが、左端から中央に向けてスワイプすると移動できます。これ、便利。



内蔵ブラウザを閉じるとき、メニューが出てきます。「Link」はMarkdown形式のリンク。「Card」はブログカードもどきです。サムネ付きの解説カードが生成されます。対象のページがYouTubeニコニコ動画Twitterのときは専用の表示形式に変わります。「Safari」はSafariで該当ページを開く。「HTML」はサイトのソースを読み込みます。



で、今回の目玉。カーソル行が空行のとき、Markdownプレビューになります。表示変換のためネット接続が前提となります。styleはソース内のCSSでカスタマイズ可能。
そしてプレビューでは、二度タップすると編集モードになります。プレビューのまま、文字の追加や削除が可能になります。内蔵ブラウザを閉じるとき「HTML」を選べば、HTMLタグに変換して保存します。ただし、微妙な変換ミスがあるのでチェックはお忘れなく。

123|456|789
-|-
abc|def|ghi

上記の書式で表が作れるので、Markdownは重宝します。しかも、プレビューで表の項目を書き加えると、横幅が自動で変化。リストも改行すると、新しいリストが次の行に出てきます。表示変換に使っているmarked.jsがとても柔軟。Markdown表示を編集できるのは新鮮で、コンテキストメニューからボールドやイタリックを選んでも反映します。


【ソース】

<meta name=viewport content=initial-scale=1>
<title>ReView</title><div id=wine></div>
<script src=https://raw.github.com/chjj/marked/master/lib/marked.js></script>
<script>

// リンクカードのスタイル(${url}:アドレス ${title}:題名 ${text}:引用)
CARD="<div style='color:gray;background:white;border:1px solid lightgray;width:500;padding:10;border-radius:5;'><a href='${url}' target='_blank' style='color:darkblue;text-decoration:none;font-weight:bold;'><img align=right src='http://capture.heartrails.com/120x120/?${url}' style='border-radius:5;margin:5;box-shadow:1px 2px 3px gray;'>${title}</a> <a href='http://b.hatena.ne.jp/entry/${url}' target='_blank'><img src='http://b.hatena.ne.jp/entry/image/${url}'></a><br><span style=font-size:80%;>${text}...</span><br style=clear:both;></div>\n";

// タイトルから省く文字列
BLOG=" - W&R : Jazzと読書の日々";

// サーチエンジンのURLアドレス
SEARCH="http://www.google.co.jp/search?q="+T.current;

// スクリプト本体
T.closelets([
  {title: "Link",
    fn: function(arg){
        url=location.href;
        title=document.title.replace(arg.blog,"");
        text=getSelection().toString();
        if(text){
          link="> "+text+"  \nvia ["+title+"]("+url+")\n\n";
        }else{
          link="- ["+title+"]("+url+")  \n";
        }
        T("replaceCurrent",{text:link})},
    arg:{blog:BLOG}},
  {title: "Card",
    fn: function(arg){
        url=location.href;
        switch(true){
          case /^https?:\/\/m.youtube.com/.test(url):
            url=url.replace(/m\./,"").split("&desktop")[0];
            id=url.split("v=")[1];
            title=document.title.replace(/- YouTube/,"");
            link="<iframe width=500 height=280 src=\"http://www.youtube.com/embed/"+id+"\" frameborder=0 allowfullscreen></iframe>\n";
            break;
          case /^https?:\/\/mobile.twitter.com/.test(url) && /status/.test(url):
            url=url.replace(/mobile/,"www");
            s=document.getElementsByClassName("tweet")[0];
            tweet=s.getElementsByClassName("dir-ltr")[0].innerHTML;
            fullname=s.getElementsByClassName("full-name")[0].innerText;
            name=s.getElementsByClassName("screen-name")[0].innerText.substr(1);
            time=s.getElementsByClassName("timestamp-row")[0].innerText;
            link="<blockquote class='twitter-tweet'><p>"+tweet+"</p>&mdash; "+fullname+" (@"+name+") <a href='"+url+"'>"+time+"</a></blockquote><scr"+"ipt async src='http://platform.twitter.com/widgets.js' charset='utf-8'></scr"+"ipt>\n";
            break;
          case /^https?:\/\/sp.nicovideo.jp\/watch\/s?m?[0-9]+/.test(url):
            s=url.match(/s?m?[0-9]+/);
            link="<iframe src=\"http://ext.nicovideo.jp/thumb/"+s+"\"><a href=\"http://www.nicovideo.jp/watch/"+s+"\">"+document.title+"</a></iframe>\n";
            break;
          default:
            title=document.title.replace(arg.blog,"");
            text=getSelection().toString();
            if(!text){
              p=document.getElementsByTagName("p");
              for(k in p) text+=p[k].innerText;
            }
            text=text.replace(/\n/g,"").replace(/undefined/g,"").substr(0,100);
            link=arg.card.replace(/\${url}/g,url).replace(/\${title}/g,title).replace(/\${text}/g,text);
        }
        T("replaceCurrent",{text:link})},
    arg:{blog:BLOG,card:CARD}},
  {title: "Safari",
    fn: function(){
        T(location.href)}},
  {title: "HTML",
    fn: function(){
        if(/^http/.test(location)){
            text=document.documentElement.innerHTML;
        }else{
            text=wine.innerHTML.replace(/\n/g,"<br />").replace(/<\/li><li>/g,"</li><br /><li>").replace(/<p>(.*?)<\/p>/g,"<br />$1").replace(/<div>(.*?)<\/div>/g,"<br />$1").replace(/&nbsp;/g,"  <br />").replace(/<br \/>/g,"\n");
        }
       T("replace",{text:text});
        }
  }],
function(){
    s=T.current;
    if(s){
      location=(/^https?:/.test(s))? s:SEARCH;
    }else{
      text=T.text || "- list";
      wine.innerHTML=marked(text);
      wine.onclick=function(){wine.contentEditable=true;};
    }
})

</script>
<style>
body{font-family:Hiragino Mincho ProN;}
h1,h2,h3,h4,h5,h6{font-family:Hiragino Kaku Gothic ProN;}
h1{font-size:150%;border-top:3px solid #aaa;}
h2{font-size:130%;border-top:3px solid #eee;}
h3{font-weight:bold;font-size:110%;color:#551;}
h4{font-weight:bold;font-size:100%;color:#551;}
table{border-left:1px gray solid;border-top:1px gray solid;border-spacing:0;}
th{background:ivory;border-right:1px gray solid;border-bottom:1px gray solid;}
td{border-right:1px gray solid;border-bottom:1px gray solid;}
</style>