/**
 * @name ComponentManager
 * @memberOf ui
 * @class
 * Central Manager that provides utility methods for ui components.
 * <warning>
 * This manager is intended to be used internally by the framework.
 * </warning>
 */
module.set("ui.ComponentManager")
.as(/**@lends ui.ComponentManager*/{
    /**
     * Map between class aliases registered and class names.
     * @type object
     */
    aliases:{},
    _setupŔeady:false,
    /**
     * Initalizes the enviroment for the ui components to work in the current
     * html document.
     */
    setup: function(){
        if(this._setupŔeady) return;

        $("html").attr("xmlns:bhv","bhv");

        var className;
        if ($.browser.msie){
            className = "ie ie"+parseInt($.browser.version).toString();
        }
        else if ($.browser.webkit){
            className = "webkit";
        }
        else if ($.browser.opera){
            className = "opera";
        }
        else if ($.browser.mozilla){
            className = "mozilla";
        }
        else{
            className="other";
        }
        
        $("body").addClass(className);
        this._setupŔeady = true;
    },
    /**
     * Register a class alias.
     * @param {string} alias
     * The alias name to register.
     * @param {string} class
     * The fully-qualified name of the class to register.
     */
    registerClassAlias: function(alias, clss){
        this.aliases[alias] = clss;
    },
    /**
     * Get a class reference from a prevoiously registered alias.
     * @param {string} alias
     * The alias name to get the corresponding class reference from.
     * @returns {function}
     * The reference to the class mapped with the alias.
     */
    getClassByAlias: function(alias){
        return module.get(this.aliases[alias]);
    },
    /**
     * Ultity method to convert a package like reference of jquery ui's file
     * to a file path.
     * @param {string} the package like name of the JQ UI dependency
     * @returns {string} the file path of the dependency.
     */
    coreFile: function(file){
        return "!"+module.getConfig("basePath")+"/ui/jq/jquery."+file+".js";
    },
    /**
     * A map between converter descriptors and converter functions.
     * The following format is used:
     * <pre>
     * {
     *  "&lt;from type&gt; &lt;to type&gt;": &lt;converter function&gt;
     * }
     * </pre>
     * @example
     * {
     *      "string module": function(value){... return convertedValue}
     * }
     */
    converters:{
        "string module": function(value){
            if(typeof value === "string"){
                return module.get(value);
            }
            else return value;
        }
    },
    /**
     * Returns a converter function by its registered id.
     * @param {string} id the id of the converter function
     * @returns {function} the converter function.
     */
    getConverter:function(id){
        return this.converters[id];
    }
});

/**
 * @name ui.Component
 * @class
 * Foundation class for all visual user interface components provided by
 * the framewrok.
 * @augments core.Trigger
 */
module("ui.Core")
.require("core.Trigger", "core.PrototypeAggregator","ui.StylesheetLoader","ui.Template",
         ui.ComponentManager.coreFile("plugin.resize"),
         ui.ComponentManager.coreFile("ui.core"),
         ui.ComponentManager.coreFile("ui.widget"))
.declare("ui.Component").extend("core.Trigger").implement("core.PrototypeAggregator")
.as(/**@lends ui.Component.prototype*/{
    /**
     * The alias name to identify this component. This name has two main purposes:
     * Can be used in the <code>type</code> property when defining a child of a
     * {@link ui.Group} component.
     * Will create a jquery ui like constructor plugin.
     * <pre>
     *    module("MyComponent")
     *    .declare("MyComponent").extend("ui.Component")
     *    .as({
     *       widgetName:"myComponent",
     *       ...
     *    })
     *
     *    $("#my-component-element").myComponent();
     * </pre>
     * @type string
     */
    widgetName:"widget",
    /**
     * Holds a reference to the ui.ComponentManager object.
     */
    manager:ui.ComponentManager,
    /**
     * Similar to the <code>events</code> property, but unlike the former, the
     * events of this list are triggered by the component's dom element.
     * This events can be registered to the component but are actually triggered
     * by the dom element.
     * @type array<string>
     * @see core.Trigger#events
     */
    domEvents: ["click",
             "dblclick",
             "focusin",
             "focusout",
             "focus",
             "blur",
             "mousedown",
             "mouseenter",
             "mouseleave",
             "mousemove",
             "mouseout",
             "mouseover",
             "mouseup",
             "keydown",
             "keyup",
             "keypress"],
    /**
     * Holds a reference to the options passed to the component upon construction.
     * @type object
     */
    options:{
        floating:false
    },

    /**
     * @constructor
     * Creates a new instance of a visual component.
     * @param {object} options The options object to customize the component.
     * @param {DOMElement} element The Dom element whre the component will be
     * rendered.
     */
    constructor: function(options, element){
        if(arguments.length){
            this._createWidget(options, element);
        }
    },

    /**
     * Initializes the component. This is the first function to be called when
     * constructing a component instance.
     */
    _createWidget: function(options, element){
        this.element = $(element).first();

        jQuery.data( this.element[0], this.widgetName, this );

        this.options = {};
        
        jQuery.extend(this.options,this.prototypeAggregate("options"),options);
    },

    bind: function(type){
        var domEvents = this.prototypeAggregate("domEvents","array");
        if(domEvents.indexOf(type) > -1){
            return this.element && this.element.bind.apply(this.element, arguments);
        }
        else{
            return this.base.apply(this, arguments);
        }
    },

    triggers: function(type){
        var domEvents = this.prototypeAggregate("domEvents","array");
        if(domEvents.indexOf(type) > -1){
            return true;
        }
        else {
            return this.base.apply(this, arguments);
        }
    },
    
    _getCurrentNamespace:function(){
        if(this.parent){
            var parentNS = this.parent._getCurrentNamespace();
            if(parentNS){
                return parentNS+"."+this.parent.getChildId(this);
            }
            else return this.parent.getChildId(this);
            
        }
        else return "";
    }
})
/**
 * @name ui.MVCComponent
 * @class
 * Base class for all MVC components.
 * This class allows to:
 * <ul>
 * <li>Define Templated views for the component.</li>
 * <li>Define stylesheets associated with the component to be loaded
 * upon instantiation.</li>
 * <li>Define decalrative parametrization of the component via a
 * simple <code>options</code> object.</li>
 * </ul>
 * @augments ui.Component
 */
.declare("ui.MVCComponent").extend("ui.Component")
.as(/**@lends ui.MVCComponent.prototype*/{
    options:{
        viewType:"file",
        disabled:false
    },

    styles:["%basePath%/resources/styles/ui-core.css"],

    events:["initialized","displayUpdated"],

    _createWidget: function(options, element){
        var self = this;
        this.base(options,element);
        this.viewData = {};
        this.__constructing = true;
        ui.ComponentManager.setup();
        $.when(
            this._loadStyles(),
            this._parseOptions(),
            this._initView().pipe(function(){
                return self._proccessOverridenView();
            })
        )
        .then($.proxy(this,"_doInit"))
        .fail(function(e){
             throw new Error(e);
         })
        this.__constructing = false;
    },

    /**
     * Cleans up the data and dom structures generated by the component.
     * You can call this method directly when you want to explicitly remove a
     * component, or it gets called automatically when removing via jQuery
     * the dom element associated with the component.
     */
    destroy: function(){
        jQuery.Widget.prototype.destroy.call(this);
    },

    _getConverters: function(){
        var converters = this._cachedConverters
                 || (this._cachedConverters =
                        this.prototypeAggregate("converters"));
        return converters;
    },

    _parseOptions:function(){
        var self = this;
        return $.Deferred(function(d){
            var converters = self._getConverters();
            $.each(converters, function(opt){
                self._parseOption(opt);
            });
            d.resolve();
        }).promise();
    },

    _parseOption:function(option){
        var options = this.options;
        var converters = this._getConverters();
        var converter = converters[option];
        if(converter){
            if(typeof converter == "string"){
                converter = ui.ComponentManager.getConverter(converter);
            }

            var value0 = module.get(option,options);
            var value1 = converter.call(null,value0);
            module.set(option,value1, true, options);
        }
    },

    /**
     * Sets or gets an option for the component.
     * @param {string} name
     * The name or path of the option to get or set.
     * @param {*} [value]
     * The value to set to the option.
     * If not passed the method acts as a getter.
     * @returns {*} the value stored for the option.
     * @example
     * var b = new ui.Button({label:"My Button"},$('#button'));
     * // GET
     * b.option("label") //"My Button"
     * // SET
     * b.option("label","Other Label")
     */
    option: function(name, value){
        if(value){
            module.set(name,value, true, this.options);
            this._parseOption(name);
            this.updateDisplay();
        }
        return module.get(name,this.options);
    },

    _loadStyles: function(){
        var self = this;
        return $.Deferred(function(d){
            var styles = self.prototypeAggregate("styles","array");
            ui.StylesheetLoader.loadStyles(styles);
            d.resolve();
        }).promise();
    },
    
    _init: function(){
        jQuery.extend(this.viewData, {$this:this});
        this.__initReady = true;
    },

    _doInit:function(){
        if(this.__constructing){
            setTimeout(jQuery.proxy(this,"_doInit"), 100);
        }
        else{
            this._init();
            this.trigger("initialized");
            this.updateDisplay();
        }
    },

    _proccessOverridenView:function(){
        var self = this;
        return jQuery.Deferred(function(d){
            var currentTemplate = self.template._templateString;
            var oViewRegexp = /<OVERRIDENVIEW\b[^>]*>(.*?)<\/OVERRIDENVIEW>/i;
            var hasOverriden = currentTemplate.match(oViewRegexp);
            if(hasOverriden){
                var proto = Object.getPrototypeOf(self);
                var options = core.PrototypeAggregator.prototypeAggregate(proto,"options");
                while(options.view == self.options.view){
                    proto = Object.getPrototypeOf(proto);
                    options = core.PrototypeAggregator.prototypeAggregate(proto,"options");
                }
                if(options.view){
                    switch(options.viewType){
                        case "string":
                            var replaced = currentTemplate.replace(oViewRegexp,options.view);
                            self.template = ui.Template.fromString(replaced,self);
                            d.resolve();
                            break;
                        case "file":
                            core.FileLoader.load(options.view)
                            .then(function(data){
                                var replaced = currentTemplate.replace(oViewRegexp,data);
                                self.template = ui.Template.fromString(replaced,self);
                                d.resolve();
                            })
                            .fail(function(error){
                                d.reject(error);
                            })
                            break;
                        default:d.reject("Unsopported viewType: " + options.viewType);
                    }
                }
            } else {
                d.resolve();
            }
        }).promise();
    },

    _initView: function(){
        var self = this;
        return jQuery.Deferred(function(d){

            if(self.options.view){
                switch(self.options.viewType){
                    case "string":
                        self.template = ui.Template.fromString(self.options.view, self);
                        d.resolve();
                        break;
                    case "file":
                        ui.Template.fromFile(self.options.view, self)
                        .then(function(template){
                            self.template = template;
                            d.resolve();
                        })
                        .fail(function(error){
                            throw d.reject(error);
                        })
                        break;
                    default:d.reject("Unsopported viewType: " + self.options.viewType);
                }
            }
            else{
                d.resolve();
            }

        }).promise();
    },

    /**
     * This method generates the display of the component by generating the
     * markup based on the component's template.
     */
    updateDisplay: function(){
        if(this.template && this.viewData){
            var result = this.template.process(this.viewData,this);
            //empty element but keep metadata
            this.element.empty(true);
            //cast redered template into a dom tree
            var resultdom = $(result);
            //take first element as template root, the rest will be ignored.
            var targetdom = resultdom.first();
            //copy result tree root attrs into this component's element reference
            //in orther to apply template but keeping the original reference to
            //the element
            var attrs = targetdom.getAttributes();
            if(attrs["class"]){
                this.element.addClass(attrs["class"]);
                delete attrs["class"];
            }
            if(attrs["style"]){
                this.element.css(attrs["style"]);
                delete attrs["style"];
            }
            this.element.attr(attrs);

            //append template contents
            this.element.append(targetdom.contents());

            this.trigger({type:"displayUpdated"});
        }
    }
});

module.bind("moduleDefined",function(e){
    //console.log("Event: Module Defined", e.declare);
    var clss = e.clss;
    var alias;
    if(typeof clss == "function"){
        if(clss.prototype.widgetName){
            alias = clss.prototype.widgetName;
            if(alias && alias !== "widget"){
                ui.ComponentManager.registerClassAlias(alias,e.declare);

                //var proto = clss.prototype;
                $.widget("ui."+alias,clss,{});
                //console.log("widget", alias, e.declare);
            }
        }
        else if(clss.prototype.layoutName){
            var layoutName = jQuery.makeArray(clss.prototype.layoutName)
            for (var i = 0; i < layoutName.length; i++) {
                ui.ComponentManager.registerClassAlias(layoutName[i],e.declare);
            }
            //console.log("layout", clss.prototype.layoutName, e.declare);
        }
    }
});

// modify $.fn.empty to allow delete childs keeping data structures.
(function($){

var _empty = $.fn.empty;
module.plugin({
    empty:function(keepData){
        if(keepData){
            for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
                // Remove any remaining nodes
                while ( elem.firstChild ) {
                        elem.removeChild( elem.firstChild );
                    }
            }
            return this;
        }
        else return _empty.apply(this, arguments);
    }
})

})(jQuery);
// SIG // Begin signature block
// SIG // MIIMzQYJKoZIhvcNAQcCoIIMvjCCDLoCAQExDjAMBggq
// SIG // hkiG9w0CBQUAMGYGCisGAQQBgjcCAQSgWDBWMDIGCisG
// SIG // AQQBgjcCAR4wJAIBAQQQEODJBs441BGiowAQS9NQkAIB
// SIG // AAIBAAIBAAIBAAIBADAgMAwGCCqGSIb3DQIFBQAEECk7
// SIG // /TJjXw89W/QI/1BGonigggoPMIIE/DCCBGWgAwIBAgIQ
// SIG // ZVIm4bIuGOFZDymFrCLnXDANBgkqhkiG9w0BAQUFADBf
// SIG // MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s
// SIG // IEluYy4xNzA1BgNVBAsTLkNsYXNzIDMgUHVibGljIFBy
// SIG // aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcN
// SIG // MDkwNTIxMDAwMDAwWhcNMTkwNTIwMjM1OTU5WjCBtjEL
// SIG // MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJ
// SIG // bmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3
// SIG // b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQgaHR0
// SIG // cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykwOTEw
// SIG // MC4GA1UEAxMnVmVyaVNpZ24gQ2xhc3MgMyBDb2RlIFNp
// SIG // Z25pbmcgMjAwOS0yIENBMIIBIjANBgkqhkiG9w0BAQEF
// SIG // AAOCAQ8AMIIBCgKCAQEAvmcdtGCqEElvVhd8Zslehg3V
// SIG // 8ayncYOOi4n4iASJFQa6LYQhleTRnFBM+9IivdrysjU7
// SIG // Ho/DCfv8Ey5av4l8PTslHvbzWHuc9AG1xgq4gM6+J3Rh
// SIG // ZydNauXsgWFYeaPgFxASFSew4U00fytHIES53mYkZorN
// SIG // T7ofxTjIVJDhcvYZZnVquUlozzh5DaowqNssYEie16oU
// SIG // AamD1ziRMDkTlgM6fEBUtq3gLxuD3KgRUj4Cs9cr/SG2
// SIG // p1yjDwupphBQDjQuTafOyV4l1Iy88258KbwBXfwxh1rV
// SIG // jIVnWIgZoL818OoroyHnkPaD5ajtYHhee2CD/VcLXUEN
// SIG // Y1Rg1kMh7wIDAQABo4IB2zCCAdcwEgYDVR0TAQH/BAgw
// SIG // BgEB/wIBADBwBgNVHSAEaTBnMGUGC2CGSAGG+EUBBxcD
// SIG // MFYwKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZlcmlz
// SIG // aWduLmNvbS9jcHMwKgYIKwYBBQUHAgIwHhocaHR0cHM6
// SIG // Ly93d3cudmVyaXNpZ24uY29tL3JwYTAOBgNVHQ8BAf8E
// SIG // BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ
// SIG // aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5r
// SIG // w8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNp
// SIG // Z24uY29tL3ZzbG9nby5naWYwHQYDVR0lBBYwFAYIKwYB
// SIG // BQUHAwIGCCsGAQUFBwMDMDQGCCsGAQUFBwEBBCgwJjAk
// SIG // BggrBgEFBQcwAYYYaHR0cDovL29jc3AudmVyaXNpZ24u
// SIG // Y29tMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwu
// SIG // dmVyaXNpZ24uY29tL3BjYTMuY3JsMCkGA1UdEQQiMCCk
// SIG // HjAcMRowGAYDVQQDExFDbGFzczNDQTIwNDgtMS01NTAd
// SIG // BgNVHQ4EFgQUl9BrqCZwyKE/lB8ILcQ1m6ShHvIwDQYJ
// SIG // KoZIhvcNAQEFBQADgYEAiwPA3ZTYQaJhabAVqHjHMMaQ
// SIG // PH5C9yS25INzFwR/BBCcoeL6gS/rwMpE53LgULZVECCD
// SIG // bpaS5JpRarQ3MdylLeuMAMcdT+dNMrqF+E6++mdVZfBq
// SIG // vnrKZDgaEBB4RXYx84Z6Aw9gwrNdnfaLZnaCG1nhg+W9
// SIG // SaU4VuXeQXcOWA8wggULMIID86ADAgECAhAjTaBL0kKF
// SIG // PErLmuhhwhm7MA0GCSqGSIb3DQEBBQUAMIG2MQswCQYD
// SIG // VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4x
// SIG // HzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsx
// SIG // OzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov
// SIG // L3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTA5MTAwLgYD
// SIG // VQQDEydWZXJpU2lnbiBDbGFzcyAzIENvZGUgU2lnbmlu
// SIG // ZyAyMDA5LTIgQ0EwHhcNMDkwODI3MDAwMDAwWhcNMTIw
// SIG // OTIwMjM1OTU5WjCByDELMAkGA1UEBhMCVVMxEzARBgNV
// SIG // BAgTCkNhbGlmb3JuaWExEzARBgNVBAcTClNhbiBSYWZh
// SIG // ZWwxFjAUBgNVBAoUDUF1dG9kZXNrLCBJbmMxPjA8BgNV
// SIG // BAsTNURpZ2l0YWwgSUQgQ2xhc3MgMyAtIE1pY3Jvc29m
// SIG // dCBTb2Z0d2FyZSBWYWxpZGF0aW9uIHYyMR8wHQYDVQQL
// SIG // FBZEZXNpZ24gU29sdXRpb25zIEdyb3VwMRYwFAYDVQQD
// SIG // FA1BdXRvZGVzaywgSW5jMIGfMA0GCSqGSIb3DQEBAQUA
// SIG // A4GNADCBiQKBgQC1R1YKdvQF2Rj4+knWfj81afUtVyep
// SIG // GF2P4tcYD53qlYmYi9t9Csc6PMWlUZhMGbOceQKYcBjc
// SIG // mWvyghJnoaEQ6796ZcsLD0pMH2R4SpV/SjxiQ280beig
// SIG // NerJS5X+ftOMCs1qSQ1LXlRFEohaewkNWsIp/+f1Y0vE
// SIG // dGzggnEKOQIDAQABo4IBgzCCAX8wCQYDVR0TBAIwADAO
// SIG // BgNVHQ8BAf8EBAMCB4AwRAYDVR0fBD0wOzA5oDegNYYz
// SIG // aHR0cDovL2NzYzMtMjAwOS0yLWNybC52ZXJpc2lnbi5j
// SIG // b20vQ1NDMy0yMDA5LTIuY3JsMEQGA1UdIAQ9MDswOQYL
// SIG // YIZIAYb4RQEHFwMwKjAoBggrBgEFBQcCARYcaHR0cHM6
// SIG // Ly93d3cudmVyaXNpZ24uY29tL3JwYTATBgNVHSUEDDAK
// SIG // BggrBgEFBQcDAzB1BggrBgEFBQcBAQRpMGcwJAYIKwYB
// SIG // BQUHMAGGGGh0dHA6Ly9vY3NwLnZlcmlzaWduLmNvbTA/
// SIG // BggrBgEFBQcwAoYzaHR0cDovL2NzYzMtMjAwOS0yLWFp
// SIG // YS52ZXJpc2lnbi5jb20vQ1NDMy0yMDA5LTIuY2VyMB8G
// SIG // A1UdIwQYMBaAFJfQa6gmcMihP5QfCC3ENZukoR7yMBEG
// SIG // CWCGSAGG+EIBAQQEAwIEEDAWBgorBgEEAYI3AgEbBAgw
// SIG // BgEBAAEB/zANBgkqhkiG9w0BAQUFAAOCAQEAIfxkh8Fd
// SIG // IHK+qtTf+9Eq5fjkk5gMMTQCOa+2aR7mqDzLfIFRGQzr
// SIG // tcc5/izsb/wPLUwdw1cRpnxO8/qLA2Ol+b99FONTOyyc
// SIG // Y8unwviYhdEjuFmqHkyC5MUYroZEjvTObFkkgN98Y48h
// SIG // C+mG2hdlVKXR0zr9r5q/rmmivVWbojvcYKHoW31O5OcM
// SIG // PsrWuOD305Ygmpck/91iopE3UN4tfeWYhzNj1F6Ai4Xa
// SIG // 5KiaMcs3zmoZ+6SePyI2YUgtff6yvy/rk8KVT41KGD2h
// SIG // fC+QrtVSkoDWP/MDtLjbTPCWElC1NWSEKowRI8t2x594
// SIG // Skjnzu/W5Lh97ixircOuM4IvBzGCAigwggIkAgEBMIHL
// SIG // MIG2MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNp
// SIG // Z24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0
// SIG // IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBh
// SIG // dCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChj
// SIG // KTA5MTAwLgYDVQQDEydWZXJpU2lnbiBDbGFzcyAzIENv
// SIG // ZGUgU2lnbmluZyAyMDA5LTIgQ0ECECNNoEvSQoU8Ssua
// SIG // 6GHCGbswDAYIKoZIhvcNAgUFAKCBsDAZBgkqhkiG9w0B
// SIG // CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w
// SIG // DAYKKwYBBAGCNwIBFTAfBgkqhkiG9w0BCQQxEgQQP3hM
// SIG // hK9L08hz6xD5ZUuVsTBUBgorBgEEAYI3AgEMMUYwRKAm
// SIG // gCQAQQB1AHQAbwBkAGUAcwBrACAAQwBvAG0AcABvAG4A
// SIG // ZQBuAHShGoAYaHR0cDovL3d3dy5hdXRvZGVzay5jb20g
// SIG // MA0GCSqGSIb3DQEBAQUABIGAhhGO0U351baZ+5fsGBNm
// SIG // k1IbxZqCb3Uw+QDqbarrDdqWh4KgY51qPkz+KG5WR/co
// SIG // GGIwmgtkU+Mg8OTfeXYCYRStfgPY4B/5khBE1eXvTk3J
// SIG // uFueFo+owjahhJkjw3hkC5tPDdcnC+XrJGnIyKdKSOHk
// SIG // wvU2LmluYaDXzr7Iq9A=
// SIG // End signature block
