[IM]エンクロージャーの展開の前に

エンクロージャーの展開関数expandEnclosureは作ったのですが、そこに至るまでにいくつかユーティリティ関数を作ったので、それをまず照会しておきます。また、SafariだけでなくIEでも動作チェックを下結果も反映させます。

まず、すでに紹介したisLinkedElement関数は、このままだと、IEでの動作に不都合がありました。Safari/Firefoxだと、getAttributeによって、存在しない属性を指定するとnullが得られますが、IEはnullではなかったので、併せて文字列の長さが0より大きいという条件を付けました。なお、現在はTITLEタグを使ってリンクした要素とすることにしていますが、CLASS属性を使うのがいいのではとの指摘もあります。とりあえずはTITLEで続けますが、CLASSタグを使う場合にはこの関数を改良すればいいということになります。

/**
* Cheking the argument is the Linked Element or not.
*/
function isLinkedElement( node ) {
<div class=”wiki_entry”> // IE: If the node doesn’t have a title attribute, getAttribute doesn’t return null. So it requrired check if it’s empty string.</div 2010-03-08 02:49:50
it’s empty string.</div>
if ( node.getAttribute( ‘TITLE’ ) != null &amp;&amp; node.getAttribute( ‘TITLE’ ).length &gt; 0 ) {
return true
} else {
return false;
}
}
エンクロージャーなのか、リピーターなのかを判定する関数を作って正確には、エンクロージャーの候補となる場合やリピーターの候補となる場合にtrueを返すと言えばいいでしょうか。タグと一部のclass属性の比較だけを行っているシンプルな関数です。

/**
* Check the argument node is an enclosure or not
*/
function isEnclosure( node ) {
<div class=”wiki_entry”><span 2010-03-08 02:49:50
>
if ( node == null || node.nodeType != 1 )
return false;
var tagName = node.tagName;
var className = getClassAttributeFromNode( node );
if ( ( tagName == ‘TBODY’ ) || ( tagName == ‘UL’ ) || ( tagName == ‘OL’ )
|| ( tagName == ‘SELECT’ ) || ( tagName == ‘DIV’ &amp;&amp; className == ‘_im_repeater’ ))
return true;
else
return false;
}

/**
* Check the argument node is an repeater or not
*/
function isRepeater( node ) {
if ( node.nodeType != 1 )
return false;
var tagName = node.tagName;
var className = getClassAttributeFromNode( node );
if ( ( tagName == ‘TR’ ) || ( tagName == ‘LI’ ) || ( tagName == ‘OPTION’ )
|| ( tagName == ‘DIV’ &amp;&amp ( tagName == ‘DIV’ &amp;&amp; className == ‘_im_enclosure’ ))
return true;
else
return false;
}
エンクロージャーのタグに対応するリピーターのタグを戻す関数を以下の通り定義しておきました。これも単なる文字列の関数です。

/**
* Get the repeater tag from the enclosure tag.
*/
function repeaterTagFromEncTag( tag ) {
if ( tag == ‘TBODY’ ) return ‘TR’;
else if ( tag == ‘SELECT’ ) return ‘OPTION’;
else if ( tag == ‘UL’ ) return ‘LI’;
else if ( tag == ‘OL’ ) return ‘LI’;
else if ( tag == ‘DIV’ ) return ‘DIV’;
return null;
}
あるノードがリンクした要素だった場合、そこから親ノードをたぐり、エンクロージャーのノードを求めて返す関数です。見つからない場合にはnullを返します。たとえば、BODY要素の直下にあるINPUTだとnullを返し、結果的にはデータベースとのリンクはなされないことになります。

/**
* Detect the enclosure of the argument node.
*/
function getEnclosure( node ) {
<div class=”wiki_entry”><span class=”Apple-tab-span”> </span 2010-03-08 02:49:50
class=”Apple-tab-span”> </span>var detectedRepeater = null;</div>
var currentNode = node;
while ( currentNode != null ) {
if ( isRepeater( currentNode ) ) {
detectedRepeater = currentNode;
} else if ( isRepeaterOfEnclosure( detectedRepeater, currentNode ) ) {
return currentNode;
}
currentNode = currentNode.parentNode;
}
}
return null;
}
引数に指定した2つのノードが、リピーターとエンクロージャーの組み合わせかどうかを判定します。単にタグと、一部のタグについてはclass属性の値を調べて分岐させているだけです。

/**
* Check the pair of nodes in argument is valid for repater/enclosure.
*/
function isRepeaterOfEnclosure( repeater, enclosure ) {
if ( repeater == null || enclosure == null )
return false;
var repeaterTag = repeater.tagName;
var enclosureTag = enclosure.tagName;
if ( ( repeaterTag == ‘TR’ &amp;&amp; enclosureTag == ‘TBODY’ )
|| ( repeaterTag == ‘OPTION’ &amp;&amp; enclosureTag == ‘SELECT’ )
|| ( repeaterTag == ‘LI’ &amp;&amp; enclosureTag == ‘OL’ )
|| ( repeaterTag == ‘LI’ &amp;&amp; enclosureTag == ‘UL’ ))
return true;
else if ( repeaterTag == ‘DIV’ &amp;&amp; enclosureTag == ‘DIV’ ){</div>
var repeaterClass = getClassAttributeFromNode( repeater );
var enclosureClass = getClassAttributeFromNode( repeater );
if ( repeaterClass != null &amp;&amp; enclosureClass != null
&amp;&amp; repeaterClass.indexOf(‘_im_repeater’)&gt;=0
&amp;&amp; enclosureClass.indexOf(‘_im_enclosure’)&gt;=0 ) {
return true;
}
}
return false;
}
以下の2つのメソッドは、ノードにclass属性を設定する関数と、ノードからclass属性を取り出す関数です。getAttribute/setAttributeで取り出せるのですが、IEはclassName、他のブラウザはclassという属性名を指定する必要があるので、ブラウザによる分岐が必要になります。そのため、関数を作っておくのが得策でしょう。

function setClassAttributeToNode( node, className ) {
if (node == null) return ;
if ( ! navigator.appName.match(/Explorer/)) {
node.setAttribute( ‘class’, className );
} else {
node.setAttribute( ‘className’, className );
}
}

function getClassAttributeFromNode( node ) {
if (node == null) return ”;
var str = ”;
if ( ! navigator.appName.match(/Explorer/)) {
str = node.getAttribute( ‘class’ );
} else {
str = node.getAttribute( ‘className’ );
}
return str;
}