/* -----------------------------------------------------------------------------------
 *
 *  QuickEdit - version 1.0.5
 *  by seratch (Kazuhiro Sera) - http://seratch.net/
 *  Last Modification: 06/07/2009
 *
 *  This library requires prototype.js (using Class, Ajax.Request, Ajax.Updater)
 *
 *  QuickEdit is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://seratch.net/works/js/quickedit/
 *
 * ----------------------------------------------------------------------------------- */

// QuickEdit class

var QuickEdit = Class.create();

// version information
QuickEdit.Version = '1.0.5';

// common function
QuickEdit.CommonUtil = {
    $id : function(argId) {
        return document.getElementById(argId);
    },
    $tag : function(argTagName) {
        return document.getElementsByTagName(argTagName);
    },
    newElement : function(tagName) {
        return document.createElement(tagName);
    },
    notityUser : function(message) {
        window.alert(message);
    },
    confirmUser : function(message) {
        return window.confirm(message);
    },
    reload : function() { location.reload(); }
}

QuickEdit.HandlerType = {};
QuickEdit.HandlerType.Update = 'Update';
QuickEdit.HandlerType.Delete = 'Delete';
QuickEdit.HandlerType.Insert = 'Insert';
QuickEdit.HandlerType.Default = QuickEdit.HandlerType.Update;

// Options - for customize this library 
QuickEdit.Options = {};

// Language Setting
QuickEdit.Options.Lang = {};
QuickEdit.Options.Lang.Japanese = { 
    AUTO_BIND_ERROR       : '自動バインド設定が不正です。見直して下さい。', 
    SUBMIT                : '確定', 
    CANCEL                : 'キャンセル', 
    SERVER_ERROR_MESSAGE  : 'サーバアクセスでエラーが発生しました。', 
    DELETE_CONFIRM        : '本当に削除してもよろしいですか？' 
}; 
QuickEdit.Options.Lang.English = { 
    AUTO_BIND_ERROR       : 'Auto Bind Error! Please Check Again!', 
    SUBMIT                : 'Submit', 
    CANCEL                : 'Cancel', 
    SERVER_ERROR_MESSAGE  : 'Server Access Error Occurred!', 
    DELETE_CONFIRM        : 'Delete OK?' 
}; 
QuickEdit.Options.Lang.Default = QuickEdit.Options.Lang.Japanese;

// Ajax Request Mode Setting
QuickEdit.Options.Mode = {};
QuickEdit.Options.Mode.AjaxRequest = 'AjaxReuqest';
QuickEdit.Options.Mode.AjaxRequestReload = 'AjaxReuqestReload';
QuickEdit.Options.Mode.AjaxUpdater = 'AjaxUpdater';

// Editor Setting
// Mapping element classname and editor config
QuickEdit.Options.EditorConfig = {};
QuickEdit.Options.EditorConfig.Type = {
    TextArea : "textarea",
    InputText : "inputtext"
};
QuickEdit.Options.EditorConfig.Default = { 
    type : 'textarea',
    width : 30,
    height : 3
};
QuickEdit.Options.EditorConfig.ClassList = {};

// Fixed Common Setting (maybe not used)
QuickEdit.Options.Common = {};
QuickEdit.Options.Common.AjaxMethod = 'post';
QuickEdit.Options.Common.SubmitUrl = '';
QuickEdit.Options.Common.CancelUrl = '';

// Editable : attach event handler
QuickEdit.editable = function(initParams) {
    var trigger = document.getElementById(initParams['triggerId']);
    // TODO: default handler alive
    trigger.onclick = new QuickEdit().handler(initParams);
};

// Deletable : attach event handler
QuickEdit.deletable = function(initParams) {
    var trigger = document.getElementById(initParams['triggerId']);
    // TODO: default handler alive
    trigger.onclick = new QuickEdit().handler(initParams);
};

// Insertable : attach event handler
QuickEdit.insertable = function(initParams) {
    var trigger = document.getElementById(initParams['triggerId']);
    // TODO: default handler alive
    trigger.onsubmit= new QuickEdit().handler(initParams);
};

QuickEdit.prototype = {

    initialize : function() {},

    /* main function
     * executed by event handler */
    handler : function(initParams) {

        var util = QuickEdit.CommonUtil;
        var qeOptions = QuickEdit.Options;

        /* private properties on each instance */

        // basic setting
        var isActive = false;
        var lang = (initParams.lang != undefined) 
                       ? initParams.lang 
                       : QuickEdit.Options.Lang.Default;
        var handlerType = (initParams.handlerType != undefined) 
                              ? initParams.handlerType 
                              : QuickEdit.HandlerType.Default;

        // ajax setting
        var mode;
        if ( handlerType == QuickEdit.HandlerType.Update ) {
            mode = qeOptions.Mode.AjaxUpdater;
        } else if ( handlerType == QuickEdit.HandlerType.Delete
            || handlerType == QuickEdit.HandlerType.Insert ) {
            mode = qeOptions.Mode.AjaxRequestReload;
        } else {
            mode = qeOptions.Mode.Default;
        }
        var submitUrl = (initParams.submitUrl != undefined)
                            ? initParams.submitUrl
                            : QuickEdit.Options.Common.SubmitUrl;
        var cancelUrl = (initParams.cancelUrl != undefined)
                            ? initParams.cancelUrl
                            : QuickEdit.Options.Common.CancelUrl;

        // ajax event listener
        var reload = function() { util.reload(); };
        var handleAjaxRequestError = function(request) { 
            util.notityUser(InnerValueManager(lang)('SERVER_ERROR_MESSAGE'));
        };
        
        // ajax request setting
        var reqSettings = {
            url : undefined
        };
        var reqParams = (function () {
            var _reqParams = initParams.reqParams;
            // prototype.js old version
            var pv = Prototype.Version.split('.');
            var prototypeVer = pv[0] + '.' + pv[1];
            return function(k, v) {

                // all clear
                if ( k == 'clear' ) { _reqParams = {}; return; }

                // setter
                if ( k != undefined && v != undefined ) { _reqParams[k] = v; return; }

                // getter
                if ( prototypeVer < 1.6 ) {
                    var reqParamsString = '?';
                    for (var each in _reqParams) {
                        if ( each != undefined ) { 
                            reqParamsString += each + '=' + _reqParams[each] + '&'; 
                        }
                    }
                    reqParamsString = reqParamsString.replace(/\&$/, '');
                    return reqParamsString;
                } else {
                    return _reqParams;
                }

            };
        })();
        var reqOptions = (function() {
            var requestOptions = { method : qeOptions.Common.AjaxMethod, 
                parameters : reqParams(),
                onFailure  : handleAjaxRequestError 
            };
            var updaterOptions = { method : qeOptions.Common.AjaxMethod, 
                parameters : reqParams(),
                onFailure  : handleAjaxRequestError 
            };
            return function(type, newOptions) {
                if ( newOptions != undefined ) {
                    if ( type == qeOptions.Mode.AjaxRequest 
                        || type == qeOptions.Mode.AjaxRequestReload ) {
                        requestOptions = newOptions
                    } else { updaterOptions = newOptions; }
                } else {
                    if ( type == qeOptions.Mode.AjaxRequest ) {
                        requestOptions.parameters = reqParams();
                        return requestOptions;
                    } else if ( type == qeOptions.Mode.AjaxUpdater ) {
                        updaterOptions.parameters = reqParams();
                        return updaterOptions;
                    }
                }
            };
        })();

        // target element setting
        var targetId = initParams.targetId;
        var target = util.$id(targetId);

        // common editor property
        var editor;

        // trigger element setting
        var triggerId = initParams.triggerId;
        var trigger = util.$id(triggerId);
        var triggerValue;
        var triggerBackup;
        
        // cancel trigger element setting
        var cancelTrigger;
        var cancelTriggerId;

        // element inner string value manager
        // (innerHTML or value)
        var InnerValueManager = function(lang) {
            return function(k, v) { 
                // getter 
                if ( k != undefined && v == undefined ) return lang[k]; 
                // settter 
                if ( k != undefined && v != undefined ) { lang[k] = v; return null; } 
            };
        };

        // set element inner string value
        var setInnerValue = function(el, str) {
            if ( el.value != undefined ) { 
                el.value = str;
            } else if ( el.innerHTML != undefined ) { 
                el.innerHTML = str;
            }
        };

        // get element inner string value
        var getInnerValue = function(el) {
            return ( el.value != undefined ) ? el.value : el.innerHTML;
        };

        // convert html text to plain text
        // (remove BR tag and so on)
        var getPlainTextValue = function(el) {
            var inputText = getInnerValue(el);
            inputText = inputText.replace(/\r([^\n])/g, '\r\n$1')
                            .replace(/([^\r])\n/g, '$1\r\n')
                            .replace(/^\s$/g, '\r\n')
                            .replace(/^\s+|\s+$/g, '')
                            .replace(/<(br|BR)+?\s*?\/?\s*?>(\r\n)?/g, '\r\n')
                            .replace(/<[\s]*?[aA].*?>((http|https):\/\/[0-9a-z-\/._?=&%\[\]~]+)<\s*?\/?\s*?[aA]\s*?>/g, '$1');
            return inputText;
        }
        // convert plain text to html text
        // ( CR/LF to BR tag and so on)
        var getHTMLDecoratedValue = function(el) {
            var inputText = getInnerValue(el);
            inputText = inputText.replace(/\r([^\n])/g, '\r\n$1')
                            .replace(/([^\r])\n/g, '$1\r\n')
                            .replace(/\r\n/g, '<br/>\r\n');
            return inputText;
        }

        // get element className
        var getClassName = function(target) {
            return (target.className != undefined && target.className != '') 
                       ? target.className : 'default';
        };

        // return editorConfig setting
        var editorConfig = (function() {
            var editorConfigList = {};
            editorConfigList['default'] = qeOptions.EditorConfig.Default;
            for ( var className in qeOptions.EditorConfig.ClassList ){
                editorConfigList[className] = 
                    qeOptions.EditorConfig.ClassList[className];
            }
            return function(k, v) {
                if ( v != undefined ) {
                    editorConfigList.k = v;
                } else {
                    if ( k == undefined || k == '' ) k = 'default';
                    return editorConfigList[k];
                }
            };
        })();

        // get editor object
        var getEditor = function(editorConfig) {
            if ( editorConfig.type == qeOptions.EditorConfig.Type.TextArea ) {
                var txtElement = util.newElement('textarea'); 
                txtElement.id = target.id + '_input';
                txtElement.value = target.innerHTML; 
                txtElement.cols = editorConfig.width;
                txtElement.rows = editorConfig.height;
                return txtElement;
            } else if ( editorConfig.type == qeOptions.EditorConfig.Type.InputText ) {
                var inptElement = util.newElement('input'); 
                inptElement.id = target.id + '_input';
                inptElement.type = 'text'; 
                inptElement.size = editorConfig.width;
                inptElement.value = target.innerHTML;
                return inptElement;
            }
        };

        // ajax request executer
        var AjaxRequest = function(url, reloadRequired) {
            var url = url;
            var reloadRequired = reloadRequired;
            return function() {
                var options = reqOptions(qeOptions.Mode.AjaxRequest);
                if ( reloadRequired ) options['onComplete'] = reload;
                new Ajax.Request(url, options);
            }
        };

        // ajax request executer
        var AjaxUpdater = function(url) {
            var url = url;
            return function() {
                var options = reqOptions(qeOptions.Mode.AjaxUpdater);
                new Ajax.Updater({success:targetId}, url, options);
            }
        };

        var ajaxRequestStart = function(mode) {
            if ( mode == qeOptions.Mode.AjaxUpdater ) {
                AjaxUpdater(submitUrl)();
            } else if ( mode == qeOptions.Mode.AjaxRequest ){
                AjaxRequest(submitUrl, false)();
            } else if ( mode == qeOptions.Mode.AjaxRequestReload ){
                AjaxRequest(submitUrl, true)();
            }
        }
                
        // cancel event listener
        var cancelHandler = function() {
            return function() {
                // target
                target.removeChild(editor); 
                target.innerHTML = getHTMLDecoratedValue(editor);
                // ajax request
                AjaxUpdater(cancelUrl)();
                // trigger
                setInnerValue(trigger, getInnerValue(triggerBackup));
                // cancel trigger
                trigger.parentNode.removeChild(cancelTrigger); 
                isActive = false;
                return false;
            };
        };
        
        /* main closure function 
         * will attach to dom event listener */
        return function() {

            // delete
            if ( handlerType == QuickEdit.HandlerType.Delete ) {
                if ( util.confirmUser(InnerValueManager(lang)('DELETE_CONFIRM')) ) {
                    ajaxRequestStart(mode);
                }
                return false;
            }
            // insert
            if ( handlerType == QuickEdit.HandlerType.Insert ) {
                reqParams('clear');
                var formElements = target.elements;
                for ( var idx in formElements ) {
                    var eachInput = formElements[idx];
                    if ( eachInput == undefined 
                        || eachInput.name == undefined 
                        || eachInput.name == '' 
                        || eachInput.value == undefined 
                    ) continue;
                    reqParams(eachInput.name, eachInput.value);
                }
                tmpOptions = reqOptions(qeOptions.Mode.AjaxRequest);
                tmpOptions.parameters = reqParams();
                reqOptions(qeOptions.Mode.AjaxRequest, tmpOptions);
                ajaxRequestStart(mode);
                return false;
            }
            
            // initialize
            if ( cancelTrigger == undefined ) {
                cancelTrigger = trigger.cloneNode(true);
                cancelTrigger.onclick = cancelHandler();
                setInnerValue(cancelTrigger, InnerValueManager(lang)('CANCEL'));
            }

            if ( isActive ) {
                // active -> request -> standby

                // target
                var inputText = getHTMLDecoratedValue(editor);
                target.removeChild(editor);
                target.innerHTML = inputText;

                // ajax request
                reqParams('value', getPlainTextValue(target));
                ajaxRequestStart(mode);
                
                // trigger
                setInnerValue(trigger, getInnerValue(triggerBackup));

                // cancel trigger
                trigger.parentNode.removeChild(cancelTrigger); 

                isActive = false;
                return false;

            } else {
                // standby -> active

                // target
                editor = getEditor(editorConfig(getClassName(target)));
                editor.value = getPlainTextValue(target);
                target.innerHTML = '';
                target.appendChild(editor); 

                // trigger
                triggerBackup = trigger.cloneNode(true);
                setInnerValue(trigger, InnerValueManager(lang)('SUBMIT'));

                // cancel trigger
                trigger.parentNode.appendChild(cancelTrigger); 

                isActive = true;
                return false;

            }
        };
    }
};

// QuickEditManager class
var QuickEditManager = Class.create();

// Options - for customize this library 
QuickEditManager.Options = {};
QuickEditManager.Options.Rules = {
    prefixArea       : 'qe-area_',
    prefixTarget     : 'qe-target_',
    prefixUpdatable   : 'qe-update_',
    prefixDeletable  : 'qe-delete_',
    prefixInsertable : 'qe-insert_',
    delemiter        : '_'
};

// CoC auto binding function
QuickEditManager.autoBind = function(autoBindParams) {
        
    var rules = QuickEditManager.Options.Rules;
    var util = QuickEdit.CommonUtil;
    var qeOptions = QuickEdit.Options;
    
    var divList = util.$tag('div');
    for ( var each in divList ) {
        var areaId = (/*@cc_on!@*/false) ? each : divList[each].id; // IE or not
        var area = util.$id(areaId);
        if ( areaId != undefined && areaId.match(rules.prefixArea) ) {
            var targetIdList = [];
            var editableIdList = [];
            var deletableId = undefined;
            var children = area.childNodes;
            for ( var idx in children ) {
                var child = children[idx];
                if ( child.id != undefined
                    && child.id.match(rules.prefixTarget) ) {
                    targetIdList.push(child.id);
                } else if ( child.id != undefined
                        && child.id.match(rules.prefixUpdatable) ) {
                    editableIdList.push(child.id);
                } else if ( child.id != undefined
                        && child.id.match(rules.prefixDeletable) ) {
                    deletableId = child.id;
                }
            }
            for ( var i=0; i<editableIdList.length; i++ ) {
                var editableId = editableIdList[i];
                var editableIdParams = editableId.split(rules.delemiter);

                var targetId = targetIdList[i];
                var targetIdParams = targetId.split(rules.delemiter);
                var reqParamsId = targetIdParams[1];
                var reqParamsColumn = targetIdParams[2];
                var reqParamsTableName = targetIdParams[3];
                
                // check targetId is valid
                if ( editableIdParams[1] != targetIdParams[1]
                    || editableIdParams[2] != targetIdParams[2] 
                    || editableIdParams[3] != targetIdParams[3] ) {
                    
                    targetId = undefined;
                    for ( var j=0; j<targetIdList.length; j++ ) {
                        targetIdParams = targetIdList[j].split(rules.delemiter);
                        if ( editableIdParams[1] == targetIdParams[1]
                           && editableIdParams[2] == targetIdParams[2] 
                           && editableIdParams[3] == targetIdParams[3] ) {
                            targetId = targetIdList[j];
                        }
                    }
                }
                if ( targetId == undefined )
                    util.notityUser(QuickEdit.Options.Lang.Default.AUTO_BIND_ERROR);

                // bind
                var params = {
                        handlerType : QuickEdit.HandlerType.Edit,
                        targetId    : targetId,
                        triggerId   : editableId,
                        submitUrl   : autoBindParams.updateSubmitUrl,
                        cancelUrl   : autoBindParams.cancelUrl,
                        reqParams   : { id        : reqParamsId,
                                        column    : reqParamsColumn,
                                        tableName : reqParamsTableName
                                      }
                };
                QuickEdit.editable(params);
            }
            if ( deletableId != undefined ) {
                var deletableIdParams = deletableId.split(rules.delemiter);
                targetId = undefined;
                for ( var i=0; i<targetIdList.length; i++ ) {
                    targetIdParams = targetIdList[i].split(rules.delemiter);
                    if ( deletableIdParams[1] == targetIdParams[1]
                       && deletableIdParams[2] == targetIdParams[2] 
                       && deletableIdParams[3] == targetIdParams[3] ) {
                        targetId = targetIdList[i];
                    }
                }
                if ( targetId == undefined )
                    util.notityUser(QuickEdit.Options.Lang.Default.AUTO_BIND_ERROR);
                var targetIdParams = targetId.split(rules.delemiter);
                var reqParamsId = targetIdParams[1];
                var reqParamsTableName = targetIdParams[3];
                var params = {
                        handlerType : QuickEdit.HandlerType.Delete,
                        targetId    : targetId,
                        triggerId   : deletableId,
                        submitUrl   : autoBindParams.deleteSubmitUrl,
                        cancelUrl   : autoBindParams.cancelUrl,
                        reqParams   : { id        : reqParamsId,
                                        tableName : reqParamsTableName
                                      }
                };
                QuickEdit.deletable(params);
            }
        }
    }
    
    var formList = util.$tag('form');
    for ( var each in formList ) {
        var insertableId = (/*@cc_on!@*/false) ? each : formList[each].id; // IE or not
        if (  insertableId != undefined 
            && insertableId.match(rules.prefixInsertable) ) {
        	var insertableForm = util.$id(insertableId);
            if ( insertableForm == undefined ) continue;
            var params = {
                    handlerType : QuickEdit.HandlerType.Insert,
                    targetId    : insertableId,
                    triggerId   : insertableId,
                    submitUrl   : autoBindParams.insertSubmitUrl,
                    reqParams   : undefined
            };
            QuickEdit.insertable(params);
        }
    }
};
