それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

EmEditorでブログを投稿する

EmEditorでブログ書きたい!!そんな欲望に流されました。


ブログかいてるとさ、ブラウザの戻るでフォームでうっかり消しちゃったり、アレコレ面倒なんだよね。コピーペーストも面倒だし、範囲選択が面倒だし。ソースコードのために、エスケープやら・・・面倒。


そこでだ。EmEditorのマクロが役に立つはずだ*1


以前から、EmEditorでXmlHTTPが動いてどうなんだ?と思ってて、良い機会だ。BloggerAPIをXMLRPC呼び出してみよう。

とりあえず、ココログで動作するようにしてみた。

EmEditor用マクロというより、WSH用スクリプトだろ。といわれたら否定できないが。

以下ソース

/**
* Emeditorでブログ投稿マクロ。
* 
* 対応XML-RPC API は Blogger APIです。
* BloggerAPIに対応しているブログなら投稿できるはずです
* 動作確認:
*         ニフティ ココログ
*         動作報告、不具合、改良をお待ちしています。
* 報告先
* @ref http://d.hatena.ne.jp/takuya_1st/20061013/1160753654
* @author takuya_1st
* @modified 06/10/14
* @version 1.0 RC1.0
*/
/////////////////////////////////////////////////
//////以下の項目を書き換えて使ってください     //
//ユーザーID                                   //
//パスワード                                   //
//エンドポイント(ブログの投稿用URL)            //
/////////////////////////////////////////////////
var pwd = "**********";
var usr = "**********";
var url = "http://app.f.cocolog-nifty.com/t/api";
/////////////////////////////////////////////////
/////////////////////////////////////////////////
var BlogInfo = {
  getUsersBlogs : ""
  +'<?xml version="1.0"?>'
  +'<methodCall>'
  +'   <methodName>blogger.getUsersBlogs</methodName>'
  +'   <params>'
  +'       <param><value><string></string></value></param>'
  +'       <param><value><string>'+usr+'</string></value></param>'
  +'       <param><value><string>'+pwd+'</string></value></param>'
  +'   </params>'
  +'</methodCall>',
  id      : "",
  blogURL : "",
  title   : "",
  check   : function (){
    var req = XMLHttpRequest();
    req.open("post", url , false);
    req.onreadystatechange = function() { 
        if (req.readyState==4){ 
            xDoc = req.responseXML;
            nodes = xDoc.getElementsByTagName("string");
            BlogInfo.title   = nodes[0].firstChild.nodeValue;
            BlogInfo.blogURL = nodes[1].firstChild.nodeValue;
            BlogInfo.id      = nodes[2].firstChild.nodeValue;
        }
    }
    req.setRequestHeader("Content-Type","text/xml; charset=UTF-8");
    req.send( this.getUsersBlogs );
  }
}
function XMLHttpRequest(){
    return new ActiveXObject("Microsoft.XMLHTTP");
}
var NewEntry = function( BlogId ){
  this.id = BlogId;
  this.xml = function(){
    newPost = ""
    +"<?xml version=\"1.0\"?>"
    +"<methodCall>"
    +"   <methodName>blogger.newPost</methodName>"
    +"   <params>"
    +"       <param><value><string>C6CE3FFB31</string></value></param>"
    +"       <param><value><string>"+this.id+"</string></value></param>"
    +"       <param><value><string>"+usr    +"</string></value></param>"
    +"       <param><value><string>"+pwd    +"</string></value></param>"
    +"       <param><value><string>"+this.content+"</string></value></param>"
    +"       <param><value><boolean>false</boolean></value></param> "
    +"   </params>"
    +"</methodCall>";
    return newPost;
  }
  this.content = "";
  this.post = function(){
    var req = XMLHttpRequest();
    req.open("post", url , false);
    req.onreadystatechange = function() { 
        if (req.readyState==4){ 
            xDoc = req.responseXML;
            if( xDoc.getElementsByTagName("string").length > 0 ){
              nodes = xDoc.getElementsByTagName("string");
              alert( "エントリを投稿しました\n"
                     +"エントリのIdは "
                     +nodes[0].firstChild.nodeValue
                     +" です" );
              var his = new PostHistory();
              his.push( nodes[0].firstChild.nodeValue );
              his.saveHistory();
            } else if( xDoc.getElementsByTagName("boolean").length > 0 
&& xDoc.getElementsByTagName("boolean")[0].firstChild.nodeValue ==1 )
            {
              alert( "エントリを更新しました\n");
            } else {
              alert("エントリの投稿に失敗しました");
            }
        }
    }
    req.setRequestHeader("Content-Type", "text/xml; charset=UTF-8");
    req.send( this.xml() );
  }
}
var EditEntry = function( PostID ){
  this.id = PostID;
  this.xml = function(){
    editPost = ""
    +"<?xml version=\"1.0\"?>"
    +"<methodCall>"
    +"   <methodName>blogger.editPost</methodName>"
    +"   <params>"
    +"       <param><value><string>C6CE3FFB3174106</string></value></param>"
    +"       <param><value><string>"+this.id+"</string></value></param>"
    +"       <param><value><string>"+usr+"</string></value></param>"
    +"       <param><value><string>"+pwd+"</string></value></param>"
    +"       <param><value><string>"+this.content+"</string></value></param>"
    +"       <param><value><boolean>false</boolean></value></param> "
    +"   </params>"
    +"</methodCall>"
    return editPost;
  }
}
EditEntry.prototype = new NewEntry;


//投稿履歴の管理
var PostHistory = function(){
  this.log = Window.ScriptFullName + ".history";
  this.loadFile = function( name ){
    var fs = new ActiveXObject("Scripting.FileSystemObject");
    var p =fs.OpenTextFile( name , 1, 
                      true,fs.TristateUseDefault );
    var str = p.ReadAll();
    p.Close();
    return str;
  }
  this.loadHistory = function (){
    try{
      var array_str = this.loadFile( this.log );
      var src = "this.history = [" + array_str + "];";
      eval(src);//英数字以外はエラーになる
    }catch(e){
      this.history = new Array();
    }
  }
  this.saveHistory = function(){
    var str = this.history.toString();
    var fs = new ActiveXObject("Scripting.FileSystemObject");
    var p  = fs.OpenTextFile( this.log , 2, 
                        true, fs.TristateUseDefault );
    p.write( str );
    p.Close();
  }
  this.push = function( code ){
    for( i in this.history ){
      if( i == code ){
        return;
      }
    }
    this.history.unshift(code);
    return;
  }
  
  //init
  this.loadHistory();
  this.HistoryMenu = function(){
    menu = Window.CreatePopupMenu();
    for( i in this.history ){
      menu.add( this.history[i], i+100 );
    }
    return menu;
  }
}

var EditMenu = function(){
  this.client = null;
  this.type = "new";
  this.menu = function(){
    history = new PostHistory();
    submenu = history.HistoryMenu();
    menu = Window.CreatePopupMenu();
    menu.Add("新規投稿", 1);
    menu.Add( "", 0, eeMenuSeparator );
    menu.AddPopup( "更新", submenu);
    return menu;
  }
  this.showMenu = function(){
    m = this.menu();
    result = m.Track(0);
    if( result >= 100 ){
      this.client = new EditEntry( m.GetText(result) );
    }else{
      BlogInfo.check();
      this.client = new NewEntry( BlogInfo.id );
    }
    this.postEntry();
  }
  this.postEntry = function(){
    ret = Window.confirm("BlogをPOSTします。\n"
                       +"準備は良いでしょうか?");
    if(ret){
      Window.document.selection.SelectAll();
      var entry = Window.document.selection.Text;
      Window.document.selection.Collapse();
      this.client.content = entry;
      this.client.post();
    }
  }

}

mainMenu = new EditMenu();
mainMenu.showMenu();


これで、仕事さぼって、ブログ更新しててもバレないよね(違

PythonAtomPPとかXMLRPCしてるコードと、HTTPのヘッダをキャプチャを参考にしながら、JavaScriptで書き直してみた。2時間ほどでパパっと、書いたからバグあるかも。また週末にデバッグしつつバージョンアップしていくとしましょう。EmEditorPythonマクロのままでもよかったけれど、Pythonはまだまだマイナーなので。



しかし、エディタからXmlHttpは便利だな。

モードレスダイアログがあれば、スペルチェックも出来そうですね。
URL上にカーソルきたら、iframeを描画するとか。


WebDAVと連携したら、subversionそのままいじれるかも。


[ctrl+S]でFTP送信とか出来るとよいな。EmFTPをActiveXObjectで呼べたらできるなぁ。


ActiveXで使えるFTPライブラリないのかな。[Ctrl+S]で保存&FTP更新&IEリロードを、1アクションで呼べたら最高。XmlHttpとAtomPPFTPライブラリがなくともサーバへへPushできるから、意外と実現可能かも?

*1:断然、EmEditor派ですから