小ネタ tablesorterをデフォルトで複数行ソートにする

ほんとは今日はSeam2.1.0GAのRSSサポートを技術検証しようと思ってたのですが、
さっき帰宅したばっかりなので今日は小ネタだけにします。


jQueryプラグインのtablesorterは、shiftキーを押しながらヘッダ行をクリックすると
複数行ソート、そのまま押せば単一行ソートになります。
では、はじめから複数行ソートだけにしたい時はどうすれば良いか?
そのまま真っ正直に使おうとするとこれは実現できません。もちろん改造すればOKですが、
できれば改造なしで複数行ソートをデフォルトにしたいものです。

tablesorterには"sortMultiSortKey"というオプションがあります。これは、複数行ソートする
ためのキーはどれにしますか?というもので、デフォルトは"shiftkey"になっています。
この値はjs内でイベントオブジェクト(window.event)に使われます。

問題の部分のjsはこのようになっています。

// user only whants to sort on one column
if(!e[config.sortMultiSortKey]) {
	
	// flush the sort list
	config.sortList = [];
	
	if(config.sortForce != null) {
		var a = config.sortForce; 
		for(var j=0; j < a.length; j++) {
			if(a[j][0] != i) {
				config.sortList.push(a[j]);
			}
		}
	}
	
	// add column to sort list
	config.sortList.push([i,this.order]);

// multi column sorting
} else {
	// the user has clicked on an all ready sortet column.
	if(isValueInArray(i,config.sortList)) {	 
		
		// revers the sorting direction for all tables.
		…
	}
	…
}

「if(!e[config.sortMultiSortKey]) {」が常にelseを通るようにすれば良いので、
解決方法は「sortMultiSortKeyにtypeと設定する」になります。
クリックというイベントは必ず発生しているからです。


本家サイトのサンプルの書き方を真似れば

$(document).ready(function() 
    { 
        $("#myTable").tablesorter( {sortMultiSortKey: 'type'} ); 
    } 
); 

となる、という事ですね。これでデフォルトで複数行ソートになります。

SeamでModalDialog風->Wizardなページ遷移(1)


これを実現するのに、はじめはjqModalとかを改造して…
と思っていたのだけれども、そもそもSeam(というかRichFacesとAjax4jsf)
に存在する機能だそうで、それらを使ってやってみます。

という事で、まずはWizardな感じからやってみましょうかと。
(ちなみに私のSeamは2.0.2SP1です。ちと古い?)

a4j:includeタグ。こいつを使えばWizard風味の埋め込み画面が実現できるそうな。
http://jsfunit.demo.jboss.com/jboss-jsfunit-examples-richfaces/richfaces/include.jsf
こいつですな。


ui:includeとはべつものです。これはFaceletsのタグですね。
で、ui:includeを上図の形式で使いつつ、目的のa4j:includeタグを
上図のtemplate側(図だと左側)に書いてしまうと、なぜだか何だかうまくいかないのです。
具体的にNGなところはというと、
 pages.xmlへの記述は読み取ってくれない(がfaces-config.xmlならOK)
という事です。ソースから原因を調べる元気はありませんでした。。
んー、まいっか。

a4j:includeタグを直接templateには入れずに別xhtmlにして、そいつをtemplateから
ui:includeタグを使ってインクルードさせるようにしたら解決しました。
きっとそうゆうもんなんだろうなー。。

あ、あと、pages.xmlの遷移先ページを記述するタグに注意。

https://jira.jboss.org/jira/browse/JBSEAM-2254

遷移先指定のタグはredirectじゃなくてrenderで!


続きはまた後日

最近忙しくて書けない…

なので、一文だけ。。

http://mainichi.jp/enta/mantan/graph/anime/20080730/15.html
だ~れだ!?

いやー、この写真。ぱっと見「ザブングルの加藤歩氏」か…
と思ってしまうが、もうちょっと見てみると
ハイキングウォーキング鈴木Q太郎氏」なわけですよ。
もうQ太郎氏にしか見えない。
だまし絵的な一枚でしたね。


★ホントはきれいな人なんだろうと思います!

EdBrowserおもしろいじゃん

しかじろうさんが作られたEdoBrowser(id:re_shikajiro:20080913)というものが
MashupAword4で受賞したそうな。
触ってみたらこれおもしろい。受賞に納得。

ちなみにコレ、EdBrowserで「憂木瞳」をsearchしてみるの図。
やっぱ豊丸より憂木瞳がいいな。


…さすがに心が痛くなったので
「マリ○ブラザーズ」でsearchしてみた結果を下に載せてみる。。

継承して差分だけでSeamカスタムタグをつくる

さっそく継承でやりなおしてみました。
レンダラは元々のやつを使えば良いので自作しません。なのでファイル数も減りました。

org.richfaces.component.html.HtmlDataList2

package org.richfaces.component.html;

import java.util.Iterator;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.model.DataModel;
import org.richfaces.component.UIDataList;

/**
 * @author tagawaman
 *
 */
@SuppressWarnings("all")
public class HtmlDataList2 extends HtmlDataList {

	private String _preVar;
	public static final String COMPONENT_TYPE = "org.richfaces.DataList2";
	public static final String COMPONENT_FAMILY = "org.richfaces.DataList2";

    public HtmlDataList2() {
    	super();
    	_preVar = null;
	}

	public void setPreVar(String __preVal) {
		_preVar = __preVal;
	}

	public String getPreVar() {
		if (null != _preVar)
			return _preVar;
		ValueBinding vb = getValueBinding("preVar");
		if (null != vb)
			return (String) vb.getValue(getFacesContext());
		else
			return null;
	}

	protected void setupVariable(FacesContext faces, DataModel localModel, boolean rowSelected) {
		Map attrs = faces.getExternalContext().getRequestMap();
		if (rowSelected && isRowAvailable()) {
			setupVariable(getPreVar(), attrs, getNowVariable(getVar(), attrs));
			setupVariable(getVar(), attrs, localModel.getRowData());
			setupVariable(getStateVar(), attrs, getComponentState());
			setupVariable(getRowKeyVar(), attrs, getRowKey());
		} else {
			removeVariable(getPreVar(), attrs);
			removeVariable(getVar(), attrs);
			removeVariable(getStateVar(), attrs);
			removeVariable(getRowKeyVar(), attrs);
		}
	}

	private Object getNowVariable(String var, Map attrs) {
		return var != null ? attrs.get(var) : null;
	}

	private void setupVariable(String var, Map attrs, Object rowData) {
		if (var != null)
			attrs.put(var, rowData);
	}

	private void removeVariable(String var, Map attrs) {
		if (var != null)
			attrs.remove(var);
	}

	public Object saveState(FacesContext context) {
                Object values[] = new Object[12];
                values[0] = super.saveState(context);
                values[1] = getTitle();
                values[2] = getRowClasses();
                values[3] = getFooterClass();
                values[4] = getDir();
                values[5] = getStyleClass();
                values[6] = getStyle();
                values[7] = getLang();
                values[8] = getType();
                values[9] = getColumnClasses();
                values[10] = getHeaderClass();
                values[11] = _preVar;
                return ((Object) (values));
	}

	public void restoreState(FacesContext context, Object state) {
                Object values[] = (Object[])(Object[])state;
                super.restoreState(context, values[0]);
                setTitle((String)values[1]);
                setRowClasses((String)values[2]);
                setFooterClass((String)values[3]);
                setDir((String)values[4]);
                setStyleClass((String)values[5]);
                setStyle((String)values[6]);
                setLang((String)values[7]);
                setType((String)values[8]);
                setColumnClasses((String)values[9]);
                setHeaderClass((String)values[10]);
                _preVar = (String) values[11];
	}

	public String getFamily() {
		return "org.richfaces.DataList2";
	}
}

org.richfaces.taglib.DataList2Tag

package org.richfaces.taglib;

import javax.faces.component.UIComponent;

/**
 * @author tagawaman
 *
 */
public class DataList2Tag extends DataListTag {
	private String _preVar;

    public DataList2Tag(){
    	super();
    	_preVar = null;
    }

    public String get_preVar() {
	return _preVar;
    }

    public void set_preVar(String preVar) {
	this._preVar = preVar;
    }

    public void release(){
        super.release();
	_preVar = null;
    }

    protected void setProperties(UIComponent component){
        super.setProperties(component);
	setStringProperty(component, "preVar", _preVar);
    }

    public String getComponentType() {
	return "org.richfaces.DataList2";
    }
}

faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
                              "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
	<component>
		<description />
		<component-type>org.richfaces.DataList2</component-type>
		<component-class>
			org.richfaces.component.html.HtmlDataList2
		</component-class>

		<component-extension>
			<component-family>org.richfaces.DataList2</component-family>
			<renderer-type>
				org.richfaces.DataListRenderer
			</renderer-type>
		</component-extension>
	</component>
	<render-kit>
		<render-kit-id>HTML_BASIC</render-kit-id>
		<renderer>
			<component-family>org.richfaces.DataList2</component-family>
			<renderer-type>
				org.richfaces.DataListRenderer
			</renderer-type>
			<renderer-class>
				org.richfaces.renderkit.html.DataListRenderer
			</renderer-class>
		</renderer>
	</render-kit>
</faces-config>

rich.taglib.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE facelet-taglib PUBLIC
  "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
  "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
<facelet-taglib xmlns="http://java.sun.com/JSF/Facelet">
	<namespace>http://richfaces.org/rich</namespace>
	<tag>
		<tag-name>dataList2</tag-name>
		<component>
			<component-type>org.richfaces.DataList2</component-type>
			<renderer-type>
				org.richfaces.DataListRenderer
			</renderer-type>
		</component>
	</tag>
</facelet-taglib>

rich.tld

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
                        "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
	<tlib-version>1.2</tlib-version>
	<jsp-version>1.2</jsp-version>
	<short-name>rich</short-name>
	<uri>http://richfaces.org/rich</uri>
	<display-name>RichFaces</display-name>
	<description>RichFaces components</description>


	<tag>
		<name>dataList2</name>
		<tag-class>org.richfaces.taglib.DataList2Tag</tag-class>
		<body-content>JSP</body-content>
		<description />
		<attribute>
			<name>title</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				Advisory title information about markup elements
				generated for this component
			</description>
		</attribute>
		<attribute>
			<name>rowClasses</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				A comma-delimited list of CSS style classes that is
				applied to popup table rows. A space separated list of
				classes may also be specified for any individual row.
				The styles are applied, in turn, to each row in the
				table. For example, if the list has two elements, the
				first style class in the list is applied to the first
				row, the second to the second row, the first to the
				third row, the second to the fourth row, etc. In other
				words, we keep iterating through the list until we reach
				the end, and then we start at the beginning again
			</description>
		</attribute>
		<attribute>
			<name>stateVar</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				The attribute provides access to a component state on
				the client side
			</description>
		</attribute>
		<attribute>
			<name>componentState</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				It defines EL-binding for a component state for saving
				or redefinition
			</description>
		</attribute>
		<attribute>
			<name>rowKeyVar</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				The attribute provides access to a row key in a Request
				scope
			</description>
		</attribute>
		<attribute>
			<name>rendered</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				If "false", this component is not rendered
			</description>
		</attribute>
		<attribute>
			<name>footerClass</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				Space-separated list of CSS style class(es) that are be
				applied to any footer generated for this table
			</description>
		</attribute>
		<attribute>
			<name>dir</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				Direction indication for text that does not inherit
				directionality. Valid values are "LTR" (left-to-right)
				and "RTL" (right-to-left)
			</description>
		</attribute>
		<attribute>
			<name>id</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				Every component may have a unique id that is
				automatically created if omitted
			</description>
		</attribute>
		<attribute>
			<name>styleClass</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				Corresponds to the HTML class attribute
			</description>
		</attribute>
		<attribute>
			<name>ajaxKeys</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				This attribute defines rows that are updated after an
				AJAX request
			</description>
		</attribute>
		<attribute>
			<name>rowKey</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				RowKey is a representation of an identifier for a
				specific data row
			</description>
		</attribute>
		<attribute>
			<name>style</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				CSS style(s) is/are to be applied when this component is
				rendered
			</description>
		</attribute>
		<attribute>
			<name>var</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				A request-scope attribute via which the data object for
				the current row will be used when iterating
			</description>
		</attribute>
		<attribute>
			<name>preVar</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				A request-scope attribute via which the data object for
				the pre-row will be used when iterating
			</description>
		</attribute>
		<attribute>
			<name>value</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				The current value for this component
			</description>
		</attribute>
		<attribute>
			<name>rows</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				A number of rows to display, or zero for all remaining
				rows in the table
			</description>
		</attribute>
		<attribute>
			<name>lang</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				Code describing the language used in the generated
				markup for this component
			</description>
		</attribute>
		<attribute>
			<name>type</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				Corresponds to the HTML DL type attribute
			</description>
		</attribute>
		<attribute>
			<name>columnClasses</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				Comma-delimited list of CSS style classes that are be
				applied to the columns of this table. A space separated
				list of classes may also be specified for any individual
				column. If the number of elements in this list is less
				than the number of columns specified in the "columns"
				attribute, no "class" attribute is output for each
				column greater than the number of elements in the list.
				If the number of elements in the list is greater than
				the number of columns specified in the "columns"
				attribute, the elements at the position in the list
				after the value of the "columns" attribute are ignored
			</description>
		</attribute>
		<attribute>
			<name>first</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				A zero-relative row number of the first row to display
			</description>
		</attribute>
		<attribute>
			<name>headerClass</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				Space-separated list of CSS style class(es) that are be
				applied to any header generated for this table
			</description>
		</attribute>
		<attribute>
			<name>binding</name>
			<rtexprvalue>false</rtexprvalue>
			<description>
				The attribute takes a value-binding expression for a
				component property of a backing bean
			</description>
		</attribute>
	</tag>
</taglib>

以上。どっちも同じ機能だけど、やっぱり楽な方がいいよね。
継承したやつ使おっと。

全部もりもり書いてSeamカスタムタグをつくる

繰り返し系のタグで、「リストの中から1つ前の値が欲しい」時があるわけです。

1つ前の値と比較して、違っていたらxxする…とか。

福岡県
 北九州市
name age sex
taro 40 MAN
jiro 35 MAN
 福岡市
name age sex
hana 20 WOMAN
大分県
 別府市
name age sex
aiko 18 WOMAN

これをSeamで出力しようとすると、dataListやdataTable等の繰り返しタグを入れ子にして、
この例だと3段階の入れ子?にしないといけません。
リストの1つ前の値が取れれば、入れ子にせずにdataList1つだけで
できるのになぁ。
というわけでつくってみました。

こんな感じのタグです。

<rich:dataList2 var="u" preVar="p" value="#{users}">
  <s:div rendered="ここにEL式でuとpを使って比較"></s:div></rich:dataList2>

preVar属性に、前回(1繰り返し前)にvar属性で指定されたオブジェクトが入ります。

以下が、拡張タグのコードになります。

続きを読む