モノノフ日記

普通の日記です

Ajax.InPlaceEditorでフィールド値がemptyの時の動作

Ajax.InPlaceEditorで対象ブロックがブランクの時に指定した文字列(ex. "click to edit..."とか)を表示させる事ができるんですが、1.5.1では正常に動いてたのに、1.6.0.2にすると一部分動作しない箇所があって困ってました。

動かなくなってたのは動作をキャンセルしたり、input内の入力文字が空っぽのままsubmitしたときに指定文字列が表示されずに終了してしまう、という現象。

script.aculo.us公式Wiki*1で公開されてた拡張コード
/*
 * InPlaceEditor extension that adds a 'click to edit' text when the field is 
 * empty.
 */
Ajax.InPlaceEditor.prototype.__initialize = Ajax.InPlaceEditor.prototype.initialize;
Ajax.InPlaceEditor.prototype.__getText = Ajax.InPlaceEditor.prototype.getText;
Ajax.InPlaceEditor.prototype.__onComplete = Ajax.InPlaceEditor.prototype.onComplete;
Ajax.InPlaceEditor.prototype = Object.extend(Ajax.InPlaceEditor.prototype, {

    initialize: function(element, url, options){
        this.__initialize(element,url,options)
        this.setOptions(options);
        this._checkEmpty();
    },

    setOptions: function(options){
        this.options = Object.extend(Object.extend(this.options,{
            emptyText: 'click to edit...',
            emptyClassName: 'inplaceeditor-empty'
        }),options||{});
    },

    _checkEmpty: function(){
        if( this.element.innerHTML.length == 0 ){
            this.element.appendChild(
                Builder.node('span',{className:this.options.emptyClassName},this.options.emptyText));
        }
    },

    getText: function(){
        document.getElementsByClassName(this.options.emptyClassName,this.element).each(function(child){
            this.element.removeChild(child);
        }.bind(this));
        return this.__getText();
    },

    onComplete: function(transport){
        this._checkEmpty();
        this.__onComplete(transport);
    }
});

ページ開いたときは指定文字が表示されていたのでinitializeは大丈夫そう。たぶんonCompleteがちゃんと動作していない。

こりゃprototype.jsのソース読むしかないなぁ、と思いながらググってたらソース解説してくれてる記事を発見!gihyo++
script.aculo.usを読み解く:第3回 controls.js(後編) InPlaceEditor |gihyo.jp … 技術評論社

解説記事の出だしに、

それでは,実際にcontrols.jsの後半部分から,Ajax.InPlaceEditorのコードを見ていきましょう。

去年の夏に書き直されたおかげで,よく整理されていて理解しやすいコードです。

http://gihyo.jp/dev/feature/01/scriptaculous/0003?page=1

どうやらInPlaceEditorは1.6.Xでフルスクラッチされた様子。。
ザッと解説見ても、Ajax.InPlaceEditor.prototype.onCompleteプロパティ自体が無い。

それで代わりになるのはどれなのかなと調べてたら、leaveEditModeが相当するみたいです。

I've been toying with a fix for it, basically what it looks like is back in the summer from 1.6 - 1.8 InplaceEditor was completely rewrote.... onComplete is no longer called when you leave the field so it never checks to see if its empty.

If you change the code to read

FROM
Ajax.InPlaceEditor.prototype.__onComplete = Ajax.InPlaceEditor.prototype.onComplete;
TO
Ajax.InPlaceEditor.prototype.__onComplete = Ajax.InPlaceEditor.prototype.leaveEditMode;

and

FROM
onComplete: function(transport){
TO
leaveEditMode: function(transport){

http://codingforums.com/showthread.php?t=135868

leaveEditModeを使って、上のコードを書き換えます。

/*
 * InPlaceEditor extension that adds a 'click to edit' text when the field is 
 * empty.
 */
Ajax.InPlaceEditor.prototype.__initialize = Ajax.InPlaceEditor.prototype.initialize;
Ajax.InPlaceEditor.prototype.__getText = Ajax.InPlaceEditor.prototype.getText;
Ajax.InPlaceEditor.prototype.__onComplete = Ajax.InPlaceEditor.prototype.leaveEditMode;
Ajax.InPlaceEditor.prototype = Object.extend(Ajax.InPlaceEditor.prototype, {

    initialize: function(element, url, options){
        this.__initialize(element,url,options)
        this.setOptions(options);
        this._checkEmpty();
    },

    setOptions: function(options){
        this.options = Object.extend(Object.extend(this.options,{
            emptyText: 'click to edit...',
            emptyClassName: 'inplaceeditor-empty'
        }),options||{});
        if (this.options.externalControl)
          this.options.externalControl = $(this.options.externalControl);
    },

    _checkEmpty: function(){
        if( this.element.innerHTML.length == 0 ){
            this.element.appendChild(
                Builder.node('span',{className:this.options.emptyClassName},this.options.emptyText));
        }
    },

    getText: function(){
        document.getElementsByClassName(this.options.emptyClassName,this.element).each(function(child){
            this.element.removeChild(child);
        }.bind(this));
        return this.__getText();
    },

    leaveEditMode: function(transport){
        this._checkEmpty();
        this.__onComplete(transport);
    }
});

ちゃんと動いた!これで1.6.0.2に移行できるー!
ただenterEditModeを利用するたびにエラーが返ってくるのは気になる。。
動作自体はちゃんと動いているので、とりあえずOKとしましたけど。
ちなみにFirebugがこんなエラーを拾ってます。

event.preventDefault is not a function
http://xxx.test.com/sfPrototypePlugin/js/prototype.js
Line 3801

*1:今Rebuild中のようでsnapshot状態ですが