2010年12月31日金曜日

Pukiwikiでブックマークレットを紹介できるようにする

 通常のPukiwikiのページに「javascript:スクリプト」と直接書いてもリンクにならないし、[[ブックマークレットタイトル>javascript:スクリプト]]と書いてもjavascriptを実行できるリンクにはならない。
 誰でも自由いページを編集できるpukiwikiでデフォルトでそんな設定になっていたら、XSS対策も何もあったものではないからだ。自分の管理するpukiwikiで「file://」をリンクとして扱のと同じよう設定を入れ、「javascript:スクリプト」や「[[ブックマークレットタイトル>javascript:スクリプト]]」をリンクとして扱うようにすることはできるが、インターネットに公開しているサイトではリスクが大きくてする気にもならないだろう。
 悪意のある訪問者が、既存のリンクをjavascriptに書き換え、
「一見通常通りページを開くが、10秒後にスパムサイトがポップアップしてくる」とか、
「リンクをクリックするとスパムURLがついたコメントがPOSTされる」
なんてことを全ページにされたら、気が滅入るどころの話ではない。

とはいえPukiwikiページでブックマークレットを紹介したいニーズもあるので、比較的安全な「ブックマークレット用のリンクを別サイトに表示する」という方法を考えてみた。

 ブックマークレットを紹介したい人は、上記サイトにタイトル1行のスクリプトを入力し、「PukiWiki用リンクの作成」ボタンを押すと[[タイトル>上記サイトのURL?パラメータ]]のようなブランケット形式のリンクを作成し、それを自分のページに張ることで、読者にブックマークレットのリンクを示すことができる。
 読者が張られたリンクをクリックすると、紹介したいブックマークレットのリンクが上記サイトに表示されるというものだ。

読者がリンクをクリックすると下記のようなページが表示される。
このページで実際のブックマークレットのリンクと、確認用に整形されたスクリプトと、お気に入りに登録する方法を確認することができる。

ブックマークレットのリンクの下にはリンク中のjavascriptを見やすく整形したものが表示されるので、読者はその内容を確認したのちに登録することが可能となります。

javascriptを登録できて、このサイト自体のXSSのリスクはどうかという点を心配されるかも知れませんが、このサイトは上記の目的のみで使用されており、置かれているファイルもindex.htmlとjavascript整形用のbeautify.jsの2つだけとなっており、このサイトの改変などを行うことはできません。また、悪意のあるスクリプトを表示させることは可能ですが、このサイトでスクリプトを表示しても、ユーザがリンクをクリックしなければjavascriptは実行されないこと、ユーザは実行またはお気に入りに登録する前に、整形されたスクリプトの表示を確認できることで、リスクの低減になるかと考えています。

サイトの使い方

(1)事前にブックマークレットとするjavascriptを準備し、お気に入りのタイトルを決めておきます。
(2)サイト http://bookmarklet.dip.jp/ を開きます。
(3)「お気に入りタイトル」と「ブックマークレット用1行のjavascript」を入力し、「PukiWiki用リンク作成」を押す
※ここでjavascriptの簡単なチェックを行い、問題があればメッセージが表示されます。
(4)「確認用整形済みコード」で入力したjavascriptが意図したものか確認し、問題がなければ、「pukiwiki用リンク」に表示されたリンク文字列をコピーします。
(5)編集したいpukiwikiページを編集モードで開き、張り付けて使ってください。
(6)張り付けたリンクが正しく動くか確認する

2010年12月30日木曜日

Javascriptで、Javascriptが文法的に正しいか確認する

以下のような関数で、引数の文字列がJavascriptとして正しいか確認できます。
正しい場合false、何か間違いがあれば簡単なエラーメッセージを返します。

// スクリプトに文法エラーがあるか
function checkScript(script) {
  var ret = false;
  try{
    eval('if(0){'+script+'}');
  } catch(e){
    ret = e.message;
  }
  return ret;
}
上記関数は、スクリプトを実行しないように「if(0){}」を付けて実行し、エラーがあるかチェックします。渡されたスクリプトは結局実行されないので、ランタイムエラーの確認はできません。

Textareaでデータとしてjavascriptを入力させたい時などに役に立つかと思います。

ブラウザをFireFoxに限れば、少しコードは複雑になりますが、エラーの発生した行番号も取得することが可能です。少々トリッキーですが、tryの前でエラーオブジェクトをnewしてその行番号を調べることにより、evalの行番号を求め、catch中でeのエラー行の行番号から、evalの行番号を引いた結果に1を足したものが、入力スクリプトのエラー行になります。残念ながらIEではエラーオブジェクトから行番号が取得できないようです。

// スクリプトに文法エラーがあるか(行番号付き)
function checkScript(scr) {
 var ret = false;
 if (!window._lineEval) _lineEval=(new Error()).lineNumber+2 || -1;
 try{
  eval('if(0){'+scr+'}');
 } catch(e){
  ret = {
   line:(e.lineNumber?e.lineNumber-_lineEval+1:-1),
   msg:e.message
  };
 }
 return ret;
}

Javascriptで、Javascriptを整形する

下記のページでJavascriptを整形できる。

http://jsbeautifier.org/

Javascirptを整形してくれるサイトはよくあるが、このサイトはそれをjavascriptで行っている。
サイトに行くといろいろなjavascriptがscriptタグで読み込まれるが、

http://jsbeautifier.org/beautify.js」が整形のエンジンのようだ。

ソースのコメントを見てみると、使いたければ自由に使っていいと書いてある
JS Beautifier
---------------


Written by Einar Lielmanis,
http://jsbeautifier.org/

Originally converted to javascript by Vital,

You are free to use this in any way you want, in case you find this useful or working for you.

これは役に立ちそうだ。ソースのコメントには続けて使い方が書いてある
Usage:
js_beautify(js_source_text);
js_beautify(js_source_text, options);

The options are:
indent_size (default 4) — indentation size,
indent_char (default space) — character to indent with,
preserve_newlines (default true) — whether existing line breaks should be preserved,
preserve_max_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk,
indent_level (default 0) — initial indentation level, you probably won't need this ever,

space_after_anon_function (default false) — if true, then space is added between "function ()"
(jslint is happy about this); if false, then the common "function()" output is used.
braces_on_own_line (default false) - ANSI / Allman brace style, each opening/closing brace gets its own line.

e.g

js_beautify(js_source_text, {indent_size: 1, indent_char: '\t'});

とりあえずは、そのまま呼んでみればいいのね。さっそく使ってみよう。

2010年12月22日水曜日

ブックマークレットでpukiwikiのtracker_listを並べ替え可能にする

ブックマークレットでpukiwikiのtracker_listを並べ替え可能にする

ブックマークレットでpukiwkiの編集中のDiffをとるサンプルを先日示したが、同様に表題のようなサンプルを作成してみた。前回同様、EUC/utf-8、PukiWiki/PukiWikiPlusなどのターゲットの環境を選ばないようにしています。

このブックマークレットでできること

  • pukiwiki及びPlusなどの派生版で、tracker_list、bugtrack_listで表示される表を、ExtJSのGridPanelで置き換えます
  • 置き換えられたGridPanelは、ヘッダ部をクリックすることにより、サーバと通信することなくソート可能です
  • 同じくヘッダ部を操作することにより、列の表示/非表示切り替え、列幅の調整、列の表示位置変更等が可能です
  • 「ページ名」というヘッダを持つ列は、単に文字列によるソートでなく、ページ名に現れる番号を意識して並べ替えられるので、「BugTrack1/100」は「BugTrack1/22」や「BugTrack1/9」よりも後に表示されます((並べ替えの際、スラッシュで区切られる文字列が数字のみである場合、5ケタになるよう前ゼロを付けたのちに比較されます。「BugTrack1/22」は「BugTrack1/00022」に、「ほげ/2/3」は「ほげ/00002/00003」に、比較時に内部的に変換されます))
サンプルの使い方
  • 1.まず下記のURLをブックマーク(IEはお気に入り)に登録します。名前はなんでもいいです。(たとえば「pukiwiki Table」)
javascript:(function(){var%20e=document.createElement('script');e.charset='utf-8';e.src='http://www123.ddo.jp/tools/tracker00.js';document.body.appendChild(e);})()
  • 2.いつも使っているpukiwikiのサイトのBugTrackの画面等を開き、登録したブックマーク(お気に入り)を選びます。
  • 3.すると、上記のようにテーブルがExtJSのgridに変換され、ソートなどの操作ができるようになります

ブックマークレットとスクリプトの解説

上記のブックマークは改行とインデントを入れ読みやすくすると
 javascript:(
     function(){
         var e=document.createElement('script');
         e.charset='utf-8';
         e.src='http://www123.ddo.jp/tools/tracker00.js';
         document.body.appendChild(e);
     }
 )()
見ての通り、これでwww123.ddo.jpからtracker00.jsというスクリプトを読み込んで実行しているわけです。

読み込んだtracker00.jsは下記のような動作をします。

  • ExtJS 3.3.1を動かすために必要なライブラリやスタイルシートを読み込む
  • 最初のtrの要素がtdでなくすべてthであるtableタグについて下記を行います
  •  ヘッダー部の解析とデータストアの作成
  • データの読み込みとGridPanelの描画

下記ページにて確認をしています
セキュリティについて
 
edit00.jsと同様なセキュリティのリスクがあります。理解したうえでお試しください。 

tracker00.jsソースコード 


    // tracker.js v 00 -- pukiwikiのTrackerの機能アップ
    // Bookmarklet example
    //   javascript:(function(){var%20e=document.createElement('script');e.charset='utf-8';e.src='http://www123.ddo.jp/tools/tracker00.js?';document.body.appendChild(e);})()
    // Copyright(c) 2010 mashiki
    // License: GPL version 3
    (function(){
        var cf='http://extjs.cachefly.net/ext-3.3.1/';
        addEl('style', {type:'text/css',innerHTML:'td {background-color:transparent}'});
        addEl('link',{rel:'stylesheet',type:'text/css',href:cf+'resources/css/ext-all.css'});
        addEl('script',{src:cf+'adapter/ext/ext-base.js'});
        addEl('script',{src:cf+'ext-all.js'});
        addEl('script',{src:cf+'examples/ux/TableGrid.js'});
       
        function addEl(tag, cfg) {
            var e=document.createElement(tag);
            for (var key in cfg) e[key]=cfg[key];
            document.body.appendChild(e);
        }
        var id=setInterval(function(){

            if(window.Ext && Ext.MessageBox){
                clearInterval(id);

                var ce = Ext.select('table');
                ce.each(function(tbl){
                    var tr0 = tbl.select('tr', true).first();
                    var hdrs = tr0.query('th');
                   
                    if (hdrs.length>0 && tr0.query("td").length===0 ) {
                        convertToGrid(tbl, hdrs);
                    }
                },true);
            }
        },100);

        function convertToGrid(tbl, hdrs) {
            var ct = tbl.insertSibling(),
                flds = [{name:'no'}],
                cols = [];

            for (var i=0, h; h=hdrs[i]; ++i) {
                var text = h.innerHTML.replace(/]+>(.+?)<\/a>/gi,'$1')
                                    .replace(/\s*(:?↑|↓)\([\d]+\)/,''),
                    name = 'tcol-' + i;
                var fld = {
                    name: name,
                    mapping: 'td:nth('+ (i+1) +')/@innerHTML'
                };
                if (text==='ページ名') {
                    fld.sortType = function(val) {
                        var parts = val.replace(/]+>(.+?)<\/a>/gi,'$1').split("/");
                        for (var i=0,p; p=parts[i];++i) {
                            if (p.match(/^[\d]+$/)) {
                                parts[i]=String.leftPad(p, 5, '0')
                            }
                        }
                        return parts.join('/');
                    }
                }
                flds.push(fld);

                cols.push({
                    header: text,
                    dataIndex: name,
                    width: h.offsetWidth,
                    tooltip: h.title,
                    align: 'left',
                    sortable: true
                });
            }

            var ds = new Ext.data.Store({
                reader: new Ext.data.XmlReader({
                    record: 'tbody tr'
                }, flds)
            });

            ds.loadData(tbl.dom);
            var r = ds.getAt(0);
            if (!(r.get(0))) ds.remove(r);
            i = 0;
            ds.each(function(r){r.set('no',++i)});
            ds.commitChanges();

            ct.setWidth(tbl.getWidth());

            tbl.remove();

            var grid = new Ext.grid.GridPanel({
                stripeRows: true,
                'ds': ds,
                'cm': new Ext.grid.ColumnModel(cols),
                'sm': new Ext.grid.RowSelectionModel(),
                autoHeight: true,
                autoWidth: false,
                renderTo:ct
            });
        }

    })();


2010年12月20日月曜日

ブックマークレットってすごい可能性を持っている と思う

ブックマークレット
Wikipedia 
http://ja.wikipedia.org/wiki/%E3%83%96%E3%83%83%E3%82%AF%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%AC%E3%83%83%E3%83%88

ブックマークレットって実は最近まで注目していなかったのですが、フォームへの半自動入力で使えないかと、知り合いに頼まれて少し調べてみました。

ブックマークレットはブラウザのURL入力欄に書ける1行のjavascriptをブックマークしたものです。たった1行ですが、javascriptなのでDOM操作や、ajax通信などもできます。DOM操作ができるということは、ボタンを表示したり、scriptタグを生成してjavascriptのライブラリを読み込んだり、linkタグを生成してスタイルシートを読み込んだりもできるわけです。
「自分が管理していないが、よく利用しているちょっと不便なサイト」で、ブラウザ上にあるコンテンツを利用していろいろなことができます。

  • フォームの入力を自動で行う
  • 入力のインターフェースを変えたり、入力のチェックを行う
  • 自分の見やすいように表示を並べ替える
  • 他のサイトの情報と合わせて加工して表示する

「javascriptを使えばそんなのできるの当たり前じゃん」と思われますが、自分の管理しているわけでないwebサイトのカスタマイズができちゃうわけです。

サンプルとして、pukiwikiのページの編集で、編集中文書の編集前との差分をExtJSのWindowで表示するブックマークレットを作成してみました。これは標準のpukiwikiにはない機能です。

サンプルの使い方

  • 1.まず下記のURL?をブックマーク(IEはお気に入り)に登録します。名前はなんでもいいです。(たとえば「pukiwiki Diff」)
javascript:(function(){var%20e=document.createElement('script');e.charset='utf-8';e.src='http://www123.ddo.jp/tools/edit00.js';document.body.appendChild(e);})()
  • 2.いつも使っているpukiwikiのサイトのページを編集中に、登録したブックマーク(お気に入り)を選びます。
  • 3.すると、「ページの更新」ボタンの横に「Diff」ボタンが表示されます。
  • 4.「Diff」ボタンを押すと、テキストエリアに何も変更を加えていない場合、「変更箇所はありません」とメッセージが表示され、変更があった場合、次のように変更点が表示されます
    ※このブックマークレットサンプルは、本家のPukiWikiや、派生版のPukiWikiPlus等で、使用可能です。utf-8、EUCのどちらで構築されていても動くと思います。

      ブックマークレットとスクリプトの解説

      見やすさのために改行とインデントを入れると以下のようになります
       javascript:(
           function(){
               var e=document.createElement('script');
               e.charset='utf-8';
               e.src='http://www123.ddo.jp/tools/edit00.js';
               document.body.appendChild(e);
           }
       )()
      見た通り、これでwww123.ddo.jpからedit.jsというスクリプトを読み込んで実行しているわけです。

      読み込んだedit00.jsは下記のような動作をします。

      • ExtJS 3.3.1を動かすために必要なライブラリやスタイルシートを読み込む
      • 'write'という名前のサブミットボタン(ページの更新)があることを確認する
      • 'wirte'ボタンの左に、押されたらdo_diffを実行するような「Diff」ボタンを生成する。
      • do_diffはフォーム中の'original'と'msg'という、フィールドの値を比べ結果を表示する

      edit00.jsのソースは最後に添付します。まあ、http://www123.ddo.jp/tools/edit00.js をDLしソースを確認すればその通りなのですが。

      セキュリティについて

      • http://www123.ddo.jp/tools/edit00.js を信じない方は賢明です。サイトが誰かに乗っ取られ、悪意のあるロジックを混入される恐れはいつでもあります。edit00.jsを信頼できるサイトにコピーして使用いただいて結構です。GPL ver3としますので、適当に改変してください。便利な機能を追加したなら公開していただくとありがたいです。
      • edit00.dsで、cacheflyのサイトからExtJSのライブラリを読み込んでいます。より安全に使いたければ、上記と同様に自分の制御下のサーバに入れることを検討してください。
      edit00.js ソースコード

          // edit.js v 00 -- pukiwikiの編集を支援
          // Bookmarklet example
          //   javascript:(function(){var%20e=document.createElement('script');e.charset='utf-8';e.src='http://www123.ddo.jp/tools/edit00.js?';document.body.appendChild(e);})()
          // Copyright(c) 2010 mashiki
          // License: GPL version 3
          (function(){
              var cf='http://extjs.cachefly.net/ext-3.3.1/';
              addEl('style', {type:'text/css',innerHTML:'td {background-color:transparent}'});
              addEl('link',{rel:'stylesheet',type:'text/css',href:cf+'resources/css/ext-all.css'});
              addEl('script',{src:cf+'adapter/ext/ext-base.js'});
              addEl('script',{src:cf+'ext-all.js'});
              function addEl(tag, cfg) {
                  var e=document.createElement(tag);
                  for (var key in cfg) e[key]=cfg[key];
                  document.body.appendChild(e);
              }
              var id=setInterval(function(){
                  if(window.Ext && Ext.MessageBox){
                      clearInterval(id);
                      var pv = document.getElementsByName('write');
                      if (pv[0]) {
                          pv = pv[0];
                      } else {
                          Ext.Msg.alert(
                              'pukiwiki編集支援機能',
                              '「ページの更新」ボタンが見つかりません。
      '
                              +'この機能はpukiwikiの編集画面でご使用ください'
                          );
                          return;
                      }
                      var df = document.createElement("input");
                      Ext.apply(df, {type:"button",value:"Diff",onclick:diff_click});
                      Ext.get(df).insertBefore(pv);
                  }
              },100);
            
              function diff_click() {
                  var msg = document.getElementsByName('msg');
                  var org = document.getElementsByName('original');
                  if (msg[0] && org[0]) {
                      msg = msg[0].value;
                      org = org[0].value;
                  } else {
                      Ext.Msg.alert(
                          'pukiwiki編集支援機能',
                          '"msg"または"original" フィールドが見つかりません。
      '
                          +'この機能はpukiwikiの編集画面でご使用ください'
                      );
                      return;
                  }
                  if (msg===org) {
                      Ext.Msg.alert('pukiwiki編集支援機能', '変更箇所はありません');
                  } else {
                      var res = diff(
                          org.replace(/^\n+|\n+$/g,'').split("\n"),
                          msg.replace(/^\n+|\n+$/g,'').split("\n")
                      );
                      // 結果加工
                      var map = {
                          '-':{edit:'-',style:'background-color:#FFDDDD;'},
                          '+':{edit:'+',style:'background-color:#DDDDFF;'},
                          '=':{edit:' ',style:'background-color:#FFFFFF;'}
                      }
                      var table = ['
      '];                 for (var i=0; d=res[i]; ++i) {                                        table.push(""                             + "");                 }                 table.push('
      "+map[d.edit].edit+""
                                  + d.arr[d.line].replace(//g,">")
                                  + "
      ');
                      var win = new Ext.Window({
                          title:'pukiwiki編集支援機能',
                          html: table.join(''),
                          width:400,
                          height:400,
                          autoScroll:true,
                          closeAction:'close',
                          plain: true,
                          buttons: [{
                              text: 'Close',
                              handler: function(){
                                  win.close();
                              }
                          }]
                      });
                      win.show();
                  }
              }

              function diff(arr1, arr2, rev) {
                  var len1=arr1.length,
                      len2=arr2.length;
                  // len1 <= len2でなければひっくり返す
                  if (!rev && len1>len2)
                      return diff(arr2, arr1, true);
                  // 変数宣言及び配列初期化
                  var k, p,
                      offset=len1+1,
                      delta =len2-len1,
                      fp=[], ed=[];
                  for (p=0; p
                      fp[p] = -1;
                      ed[p] = [];
                  }
                  // メインの処理
                  for (p=0; fp[delta + offset] != len2; p++) {
                      for(k = -p       ; k <  delta; ++k) snake(k);
                      for(k = delta + p; k >= delta; --k) snake(k);
                  }
                  return ed[delta + offset];

                  // snake
                  function snake(k) {
                      var x, y, e0, o,
                          y1=fp[k-1+offset],
                          y2=fp[k+1+offset];
                      if (y1>=y2) { // 経路選択
                          y = y1+1;
                          x = y-k;
                          e0 = ed[k-1+offset];
                          o = {edit:rev?'-':'+',arr:arr2, line:y-1}
                      } else {
                          y = y2;
                          x = y-k;
                          e0 = ed[k+1+offset];
                          o = {edit:rev?'+':'-',arr:arr1, line:x-1}
                      }
                      // 選択した経路を保存
                      if (o.line>=0) ed[k+offset] = e0.concat(o);

                      var max = len1-x>len2-y?len1-x:len2-y;
                      for (var i=0; i
                          // 経路追加
                          ed[k+offset].push({edit:'=', arr:arr1, line:x+i});
                      }
                      fp[k + offset] = y+i;
                  }
              }

          })();



      2010年12月19日日曜日

      javascriptでdiff実装

      高速とされるO(NP)のアルゴリズムをベースに 、javascriptで実装してみた。
      [1]E.W.Myers, "An O(ND) difference algorithm and its variations", Algorithmixa, 1 (1986), pp.251-266 に論文を日本語に訳したものが載っており、参考になりました。
       http://hp.vector.co.jp/authors/VA007799/viviProg/doc5.htm
      上記の論文中のコードは「SED Shotest Edit Distance」の値を求めるだけで、
      Diffの結果をどう組み立て格納していくかについてと、fpという配列の初期値を
      何で埋めればよいのかが分からず、 かなり苦労しました。

      アルゴリズムは上記のO(NP)そのままですが、javascriptで実装していく中で、
      メイン処理とした親関数内でのループの部分はかなりすっきりさせ、
      snakeへのパラメータは整数kを1つだけとする、snakeは値を返さない、等、
      サンプルコードの原形はほとんどなくなってしまった感じです。

      また、O(NP)はarr1.lengthがarr2.length以下であることが前提なのですが、
      最初の段階でパラメータをチェックし、arr1.lengthがarr2.lengthより大きければ、
      順番をひっくり返して、自分自身を呼ぶことにより、前提を満たすようにしました。

      動くサンプルページ http://www123.ddo.jp/tools/diff.html
      
      //@license http://www.opensource.org/licenses/mit-license.html MIT License
      //@author Mashiki
      //@see http://hp.vector.co.jp/authors/VA007799/viviProg/doc5.htm
      function diff(arr1, arr2, rev) {
          var len1=arr1.length,
              len2=arr2.length;
          // len1 <= len2でなければひっくり返す
          if (!rev && len1>len2)
              return diff(arr2, arr1, true);
          // 変数宣言及び配列初期化
          var k, p,
              offset=len1+1,
              delta =len2-len1,
              fp=[], ed=[];
          for (p=0; p<len1+len2+3; ++p) {
              fp[p] = -1;
              ed[p] = [];
          }
          // メインの処理
          for (p=0; fp[delta + offset] != len2; p++) {
              for(k = -p       ; k <  delta; ++k) snake(k);
              for(k = delta + p; k >= delta; --k) snake(k);
          }
          return ed[delta + offset];
      
          // snake
          function snake(k) {
              var x, y, e0, o,
                  y1=fp[k-1+offset],
                  y2=fp[k+1+offset];
              if (y1>=y2) { // 経路選択
                  y = y1+1;
                  x = y-k;
                  e0 = ed[k-1+offset];
                  o = {edit:rev?'-':'+',arr:arr2, line:y-1}
              } else {
                  y = y2;
                  x = y-k;
                  e0 = ed[k+1+offset];
                  o = {edit:rev?'+':'-',arr:arr1, line:x-1}
              }
              // 選択した経路を保存
              if (o.line>=0) ed[k+offset] = e0.concat(o);
      
              var max = len1-x>len2-y?len1-x:len2-y;
              for (var i=0; i<max && arr1[x+i]===arr2[y+i]; ++i) {
                  // 経路追加
                  ed[k+offset].push({edit:'=', arr:arr1, line:x+i});
              }
              fp[k + offset] = y+i;
          }
      }
      

      2010年9月19日日曜日

      javascriptで配列やオブジェクトの中身をまとめて確認する

      IEは8でも使えないが、FireFoxを使って開発/デバッグいる場合に、いちいちステップ実行を設定して変数の内容を確認しなくても、console.dir(変数)でサクッとFireBugである時点での変数の値を確認できる。
      下記はExtJSのAPIドキュメント内のExt.Ajax.requestのサンプルだが、

          Ext.Ajax.request({
             url: 'aaa/bbb',
             success: function(response, opts) {
                var obj = Ext.decode(response.responseText);
                console.dir(obj);
             },
             failure: function(response, opts) {
                console.log('server-side failure with status code ' + response.status);
             }
          });
      と、記述するだけでお手軽にFireBugのコンソールから確認できる。
       また、 console.log(文字列)でコンソールに文字列を表示可能となる。alertの代わりに使えば、実行のタイミングを変えずにデバッグすることができるので、こっちも大いに利用したい。って何年か前に思ったけどあまり使っていないなあ。FireFoxでしか使えないこと(IEでしか発生しない問題なんてのもよくある)と、僕がすぐ忘れてしまうことが原因?! と、ここにメモっておこう。

      2010年7月20日火曜日

      ExtJS3.2.1 のマニュアルを改造しサンプルコードを検索

      ExtJSのマニュアルをみていて、あるクラスの具体的なサンプルを見たくなることが多々ある。
      マニュアルには最低限の情報しか載っていないことが多く、サンプルはたくさんの例が用意されて
      いるのだが、見た目が派手なクラス以外、探しにくいこともおおい。

      そこで、マニュアルからサンプルコードを検索できるような改造をマニュアルページに仕込んでみた。
      http://www123.ddo.jp/ext-3.2.1/docs/


      マニュアルで左側のツリーでクラスを選択したとき、下の画像のような

      「SearchExample」というボタンが表示される。サンプルを見たいクラスでこのボタンを押すと、下の画像のような、ファイルパスと行数とヒットした行の内容からなるリストが表示される。

      このリストを右クリックすると、「Open」と「Download」と選択するメニューが表示されるので、ブラウザ上で軽く見たい時は「Open」を、エディタなどでじっくり見たい場合は「Download」 を選べばよい

      2010年7月7日水曜日

      ハングルを入力する

      ちょうど去年の今頃、DSiで「学研ハングル三昧DS」を通勤中に勉強していました。結局、韓国語を覚えるのには挫折してしまいましたが、その時、韓国語のIMEなど入っていないPCで、ブラウザだけでハングルを入力し、日本語に翻訳するためのjavascirptを作りました。最近、手を入れ使いやすくしましたので、リンクと使い方を簡単に説明します。



      1.まず、上記のリンクをクリックし、ハングル入力ツールのウインドウをポップアップで開きます
      2.①子音ボタン、②母音ボタン、③パッチムボタン(必要なら)の順にボタンを押していくと、④のテキストフィールドにハングル文字が入力できます
      3.入力中に、スペースキー、数字、記号などキーボードから自由に入力することができます。
      4.④「削除」ボタン(もしくはバックスペース)を押すことで、入力中の文字、もしくは最後の1文字を削除することができます
      5.単語もしくは文章が入力できたら、テキストフィールドのハングル文字を切り取って、使用するか、⑥の「翻訳」ボタンを押し、Googleでの翻訳結果を表示することができます
      ※韓国語翻訳はまだGoogle翻訳よりExcite翻訳のほうが質が高い気がしますが、UTF-8で渡せるのでGoogleを翻訳のサイトとして指定しています

      2010年7月6日火曜日

      xrea+ と coreserver の仕様比較

      「core-mini」契約前に「XREA Plus」最終的な比較
       ・初年度は合計600円高くなる
       ・スペックは少しづつ高くなっている
      「XREA Plus」でスクリプトの実行速度以外制限に不満があったわけではないが、core-miniに変えてみるか
      プランXREA Free XREA Plus Mail & BackupCORE MINI CORE A CORE B
      初期費用 無料 無料 無料500円 1,000円 2,000円
      月額 無料 400円 200円400円 500円 990円
      年額 無料 2,400円990円2,500円5,000円9,900円
      お試し期間 - 7日 7日5日 5日 5日
       容量・転送量
      ディスク容量 50MB 3GB 2GB6GB 15GB 60GB
      転送量目安(月)※1 1GB3GB -100GB 150GB 300GB
      許容負荷率 ※ 100%125%250%1000%
       基本機能
      コントロールパネル
      ファイルマネージャ
      CRONジョブ ×
      MySQL 1個 5個 ×10無制限 無制限
      PostgreSQL 1個 5個 ×10無制限 無制限
      SQLite2/3
       ドメイン機能
      マルチドメイン 1020×50無制限 無制限
      サブドメイン 1020×50無制限 無制限
      カスタムマッピング ×
      URL転送 ×
      共有SSL ×
      独自IP/SSL × 1500円/月 1500 円/月
      あり なし ×
      提供サブドメイン なし 3個3個10個10個10個
       メール機能
      メールアカウント数 100100無制限200無制限 無制限
      マルチドメイン数 100100無制限
      送受信メール数(通/日) 1,000 通 3,000 通 10,000通50001000040000
      POP / POPS
      SMTP / SMTPS
      IMAP / IMAPS
      APOP / Port 587
      ウェブメール
      携帯ウェブメール
      スパムフィルター ×
      メール転送
      カスタムフィルター ×
      メーリングリスト × ×
      ウィルスフィルター
      メールマガジン × × 準備中です
      添付ファイル容量 20MB 50MB 100MB
      メールへの広告 あり なし なし
       FTP機能
      FTPアカウント
      サブFTPアカウント × 3×10無制限 無制限
      FTPS
      サーバー間コピー×
       ログ
      生ログ ×
      アクセス解析 ×
       ソフトウェア仕様
      CGI/SSI ×
      Perl ×
      PHP5(モジュール版) ×
      PHP4/5/6(CGI版) ×
      Ruby ×
      SQLite ×
      Python ×
      C/C++ ×
      .htaccess ×
      CGI自動設置
      sendmail
      SSH
       ハードウェア仕様
      CPU+ メモリー(※2) 2コアCPU 2GB2コアCPU 2GB2コアCPU 2GBXEON 4GB XEON 4GB XEON4コア 8GB
      バックアップ RAID RAID RAID RAID RAID RAID
      最大アカウント数 300以下 128以下 64 以下
      ネットワーク・回線 計2Gbps 以上計2Gbps 以上計2Gbps 以上計4Gbps以上計4Gbps以上計4Gbps以上
      サーバー管理 自社自社自社自社自社自社
      設置 国内国内国内国内国内国内
       サポート
      24時間障害対応
      メールサポート
       その他
      アダルトサイト × × -×(テキストは可)×(テキストは可)×(テキストは可)
      広告メールの送信 ×※3×※3×※3
      広告メールの宣伝サイト ×※3×※3×※3
      公開アップローダー △※4△※4△※4
      商用利用
      再販 × × ×△※5

      2010年7月5日月曜日

      HTML直接記入テスト

      Bloggerではhtmlの直接記入をかなり自由にできるようだ

      実態参照
      「&lt;」で「<」を、「&gt;」で「>」を、「&amp;」で「&」を表示可能

      <pre>タグ
      あああ
      いいい
      

      <table>タグ
      <table border=1><tbody>
      <tr><td>あああ</td><td>いいい</td><td>ううう</td></tr>
      <tr><td>あああ</td><td>いいい</td><td>ううう</td></tr>
      <tr><td>あああ</td><td>いいい</td><td>ううう</td></tr>
      </tbody></table>
      
      あああいいいううう
      あああいいいううう
      あああいいいううう

      javascript
      <a href='javascript:alert("javascript テスト")'>javascriptテスト</a>
      
      javascriptテスト

      2010年7月4日日曜日

      サクラでajax?!

      サクラのマクロでブラウザなど使用せず、web上の情報にアクセスできる。
      //httptest.js
      var http = new ActiveXObject("WinHttp.WinHttpRequest.5.1");
      var url="http://www.php.net/";
      http.Open("GET", url);
      http.Send();
      var wsh = new ActiveXObject("WScript.Shell");
      wsh.Popup("Status:" + http.status+" Result: "
      +http.ResponseText.substr(0,50), 0, "RESULT", 1);

      ajax的なことは普通にできそうなのだが、サクラのマクロにはメニューやドロップダウンリスト的なものをコントロールする機能がないので、応用が思いつかない

      XREA+ から coreserver への移行

      XREA+の契約が7/6に切れるので、継続しようと思ったが、上位のcoreserverと金額を比較すると、年間の金額はほぼ変わらないで、性能や容量がアップする。
      XREA+(年間2400円) の機能や容量の制限で不満な部分があるわけではないが、100円プラスの年額2500円でコアサーバ(mini)に切り替えられるので、切り替えを検討。かなりドロナワ的。

      仕様比較
      仕様比較
      coreserer仕様 http://www.coreserver.jp/
      xrea仕様 https://www.value-domain.com/xrea.php

      同じことをやった人の情報
      http://lab.urume.net/2009,0203,800.html
      http://www.fxtechnical.net/2008/10/_xreacoreserver_movablety.html
      http://gootara.jp/mt/2009/04/exreacoreserver.html

      お試し版を登録
      トップページには15日使用可能とあったが、実際には5日間。

      ファイルのコピー
      サーバー間コピー機能で全ディレクトリをコピー
      これは便利かも。
      構わずコピーしたら、.bash_historyまでコピーされてた

      DNS登録変更
      ddo.jp にて新サーバのIPアドレスに変更

      ネームバーチャルサーバ設定
      2つのサーバの「ドメインウェブ」の設定設定ページを開き内容を同じにする

      必要なフォルダ/ファイルのパーミッション変更
      他のサイトの情報ではパーミッションは自動的に同じになるような情報もあったが、groupとotherへのwrite権限はなくなるみたい

      とりあえずはこれで試用してみる。といっても期限切れまで2日しかないが...。

      2010年7月3日土曜日

      サクラでphpのマニュアルを検索する

      エディターでphpのコードを編集する時、関数の正確なスペルや引数の仕様を調べるのに重宝します

      スクリプト(phpfunc.js)

      ほぼ、『英辞郎 on the WEB』と同じスクリプトですが、
      // phpfunc.js
      // カーソル位置の単語で phpの関数マニュアル を検索する
      var word = Editor.ExpandParameter("$C");
      var url = "http://jp.php.net/manual-lookup.php?pattern="+word;
      (new ActiveXObject("Wscript.Shell")).run(url)
      

      登録方法は「サクラエディタでマクロの登録」を参照

      サクラエディタでマクロの登録

      1.サクラエディタ導入フォルダに「macro」フォルダを作成する
      C:\Program Files\sakura\macro
      2. Javascriptのファイルを作成する

      マニュアルを見てもマクロファイルの文字コードの指定は見つからなかったのでとりあえずS-JISで保存
      // alc.js
      // カーソル位置の単語で 英辞郎on the Web を検索する
      var word = Editor.ExpandParameter("$C");
      var url = "http://eow.alc.co.jp/"+word+"/UTF-8/?ref=sa";
      (new ActiveXObject("Wscript.Shell")).run(url)
      
      今回はalc.jsで保存した

      3. テスト実行する
      エディタ上の検索したい単語にカーソルを当て
      [ツール]→[名前を指定してマクロを実行...]
      macroフォルダ内のalc.jsを指定すると無事ブラウザが開き、結果が表示された

      4. マクロを登録する
      [設定]→[共通設定...]の[マクロタブ]を開き
      マクロ一覧で[参照]ボタンを押し、マクロを配置したフォルダを選択する
      C:\Program Files\sakura\macro\

      名前: 英辞郎on the Web
      Id: 1
      file: alc.js
      ■マクロを実行するたびにファイルを読み直す

      5. キー割り当て
      よく使うマクロの場合、簡単に呼び出したいもの。
      [設定]→[共通設定...]の[キー設定]タブを開き、
      種別: 外部マクロ
      機能: 英辞郎on the Web
      Ctrl: チェックを付ける
      キー: Ctrl+1
      を選択し[OK]ボタンを押す

      参考
      サクラエディタ マクロのマニュアル
       http://sakura.qp.land.to/SakuraMacro/

      Bloggerアカウント取得

      Googleのブログに登録してみました。

      登録時、本人確認のため、確認コードを携帯電話に自動音声通話で知らせるようになっています。
      悪戯防止には有効でしょうけど、携帯番号の入力は少々抵抗がありますね。