/*global UNIFACE document */

/*******************************************************************************
date   refnum   version who description
090205 c27248   9.ajax  jdk acceskeys do not work & labels etc show the %sign
090305 c27328   9.ajax  fd  Move common code up in widget class inheritance tree
date   refnum   version who description
*******************************************************************************/

(function() {

/**
 * UNIFACE widgetFactory for creating widgets.
 *      addCreator:
 *          Accepts a function that is a widget creator. This function
 *          is then added to the list of known widget creator functions.
 *          It should implement one method: createWidget().
 *      create:
 *          Creates a widget by calling the appropriate widget creator.
 *          Throws an error if there is no appropriate widget creator.
 */
UNIFACE.widgetFactory = (function() {
    
    var widgetCreators = { };
    
    function restoreHtmlProperties(widget, backupHtmlProps) {
        var p, lSetter;
        for (p in backupHtmlProps) if (backupHtmlProps.hasOwnProperty(p)) {
            lSetter = widget["setHtml_" + p];
            if (typeof lSetter === "function") {
                lSetter.call(widget, backupHtmlProps[p]);
            } else {
                widget.setHtmlProp(p, backupHtmlProps[p]);
            }
        }
    }
    
    function restoreStyleProperties(styleNodes, backupCssTexts) {
        var cssText;
        for (var i=0, len = styleNodes.length; i < len; i++) {
            styleNodes[i].style.cssText = backupCssTexts[i];
        }
    }

    function setHtmlProperties(widget, aProps, backupHtmlProps) {
        var lSetter, oldValue;
        // Set the html properties
        for (var p in aProps.html ) if (aProps.html.hasOwnProperty(p)) {
            lSetter = widget["setHtml_" + p];
            if (typeof lSetter === "function") {
                oldValue = lSetter.call(widget, aProps.html[p]);
            } else {
                oldValue = widget.setHtmlProp(p, aProps.html[p]);
            }
            backupHtmlProps[p] = oldValue;
        }
    }

    function setStyleProperties(widget, aProps) {
        var element = widget.getElement();
        if (element && element.style != undefined) { // pragma(allow-loose-compare)
            var l_style = element.style;
            var p, lSetter;
            for (p in aProps.preStyle) if (aProps.preStyle.hasOwnProperty(p)) {
                lSetter = widget["setStyle_" + p];
                if (typeof lSetter === "function") {
                    lSetter.call(widget, aProps.preStyle[p]);
                } else {
                    widget.setStyleProp(p, aProps.preStyle[p]);
                }
            }
            
            if (aProps.preStyle.display != undefined ) { // pragma(allow-loose-compare)
                element.U_display = aProps.preStyle.display;
            }
        }
    }
    
    function setUnifaceProperties(widget, aProps) {
        if (typeof widget.setUnifaceProp === "function") {
            for (var p in aProps.uniface) if (aProps.uniface.hasOwnProperty(p)) {
                widget.setUnifaceProp(p, aProps.uniface[p]);
            }
        }
    }
    
    function setTriggerProperties(widget, aProps) {
        if (typeof widget.setTriggerProp === "function") {
            for (var p in aProps.trigger) if (aProps.trigger.hasOwnProperty(p)) {
                widget.setTriggerProp(p, aProps.trigger[p]);
            }
        }
    }
    
    /*
     * Calls the mapEvent() function of the widget for each of
     * the "public web" triggers that is defined in triggerDefs.
     * The widget may choose to actually map an event to the trigger.
     */
    function mapEvents(widget, triggerDefs) {
        var triggerDef;
        var trg;
        var mapper;
        UNIFACE.eventMapper.clear(widget);
        for (trg in triggerDefs) if (triggerDefs.hasOwnProperty(trg)) {
            triggerDef = triggerDefs[trg];
            if (triggerDef.requesttype === "update") {
                mapper = {
                    "triggerName" : trg,
                    "map" : function(element, eventName) {
                               UNIFACE.eventMapper.map(element, eventName, widget, trg);
                            }
                };
                widget.mapEvent(mapper);
            }
        }
    }
    

    function createWrapper(widget, widgetClassName) {
        var widgetStyleNodes = [];
        var widgetBackupCssTexts = [];
        var widgetBackupHtmlProps = {};
        var style;

        var _getValrep;
        if (typeof(widget.getValrep) === "function") {
            _getValrep = function() {
                return widget.getValrep();
            };
        }
        var _setRep;
        if (typeof(widget.setRepresentation) === "function") {
            _setRep = function(rep) {
                return widget.setRepresentation(rep);
            };
        }

        return {
        	callBack : widget.callBack,         // Shortcut to the widget's callBack.   Utility for subclasses.
        	className : widgetClassName,        // Gets the widget's "class name".      Only used by tests.
            getElement :                        // Gets the widget's DOM node.          Utility for subclasses.
                function() {
                    return widget.getElement();
                },
            getStyleNode :                      // Gets DOM node for a style property.  Utility for subclasses.
                function(propertyName) {
                    return widget.getStyleNode(propertyName);
                },
            getValrep : _getValrep,             // Gets valrep from widget itself.      Only used by tests.
                                                //                                      Caller should test for null.
            getValue :                          // Gets the widget value.               Used from uluv.
                function() {
                    return widget.getValue();
                },
            validate :                          // Lets the widget check its value.     Used from uluv.
                function() {
                    if (typeof widget.validate === "function") {
                        return widget.validate();
                    }
                },
            render :                            // Renders the widget.                  Used from uluv.
                function(placeholder, triggerDefs) {

                    // Define a nodesChanged function.
                    // This will be always be called after rendering of the widget
                    // but the widget itself may choose to call it at other times
                    // as well, when its nodes change.
                    widget.nodesChanged = function() {
                        widgetBackupCssTexts = [];
                        widgetStyleNodes = widget.getAllStyleNodes();
                        for (var i=0, len = widgetStyleNodes.length; i < len; i++) {
                            widgetBackupCssTexts.push(widgetStyleNodes[i].style.cssText);
                        }
                        mapEvents(widget, triggerDefs);
                    };    

                    widget.preRender(placeholder);
                    widget.doRender(placeholder);
                    widget.postRender(placeholder);
                    
                    widget.nodesChanged();

                    // Possibly set during render(), and used in UDOH tests:
                    this.control = widget.control;
                    this.controls = widget.controls;
                },
            setLayout :                         // DSP container only.                  Used from uluv.
                function(layout) {
                    widget.setLayout(layout);
                },
            setProperties :                     // Sets widget properties.              Used from uluv.
                function() {
                    restoreHtmlProperties(widget, widgetBackupHtmlProps);
                    restoreStyleProperties(widgetStyleNodes, widgetBackupCssTexts);
                    var props = widget.callBack.getProperties();
                    widgetBackupHtmlProps = {};
                    setHtmlProperties(widget, props, widgetBackupHtmlProps);
                    setStyleProperties(widget, props);
                    setUnifaceProperties(widget, props);
                    setTriggerProperties(widget, props);

                    // Give widget a chance too...                  
                    widget.setProperties();
                    
                    // Possibly updated by setProperties, and used in UDOH tests:
                    this.controls = widget.controls;
                },
            setRepresentation : _setRep,        // Sets widget's representation         Only used by tests.
                                                //                                      Caller should test for null.
            setResource :                       // Sets widget resource.                Used from uluv.
                function(resource) {
                    widget.setResource(resource);
                },
            setSyntax :                         // Sets widget syntax.                  Used from uluv.
                function () {
                    widget.setSyntax();     // The actual syntax is available through callback
                    // Possibly updated by setSyntax, and used in UDOH tests:
                    this.control = widget.control;    // udohext
                },
            setValrep :                         // Sets widget valrep.                  Used from uluv.
                function(valrep) {
                    widget.setValrep(valrep);
                },
            setValue :                          // Sets widget value.                   Used from uluv.
                function(value) {
                    widget.setValue(value);
                },
            showError :                         // Shows error on widget.               Used from uluv.
                function(msg) {
                    if (typeof(widget.showError) === "function") {
                        widget.showError(msg);
                    } else {
                        UNIFACE.extension.errorWidget.show(widget.getElement(), msg);
                    }
                },
            unrender :                          // Unrenders widget.                    Used from uluv.
                function() {
                    widget.unrender();
                }
        };
    }
    return {
        addCreator : function(widgetClassName, widgetCreator) {
            widgetCreators[widgetClassName] = widgetCreator;
        },
        create : function(widgetClassName, callBack) {
            var creator = widgetCreators[widgetClassName];
            if (creator == undefined) { // pragma(allow-loose-compare)
                UNIFACE.throwException("No widget creator found for " + widgetClassName + ". Are you missing a Javascript include?");
            }
            var widget = creator(callBack.getId());
            widget.callBack = callBack;     // The actual widget wants to know the callback.
            if (!widget) {
                UNIFACE.throwException("Creation of \"" + widgetClassName + "\" widget for " + callBack.getId() + " failed.");
            }
            return createWrapper(widget, widgetClassName);
        }
    };
	
})();

///////////////////////////////////////////////////////////////////////////////
// UNIFACE.widget
// Namespace.
///////////////////////////////////////////////////////////////////////////////
UNIFACE.widget = function() {};

///////////////////////////////////////////////////////////////////////////////
// UNIFACE.widget.AbstractWidget
// Base class widget implementation.
///////////////////////////////////////////////////////////////////////////////

UNIFACE.widget.AbstractWidget = function() {
};

UNIFACE.widget.AbstractWidget.prototype = {
    setValue : function(aVal) {},
    getValue : function() {    return ""; },
    setValrep : function(aValrep, aProps) {},
    setResource : function(aRes) {},
    unrender : function() {},
    setSyntax : function(aSyntax) {},
    setProperties: function() {},
    mapEvents : function() {},
    resetProperties : function() {},
    getElement : function() { return null; },
    preRender : function() {},
    doRender : function() {},
    getEventCatcher : function() { return null; },
    setLayout : function() {}
};

UNIFACE.widget.AbstractWidget.prototype.setCssProperty = function(aStyle, aProp, aValue, aPriority) {
    if ( aStyle[aProp] !== aValue ) {
        if ( aPriority != null ) { // pragma(allow-loose-compare)
            if ( typeof aStyle.setProperty === "function" ) {
                aStyle.setProperty(aProp, aValue, aPriority);
            } else {
                aStyle.cssText += ";" + aProp + ":" + aValue + "! " + aPriority + ";";
            }
        } else {
            aStyle[aProp] = aValue;
        }
    }
};

UNIFACE.widget.AbstractWidget.prototype.setHtml_className = function(aValue) {
    var oldValue;
    var element = this.getElement();
    if (element ) {
        oldValue = element.className;
        element.className = aValue;
    }
    return oldValue;
};
        
UNIFACE.widget.AbstractWidget.prototype.setHtmlProp = function(aProp, aValue) {
    var oldValue;
    try {
        var element = this.getElement();
        if (element ) {
            oldValue = element[aProp];
            element[aProp] = aValue;
        }
    } catch ( e ) {  // happen for ie6
        //oldValue = null;
    }
    return oldValue;
};
        
UNIFACE.widget.AbstractWidget.prototype.getStyleNode = function(aProp) {
    return this.getElement();
};
        
UNIFACE.widget.AbstractWidget.prototype.setStyleProp = function(aProp, aValue) {
    try {
        var styleNode = this.getStyleNode(aProp);
        this.setCssProperty(styleNode.style, aProp, aValue);
    } catch ( e ) {  // happen for ie6
        // ignore;
    }
};
UNIFACE.widget.AbstractWidget.prototype.postRender = function(a_placeHolder) {
    this.setValue(this.callBack.getValue());
};
UNIFACE.widget.AbstractWidget.prototype.getAllStyleNodes = function() {
    return [this.getElement()];
};

var g_helperNode;
///////////////////////////////////////////////////////////////////////////////
// UNIFACE.widget.AbstractWidget.prototype.accesskey
// Removes the UNIFACE % signs from the string and fills in the accessKey 
// member which can be used by the widget to set the access key.
// Parameters:
// labelString - string containing % signs
// bSupportsAccess - Some widgets do not support accesskeys (dropdown/listbox)
//                   but % should be removed.
// bAsHTML         - Return string as HTML to under accelerator char.
///////////////////////////////////////////////////////////////////////////////
UNIFACE.widget.AbstractWidget.prototype.accesskey = function(labelString,bSupportsAccess,bAsHTML) {
  var str = null;

  if (bAsHTML) {
      if (g_helperNode === undefined) {
          g_helperNode = document.createElement("div");
          g_helperNode.style.display = "none";
          document.body.appendChild(g_helperNode);
      }
      var l_text = document.createTextNode(labelString);
     
      g_helperNode.innerHTML = "";
      g_helperNode.appendChild(l_text);
      str =  g_helperNode.innerHTML;
  } else {
      str =labelString;
  }
  this.ackey = 0; //remember for rendering
  var me = this;
  
  return str.replace(/%(.)/g, function(aMatch, aChar) {
        if (me.ackey===0 && /[a-zA-Z]/.test(aChar)) {
            me.ackey = aChar;
            if (bAsHTML){
                return "<u>" + aChar + "</u>";
            }
        }
        return aChar;
    }
  );
};

UNIFACE.eventMapper.map = function(source, eventName, widget, trg) {
        // Get the mapped javascript function
        var f = widget.callBack.bindEvent(trg);
        if (typeof f === "function") {
            // Map the event to the trigger
            if (source.addEventListener) {
                source.addEventListener(eventName.substring(2), f, false);
            } else if (source.attachEvent) {
                source.attachEvent(eventName, f);
            } else {
            	source[eventName] = f;
            }
		}
};

UNIFACE.eventMapper.clear = function(widget) {
	if (widget && widget.callBack) {
		UNIFACE.eventMapper.clearForId(widget.callBack.getId());
	}
};

}());

