ppBlog Warning: LINE 953 of utils.php: A non-numeric value encountered:

ppBlog Warning: LINE 1008 of utils.php: A non-numeric value encountered:

ppBlog Warning: LINE 1034 of utils.php: A non-numeric value encountered:

ppBlog official

[ Tags :: JavaScript ]

ソースコード表示用の軽いスクリプト:pettieSyntax

category-icon

 こんにちは。たまにはアップデート以外の記事も書きたいので。

 前々から思っていたのですが、このブログではPHPとかJavaScriptのソースを提示することがままあります。現状、モノクロの味気ないソースコードだったので、PHPマニュアルのユーザーノートLink にあるようなカラフルな色付けにしたいなと思っていたわけです。この手のことを実現するには大きく2通りのアプローチがあって、サーバー側でハイライト表示の処理をして読み込ませる方法と、もうひとつはクライアント側のJavaScriptに解析させてその場でハイライト表示させる方法です。前者の例だと、PHPライブラリではGeSHi - Generic Syntax HighlighterLink などがあり、後者だとグーグルのgoogle-code-prettifyLink が有名でしょうか。

 でも、この手のやつって、これようのCSSファイルも用意しないといけないし、ファイルサイズはでかいし、設置が面倒そうだなと。なので書いてみました。基本的に、自分用なのでPHPとJavaScriptの一部の関数しかサポートしていませんが、まぁ、要は見栄えを良くすることが目的なので、すべてを網羅する必要はないと考えてます。PHPの組み込み関数なんて山ほどありますし。おかげで随分とサイズが小さいスクリプトになりましたが、それっぽく見えます。

 特徴としては、このスクリプト単体で動作可能ということです。他の見栄えのためのCSSファイルとかは必要ないです。記事の中のPREタグを見つけたら、その中身をハイライト表示します。初めは、PREタグに特定のクラス名を付けて、それを認識させようかと思っていましたが、そうすると過去の記事の書き換えをしないといけないし、どうせPREタグに書くのはソースコードぐらいだろうと割り切り、PREタグに絞りました。早速ですが、そのソースコードをこのスクリプトを使って表示してみます。

/*
 Lightweight syntax-analyzing script
 Copyright: modified BSD license 2008 martin
   version: 20080803.204553
*/

function pettieSyntax(){
 var cssprops = ['#c03', '#003366', '#636', /((?:[^{;_<]{2,}|[abipq*]))¥s*?¥{(¥s*[^}=_[¥]?<>]+?)¥}/ig];
 var etcetera = ['#c33', /( > | < )/g];
 var operants = ['green', /(¥/¥/¥-¥->|¥+¥+|¥-¥-|!?===?|<=|>=|=>|¥+=|¥-=|!=|&&|¥|¥|| = )/g];
 var variants = ['#669', /(¥$¥w+?¥b|var¥b¥w+¥b|@)/g];
 var htmltags = ['navy', /(<¥/¥w+?>|<¥w+|<¥?(?:xml|php)|(?:¥?|¥/)>|>)/g];
 var keywords = ['blue', /(¥b(?:alert|[Aa]rray|break|case|catch|class|charset|continue|Date|default|define|delete|do|else|false|FALSE|for|function|global|i[fn]|instanceof|new|null|Object|return|script|src|type|switch|this|throw|document¥S|TRUE|true|echo|try|typeof|var|void|while|window¥S|with|Location|version|encoding)¥b)/g];
 var decimals = ['#c69', /([¥s:¥/¥(,[+;."=>])(¥-?¥d+(?:¥.¥d+)?)(%|px|pt|em)?/g];
 var equipped = ['#036', /([¥.¥s(@>])([^'"¥(¥n=¥s<>¥/]+?)([¥( ])/g];
 var oneliner = ['#906', /('[^'¥r¥n]+?'|"[^"]*?"|<![^¥r¥n]+?¥-¥-.*?>)/g];
 var regulars = ['tomato', /([¥s(])(¥/[^¥r¥n]+?¥/)([igmy¥s¥);.])/g];
 var enoparts = ['indigo', /(¥b(?:o|oParts)¥b|¥.(?:away|moveTo|sizeTo|dimension|target)¥(¥)|¥.(?:loadScript|evt|metrics|start|css|view|each|sib))/g];
 var comments = ['#093', /([^:]|)((?:¥/¥/|#)¥s+[^'¥r¥n]+(?:¥r?¥n?|$)|¥/¥*[¥s¥S]+?¥*¥/)/g];
 var omitspan = function(m){ return m.replace(/<span[^>]*?>/ig, '').replace(/<¥/span>/ig, '');}
 var targets = document.getElementsByTagName('PRE');
 if(targets){
  for(var t, i = 0; t = targets[i++];){
   var c = t.innerHTML.replace(/<(?:span|code)[^>]*?>([^<]+?)<¥/(?:span|code)>/ig, '$1').replace(/¥t/g, '').replace(/'/g, ''').replace(/"/g, '"');
   if(/<(?:a |img )/i.test(c)) continue;
   c = c.replace(cssprops[3], function(A, B, C){ return /:/.test(C) ? '<span style=¥tcolor:'+cssprops[0]+'¥t>'+B+'</span>' + A.replace(B, '').replace(/([^:{]+?):([^;]+?);/g, '<span style=¥tcolor:'+cssprops[1]+'¥t>$1</span>:<span style=¥tcolor:'+cssprops[2]+'¥t>$2</span>;') : A;});
   c = c.replace(etcetera[1], '<span style=¥tcolor:'+etcetera[0]+'¥t>$1</span>');
   c = c.replace(operants[1], '<span style=¥tcolor:'+operants[0]+'¥t>$1</span>');
   c = c.replace(variants[1], '<span style=¥tcolor:'+variants[0]+'¥t>$1</span>');
   c = c.replace(htmltags[1], '<span style=¥tcolor:'+htmltags[0]+'¥t>$1</span>');
   c = c.replace(keywords[1], '<span style=¥tcolor:'+keywords[0]+';font-weight:bold¥t>$1</span>');
   c = c.replace(equipped[1], function (A, B, C, D){return B + '<span style=¥tcolor:'+equipped[0]+'¥t>' + C + '</span>' + D;});
   c = c.replace(enoparts[1], '<span style=¥tcolor:'+enoparts[0]+';font-weight:bold;¥t>$1</span>');
   c = c.replace(decimals[1], '$1<span style=¥tcolor:'+decimals[0]+'¥t>$2$3</span>');
   c = c.replace(regulars[1], function (A, B, C, D){return B + '<span style=¥tcolor:'+regulars[0]+'¥t>' + omitspan(C) + '</span>' + D;});
   c = c.replace(oneliner[1], function (A, B){return '<span style=¥tcolor:'+oneliner[0]+'¥t>' + omitspan(B) + '</span>';});
   c = c.replace(comments[1], function (A, B, C){return B + '<span style="color:'+comments[0]+'">' + omitspan(C) + '</span>';});
   c = c.replace(/¥t/g, '"');
   if(/*@cc_on!@*/false){
    t.outerHTML = '¥n<pre>' + c + '</pre>¥n'; /* IEの改行対策です */
   } else t.innerHTML = c;
  };
 }
};

 ファイルサイズは2キロ前後と小さいです。ppBlogで使うのであれば、とりあえずこのファイルをjsディレクトリにアップロードして、後は、lib.jsの最初の方で、このスクリプトを以下のように呼び出します。

oParts.loadScript('js/syntax.js');

次に同じlib.jsの下の方にある、oParts.start関数内で、このpettieSyntax()を呼び出せばOKです。

if(typeof pettieSyntax != UD) pettieSyntax();

 コードを見れば分かりますが、ハイライトの色はSPANタグ内でスタイルシート指定してますので、この部分を好きな色に変えれば良いです。あと、強調したいキーワードを追加したいときも、ソース内の該当箇所に追加していけばよいです。

 ppBlog使用前提なら、上のような設置になりますが、純粋に単体で動かしたい場合は、外部スクリプトとして、

<script type="text/javascript" src="path/to/syntax.js"></script>

みたく読み込ませて、ページ読み込み完了後にpettieSyntax()を呼べば良いです。

 まだろくに動作検証していないのですが、興味がある方はどうぞ。syntax.jsと、あと、ppBlogの設置例としてlib.jsを添付しておきます。

添付ファイル: syntax.jsattachedIcon  lib.jsattachedIcon 

 


— posted by martin at 01:12 pm   commentComment [1]  pingTrackBack [0]

ppBlogでのgetElementsByClassName

category-icon

こんばんは、martinです。ソーシャルブックマークへのリンクアイコンを各記事に付けるようにしました。近々配布予定の最新版に採用してます。またJavaScript関連の話をば。

 愛用しているタブブラウザFirefoxの現行バージョンは2ですが、バージョン3からはネイティブでdocument.getElementsByClassNameサポートされるLink ようです。現時点でこのメソッドをサポートしている主要なブラウザは存在しないので、document.getElementsByClassNameGでググれば分かるように、皆さん、色んなgetElementsByClassNameを書いています。ppBlogのJavaScriptのライブラリlib.jsでも、この関数(getByClass)は多用しているのですが、この関数は、ページ内のオブジェクトを逐一調べていくループ作業を伴うので、一般的に言って動作に時間がかかります。

 JavaScript使いの間では、常識かと思いますが、この手の操作で一番スピードが速いのは、XPathGを利用するやり方です。jQueryGで有名なJohn Resig氏のブログに、この手の関数のスピード比較記事があります。
 →http://ejohn.org/blog/getelementsbyclassname-speed-comparison/Link

 ネイティブサポートのFirefox3が無茶苦茶速いのは当然として、XPathもDOMを解析していく手法よりはずっと高速なのが分かります。IE6,7を除くモダンなブラウザは、このXPathをサポートしているので、ppBlogでもXPathを使うようにしました。以下のような感じです。

document.getElementsByClassName(className, pElement, tagName){
 var d = document, nodes = [], item;
 try { // XPathをサポートしているならこれを使う
  var xp = d.evaluate(
   './/'+(tagName || '*')+'[contains(concat(" ", @class, " "), " '+className+' ")]',
   (pElement || d), null, XPathResult.ANY_TYPE, null
  );
  for (item = xp.iterateNext(); item; item = xp.iterateNext()){
   nodes.push(item);
  }
 } catch(e){ // そうでなければ地道にDOM解析
  var cls, items = (pElement || d).getElementsByTagName((tagName || '*'));
  for(var i = 0, l = items.length; i < l; i++){
   item = items[i];
   if(item.className){
    cls = item.className.split(/¥s+/);
    for(var j = 0, k = cls.length; j < k; j++){
     if(cls[j]==className){
      nodes[nodes.length] = item; break;
     }
    }
   }
  }
 }
 return nodes.length > 0 ? nodes : null;
}

 John Resig氏のブログにあるのと同じ実験Link をしてみましたが(パクっただけ)、彼のサイトにある、Prototype.jsのXPathを使ったもの(xpath.htmlLink )と同等か少し速いくらいですね。

 当然ながら、XPathをサポートしていないIE6,7は、通常通りDOM解析をするアプローチしかないです。こちらの計測では、XPathを利用するより5倍ほど遅いです。

 ちなみに、1つ前のエントリで紹介したphotoeffect.jsも、この関数を使用しているので更新しました。ついでに添付しておきますね。このphotoeffect.jsは、単体で動くので、ppBlog以外の各種ブログでも外部ファイルとして呼び込むだけで簡単に使えます。要望があれば、詳しいクラス指定などの解説書きますんで、その時はたずねて下さい。

添付ファイル: photoeffect.jsattachedIcon 

 


— posted by martin at 01:22 am   commentComment [19]  pingTrackBack [0]

document.write()の実行タイミングをずらす方法

category-icon

関連エントリー: Google Adsenseの表示タイミングを制御するLink

 このサイトのppBlog's webRingのリストは、BlogPeopleLink からdocument.writeで書き出しています。というかBlogPeopleのスクリプトがそうなっているのですが。で、document.writeが、HTMLソースの最初の方にあると、document.writeによる書き出しが終わるまで、それ以降のページのレンダリング(HTMLの書き出し)はストップした状態となります。document.writeによって呼び出している外部サーバー(ここではBlogPeople)のレスポンスが速ければ、ページ描画で待たされることはありませんが、たまにはレスポンスが遅くて、ページの残りがなかなか表示されないという事態は起こりえます。このdocument.writeは、広告バナーなどでも当たり前のように使われていて、メインコンテンツの内容が知りたいのに、バナー広告がなかなか表示されず、イライラしたという経験もあるかと思います。確か、GoogleのADWordsなどでも使われているのではないでしょうか。

 こういう事態を避けるには、HTMLソースの最後の方で document.write を呼び出すというのがあり、これはこれで有効ですが、そう汎用性のあるやり方ではなく、また、スクリプト(JavaScript)がHTMLの中で、あちこちに散らばるのは美しくないです。

 ここでは、document.writeの内容をとりあえず配列などに一時的に退避させといて(この間にHTMLソースの描画は進んでいきます)、ページ読み込み完了と同時に、そのストックしておいたdocument.write内容を吐き出すという方法について書いておきます。

 まず、普通にdocument.writeを含んだ外部スクリプトを呼び出すやり方。これは、BlogPeopleがこうしましょう、と述べているものです。

<script type="text/javascript"
  src="http://www.blogpeople.net/display/usr/0f0d40535b5b4103.js"
  charset="euc-jp"></script>

これの実際の中身は、次のようになっています。

document.write('<div class=¥'blogpeople-main¥'>');
document.write('登録されたサイト')
     :
     :
document.write('</div>');
document.write('<div class=¥'blogpeople-powered-by¥'>');
document.write('<br /><a href=¥'・・・¥' target=¥'_blank¥'><img src=¥'・・・¥' border=¥'0¥' alt=¥'Powered By BlogPeople¥' /></a>');
document.write('</div>');

ここに使われているdocument.write内容をとりあえず配列に入れておけば良いのですが、それには次のようにします。

(function(){
  var alts = []; // document.writeの内容を入れておく配列を準備 
  d._write = d.write; // オリジナルはコピーしておく
  d.write = function(s){ alts.push(s);} // d.writeを新たに定義
  var src = "http://www.blogpeople.net/display/usr/0f0d40535b5b4103.js";
  var script = d.createElement('script');
  script.setAttribute('type', 'text/javascript');
  script.setAttribute('charset', 'euc-jp');
  script.setAttribute('src', src);
  d.getElementsByTagName('head')[0].appendChild(script);
  addEvent(window, "load", function(){
   o("_webRing").innerHTML = alts.join(""); // 指定した場所に流し込む
   d.write = d._write; // d.writeを元の定義に戻しておく
  });
 }());

 ポイントは、既存のdocument.writeを新たに、

document.write = function(s){ alts.push(s); }

と再定義している点です。ここではaltsという配列に退避させています。こうすることで、document.writeによる書き出しを待つことなく、残りのページレンダリングを進めていくことが出来ます。このスクリプトで、SafariでもIE6でもFirefoxでもきちんと動くことを確認しています。

 小生のブログであるmartin.p2b.jpのwebRingLink と、このサイトにあるwebRingLink とは同じ外部スクリプトを呼び出しているのですが、比較のために、このサイトのwebRingは上記の手法で、martin'sの方は従来の方法で呼び出しています。BlogPeopleのサーバーレスポンスは素早いので、なかなか差が実感できにくいかと思いますが、このサイトのやつは、(サイドバーのHTMLソースはdocument.writeよりも後にもかかわらず)サイドバーが先に表示されて、最後にwebRingのリストが表示されるのに対して、martin'sの方は、HTMLソースの順序通り、webRingリスト→サイドバーという順序で表示されています。

 ちなみに、このサイトのwebRingは、ppBlogの「ページ作成機能」を使って作成しているのですが、具体的に、どのような内容になっているのか、理解の一助のために添付しておきますね。

 まぁ、こんなアプローチもありますよということで

添付ファイル: webRing.txtattachedIcon 

 


— posted by martin at 05:09 pm   commentComment [14]  pingTrackBack [1]

window.onloadの代替スクリプト

category-icon

Updated Entry: http://p2b.jp/200805-events-orderLink

 今日は七夕Wですね。小学生の頃は、学校総出で「♪笹の葉さーらさら〜」ってやって、七夕伝説に思いを馳せたものですが。いつになってもガキの頃の心を忘れない大人でいたいものです。

 さて、Ajaxの隆盛によって、その根幹であるJavaScriptはブログに欠かせないものとなっています。ppBlogも例外ではなく、至るところで活躍してます。一般にブログの(トップ)ページは、色々な情報を詰め込んでいるために、ファイルサイズは大きくなる傾向にあり、また、画像も至るところで使われるために、読み込むべきコンテンツサイズは更に大きくなります。ブロードバンドが当たり前の今日でもページの表示にちょっと待たされるのは珍しいことではありません。で、JavaScriptは(画像も含めた)ページの内容物が全部読み込まれた後に作動させるのが一般的です。なので、もし、画像の読み込みに時間がかかったりすると、JavaScriptがうまく動かずに、狙い通りの効果が得られなかったり、スクリプトエラーが出たりします。こういうのはたいてい、window.onloadやBODYタグでのonloadイベントで制御しているんですが、JavaScript使いなら、画像の読み込みを待たずに、ページの構成要素(DOMエレメント)がパース(読み込まれて解釈)された時点でプログラムを走らせたいと思うものです。

 その解決法として、JavaScriptマスターのDean EdwardsLink 氏が、window.onload問題を解決した!Link と言ったのが昨年の9月頃で、このブログでも言及したLink ことがあります。彼のやり方は、Mozilla系ブラウザの隠し要素DOMContentLoadedを利用したもので、IEはこれに対応していないので、IEだけは、外部jsスクリプトを用意しないといけませんでした。その不完全さが好きになれなかったので、ppBlogではこれとは違うアプローチ(setTimeoutを利用するやり方)を取ってたんですが、このsetTimeoutがらみの手法は、PCの性能などにも大きく左右されるし、どうも不安定さがあるんですよね。角丸コーナースクリプトが上手く作動しなかったり。

 そんな折、先月にこれでほぼ100%解決と思われるやり方がEdwardsを含めたJavaScriptマスターたちによって「発見」されました。→http://dean.edwards.name/weblog/2006/06/again/Link

 この新しい方法では、IEでも外部スクリプトを別に用意する必要がなく、またSafariなどでも問題なく動くものです。いやぁ、みんな頭良いよなぁ。自分でも個人的に、IE向けにはdocument.readyStateを利用した方法を試したりしたんですが、IEの返すdocument.readyStateが、またいい加減なシロモノでしてあえなく挫折。しかも、DOMContentLoadedに関しては、Operaの最新バージョンであるOpera9でサポートされたりしたんで、自分もこの潮流に乗っかることにしました。そのうちSafariでもDOMContentLoadedはサポートされそうな予感。そういえば、Opera9では、とうとうdocument.designModeがサポートLink されましたね。以前、document.designModeGを利用して、ppBlog組み込みの本格的なエディターを作ろうとしたことがあったのですが、IEの吐き出すHTMLソースがあまりに後進的だったので放置したままになっていますねぇ。IE7βでどうかはまだ試していないです(期待してないけど)。

 少し逸れましたが、とにかくppBlogでもwindow.onloadに替わる新しいやり方を実装してみました。詳しい話はエドワーズのブロッグを読んでいただくとして、ppBlogでは現時点で、次のような感じにまとめています。

DOM = { /* window.onload alternative */
 complete : function(){
  if(arguments.callee.done) return;
  arguments.callee.done = true;
  if(typeof _timer != 'undefined'){
   clearInterval(_timer);
   _timer = null;
  }
  DOM.onload();
 },
 check : function(){
  if(d.addEventListener){ // Mozilla/Opera9向け
   d.addEventListener("DOMContentLoaded", DOM.complete, false);
  }
  if(navigator.userAgent.match(/webkit|safari|khtml/i)){// Safari向け
   var _timer = setInterval(function(){
    if(d.readyState.match(/loaded|complete/)){
     DOM.complete();
    }
   }, 50);
  }
   /*@cc_on @*/
   /*@if (@_win32)// IE向け
    d.write('<script id="_decoy_" defer src="javascript:void 0"><¥/script>');
    d.getElementById("_decoy_").onreadystatechange = function(){
     if(this.readyState=='complete'){
      DOM.complete();
     }
    };
   /*@end @*/
  addEvent(window, "load", DOM.complete);//その他
 },
 onload : function(){}
}

 ポイントは、DOMContentLoadedをサポートしているならそれを使い、Safari系ではdocument.readyStateを利用し(IEと違って信用できる値を返すようである)、IEではスクリプト属性のdeferを利用することです。このdeferは文字通り、(外部読み込み)スクリプトの実行を遅延させる属性でIEのみがサポートしています。このdeferは、document.readyStateよりずっと堅実です。ついでに言うと、ここで使われているdefer指定の外部スクリプト指定は、もはや単なる「飾り」になっています(それがIE7正式版でどういう扱いになるか一抹の不安が残りますが)。詳しくは、このトリッキーな手法を発見したMatthias MillerLink 氏のブログ(エントリーLink )をどうぞ。

 ちょっと話題がずれますが、IE7βは先日ベータの最新版かつベータ最終版が出ましたね。それに少し関連したErik氏の面白いエントリーがあったので紹介しておきます(まぁ、期待外れ感が漂う、ちょっとしたジョークなんでしょう)。

 →IE 7 DOM and JS ChangelogLink

 Erik氏は、昨日のエントリーにも出てきたWebFXの主宰者の一人で、彼もまたJavaScriptマスターです。このエントリーは時期的にIE7ベータ1-2についてのものかな。思わず笑ってしまいました

 


— posted by martin at 04:40 am   commentComment [0]  pingTrackBack [3]

Firefoxでのwindow.event

category-icon

 ワールドカップの準決勝ポルトガル-フランスのせいで寝むれないmartinです。

以前のエントリで「Firefoxでの擬似window.eventLink 」として、

function windowEvent(){
 if(window.event) return window.event;
 var caller = arguments.callee.caller;
 while(caller){
  var ob = caller.arguments[0];
  if(ob && ob.constructor == MouseEvent) return ob;
  caller = caller.caller;
 }
 return null;
}

というのを載せていましたが、どうせなら、Firefox(および、その他のモダンなブラウザ)にも、window.eventを認識させたいです。window.eventは使い勝手が良いので。で、このwindow.eventを簡単に認識させる方法があったので、今はこれを使っています。もともとのソースは、JavaScript使いなら誰でも知っている、昔からの有名サイトWebFXLink 内にある「Classic Event Handlers - IE EmuLink 」で紹介してあった方法です。リンク先を見れば分かりますが、これ以上ないくらいシンプルでスマートなやり方ですねぇ。最初は、__defineGetter__あたりを使ってエミュレートしようとしていたのですが。いやはや。

 js/lib.jsでは、次のようにしています。

if(w3c){ // IE以外のモダンなブラウザなら.
 (function(){
  for (var property in Event.prototype){
   if(property.match(/MOUSE|CLICK/)){
    window.addEventListener(property.toLowerCase(), function(e){
     window.event = e;
    }, true);
   }
  }
 }());
};

 イベントリスナーに逐一マウスイベントを列挙して登録しても良いのですが、Eventオブジェクトにある組み込み定数を利用してみました。マウスイベントを列挙するなら、次のような感じになると思います。

if(w3c){ // IE以外のモダンなブラウザなら.
 (function(){
  var events = ["mousedown", "mouseover", "mouseout", "mousemove",
                "mousedrag", "click", "dblclick"];  
  for (var i = 0; i < events.length; i++){
   window.addEventListener(events[i], function(e){
    window.event = e;
   }, true);
  }
 }());
};

 まぁ、こちらの方が記述も短めでオーソドックスなやり方ですが、イベントを列挙するのがメンドかったので、別のアプローチを取ってみました。お好きな方でよいと思います。

 こうすると何が嬉しいかって、例えば、次のようなスクリプトにおいて、

document.onclick = function(e){
 alert("Event X:"+e.clientX + ", Event Y:"+e.clientY);
}

この引数のeは、Mozilla系のブラウザでは必須のもので省略は出来ません。こういうシンプルな例ならいいのですが、引数がいくつかあるような関数で、いちいちこのeを付けて回るのはメンドいものです。こういうときに、上記のエミュレーションを用いれば、

document.onclick = function(){ //eが不要
 alert("Event X:"+event.clientX+", Event Y:"+event.clientY);
}

みたいに書けるわけです

   


— posted by martin at 05:34 am   commentComment [1]  pingTrackBack [1]

T: Y: ALL: Online:
Created in 0.0057 sec.
prev
2025.10
next
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31