<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:itunes="http://www.itunes.com/DTDs/Podcast-1.0.dtd">
  <channel>
    <title>apptaro's blog</title>
    <link>http://apptaro.seesaa.net/</link>
    <description>apptaro's blog</description>
    <language>ja</language>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <itunes:subtitle></itunes:subtitle>
    <itunes:summary>apptaro's blog</itunes:summary>
    <itunes:keywords>Java, JSP, JSTL, JSF, Hibernate, Oracle</itunes:keywords>
    
    <itunes:author>ERROR: NOT PERMITED METHOD: nickname </itunes:author>	
    <itunes:owner>    
       <itunes:name></itunes:name>
       <itunes:email></itunes:email>
    </itunes:owner>
        <itunes:explicit>no</itunes:explicit>
        <item>
      <link>http://apptaro.seesaa.net/article/117238110.html</link>
      <title>How to avoid borders when using UnsharpMask operation with JAI</title>
      <pubDate>Fri, 10 Apr 2009 21:07:04 +0900</pubDate>
            <description>SummaryWhen using unsharp mask with JAI (Java Advanced Imaging), a resulting image may have borders. This article explains how to avoid it.How to use unsharp mask with JAIThe following is the sample code. The key is to use RenderingHints to...</description>
            <content:encoded><![CDATA[
<p style="font-weight:bold;">Summary</p>

<div style="margin-left:20px;">
When using unsharp mask with JAI (Java Advanced Imaging), a resulting image may have borders. This article explains how to avoid it.
</div>

<p style="font-weight:bold;">How to use unsharp mask with JAI</p>

<div style="margin-left:20px;">
The following is the sample code. The key is to use RenderingHints to specify a BorderExtender. Without this, borders may appear.

<hr />
<!---------------------------------------------------------------------------->
UnsharpTest.java
<pre style="background-color:#e0e0e0;margin-left:20px;">
public class UnsharpTest {
    public static void main(String[] args) throws IOException {
        
        // load image
        
        RenderedOp image = JAI.create("stream", new FileSeekableStream(new File("test.jpg")));

        // unsharp image
        
        ParameterBlock unsharp_params = new ParameterBlock();
        unsharp_params.addSource(image);
        unsharp_params.add(null); // convolution kernel
        unsharp_params.add(1.0F); // gain factor

        RenderingHints unsharp_hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER,
            BorderExtender.createInstance(BorderExtender.BORDER_COPY));
        
        image = JAI.create("UnsharpMask", unsharp_params, unsharp_hints);
        
        // save image
        
        JPEGEncodeParam encParam = new JPEGEncodeParam();
        encParam.setQuality(0.80F);
        
        ParameterBlock filestore_params = new ParameterBlock();
        filestore_params.addSource(image);
        filestore_params.add("image_unsharpened.jpg"); // path of the file to write to
        filestore_params.add("JPEG"); // format of the file
        filestore_params.add(encParam); // encoding parameters
        
        JAI.create("filestore", filestore_params);
    }
</pre>
<!---------------------------------------------------------------------------->
</div>
<a name="more"></a>

]]><![CDATA[
]]></content:encoded>
            <category>Java/Servlet</category>
      <author>apptaro</author>
                </item>
        <item>
      <link>http://apptaro.seesaa.net/article/107877494.html</link>
      <title>Corrupted Characters in JSF due to Expired Session</title>
      <pubDate>Fri, 10 Oct 2008 13:52:25 +0900</pubDate>
            <description>SummaryWhen a session is expired, JSF may parse form data using incorrect character encoding. This leads to corrupted characters on a page, or in a worst case, corrupted data in a database.How form data can be corruptedAs I wrote in the pre...</description>
            <content:encoded><![CDATA[
<p style="font-weight:bold;">Summary</p>

<div style="margin-left:20px;">
When a session is expired, JSF may parse form data using incorrect character encoding. This leads to corrupted characters on a page, or in a worst case, corrupted data in a database.
</div>

<p style="font-weight:bold;">How form data can be corrupted</p>

<div style="margin-left:20px;">
<p>As I wrote in the previous article on <a href="/article/41505341.html">some tips for JSF character encoding</a>, JSF mostly depends on javax.faces.request.charset session attribute to determine a correct character encoding to decode form data. JSF 1.1 and 1.2 are designed this way so that you can use existing mechanism such as JSP page directive or servlet filter to change character encoding. I think people usually just set charset in JSP page directive. Then, JSF seems to take care of the rest.</p>
<p>My application uses client side state saving, and view-scoped beans (using t:saveState for request-scoped managed beans.) This is the best way because this way I can let users use multiple browser windows to access the same JSF form at once. If you use session-scoped beans, beans are shared between accesses from different browser windows, and it messes things up. I thought my application does not depend on sessions in any way, so I set short session-timeout. But there was a pitfall!</p>
<p>Users sometimes leave JSF page open, and later come back and do updates on the page. The session has been expired by that time, and JSF falls back to use ISO-8859-1 in decoding form data, when it should be using, for example, Windows-31J for Japanese characters. Corrupted data are then stored in a backing bean, leading to corrupted characters on the response page, or corrupted data can even be saved on a database.</p>
</div>

<p style="font-weight:bold;">So what to do?</p>

<div style="margin-left:20px;">
So what to do to avoid this problem? You could ...
<ul>
<li>Set long session-timeout
<p>I don't like this idea because I wanted to set it short for the first place for some reasons.</p></li>
<li>Set up a servlet filter to set request encoding
<p>I feel it waste because then JSF spec would look meaningless, and if you changed charset in JSP page directive, you would also have to modify the filter. If they don't match, it will messes things up. A servlet filter sets a request character encoding first, then JSF sets a request character encoding when found in a session, then actual encoding used to decode the form data may be the former or the latter. (Note: The servlet will use the last request character encoding set to parse form data, on an initial call to request.getParameter.)</p></li>
<li>Add a logic to disallow form data if a session is null or new
<p>This would make it worse in terms of application usability. What would the error message say? "You were away too long, so I forgot the character encoding?"</p></li>
</ul>
I wonder if there are other ways to solve this issue. For now, I would go with the second option.
</div>
<a name="more"></a>

]]><![CDATA[
]]></content:encoded>
            <category>JSF</category>
      <author>apptaro</author>
                </item>
        <item>
      <link>http://apptaro.seesaa.net/article/41505341.html</link>
      <title>Tips for JSF character encoding</title>
      <pubDate>Fri, 11 May 2007 17:06:54 +0900</pubDate>
            <description>SummaryThis article explains some tips for JSF character encoding.How request character encoding is determined by JSF.As described in section 2.5.2.2 of JSF specification 1.1 and 1.2, request character encoding is determined in the followin...</description>
            <content:encoded><![CDATA[
<p style="font-weight:bold;">Summary</p>

<div style="margin-left:20px;">
This article explains some tips for JSF character encoding.
</div>

<p style="font-weight:bold;">How request character encoding is determined by JSF.</p>

<div style="margin-left:20px;">
As described in section 2.5.2.2 of JSF specification 1.1 and 1.2, request character encoding is determined in the following order:
<ul>
<li>Charset of Content-Type is used, if exists.</li>
<li>Charset of a previous response is used, if stored in a session.</li>
<li>Otherwise, request character encoding is unmodified.</li>
</ul>
<p>Determining correct encoding is crutial because it is used to decode POST parameters.</p>
Obtaining charset from Content-Type is considered the best way, but this usually does not happen because most browsers currently do not send it.</p>
<p>JSF stores previous response character encoding in a session ("javax.faces.request.charset" in case of MyFaces.) Most browsers send requests using the same character encoding as the HTML, so following requests to JSF can assumingly use previous response character encoding as request character encoding.</p>
<p>On an initial request of a session to JSF, whatever request character encoding set by servlet/container is used unmodified. Thus it can fall back to ISO-8859-1 as specified by Servlet Specification 2.4 and 2.5. A common way to specify request character encoding is to build a small servlet filter which call request.setCharacterEncoding.</p>
</div>

<p style="font-weight:bold;">Tips</p>

<div style="margin-left:20px;">
Here's the tips:
<ul>
<li><font style="text-decoration:line-through;">Always</font> use servlet filter to set request character encoding. Otherwise, an initial request to JSF will fail to decode parameters correctly.
<p>If you have a JSF page which uses incoming POST parameters, and a calling page is a simple html page with a form, chances are JSF will fail to process the parameter correctly for the first request, but will succeed for the following requests.</p>
</li>
<li>Always use only one encoding throughout the application. Otherwise, JSF may fail to decode parameters correctly.
<p>Note that response encoding is stored by session, not by session and page. If some JSF pages renders response by encoding A, while others renders response by encoding B, accessing the former pages after accessing the latter pages will result in decode failure. This may also happen if response encoding is changed dynamically based on user's preferences, and multiple windows are open while changing the preferences.</p>
</li>
</ul>
</div>

<p style="font-weight:bold;">Notes</p>

<div style="margin-left:20px;">
<ul>
<li>Even if you set a character encoding in a servlet filter, JSF will override it with the encoding found in Content-Type or the previous response encoding stored in a session. The servlet will use the last request character encoding set to parse form data, on an initial call to request.getParameter. (Modifying q request character encoding after a call to request.getParameter is ignored.) <font style="font-style:italic;">(Added: 2008/10/10)</font></li>
</ul>
</div>
<a name="more"></a>

]]><![CDATA[
]]></content:encoded>
            <category>JSF</category>
      <author>apptaro</author>
                </item>
        <item>
      <link>http://match.seesaa.jp/ot_listing.pl?aid=87275&amp;sid=apptaro&amp;tid=seesaa_hotspot&amp;k=%E5%B7%9D%E6%9D%91%E3%82%AB%E3%82%AA%E3%83%AA%E3%81%95%E3%82%93&amp;hid=35</link>
      <title>[PR]注目のキーワード「川村カオリさん」</title>
      <pubDate>Fri, 11 May 2007 17:06:54 +0900</pubDate>
            <description><![CDATA[
<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E6%AD%8C%E6%89%8B&hid=35">歌手</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E5%BD%BC%E5%A5%B3&hid=35">彼女</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E4%B9%B3%E3%81%8C%E3%82%93&hid=35">乳がん</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E4%BA%A1%E3%81%8F%E3%81%AA%E3%81%A3%E3%81%9F&hid=35">亡くなった</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E6%B6%99&hid=35">涙</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E3%81%8C%E3%82%93&hid=35">がん</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E3%81%94%E5%86%A5%E7%A6%8F&hid=35">ご冥福</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E5%8F%82%E5%88%97%E8%80%85&hid=35">参列者</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E9%80%9A%E5%A4%9C&hid=35">通夜</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E6%AD%BB%E5%8E%BB&hid=35">死去</a>
]]></description>
      <author>ads by Seesaa</author>
          </item>
        <item>
      <link>http://apptaro.seesaa.net/article/37718310.html</link>
      <title>MyFaces Tomahawk: Tree2 Component Improvement</title>
      <pubDate>Wed, 04 Apr 2007 11:58:33 +0900</pubDate>
            <description>SummaryMyFaces Tomahawk's Tree2 is a great JSF component to display hierarchic data, such as directory structure or sitemap. When used with dynamic data, however, its default implementation has some issues with tree state as described in TO...</description>
            <content:encoded><![CDATA[
<p style="font-weight:bold;">Summary</p>

<div style="margin-left:20px;">
MyFaces Tomahawk's <a href="http://myfaces.apache.org/tomahawk/tree2.html">Tree2</a> is a great JSF component to display hierarchic data, such as directory structure or sitemap. When used with dynamic data, however, its default implementation has some issues with tree state as described in <a href="http://issues.apache.org/jira/browse/TOMAHAWK-244">TOMAHAWK-244</a> and <a href="http://issues.apache.org/jira/browse/TOMAHAWK-296">TOMAHAWK-296</a>. In this article, I will solve the issues by providing custom implementations of TreeModel and TreeWalker interfaces.
</div>

<p style="font-weight:bold;">Scope</p>

<div style="margin-left:20px;">
This article focuses on usage of tree2 component with the following attributes:
<ul>
<li>clientSideToggle="true"</li>
<li>preserveToggle="true"</li>
</ul>
Tested with MyFaces Core 1.1.3 and MyFaces Tomahawk 1.1.3. For server side toggle, no test has been done.
</div>

<p style="font-weight:bold;">Problem Description</p>

<div style="margin-left:20px;">
Tree2 component stores tree state (if each node is expanded or collapsed) in index-based node naming schema such that:
<ul>
<li>root node is named "0"</li>
<li>first child is named "0:0"</li>
<li>second child is named "0:1"</li>
<li>third child of second child is named "0:1:2"</li>
</ul>

This simple schema does not work as expected when tree data is dynamic. For example, in the diagram below, a root node has two nodes A and B, and node B is expanded. This tree state is described as "node 0:1 is expanded."
<ul>
<li style="list-style:none;">ROOT</li>
<li style="list-style:none;margin-left:20px;">+ A</li>
<li style="list-style:none;margin-left:20px;">- B</li>
<li style="list-style:none;margin-left:40px;">+ B1</li>
<li style="list-style:none;margin-left:40px;">+ B2</li>
</ul>

Let's look at the situation when node C is inserted as a first child of ROOT. Then what you would expect is:
<ul>
<li style="list-style:none;">ROOT</li>
<li style="list-style:none;margin-left:20px;">+ C</li>
<li style="list-style:none;margin-left:20px;">+ A</li>
<li style="list-style:none;margin-left:20px;">- B</li>
<li style="list-style:none;margin-left:40px;">+ B1</li>
<li style="list-style:none;margin-left:40px;">+ B2</li>
</ul>

However, this is what really happens. Because tree state is stored as "node 0:1 is expaned," in this case what is expanded is node A:
<ul>
<li style="list-style:none;">ROOT</li>
<li style="list-style:none;margin-left:20px;">+ C</li>
<li style="list-style:none;margin-left:20px;">- A</li>
<li style="list-style:none;margin-left:40px;">+ A1</li>
<li style="list-style:none;margin-left:40px;">+ A2</li>
<li style="list-style:none;margin-left:20px;">+ B</li>
</ul>

Because tree state is index-based, when data is inserted/deleted/moved/sorted, tree state becomes unfavorable.
</div>

<p style="font-weight:bold;">Solution Description</p>

<div style="margin-left:20px;">
Good news is, Tree2 component has a mechanism to solve this problem by supplying custom naming schema implementation. Bad news is, there is no working example or good documentation on the Internet (as far as I know.) So, I have done some investigation and created one!<br />Idea is to use naming schema based on node identifier, as suggested in <a href="http://issues.apache.org/jira/browse/TOMAHAWK-244">TOMAHAWK-244</a>. In the above example, "node 0:1 is expanded" is instead described as "node ROOT:B is expanded" or "node 0:200 is expanded" if ROOT's identifier is 0 and B's identifier is 200 (I think this is typical case where record ids are integers generated by database sequence.)
</div>

<p style="font-weight:bold;">Implementation</p>

<div style="margin-left:20px;">
The following two files are the implementation of the solution.
<ul>
<li>CustomTreeModel.java</li>
<li>CustomTreeWalker.java</li>
</ul>
Naming schema can be customized by implementating TreeWalker interface, but besides that, a custom implementation of TreeModel is needed.<br />
The code uses Java5 generics syntax, but you can remove them if you are using Java1.4.

<hr />
CustomTreeModel.java
<!---------------------------------------------------------------------------->
<pre style="background-color:#e0e0e0;margin-left:20px;">
package example;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.myfaces.custom.tree2.TreeModel;
import org.apache.myfaces.custom.tree2.TreeNode;
import org.apache.myfaces.custom.tree2.TreeState;
import org.apache.myfaces.custom.tree2.TreeStateBase;
import org.apache.myfaces.custom.tree2.TreeWalker;

public class CustomTreeModel implements TreeModel {

    private TreeNode rootTreeNode;
    private TreeState treeState;

    public CustomTreeModel(TreeNode rootTreeNode) {
        this.rootTreeNode = rootTreeNode;
        this.treeState = new TreeStateBase();
    }

    //

    public String[] getPathInformation(String nodeId) {
        if (nodeId == null) {
            throw new IllegalArgumentException("Cannot determine path for a null node.");
        }

        ArrayList<String> pathList = new ArrayList<String>();
        pathList.add(nodeId);
        
        while (nodeId.lastIndexOf(SEPARATOR) != -1) {
            nodeId = nodeId.substring(0, nodeId.lastIndexOf(SEPARATOR));
            pathList.add(nodeId);
        }

        Collections.reverse(pathList);

        return pathList.toArray(new String[0]);
    }

    public boolean isLastChild(String nodeId) {
        if (nodeId.lastIndexOf(SEPARATOR) == -1) { // return true for root node
            return true;
        }

        String parentNodeId = nodeId.substring(0, nodeId.lastIndexOf(SEPARATOR));
        String childIdentifier = nodeId.substring(nodeId.lastIndexOf(SEPARATOR) + 1);

        TreeNode parentNode = getNodeById(parentNodeId);
        TreeNode lastChildNode = (TreeNode)parentNode.getChildren().get(parentNode.getChildCount() - 1);

        return lastChildNode.getIdentifier().equals(childIdentifier);
    }

    public TreeNode getNodeById(String nodeId) {
        if (nodeId == null) return null;

        StringTokenizer st = new StringTokenizer(nodeId, SEPARATOR);

        // check first node identifier equals root node identifier
        if (!rootTreeNode.getIdentifier().equals(st.nextElement())) {
            throw new IllegalArgumentException("Cannot find a root node.");
        }
        
        TreeNode node = rootTreeNode;
        while (st.hasMoreTokens()) {
            String nodeIdentifier = st.nextToken();
            boolean found = false;
            for (TreeNode childNode : (List<TreeNode>)node.getChildren()) {
                if (childNode.getIdentifier().equals(nodeIdentifier)) {
                    node = childNode;
                    found = true;
                    break;
                }
            }
            if (!found) {
                throw new IllegalArgumentException("Cannot find a node. (" + nodeId + ")");
            }
        }

        return node;
    }

    public void setTreeState(TreeState treeState) {
        this.treeState = treeState;
    }

    public TreeState getTreeState() {
        return treeState;
    }

    public TreeWalker getTreeWalker() {
        return new CustomTreeWalker();
    }

    //
    
    public TreeNode getRootTreeNode() {
        return rootTreeNode;
    }

}
</pre>
To use a custom TreeWalker implementation, getTreeWalker() method of TreeModel must be implemented to return it. I first tried just to override getTreeWalker() method of TreeModelBase (the default implementation of TreeModel,) but I found it not possible because part of the code depends of index-based naming schema. So I created a new CustomTreeModel class. getRootTreeNode() is added so that CustomTreeWalker can get access to TreeNode's.
<!---------------------------------------------------------------------------->

<hr />
CustomTreeWalker.java
<!---------------------------------------------------------------------------->
<pre style="background-color:#e0e0e0;margin-left:20px;">
package example;

import java.util.List;
import java.util.Stack;

import org.apache.commons.lang.StringUtils;
import org.apache.myfaces.custom.tree2.TreeModel;
import org.apache.myfaces.custom.tree2.TreeNode;
import org.apache.myfaces.custom.tree2.TreeWalker;
import org.apache.myfaces.custom.tree2.UITreeData;

public class CustomTreeWalker implements TreeWalker {

    private static final String SEPARATOR = TreeModel.SEPARATOR;

    private UITreeData treeData;
    private Stack<String> idStack = new Stack<String>();
    private Stack<TreeNode> nodeStack = new Stack<TreeNode>();
    private boolean checkState = true;
    private boolean startedWalking = false;

    //

    public boolean isCheckState() {
        return checkState;
    }

    public void setCheckState(boolean checkState) {
        this.checkState = checkState;
    }

    public boolean next() {
        if (!startedWalking) { // first step of walking : start from the root
            CustomTreeModel treeModel = (CustomTreeModel)treeData.getDataModel();
            TreeNode rootTreeNode = treeModel.getRootTreeNode();
            treeData.setNodeId(rootTreeNode.getIdentifier());
            idStack.push(rootTreeNode.getIdentifier());
            nodeStack.push(rootTreeNode);

            startedWalking = true;
            return true;
        }

        if (nodeStack.isEmpty()) { // if stack is empty, end the walking
            return false;
        }

        String prevNodeId = idStack.peek();
        TreeNode prevNode = nodeStack.peek();

        if (prevNode.isLeaf()) { // if previous stacked node is a leaf, go up the hierarchy
            idStack.pop();
            nodeStack.pop();
            return next();

        } else {

            String nextNodeId = null;
            TreeNode nextNode = null;

            if (prevNodeId.equals(treeData.getNodeId())) { // going down the hierarchy

                if (checkState) { // if checkState is true, check if the node is expanded and skip it if not.
                    if (!treeData.getDataModel().getTreeState().isNodeExpanded(prevNodeId)) {
                        idStack.pop();
                        nodeStack.pop();
                        return next();
                    }
                }

                nextNode = (TreeNode)prevNode.getChildren().get(0);
                nextNodeId = prevNodeId + SEPARATOR + nextNode.getIdentifier();

            } else { // walk on the same level

                String currentNodeId = treeData.getNodeId();
                String currentNodeIdentifier = treeData.getNode().getIdentifier();
                String parentNodeId = StringUtils.substringBeforeLast(currentNodeId, SEPARATOR);
                TreeNode parentNode = treeData.getDataModel().getNodeById(parentNodeId);

                if (treeData.getDataModel().isLastChild(currentNodeId)) { // go up the hierarchy if last child
                    treeData.setNodeId(parentNodeId);
                    idStack.pop();
                    nodeStack.pop();
                    return next();
                }

                boolean nextIsNext = false;
                for (TreeNode childNode : (List<TreeNode>)parentNode.getChildren()) { // find next child
                    if (nextIsNext) {
                        nextNode = childNode;
                        nextNodeId = parentNodeId + SEPARATOR + nextNode.getIdentifier();
                        break;
                    } else if (childNode.getIdentifier().equals(currentNodeIdentifier)) {
                        nextIsNext = true;
                    }
                }

            }

            treeData.setNodeId(nextNodeId);
            idStack.push(nextNodeId);
            nodeStack.push(nextNode);

            return true;
        }
    }

    public String getRootNodeId() {
        return ((CustomTreeModel)treeData.getDataModel()).getRootTreeNode().getIdentifier();
    }

    public void setTree(UITreeData treeData) {
        this.treeData = treeData;
    }

    public void reset() {
        idStack.empty();
        nodeStack.empty();
        startedWalking = false;
    }

}
</pre>
CustomTreeWalker implementation uses naming schema based on node identifier, separated by colon(:) so it assumes that node identifier does not include any colon. next() method is the main method which walks through the tree nodes the same way as the default TreeWalkerBase does.
<!---------------------------------------------------------------------------->

<hr />
</div>

<p style="font-weight:bold;">Download</p>

<div style="margin-left:20px;">
No download available, so just copy the code above.
</div>

<p style="font-weight:bold;">Copyright?</p>

<div style="margin-left:20px;">
Please use them for free!
</div>
<a name="more"></a>

]]><![CDATA[
]]></content:encoded>
            <category>JSF</category>
      <author>apptaro</author>
                </item>
        <item>
      <link>http://apptaro.seesaa.net/article/21140090.html</link>
      <title>Emulating Disabled Options in IE with DHTML Behaviors</title>
      <pubDate>Fri, 21 Jul 2006 11:32:29 +0900</pubDate>
            <description>Internet Explorer 6 does not implement disabled OPTION's in a SELECT. "Select, Option, Disabled And The JavaScript Solution" talks about a solution to emulate them using JavaScript. In this article, I will show a cleaner solution using DHTML Behaviors.</description>
            <content:encoded><![CDATA[
<p style="font-weight:bold;">Summary</p>

<div style="margin-left:20px;">
Internet Explorer 6 does not implement disabled OPTION's in a SELECT. <a href="http://www.lattimore.id.au/2005/07/01/select-option-disabled-and-the-javascript-solution/">"Select, Option, Disabled And The JavaScript Solution"</a> talks about a solution to emulate them using JavaScript. In this article, I will show a cleaner solution using DHTML Behaviors.
</div>

<p style="font-weight:bold;">Advantages</p>

<div style="margin-left:20px;">
Advantages of my DHTML Behaviors solution over other JavaScript solutions are:
<ul>
<li style="padding-bottom:10px;"><i>No mess with JavaScript</i><br />You don't have to add JavaScript to every SELECT tag which may contain disabled options.<br />If you already have some complicated JavaScript, just leave them as they are.</li>
<li style="padding-bottom:10px;"><i>No mess with DOM operations</i><br />If you already have some complicated DOM operations, just leave them as they are.</li>
<li style="padding-bottom:10px;"><i>Only a small change in CSS</i><br />To implement the solution, you only need to make a small change to existing CSS.</li>
<li style="padding-bottom:10px;"><i>Works completely transparent</i><br />The solution works as if disabled options are supported from the beginning.<br />You can use this solution with JSF, with which it is difficult to change output HTML.</li>
</ul>
</div>

<p style="font-weight:bold;">The Solution</p>

<div style="margin-left:20px;">
To demonstrate the solution, look at the three files below:
<ul>
<li>sample.html</li>
<li>sample.css</li>
<li>sample.htc</li>
</ul>
The first two files are just to test the emulation. The last HTC file is the core of the solution. (Download is available at the bottom of this page.)

<hr />
sample.html
<!---------------------------------------------------------------------------->
<pre style="background-color:#e0e0e0;margin-left:20px;">
&lt;html&gt;
&lt;head&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;sample.css&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;form&gt;
&lt;select&gt;
&lt;option&gt;Option 1&lt;/option&gt;
&lt;option&gt;Option 2&lt;/option&gt;
&lt;option disabled&gt;Option 3&lt;/option&gt;
&lt;/select&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
Above is a simple HTML file with disabled options. Normally, disabled options are displayed as non-disabled options in IE6. Note that external stylesheet sample.css is loaded. If you like, you can internalize it.
<!---------------------------------------------------------------------------->

<hr />
sample.css
<!---------------------------------------------------------------------------->
<pre style="background-color:#e0e0e0;margin-left:20px;">
select, option {
  behavior: url(sample.htc);
}
</pre>
Above is a simple CSS file which will be loaded with the HTML file. DHTML Behaviors are attached to SELECT and OPTION. Actual behaviors are defined in sample.htc.
<!---------------------------------------------------------------------------->

<hr />
sample.htc
<!---------------------------------------------------------------------------->
<pre style="background-color:#e0e0e0;margin-left:20px;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&gt;
&lt;PUBLIC:COMPONENT LIGHTWEIGHT=&quot;true&quot;&gt;
&lt;PUBLIC:ATTACH EVENT=&quot;ondocumentready&quot; ONEVENT=&quot;onDocumentReady()&quot; /&gt;
&lt;PUBLIC:ATTACH EVENT=&quot;ondetach&quot; ONEVENT=&quot;onDetach()&quot; /&gt;

&lt;SCRIPT type=&quot;text/javascript&quot;&gt;
//&lt;![CDATA[

var nLastSelectedIndex;
var fOnChangeOriginal;

// event handlers

function onDocumentReady() {
  var sTag = element.tagName.toLowerCase();
  if (sTag == &quot;select&quot;) {
    attachEvent(&quot;onchange&quot;, onChangeSelect);
    attachEvent(&quot;onpropertychange&quot;, onPropertyChangeSelect);
    nLastSelectedIndex = element.selectedIndex;
    hackOnChange();
  } else if (sTag == &quot;option&quot;) {
    attachEvent(&quot;onpropertychange&quot;, onPropertyChangeOption);
    emulateOption();
  }
}

function onDetach() {
  var sTag = element.tagName.toLowerCase();
  if (sTag == &quot;select&quot;) {
    detachEvent(&quot;onchange&quot;, onChangeSelect);
    detachEvent(&quot;onpropertychange&quot;, onPropertyChangeSelect);
  } else if (sTag == &quot;option&quot;) {
    detachEvent(&quot;onpropertychange&quot;, onPropertyChangeOption);
  }
}

//

function onChangeSelect() {
  if (element.options[element.selectedIndex].disabled) {
    element.selectedIndex = nLastSelectedIndex;
  } else {
    nLastSelectedIndex = element.selectedIndex;
    if (fOnChangeOriginal != undefined) {
      fOnChangeOriginal();
    }
  }
}

function onPropertyChangeSelect() {
  var sChangedPropertyName = event.propertyName.toLowerCase();
  if (sChangedPropertyName == &quot;onchange&quot;) {
    hackOnChange();
  } else if (sChangedPropertyName == &quot;selectedindex&quot;) { // contributed by Zecc
    nLastSelectedIndex = element.selectedIndex;
  }
}

function onPropertyChangeOption() {
  var sChangedPropertyName = event.propertyName.toLowerCase();
  if (sChangedPropertyName == &quot;disabled&quot;) {
    emulateOption();
  }
}

// hack onChange attribute of select tag

function hackOnChange() {
  detachEvent(&quot;onpropertychange&quot;, onPropertyChangeSelect);
  fOnChangeOriginal = element.onchange;
  element.onchange = null;
  attachEvent(&quot;onpropertychange&quot;, onPropertyChangeSelect);
}

// emulate disabled option

function emulateOption() {
  if (element.disabled) {
    element.style.color = &quot;graytext&quot;;
  } else {
    element.style.color = &quot;menutext&quot;;
  }
}

//]]&gt;
&lt;/SCRIPT&gt;
&lt;/PUBLIC:COMPONENT&gt;
</pre>
Above is a core HTC file which defines the DHTML Behaviors for SELECT and OPTION. What it does are:
<ul>
<li style="padding-bottom:10px;"><i>onDocumentReady, onDetach</i><br />Initialization and cleaning up.</li>
<li style="padding-bottom:10px;"><i>onChangeSelect</i><br />When a selection is changed for a SELECT, check if the selected option is disabled. If it is disabled, revert the selection. If it is not disabled, save the selectedIndex for later use, and run the original onchange event handler set by user.</li>
<li style="padding-bottom:10px;"><i>onPropertyChangeSelect</i><br />When a onchange event handler is set or changed for a SELECT, save the event handler for later use and set the onchange to null. This ensures that onChangeSelect above is called, which then calls the onchange event handler set by the user. This mechanism is needed because, by the spec, a onchange event handler set in a HTML file is called before a onchange event handler in a HTC file. This makes it possible to work consistently when there is a onchange event handler in the HTML or there is JavaScript/DOM manipulations.<br /><b>Contributed by Zecc:</b><br />When a selectedindex value is set or changed for a SELECT, save the value for later use. This makes it possible to work consistently when there is JavaScript/DOM manipulations.</li>
<li style="padding-bottom:10px;"><i>onPropertyChangeOption</i><br />When a disabled attribute is changed for a OPTION, change the text color of the OPTION, so it will look enabled or disabled. This makes it possible to work consistently when there is JavaScript/DOM manipulations.</li>
</ul>
<!---------------------------------------------------------------------------->

<hr />
</div>

<p style="font-weight:bold;">Download</p>

<div style="margin-left:20px;">
You can download zipped files from <a href="http://apptaro.up.seesaa.net/files/sample.zip">here</a>.<br />Download includes sample-javascript.html which is a little more complicated version of sample.html, to demonstrate how the solution works when there are some JavaScript/DOM manipulations.<br />Note that if you open HTML files from a local directory, IE will display a alert, but this will not happen when you open the files from a web server.
</div>

<p style="font-weight:bold;">Copyright?</p>

<div style="margin-left:20px;">
This solution is dedicated to the public domain.
</div>
<a name="more"></a>

]]><![CDATA[
]]></content:encoded>
            <category>HTML</category>
      <author>apptaro</author>
                  <enclosure url="http://apptaro.up.seesaa.net/files/sample.zip" length="1826" type="application/x-zip" />
                            </item>
        <item>
      <link>http://match.seesaa.jp/ot_listing.pl?aid=87275&amp;sid=apptaro&amp;tid=seesaa_hotspot&amp;k=%E8%A5%BF%E6%9D%A1%E9%AB%98%E6%A0%A1&amp;hid=35</link>
      <title>[PR]注目のキーワード「西条高校」</title>
      <pubDate>Fri, 21 Jul 2006 11:32:29 +0900</pubDate>
            <description><![CDATA[
<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E7%94%B2%E5%AD%90%E5%9C%92&hid=35">甲子園</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E4%BB%A3%E8%A1%A8&hid=35">代表</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E6%84%9B%E5%AA%9B%E7%9C%8C&hid=35">愛媛県</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E6%98%A5&hid=35">春</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E7%BE%8E&hid=35">美</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E7%94%B2%E5%AD%90%E5%9C%92%E5%87%BA%E5%A0%B4&hid=35">甲子園出場</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E6%AF%8D%E6%A0%A1&hid=35">母校</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E5%84%AA%E5%8B%9D&hid=35">優勝</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E5%A4%8F&hid=35">夏</a>&nbsp;|&nbsp;<a href="http://match.seesaa.jp/ot_listing.pl?aid=87275&sid=apptaro&tid=seesaa_hotspot&k=%E5%87%BA%E5%A0%B4&hid=35">出場</a>
]]></description>
      <author>ads by Seesaa</author>
          </item>
      </channel>
</rss>
