//
// Pipeno UI controls
//
; (function(window) {
    /**
    * PipenoUI Object
    **/
    window.PipenoUI = PipenoUI = new Object();

    /*#region ConfirmMessageBox*/
    /**
    * YesOrNoMessageBox - extends the MessageBox control. When the user is calling it, this control will create 
    *   a MessageBox control with the settings from the parameters. This is builded just for message boxes that are needing 2 buttons. 
    *
    * HTML syntax:
    *   No HTML syntax needed
    *
    * Usage:
    *   var myPopup = new PipenoUI.ConfirmMessageBox('Title','This is the questions',function() {},function() {});
    */
    PipenoUI.ConfirmMessageBox = function(title, content, okButtonCallback, cancelButtonCallback) {
        var messageBox;
        /**
        *  Register events.   
        */

        this.RegisterEvents = function() { };

        /**
        *   Initialize the control and all its 
        *   inner elements. 
        */
        (this.Init = function() {
            messageBox = new PipenoUI.MessageBox();
            messageBox.SetTitle(title);
            messageBox.SetContent(content);
            messageBox.AddButton('Confirm', okButtonCallback);
            messageBox.AddButton('Cancel', cancelButtonCallback);
        })();

    }; // END YesOrNoMessageBox
    /*#endregion*/

    /*#region MessageBox*/
    /**
    * MessageBox - it creates a modal confirm popup, like the javascript confirm box
    *   - This control is singleton, if you try to create another instance, it will update the previous one
    *   - The user can set the Popup's Title using the function SetTitle('title').
    *   - The user can set the Popup's content using the function SetContent('content'), where the param can be in HTML format.
    *   - The user can add buttons with the function AddButton('name', callback), where the first param is the button text, and the second
    *       param is the callback function that will be executed on clicking the button.            
    *
    * HTML syntax:
    *   No HTML syntax needed
    *
    * Usage:
    *   var myPopup = new PipenoUI.MessageBox();
    */
    PipenoUI.MessageBox = function() {
        var self = this;
        var container = $('body');
        var messageBoxOverlay = $('<div/>', { 'class': 'message-box-overlay' });
        var buttonsHolder = $('<div/>', { 'class': 'buttons' });
        var messageBox = $('<div/>', { 'class': 'mssge-box' });
        var title = $('<div/>', { 'class': 'title' });
        var content = $('<div/>', { 'class': 'content' });

        this.SetTitle = function(value) {
            $(title).html(value);
        };

        this.SetContent = function(value) {
            $(content).html(value);
            var rate = $(content).height() / 2 + 40;
            $(messageBox).css({ 'margin-top': '-' + rate + 'px' });
            //alert($(content).height());
        };

        this.AddButton = function(value, callbackFunction) {
            var btn = $('<a/>', { 'class': 'input-button', 'html': value });
            buttonsHolder.append(btn);
            $(btn).bind('click', function() {
                $(messageBoxOverlay).fadeOut('fast', function() {
                    callbackFunction();
                });
            });
        };

        /**
        *  Register events.   
        */
        this.RegisterEvents = function() { };

        /**
        *   Initialize the control and all its 
        *   inner elements. 
        */
        (this.Init = function() {
            var holderSelector = $('.message-box-overlay');
            if (holderSelector.length > 0) {
                messageBoxOverlay = holderSelector.eq(0);

                title = $(messageBoxOverlay).find('.title');
                $(title).empty();

                content = $(messageBoxOverlay).find('.content');
                $(content).empty();

                buttonsHolder = $(messageBoxOverlay).find('.buttons');
                $(buttonsHolder).empty();

                $(messageBoxOverlay).show();
            } else {
                $('body').append(messageBoxOverlay.append(
                        $(messageBox).append($('<div/>', { 'class': 'right' }).append(
                                title, $('<div/>', { 'class': 'wrap' }).append(content, buttonsHolder))).append(
                                    $('<div/>', { 'class': 'corner' }).append(
                                        $('<div/>', { 'class': 'bottom' })))));
            }
        })();

    }; // END MessageBox
    /*#endregion*/

    /*#region SimpleInputText*/
    /**
    * SimpleInputText - it wraps an existing input (input[text] and textarea) and brings extra behavior:
    *   - Triggers change event on each key up if the option is given.
    *   - Adds the class '.focus' on 'focus' event. 
    *   - Adds the class '.modified' when the control was modified (i.e. the current value is different 
    *     from the default value).
    *   - Sets the default value.
    *   - Uses input description, only if the defaultValue is not given. The control shows a text by 
    *     default and hides it when the control is focused. The description is showed again when the 
    *     control looses focus and is empty.
    *   - Limit the input to the max length.
    *   - Filters the input and allows only the supplied key codes (characters). This works on with 
    *     key codes and not char codes. The behavior is supplied in the keydown event.
    *
    * Notes:
    *   - On focus the code adds the focus class to the input.
    *   - If you want to supply CSS for disabled or readonly states, just use the attribute selectors in CSS.
    *
    * HTML syntax:
    *   <input type="text" disabled="disabled" readonly="readonly" />
    *
    * Usage:
    *   var myInput= new PipenoUI.InputText(input, "default value", "description", 100, {enabled: true});
    */
    PipenoUI.SimpleInputText = function(input, defaultValue, description, maxLength, settings) {
        var self = this;
        var input = $(input).eq(0);

        settings = $.extend({
            // Array of regular expressions and/or char codes to restrict key input.
            // Number Default: 
            //      0-9, Tab, Enter, Backspace, Delete, Left, Up, Right, Down, Minus, Comma, Dot.
            //      [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 13, 8, 46, 37, 38, 39, 40, 109, 188, 190]
            keys: null,

            // When true, the input will trigger change event for each keyup event. (Otherwise change event is 
            // fired in the same ways as any normal textbox)
            change: false
        }, settings);

        /**
        *   Sets the value of an item and triggers the 
        *   'change' event, to extend the default change bahavior.    
        */
        this.Value = function(value) {
            var retValue;
            if (value !== null && value !== undefined) {
                retValue = input.val(value);
                input.change();
            }
            else {
                if (description && description.length) {
                    if (input.val() == description) {
                        return "";
                    }
                }
                retValue = input.val();
            }

            return retValue;
        }

        /**
        *   Checks modified state of the control.    
        */
        this.IsModified = function() {
            return (input.isModified === true) ? true : false;
        };

        /**
        *  Register events.   
        */
        this.RegisterEvents = function() {
            input.focus(function() {
                if (!input.hasClass("focus")) {
                    input.addClass("focus");
                }

                // Hide the existing text when the input has the focus, only
                // if it's the description of the input and not the defaulValue.
                if (description && !input.isModified && ($.trim(input.val()) === description)) {
                    self.Value('');
                }
            })
            .blur(function() {
                input.removeClass("focus");

                if (input.isModified === true) {
                    if (!input.hasClass("modified")) {
                        input.addClass("modified");
                    }
                }
                else {
                    input.removeClass("modified");
                }

                // If the text is empty, and there is a description
                if (description && !input.isModified && ($.trim(input.val()) === "")) {
                    self.Value(description);
                }
            })
            .bind('keyup paste', function() {
                var currentValue = input.val();
                if (currentValue.length > maxLength) {
                    currentValue = currentValue.substr(0, maxLength)
                    self.Value(currentValue);
                }

                if (currentValue === defaultValue) {
                    input.isModified = false;
                }
                else {
                    input.isModified = true;
                }

                // trigger this event on each key up
                if (settings.change) {
                    input.change();
                }
            })
            .keydown(function(event) {

                // check to see if the length is ok
                if (input.val().length >= maxLength) {
                    // ignore all keys except: delete, backspace, tab, left-arrow, right-arrow
                    if ((event.keyCode !== 46) && (event.keyCode !== 8) && (event.keyCode !== 9) &&
                        (event.keyCode !== 37) && (event.keyCode !== 39)) {
                        return false;
                    }
                }
                if (settings.keys) {
                    var found = false;
                    for (var i in settings.keys) {
                        if (settings.keys[i] === event.keyCode) {
                            found = true;
                            break;
                        }
                    }
                    if (!found) {
                        return false;
                    }
                }

                return true;
            });
        };

        /**
        *   Initialize the control and all its 
        *   inner elements. 
        */
        (this.Init = function() {

            // Set default value
            if (defaultValue) {
                self.Value(defaultValue);
                description = null;
            }

            // Bind for events
            self.RegisterEvents();

            // Remove focus from input
            input.blur();
        })();

    }; // END SimpleInputText
    /*#endregion*/

    /*#region InputText*/
    /**
    * Input-Text - creates a custom input for input[type="text"] and textarea. It relies on SimpleInputText;
    * read all the details of that control first. You can provide all the SimpleInputText settings inside
    * the current settings object.
    *
    * Notes:
    *   - Adds the class '.focus' for the container, on 'focus' event. 
    *   - Adds the class '.modified' for the container, when the control was modified.
    *   - It brings disabled and readonly classes to the container, when the control is set accordingly.
    *   - It triggers the change event after the inner input change event. It depend on the  and disabled
    *
    * HTML syntax: <div class='[your-input-class]'></div>
    *
    * When the control is initialized, it takes the container and generates a new  HTML code inside; make sure 
    * you understand the generated code and supply the right CSS formatting for its elements.
    *
    * HTML generated code:
    * <div class='[your-input-class]'>  
    *     <span>30 left</span> <!-- show the remaining characters -->
    *     <input type="text" name="" id="" /> || <textarea rows="5" cols="5"></textarea>
    * </div>
    *
    * Usage:
    *   var myInput= new PipenoUI.InputText(container, "default text", "description", 500, {isTextarea: true});
    */
    PipenoUI.InputText = function(container, defaultValue, description, maxLength, settings) {
        var self = this;
        var span, input, simpleInputText;

        container = $(container).eq(0);

        settings = $.extend({
            disabled: false,
            readonly: false,
            isTextarea: false,
            inputName: "",
            inputId: "",

            keys: null,
            change: false
        }, settings);


        /**
        *   Get and Set the value of the control.    
        */
        this.Value = function(value) {
            return simpleInputText.Value(value);
        };

        /**
        *   Clear the current value of the control
        */
        this.Clear = function() {
            return simpleInputText.Value("");
        };

        /**
        *   Enable & Disable the control.    
        */
        this.Enable = function(enabled) {
            enabled = ((enabled === false) ? false : true);
            if (enabled) {
                container.removeClass("disabled");
                input.removeAttr("disabled");
            }
            else {
                if (!container.hasClass("disabled")) {
                    container.addClass("disabled");
                }
                input.attr("disabled", "disabled");
            }
        };

        /**
        *   Enable & Disable the control.    
        */
        this.Readonly = function(readonly) {
            readonly = ((readonly === false) ? false : true);
            if (!readonly) {
                container.removeClass("readonly");
                input.removeAttr("readonly");
            }
            else {
                if (!container.hasClass("readonly")) {
                    container.addClass("readonly");
                }
                input.attr("readonly", "readonly");
            }
        };

        /**
        *   Checks enable state of the control.    
        */
        this.IsEnabled = function() {
            return !container.hasClass("disabled");
        };

        /**
        *   Checks readonly state of the control.    
        */
        this.IsReadonly = function() {
            return !container.hasClass("readonly");
        };

        /**
        *   Checks modified state of the control.    
        */
        this.IsModified = function() {
            return simpleInputText.IsModified();
        };

        this.RefreshCounter = function() {
            span.text((maxLength - input.val().length) + " left");
        };

        /**
        *  Register events.   
        */
        this.RegisterEvents = function() {

            // Input events
            input.bind('keyup paste', function() {

                // If settings.change is true then the 
                // 'change' event is doing all the refresh 
                if (!settings.change) {
                    self.RefreshCounter();
                }
            })
            .change(function() {
                self.RefreshCounter();
                $(self).trigger("change");
            })
            .focus(function() {
                $(container).addClass("focus");
            })
            .blur(function() {
                $(container).removeClass("focus");

                if (self.IsModified()) {
                    $(container).addClass("modified");
                }
                else {
                    $(container).removeClass("modified");
                }
            });

            // Container events
            $(container).click(function() {
                if (!$(this).hasClass("disabled") && !$(this).hasClass("readonly")) {
                    input.focus();
                }
            });
        };

        /**
        *   Initialize the control and all its 
        *   inner elements. 
        */
        (this.Init = function() {

            // Create inner elements and append those 
            // to the parent
            if (settings.isTextarea === true) {
                input = $("<textarea/>", {
                    rows: "5",
                    cols: "5",
                    name: settings.inputName,
                    id: settings.inputId
                });
            }
            else {
                input = $("<input />", {
                    type: "text",
                    name: settings.inputName,
                    id: settings.inputId
                });
            }
            span = $("<span/>");

            container.empty();
            container.append(span).append(input);

            // Set default states based on the settings
            self.Enable(!settings.disabled);
            self.Readonly(settings.readonly);

            // create a simpleInputText for the inner input. This brings more features
            // to the input control like limiting the control to a maxLength.
            simpleInputText = new PipenoUI.SimpleInputText(input, defaultValue, description, maxLength, settings);

            self.RefreshCounter();
            self.RegisterEvents();
        })();


    }; // END InputText
    /*#endregion*/

    /*#region RoundedSimpleInputText*/
    /**
    * RoundedSimpleInputText - creates a custom input for input[type="text"] and input[type="password"] and textarea. It relies on SimpleInputText;
    * read all the details of that control first. You can provide all the SimpleInputText settings inside
    * the current settings object.
    *
    * Notes:
    *   - Adds the class '.focus' for the container, on 'focus' event. 
    *   - Adds the class '.modified' for the container, when the control was modified.
    *   - It brings disabled and readonly classes to the container, when the control is set accordingly.
    *   - It triggers the change event after the inner input change event. It depend on the  and disabled
    *
    * HTML syntax: <div class='[your-input-class]'></div>
    *
    * When the control is initialized, it takes the container and generates a new  HTML code inside; make sure 
    * you understand the generated code and supply the right CSS formatting for its elements.
    *
    * HTML generated code:
    * <div class='[your-input-class]'>  
    *   <div class="left">
    *       <div class="right">
    *           <div class="content">
    *               <input type="text" name="" id="" /> || <input type="password" name="" id="" />
    *           </div>
    *       </div>
    *   </div>    
    * </div>
    *
    * OR
    *
    * <div class="[your-input-class]">
    *   <div class="top">
    *       <div class="left">
    *           <div class="right">
    *               <div class="middle"></div>
    *           </div>
    *       </div>
    *   </div>
    *   <div class="content">
    *       <div class="left">
    *           <div class="right">
    *               <div class="middle">
    *                   <textarea name="" id="" class=""></textarea>
    *               </div>
    *           </div>
    *       </div>
    *   </div>
    *   <div class="bottom">
    *       <div class="left">
    *           <div class="right">
    *               <div class="middle"></div>
    *           </div>
    *       </div>
    *   </div>
    * </div>
    *
    * Usage:
    *   var myInput= new PipenoUI.RoundedSimpleInputText(container, "default text", "description", 500, {type: "password"});
    */
    PipenoUI.RoundedSimpleInputText = function(container, defaultValue, description, maxLength, settings) {
        var self = this;
        var input, simpleInputText, tooltip;

        container = $(container).eq(0);

        settings = $.extend({
            disabled: false,
            readonly: false,
            inputName: "",
            inputId: "",
            type: "text",
            isTextarea: false,

            keys: null,
            change: false
        }, settings);


        /**
        *   Get and Set the value of the control.    
        */
        this.Value = function(value) {
            return simpleInputText.Value(value);
        };

        /**
        *   Clear the current value of the control
        */
        this.Clear = function() {
            return simpleInputText.Value("");
        };

        /**
        *   Enable & Disable the control.    
        */
        this.Enable = function(enabled) {
            enabled = ((enabled === false) ? false : true);
            if (enabled) {
                container.removeClass("disabled");
                input.removeAttr("disabled");
            }
            else {
                if (!container.hasClass("disabled")) {
                    container.addClass("disabled");
                }
                input.attr("disabled", "disabled");
            }
        };

        /**
        *   Enable & Disable the control.    
        */
        this.Readonly = function(readonly) {
            readonly = ((readonly === false) ? false : true);
            if (!readonly) {
                container.removeClass("readonly");
                input.removeAttr("readonly");
            }
            else {
                if (!container.hasClass("readonly")) {
                    container.addClass("readonly");
                }
                input.attr("readonly", "readonly");
            }
        };

        /**
        *   Checks enable state of the control.    
        */
        this.IsEnabled = function() {
            return !container.hasClass("disabled");
        };

        /**
        *   Checks readonly state of the control.    
        */
        this.IsReadonly = function() {
            return !container.hasClass("readonly");
        };

        /**
        *   Checks modified state of the control.    
        */
        this.IsModified = function() {
            return simpleInputText.IsModified();
        };

        /**
        *  Register events.   
        */
        this.RegisterEvents = function() {

            // Input events
            input.bind('keyup paste', function() {

                // If settings.change is true then the 
                // 'change' event is doing all the refresh 
            })
            .change(function() {
                $(self).trigger("change");
            })
            .focus(function() {
                if (!settings.isTextarea)
                    $(container).addClass("focus");
            })
            .blur(function() {
                $(container).removeClass("focus");

                if (self.IsModified()) {
                    $(container).addClass("modified");
                }
                else {
                    $(container).removeClass("modified");
                }
            });

            // Container events
            $(container).click(function() {
                if (!$(this).hasClass("disabled") && !$(this).hasClass("readonly")) {
                    input.focus();
                }
            });
        };

        this.Error = function(errorText) {
            $(container).addClass('error');
            tooltip = new PipenoUI.Tooltip(errorText);
            tooltip.Show(container);
        };

        /**
        *   Initialize the control and all its 
        *   inner elements. 
        */
        (this.Init = function() {

            // Create inner elements and append those 
            // to the parent

            container.empty();
            if (settings.isTextarea) {
                input = $("<textarea />", {
                    name: settings.inputName,
                    id: settings.inputId
                });

                container.append(
                    $('<div/>', { "class": 'top' }).append(
                        $('<div/>', { "class": 'left' }).append(
                            $('<div/>', { "class": 'right' }).append(
                                $('<div/>', { "class": 'middle' })))));
                container.append(
                    $('<div/>', { "class": 'content' }).append(
                        $('<div/>', { "class": 'left' }).append(
                            $('<div/>', { "class": 'right' }).append(
                                $('<div/>', { "class": 'middle' }).append(input)))));
                container.append(
                    $('<div/>', { "class": 'bottom' }).append(
                        $('<div/>', { "class": 'left' }).append(
                            $('<div/>', { "class": 'right' }).append(
                                $('<div/>', { "class": 'middle' })))));
            } else {
                input = $("<input />", {
                    type: settings.type,
                    name: settings.inputName,
                    id: settings.inputId
                });

                container.append(
                    $('<div/>', { "class": 'left' }).append(
                        $('<div/>', { "class": 'right' }).append(
                            $('<div/>', { "class": 'content' }).append(
                                $(input)))));
            }

            // Set default states based on the settings
            self.Enable(!settings.disabled);
            self.Readonly(settings.readonly);

            // create a simpleInputText for the inner input. This brings more features
            // to the input control like limiting the control to a maxLength.
            simpleInputText = new PipenoUI.SimpleInputText(input, defaultValue, description, maxLength, settings);

            self.RegisterEvents();
        })();
    }; // END RoundedSimpleInputText
    /*#endregion*/

    /*#region SubmitButton*/
    /**
    * SubmitButton - creates a custom submit button.
    *
    * HTML syntax: <a class='your-css-class'>[your text]</a>
    *
    * The control has 2 states: running and stoped. When the controls is in state running, it is disable and
    * has an inner running img. Make sure you understand the generated code and supply the right CSS formatting
    * for its elements. 
    *
    * HTML generated code:
    * <a class='[your-css-class disabled]'>  
    *     <img src='' style='width, height' alt='' />
    * </a>
    *
    * Usage:
    *   var myInout= new PipenoUI.SubmitButton(container, {
    *       runningImgSrc: 'http://localhost:14818/Console/img/form-button-small-yellow-fill.jpg',
    *       runningImgWidth: '23',
    *       runningImgHeight: '23',
    *       runningImgAlt: 'Super Testing'});
    *
    * The container can be:
    *   - jQuery element: $("#context a.container");  
    *   - DOM element: by using document.getElementByID(..) for example.
    */
    PipenoUI.SubmitButton = function(container, settings) {
        var self = this;
        var isEnabled = true;

        container = $(container).eq(0);

        settings = $.extend({
            runningImgSrc: '',
            runningImgWidth: '22',
            runningImgHeight: '22',
            runningImgAlt: 'Running ...'
        }, settings);

        var innerText = container.text();
        var innerImg = $("<img />", {
            src: settings.runningImgSrc,
            width: parseInt(settings.runningImgWidth),
            height: parseInt(settings.runningImgHeight),
            alt: settings.runningImgAlt
        });

        /**
        *   Set the running state for the button.    
        */
        this.Run = function() {
            self.Enable(false);
            container.empty();
            innerImg.css("margin-top", Math.floor((container.height() - (settings.runningImgHeight)) / 2));
            container.append(innerImg);
        };

        /**
        *   Set the stop state for the button.    
        */
        this.Stop = function() {
            container.empty();
            self.Enable(true);
            container.text(innerText);
        };

        /**
        *   Enable & Disable the control.    
        */
        this.Enable = function(enable) {
            isEnabled = enable = ((enable === false) ? false : true);
            if (enable) {
                if (container.hasClass("disabled")) {
                    container.removeClass("disabled");
                }
            }
            else {
                if (!container.hasClass("disabled")) {
                    container.addClass("disabled");
                }
            }
        };

        /**
        *   Checks enable state of the control.    
        */
        this.IsEnabled = function() {
            return isEnabled;
        };

        /**
        *  Register events.   
        */
        (this.RegisterEvents = function() {
            container.click(function() {
                if (!isEnabled) {
                    return false;
                }

                self.Run();
                $(self).trigger("submit-data");

                return false;
            });
        })();
    }; // END SubmitButton
    /*#endregion*/

    /*#region TextList*/
    /**
    * TextList
    *
    * It creates & manage a list of text-items. Each item can be removed from the list, 
    * by clicking the remove link located nearby (before or after). Each item has a value 
    * saved in the title if the li and a text. If the value is null, the text is used 
    * instead of. This can cause problems if the text contains special characters like "'".
    *
    * The change event is triggered each time a new item is added or removed from the list.
    *
    * HTML Format:
    * <ul class="text-list">
    *    <li>Tag1</li>
    *    <li>Tag2</li>
    *    <li>dolor</li> 
    *    <li>lorem ipsum</li>   
    * </ul>
    *
    * Note that the <a> link is automatically inserted when the object
    * is first initiated, depending on the settings.
    */
    PipenoUI.TextList = function(container, settings) {
        var self = this;
        var list = $(container);

        // Settings to configure this control
        settings = $.extend({
            linkPlacement: "after",
            linkTitle: "Remove",
            linkHref: "#",
            linkClass: "button-small remove"
        }, settings);

        /** 
        * Add a new item to the list. Depending on the settings, 
        * the new item might have the remove link before or after. 
        */
        this.Add = function(value, textValue) {

            if (value === undefined || value === null || value === '') {
                throw new Error("The value to be added is empty");
            }
            if (textValue === undefined || textValue === null || textValue === '') {
                textValue = value;
            }

            var newItem = $("<li/>", { title: value }).append(textValue).appendTo(list);
            var newLink = $("<a/>", {
                title: settings.linkTitle,
                href: settings.linkHref,
                "class": settings.linkClass,
                click: function() {
                    $(this).parent().remove();
                    $(self).trigger("change", ["remove", textValue]);
                    return false;
                }
            });

            switch (settings.linkPlacement) {
                case 'after':
                    newItem.append(newLink);
                    break;

                case 'before':
                default:
                    newItem.prepend(newLink);
                    break;
            }

            $(self).trigger("change", ["add", textValue]);
        };

        /** 
        * Remove a tag from the list
        */
        this.Remove = function(value) {
            list.find("> li[title='" + value + "']").remove();
            $(self).trigger("change", ["remove", value]);
        };

        /** 
        * Check if the specified item exists inside
        * the list.
        */
        this.Exists = function(value) {
            var items = list.find("> li[title='" + value + "']");
            return (items.length > 0) ? true : false;
        };

        /** 
        * Exports all values to an associative array: [value] = textValue.
        */
        this.ToArray = function() {
            var array = [];
            list.find("li").each(function() {
                array[$(this).attr("title")] = $(this).text();
            });

            return array;
        };

        (this.Init = function() {
            var itemValue;
            var items = list.find("li");
            list.empty();

            items.each(function() {
                itemValue = $.trim($(this).attr('title'));
                if (itemValue === '') {
                    itemValue = $(this).text();
                }

                self.Add(itemValue, $(this).text());
            });
        })();
    };  // END TextList
    /*#endregion*/

    /*#region ComboBox*/
    /**
    * ComboBox class - creates a simple combobox with text items. 
    *
    * XHTML markup 
    *    <ul class="[your-class]">
    *      <div></div>
    *      <li title="value1">option 1</li>
    *      <li title="value2">option 2</li>
    *      <li title="value3" class="selected">option 3</li>
    *      ...
    *      <li title="valuen">option N</li>
    *    </ul>
    *
    * Title is used as item value (similar with <option value="val">). It is searched and selected. When the title 
    * is not supplied, the algorithm takes the inner text and copy inside the title attribute; which case the inner text is used
    * as value. This option is not recommended for big inner texts. The title values should not contain special
    * characters. Such characters might generate issues. Use escape() java script function to replace such characters.
    *
    * The control use several classes for different changes: "selected" and "disabled". make sure you fully 
    * understand the control behavior in order to supply the right CSS formatting.
    * 
    * Events triggered:
    *    - "change" [oldValue, newValue] - when the selected value gets changed.
    *
    * Usage:
    *   var comboBox = new PipenoUI.ComboBox([container]);
    * 
    * The container can be:
    *   - jQuery element: $("#context div.container");  
    *   - DOM element: by using document.getElementByID(..) for example.
    */
    PipenoUI.ComboBox = function(container) {
        var self = this;
        var div, list;
        var slideSpeed = 100;

        container = $(container).eq(0);
        div = container.find("> div");
        list = container.find("> ul");

        /**
        * Deletes all items from the list.
        */
        this.Clear = function() {
            list.empty();
            div.attr("title", "").text("");
        };

        /**
        * Appends an element at the end of the list. The value argument should
        * be escaped using the javascript escape() function, if necessary.
        */
        this.Append = function(value, textValue) {

            if (value === undefined || value === null || value === '') {
                throw new Error("The value to be added is empty");
            }
            if (textValue === undefined || textValue === null || textValue === '') {
                textValue = value;
            }

            $("<li/>")
            .attr("id", value)
            .text(textValue)
            .appendTo(list)
            .click(function() {
                self.Select($(this).attr("id"));
            });
        };

        /**
        * Remove and element from the list. The argument should
        * be escaped using the javascript escape() function, if necessary.
        */
        this.Remove = function(value) {
            list.find("> li[id='" + value + "']").remove();
            if (div.attr('id') === value) {
                div.attr("id", "").text("");
            }
        };

        /**
        * Select a specific item from the list, by its value. The argument should
        * be escaped using the javascript escape() function, if necessary.
        */
        this.Select = function(value) {

            // check if there is at least one item to be selected
            var selectedItems = list.find("> li[id='" + value + "']");
            if (selectedItems.length === 0) {
                throw new Error("Item not found: " + value);
            }

            // deselect the current selected items; 
            // store the value of the first one;
            var oldValue = "";
            var oldItems = list.find("> li.selected").removeClass("selected");
            if (oldItems.length > 0) {
                oldValue = oldItems.attr('id');
            }

            // select the first item
            selectedItems.eq(0).addClass("selected");
            div.attr("id", value).text(selectedItems.eq(0).text());

            // trigger the event
            $(self).trigger("change", [oldValue, value]);
        };

        /**
        * Get the current value of the control
        */
        this.SelectedValue = function() {
            //return div.attr('title');
            return list.find("li.selected:first").attr("id");
        };

        /**
        * Register Events
        */
        (this.RegisterEvents = function() {

            $(document).click(function(event) {
                // Check if the user has clicked outside
                // Test if any of the elements inside our container 
                // have generated the click event - if not, slide back.

                var found = false;
                container.find("*").add(container).each(function() {
                    if (this == event.target) {
                        found = true;
                        return false;
                    }
                });
                if (!found) {
                    if (list.css("display") == "block") {
                        list.slideToggle(slideSpeed);
                    }
                }
            });

            container.click(function(event) {
                list.slideToggle(slideSpeed);

                // scroll the list to the selected element
                var selectedItem = list.find("> li.selected:eq(0)");
                list.scrollTop(selectedItem.height() * selectedItem.index());
            });
        })();

        /**
        * Initiate the control
        */
        (this.Init = function() {

            var items = list.find("li");
            list.empty();

            var selectedValue = null;
            items.each(function() {
                // Check the value of the item. If there is no value provided in the title
                // atribute, then try to get the item inner text as title value.
                var itemValue = $.trim($(this).attr('title'));
                if (itemValue === '') {
                    itemValue = $(this).text();
                }

                // add the element to the list
                self.Append(itemValue, $(this).text());

                // get the first selected value
                if ((selectedValue === null) && $(this).hasClass("selected")) {
                    selectedValue = itemValue;
                }
            });

            if (selectedValue !== null) {
                self.Select(selectedValue);
            }
        })();

    }; // END Combo-box
    /*#endregion*/

    /*#region ListBox*/
    /**
    * ListBox - creates a simple list-box with text items.
    *
    * XHTML markup 
    *      <ul class="[your-class]">
    *        <li title="value1">option 1</li>
    *        <li title="value2">option 2</li>
    *        <li title="value3" class="selected">option 3</li>
    *        ...
    *        <li title="valuen">option N</li>
    *      </ul>
    *
    * Title is used as item value. It is searched and selected. When the title is not supplied,
    * the algorithm takes the inner text and copy inside the title attribute; which case the inner text is used
    * as value. This option is not recommended for big inner texts. The title values should not contain special
    * characters. Such characters might generate issues. Use escape() java script function to replace such characters.
    *
    * The control use several classes for different changes: "selected" and "disabled". make sure you fully 
    * understand the control behavior in order to supply the right CSS formatting.
    * 
    * Events triggered:
    *    - "change" [oldValue, newValue] - when the selected value gets changed.
    *
    * Usage:
    *   var listBox = new PipenoUI.ListBox([container]);
    * 
    * The container can be:
    *   - jQuery element: $("#context div.container");  
    *   - DOM element: by using document.getElementByID(..) for example.
    */
    PipenoUI.ListBox = function(container) {
        var self = this;
        var list = $(container).eq(0);

        /**
        * Deletes all items from the list.
        */
        this.Clear = function() {
            list.empty();
        };

        /**
        * Appends an element at the end of the list. The value argument should
        * be escaped using the javascript escape() function, if necessary.
        */
        this.Append = function(value, textValue) {

            if (value === undefined || value === null || value === '') {
                throw new Error("The value to be added is empty");
            }
            if (textValue === undefined || textValue === null || textValue === '') {
                textValue = value;
            }

            $("<li/>")
            .attr("title", value)
            .text(textValue)
            .appendTo(list)
            .click(function() {
                self.Select($(this).attr("title"));
            });
        };

        /**
        * Remove and element from the list. The argument should
        * be escaped using the javascript escape() function, if necessary.
        */
        this.Remove = function(value) {
            list.find("> li[title='" + value + "']").remove();
        };

        /**
        * Select a specific item from the list, by its value. The argument should
        * be escaped using the javascript escape() function, if necessary.
        */
        this.Select = function(value) {

            // check if there is at least one item to be selected
            var selectedItems = list.find("> li[title='" + value + "']");
            if (selectedItems.length === 0) {
                throw new Error("Item not found: " + value);
            }

            // deselect the current selected items; 
            // store the value of the first one;
            var oldValue = "";
            var oldItems = list.find("> li.selected").removeClass("selected");
            if (oldItems.length > 0) {
                oldValue = oldItems.attr('title');
            }

            // select the first item
            selectedItems.eq(0).addClass("selected");

            // trigger the event
            $(this).trigger("change", [oldValue, value]);
        };

        /**
        * Returns the currently selected value in the list
        */
        this.SelectedValue = function() {
            return list.find("li.selected:first").attr("title");
        };

        /**
        * Returns the currently selected text in the list
        */
        this.SelectedText = function() {
            return list.find("li.selected:first").text();
        }

        /**
        * Returns the index of the currently selected item in the list
        * (-1 if no items are selected)
        */
        this.SelectedIndex = function() {
            return list.find("> li").index(list.find("> li.selected:first"));
        };

        /**
        * Initialize. Parse all the existing elements and
        * build the list.
        */
        (this.Init = function() {
            var items = list.find("li");
            list.empty();

            var selectedValue = null;
            items.each(function() {

                // Check the value of the item. If there is no value provided in the title
                // atribute, then try to get the item inner text as title value.
                var itemValue = $.trim($(this).attr('title'));
                if (itemValue === '') {
                    itemValue = $(this).text();
                }

                // add the element to the list
                self.Append(itemValue, $(this).text());

                // get the first selected value
                if ((selectedValue === null) & $(this).hasClass("selected")) {
                    selectedValue = itemValue;
                }
            });

            if (selectedValue !== null) {
                self.Select(selectedValue);
            }

        })();

    } // END List-box
    /*#endregion*/

    /*#region RadioGroup*/
    /**
    * RadioGroup - Creates a list of radio buttons; only one can be selected at a time;
    * 
    * HTML syntax:
    *   <ul class='[your-radio-class]'>
    *     <li title="value1" class="selected">option1</li>
    *     <li title="value2">option2</li>
    *     <li title="value3">option3</li>
    *   </ul>
    * 
    * When the control is initialized, it parses the initial code and generates new 
    * HTML code; make sure you understand the generated code and supply the right CSS formatting
    * for its elements.
    * 
    * HTML generated code:
    *   <ul class='[your-radio-class]'>
    *     <li title="value1" class="selected"><a href="#"></a>option1</li>
    *     <li title="value2" ><a href="#"></a>option2</li>
    *     <li title="value3" ><a href="#"></a>option3</li>
    *   </ul>
    *
    * The control uses the title values of the items for selection and search. For items with no title specified,
    * when the control is first initialized, it gets, escapes and saves all the inner text of the items as their title. 
    * So yes, you can use the control without titles if the inner text of the items is not too big. To escape the inner text,
    * the javascript escape() function is used.
    * 
    * Usage: 
    *   var radioGroup = new PipenoUI.RadioGroup([container],{option1 : value1, option 2 : value 2,... });
    *   Options:
    *    linkTitle - (default = "") - the default title for the link (anchor inside the li)
    *    linkHref  - (default = "#") - the default url for the href attribute of the link inside each item (anchor)
    *    enabled - (default = true) - tells if the control is initiated as enabled or disabled
    *    useImagesAsOptions - (default = false) - if TRUE, the control EXPECTS that inside each li there would be an img element that will be moved into the <a> so that it would be clickable 
    * 
    * The container can be:
    *   - jQuery element: $("#context div.container");  
    *   - DOM element: by using document.getElementByID(..) for example.
    * 
    * Event:
    *   - "change" - triggered when the current option in the group has changed.
    */
    PipenoUI.RadioGroup = function(container, settings) {
        var self = this;
        var radioGroup = $(container).eq(0);
        var isEnabled = true;

        // Settings to configure this control
        settings = $.extend({
            linkTitle: "",
            linkHref: "#",
            enabled: true,
            useImagesAsOptions: false
        }, settings);

        /**
        * Selects an item by it's value (case-sensitive search). The argument should
        * be escaped using the javascript escape() function, if necessary.
        */
        this.SelectValue = function(itemValue) {

            // check if there is at least one item to be selected
            var selectedItems = radioGroup.find("> li[title='" + itemValue + "']");
            if (selectedItems.length === 0) {
                throw new Error("SelectValue - Item not found: " + itemValue);
            }

            // deselect the current selected items; store the value of the first one;
            var oldValue = "";
            var currentSelectedItems = radioGroup.find("> li.selected").removeClass("selected");
            if (currentSelectedItems.length > 0) {
                oldValue = currentSelectedItems.attr('title');
            }

            // select the first item
            selectedItems.eq(0).addClass("selected");

            // trigger the event
            $(this).trigger("change", [oldValue, itemValue]);

            return true;
        };

        /**
        * Selects an item by it's index.
        */
        this.SelectIndex = function(index) {

            // how big is the current index?
            if ((index < 0) || (index >= radioGroup.find("> li").length)) {
                throw new Error("Index out of range: " + index);
            }

            //BUG: here it needs to throw an event that the current value has changed (fix this in the future)

            return this.SelectValue(radioGroup.find("> li:nth-child(" + (index + 1) + ")").attr('title'));
        };

        /**
        * Enable / Disable the control.
        */
        this.Enable = function(enabled) {
            //make sure we have a bool value - true is default
            isEnabled = enabled = ((enabled === false) ? false : true);

            radioGroup.find("> li").each(function() {
                if (enabled === false) {
                    if (!$(this).hasClass("disabled")) {
                        $(this).addClass("disabled");
                    }
                }
                else {
                    if ($(this).hasClass("disabled")) {
                        $(this).removeClass("disabled");
                    }
                }
            });
        };

        /**
        * Select the value of the selected item
        */
        this.SelectedValue = function() {
            var selectedItems = radioGroup.find("> li.selected");
            if (selectedItems.length === 0) {
                throw new Error("There are no selected items");
            }

            return selectedItems.attr('title');
        };

        /**
        * Select the index of the selected item
        */
        this.SelectedIndex = function() {
            return radioGroup.find("> li").index(radioGroup.find("> li.selected:first"));
        };

        /**
        * Checks the control enable state.
        */
        this.IsEnabled = function() {
            return isEnabled;
        };

        /**
        * Init the control. Parse all the elements, append a link to each,
        * select only one element from the list, clear the other and disable all
        * if necessary.
        */
        (this.Init = function() {

            isEnabled = settings.enabled;

            radioGroup.find("li").each(function() {

                //detach the image inside the LI
                var hasImage = false;
                if (settings.useImagesAsOptions === true) {
                    var img = $(this).find("img").eq(0).detach();
                    if (img) {
                        hasImage = true;
                    }
                }

                //create the anchor
                var link = $("<a/>", {
                    title: settings.linkTitle,
                    'class': 'box',
                    href: settings.linkHref,
                    click: function() {
                        if ($(this).parent().hasClass('disabled')) {
                            return false;
                        }

                        self.SelectValue($(this).parent().attr('title'));
                        return false;
                    }
                }).prependTo($(this));

                if (!isEnabled && !$(this).hasClass("disabled")) {
                    this.addClass("disabled");
                }

                //if the control is only with images use the image we found inside the LI
                if (hasImage && settings.useImagesAsOptions) {
                    link.append(img)
                }

                // Check the value of the item. If there is no value provided in the title
                // atribute, then try to get the item inner escaped text as title value. The title
                // is escaped to prevent using such characters like: "'", '"', etc.
                if ($.trim($(this).attr('title')) === '') {
                    $(this).attr('title', escape($.trim($(this).text())));
                }
            });

            //
            // Select only 1 item (the first one if possible)
            //
            var selectedItems = radioGroup.find("> li.selected");
            if (selectedItems.length == 1) {
                self.SelectValue(selectedItems.attr('title'));
            }
            else {
                //self.SelectValue(radioGroup.find("> li:first").attr('title'));
            }

        })();

    } // END RadioGroup
    /*#endregion*/

    /*#region CheckBox*/
    /**
    * CheckBox - simple custom checkbox.
    *
    * HTML syntax: 
    * <div class='[your-check-box-class]'>
    *   [your text]
    * </div>
    *
    * When the control is initialized, it parses the initial code and generates new 
    * HTML code; make sure you understand the generated code and supply the right CSS formatting
    * for its elements. The inner div is very important and is part of this control standard. Don't
    * forget to use it all the time otherwise you will run into errors. 
    *
    * HTML generated code:
    * <div class='[your-check-box-class]'>
    *     <a href="#"></a> <!-- checkbox image and hot-spot for click -->
    *     [your text]
    * </div>
    *
    * Usage:
    *   var myCheckBox = new PipenoUI.CheckBox(container, {checked: true, enabled: true} );
    * 
    * The container can be:
    *   - jQuery element: $("#context div.container");  
    *   - DOM element: by using document.getElementByID(..) for example.
    */
    PipenoUI.CheckBox = function(container, settings) {
        var self = this;
        var container = $(container);
        var isEnabled = true;
        var isChecked = false;

        settings = $.extend({
            enabled: true,
            checked: false,
            value: null
        }, settings);

        /**
        * Programatically set the control checked or unchecked.
        */
        this.Check = function(checked) {
            if (!isEnabled) {
                return;
            }

            isChecked = checked = ((checked === true) ? true : false);
            if (checked) {
                if (!container.hasClass("checked")) {
                    container.addClass("checked");
                }
            }
            else {
                container.removeClass("checked");
            }

            $(self).trigger("change", [checked]);
        }

        /**
        * Programatically set the control enabled or disabled
        */
        this.Enable = function(enabled) {
            isEnabled = enabled = ((enabled === false) ? false : true);
            if (!enabled) {
                if (!container.hasClass("disabled")) {
                    container.addClass("disabled");
                }
            }
            else {
                container.removeClass("disabled");
            }
        };

        /**
        * Checks the control cheecked state.
        */
        this.IsChecked = function() {
            return isChecked;
        };

        /**
        * Checks the control enable state.
        */
        this.IsEnabled = function() {
            return isEnabled;
        };

        /**
        * Sets or gets a value for this CheckBox
        */
        this.Value = function(value) {
            if (value) {
                settings.value = value;
            }
            return settings.value;
        };

        /**
        * Automatically intializes variables
        */
        (this.Init = function() {

            isEnabled = settings.enabled;
            isChecked = settings.checked;

            $("<a/>", {
                href: "#",
                "class": "box",
                click: function() {
                    if ($(this).parent().hasClass('disabled')) {
                        return false;
                    }

                    self.Check(!isChecked);
                    return false;
                }
            }).prependTo(container);

            if (isChecked) {
                container.addClass("checked");
            }

            if (!isEnabled) {
                container.addClass("disabled");
            }
        })();

    } // END CheckBox
    /*#endregion*/

    /*#region ShowPanel*/
    /**
    * ShowPanel
    * 
    * It takes an existing container and positions it accordingly with a specific target and options.
    *
    * Usage:
    * var showPanel = new PipenoUI.ShowPanel($("#tooltipContainer"), { placement: "top" | "bottom" | "left" | "right", align: "top" | "bottom" | "left" | "right"  });
    *
    * - $("#tooltipContainer") - The tooltip will take an existing container and will display it near the target. The container should be previously defined
    *   in the page, usually at the bottom of the page.
    * - placement - defines where to display the tooltip in rapport with the target. It can be displayed on the top, bottom, left or right of the target.
    * - align: horizontal or vertical align in rapport with the target. Depending on the placement, only some options are valid. Ex. For "top" placement, only
    *   "left" (default) and "right" are valid options for align.
    *
    * Usually, you instantiate a new class and call Show() method and then Hide() method. The container remains in the DOM; the next time you may want to display
    * the tooltip just using the same instance.
    *
    * Note that there are other 2 options which can change the position of the tooltip: left and top CSS properties. These are displayed inside the css styles and
    * represent the offset from the normal position of the tooltip.
    */
    PipenoUI.ShowPanel = function(panelContainer, settings) {
        var self = this;
        var panel = $(panelContainer).eq(0);

        // Settings to configure this control
        settings = $.extend({
            placement: "bottom",
            align: "left"
        }, settings);

        var panelWidth = panel.outerWidth();
        var panelHeight = panel.outerHeight();

        // get the default left and top, and use them as offset  
        var panelDefaultTop = (panel.css("top") === "auto") ? 0 : parseInt(panel.css("top"));
        var panelDefaultLeft = (panel.css("left") === "auto") ? 0 : parseInt(panel.css("left"));

        // get other default values to reset when hide
        var panelDefaultPosition = panel.css("position");
        var panelDefaultDisplay = panel.css("display");

        /**
        * Method called to display the panel
        */
        this.Show = function(target) {

            var target = $(target);
            var targetWidth = target.outerWidth();
            var targetHeight = target.outerHeight();

            var targetLeft = target.offset().left;
            var targetTop = target.offset().top;

            var panelTop = 0;
            var panelLeft = 0;

            switch (settings.placement) {
                case 'left':
                    panelLeft = targetLeft - panelWidth;
                    if (settings.align == 'bottom') {
                        panelTop = targetTop - (panelHeight - targetHeight);
                    }
                    else {
                        panelTop = targetTop;
                    }
                    break;

                case 'right':
                    panelLeft = targetLeft + targetWidth;
                    if (settings.align == 'bottom') {
                        panelTop = targetTop - (panelHeight - targetHeight);
                    }
                    else {
                        panelTop = targetTop;
                    }
                    break;

                case 'top':
                    panelTop = targetTop - panelHeight;
                    if (settings.align == 'right') {
                        panelLeft = targetLeft - (panelWidth - targetWidth);
                    }
                    else {
                        panelLeft = targetLeft;
                    }
                    break;

                case 'bottom':
                default:
                    panelTop = targetTop + targetHeight;
                    if (settings.align == 'right') {
                        panelLeft = targetLeft - (panelWidth - targetWidth);
                    }
                    else {
                        panelLeft = targetLeft;
                    }
                    break;
            }

            // adjust with the offset
            panelTop += panelDefaultTop;
            panelLeft += panelDefaultLeft;

            // show the panel
            panel.css({
                'position': 'absolute',
                'top': panelTop,
                'left': panelLeft,
                'z-index': '1000',
                'display': 'block'
            });
        };

        /**
        * Method called to hide the panel. This method don't remove the 
        * cloned container from the DOM.
        */
        this.Hide = function() {
            panel.css({
                'position': panelDefaultPosition,
                'top': panelDefaultTop,
                'left': panelDefaultLeft,
                'display': panelDefaultDisplay
            });
        };

    } // END ShowPanel
    /*#endregion*/

    /*#region Tooltip*/
    /**
    * Tooltip class
    * 
    * It creates a tooltip using the given text and displays it related to the target. This class relies on ShowPanel class. 
    * 
    * Usage:
    * var tooltip = new PipenoUI.Tooltip("Text to be displayed inside", {placement: "bottom", align: "left", tooltipClass: "tooltip-help"});
    *
    * Because the class relies on ShowPanel class, the left and top CSS properties can be used for the tooltipClass. The algorithm creates 
    * the following HTML code and appends it to the <body>. It is only removed on the object Remove() call or when the page reloads.
    * 
    *   <div class="[tooltipClass]">
    *       <div class="content">[tooltipText]</div>
    *       <div class="tip"></div>
    *   </div>
    *
    * Usually, you instantiate a new tooltip, call Show() method and then Hide() method. The container remains in the DOM; 
    * the next time you may want to display the tooltip by using the same instance.
    */
    PipenoUI.Tooltip = function(tooltipText, settings) {
        var self = this,

        // Settings to configure this control
        settings = $.extend({
            placement: "right",
            align: "top",
            tooltipClass: "tooltip-form-help"
        }, settings),

        // Create the tooltip and append it to the body
        tooltip = $("<div/>", { "class": settings.tooltipClass })
        .append($("<div/>", { "class": "content" }).html(tooltipText))
        .append($("<div/>", { "class": "tip" }))
        .css("display", "none")
        .appendTo("body"),

        // Create a show panel object
        showPanel = new PipenoUI.ShowPanel(tooltip, settings);

        this.Show = function(target) {
            if (self === null) {
                throw new Error('This tooltip was removed');
            }
            showPanel.Show($(target));
        };

        this.Hide = function(target) {
            showPanel.Hide();
        };

        this.Remove = function(target) {
            showPanel.Hide();
            tooltip.remove();
            self = null;
        };
    } // END Tooltip
    /*#endregion*/

    /*#region TopFixedText*/
    /**
    * Utils class
    */
    PipenoUI.TopFixedText = function() {
        var self = this;
        var container = $("<div/>")
        .attr("id", "top-fixed-text")
        .css({
            'display': 'none',
            'position': 'fixed',
            'text-align': 'center',
            'z-index': '1000'
        });

        /**
        * Displays the text
        * @param textValue The text to be displayed
        * @param cssClass An extra css class to be added to the text
        * @param duration The time (in seconds) to display the control for 
        */
        this.Show = function(textValue, cssClass, duration) {
            if (textValue === undefined || textValue === null) {
                throw new Error('Null argument');
            }

            // Hide any previous text
            this.Hide();

            // display the container
            var bodyWidth = $('body').width();
            var bodyScrollLeft = $('body').scrollLeft();
            container.text(textValue).appendTo($('body')).css({
                'display': 'block',
                'top': 0,
                'left': (bodyWidth - container.width()) / 2 - bodyScrollLeft
            });
            if ($.trim(cssClass).length > 0) {
                container.addClass(cssClass);
            }
            if (!isNaN(duration)) {
                window.topFixedTextInterval = setInterval(function() { self.Hide() }, duration * 1000);
            }
        };

        this.Hide = function() {
            if (window.topFixedTextInterval) {
                clearInterval(window.topFixedTextInterval);
            }
            $('body').find('#top-fixed-text').removeAttr("class").remove();
        };
    } // END TopFixedText
    /*#endregion*/

    /*#region UserMessage*/
    /**
    * UserMessage class - it's container that is shown/hidden in the page at a fixed location set up in the HTML
    * (similar to TopFixedText except that it is placed by the user in the page and needs a container to be init with)
    * 
    * XHTML:
    * <div class="[your-class]"></div>
    * 
    * JS:
    * var userMessage = new PipenoUI.UserMessage($('[your-class]'));
    * userMessage.Show("some text here", 3);
    * 
    */
    PipenoUI.UserMessage = function(container, settings) {
        var self = this;
        var container = $(container).eq(0);
        var intervalHandler;

        settings = $.extend({
            cssClass: "", //a class to add to the element
            animation: 0 //the duration of the animation (0 for no animation)
        }, settings);

        /**
        * Method used to display the control
        * @param textValue The text displayed inside the control
        * @param cssClass [optional] An extra css class to add to the container
        * @param seconds [optional] The number of seconds to display the control for. 0 or nothing to disable the autohide feature
        */
        this.Show = function(textValue, cssClass, seconds) {
            clearInterval(intervalHandler);

            if (textValue === undefined || textValue === null) {
                throw new Error('Null argument');
            }

            cssClass = $.trim(cssClass);
            if (settings.cssClass != cssClass) {
                container.removeClass(settings.cssClass);
                settings.cssClass = cssClass;
            }
            container.addClass(settings.cssClass);

            container.text(textValue);
            container.slideDown(settings.animation);
            if (seconds && seconds > 0) {
                intervalHandler = setInterval(function() { self.Hide() }, 1000 * seconds);
            }
        };

        this.Hide = function() {
            clearInterval(intervalHandler);
            container.slideUp(settings.animation);
            container.removeClass(settings.cssClass);
        };

        (this.Init = function() {
            clearInterval(intervalHandler);
            container.hide();
        })();

    } // END UserMessage
    /*#endregion*/

    /*#region FlashFileUpload*/

    /**
    * FlashFileUpload - creates a custom uploader with browse button and progress.
    *
    * HTML syntax: <div class='[your-uploader-class]'></div>
    *
    * When the control is initialized, it takes the container and generates a new
    * HTML code inside; make sure you understand the generated code and supply the right CSS formatting
    * for its elements.
    *
    * HTML generated code:
    *
    * <div class='[your-uploader-class]'>  
    *    <div class="swf-container"> <!-- container for the flash browse button -->
    *       <object data="FlashFileUpload.swf" id="flash-uploader" type="application/x-shockwave-flash"> <!-- the flash browse button -->
    *           <param></param> <!-- parameter tags for the flash movie(there can be multiple isntances) -->
    *       </object>
    *   </div>
    *
    *   <!--  The following code is generated each time a file has been selected for upload -->
    *
    *   <div id="container-[file name without extension]" class="progress-container"> <!-- container for the progress -->
    *       <span style="display: block;">[file name with extension]</span> <!-- selected file name -->
    *       <a href="#" class="cancel"></a> <!-- cancel button -->
    *       <div style="visibility: hidden;" class="progress-background"> <!-- progress bar background -->
    *           <div class="progress"></div> <!-- actual progress -->
    *           <span></span> <!-- progress percent -->
    *       </div>
    *       <span class="complete"></span> <!-- message for upload completed or cancelled -->
    *   </div>
    * </div>
    *
    * <!--  In case of and upload error the code above will be replaced by the code below -->
    *
    * <span class="error">[Error message]</span>
    *
    * Usage:
    *   var myUploader = new PipenoUI.FlashFileUpload(container, "FlashFileUpload.swf", { width: "119", height: "28" }, true, {autoStart: "true"});
    *   
    *
    * The container can be:
    *   - jQuery element: $("#context div.container");  
    *   - DOM element: by using document.getElementByID(..) for example.
    *
    * The dimensions parameter represents an object containing the width and height of the flash movie(e.g. the Browse button)
    * Example: var dimensions = { width: "119", height: "28" };
    *
    * The progress parameter is a Boolean value that tell the controls to show or hide the upload progress bar
    *
    * The settings parameter represents and object containing the flashvars that will be sent to the flash movie:
    *
    *  - scriptUrl - url to the uploading script. Default value: "upload.php"
    *  - autoStart - if "true" the selected file will start uploading after it ahs been selected, if "false" the startUpload() function needs to be called 
    *       in order to start uploading the files. Default value: "false"
    *  - browseIcon - path to the browse button. Default value: "img/browse-button.png"
    *              IMPORTANT: The image needs to have the same dimensions as the ones set for the flash movie!
    *  - fileTypes - extensions for the desired file filtering. Default value: "*.jpg; *.jpeg; *.gif; *.png"
    *  - fileTypesDescription - description used by the browse dialog. Default value: "Images"
    *  - postParameters - additional parameters that will be sent to the script. Default value: ""
    *                   - accepted format: "param1=value1&param2=value2"
    *
    * Events triggered:
    *   - fileSelected - when a file has been selected from the browse dialog. Parameter: fileName
    *   - uploadCompleted - when a file has finished uploading. Parameter: fileName
    *   - uploadCanceled - when a file upload has been canceled. Parameter: fileName
    *   - uploadError - when the upload process has encountered an error. Parameters: fileName, error
    */
    PipenoUI.FlashFileUpload = function(container, swfUrl, dimensions, progress, settings) {

        var inputName;
        var uploader;
        var self = this;
        var swfContainer;
        var showProgress = true;

        //flash movie parameters    
        var params = {
            allowscriptaccess: "always"

        };

        settings = $.extend({
            scriptUrl: "upload.php",
            autoStart: "false",
            browseIcon: "",
            browseIconHover: "",
            fileTypes: "*.jpg; *.jpeg; *.gif; *.png",
            fileTypesDescription: "Images",
            postParameters: ""
        }, settings);

        if (progress != null) {
            showProgress = progress;
        }

        this.container = $(container).eq(0);

        var attributes = { id: "flash-uploader" };

        /*#region Handlers*/
        PipenoUI.FlashFileUploadUtils = [];

        PipenoUI.FlashFileUploadUtils.setSelectedFile = function(fileName) {

            if (showProgress) {
                var progressContainer = $("<div/>", { "class": "progress-container", id: "container-" + fileName.split(".")[0] });
                var cancelButton = $("<a/>", { "class": "cancel", href: "#" });
                var progressBackground = $("<div/>", { "class": "progress-background" });
                var progressDiv = $("<div/>", { "class": "progress" });
                var percentSpan = $("<span/>");
                var completeSpan = $("<span/>", { "class": "complete" });
                var nameSpan = $("<span/>");

                nameSpan.html(fileName);
                nameSpan.css({
                    "display": "block"
                });

                cancelButton.click(function() {

                    $(this).parent().fadeOut();

                    uploader.cancelUpload(nameSpan.html());

                    return false;
                });

                progressBackground.append(progressDiv).append(percentSpan);
                progressContainer.append(nameSpan).append(cancelButton).append(progressBackground).append(completeSpan);
                self.container.append(progressContainer);

                $("div.progress-background").hide();
            }
            $(self).trigger("fileSelected", [fileName]);

        };

        PipenoUI.FlashFileUploadUtils.updateProgress = function(fileName, bytesLoaded, bytesTotal) {

            if (showProgress) {
                var percentLoaded = bytesLoaded / bytesTotal * 100;
                var parentContainer = $("#container-" + fileName.split(".")[0]);

                parentContainer.children("div.progress-background").children("div.progress").css({
                    "width": percentLoaded + "%"
                });

                parentContainer.children("div.progress-background").children("span").html(Math.floor(percentLoaded) + "%");
            }
        };


        PipenoUI.FlashFileUploadUtils.uploadCompleted = function(fileName) {

            if (showProgress) {
                var parentContainer = $("#container-" + fileName.split(".")[0]);

                parentContainer.fadeOut();
            }
            $(self).trigger("uploadCompleted", [fileName]);

        };

        PipenoUI.FlashFileUpload.autoStartUpload = function() {

            if (showProgress) {
                $("div.progress-background").show();

                $("div.progress-container a").show();

                $("div.progress").css({
                    "width": "0%"
                });
            }
        };

        PipenoUI.FlashFileUploadUtils.uploadCancelled = function(fileName) {

            if (showProgress) {
                var parentContainer = $("#container-" + fileName.split(".")[0]);

                parentContainer.children(".complete").html(fileName + " upload has been canceled!");
            }

            $(self).trigger("uploadCanceled", [fileName]);

        };

        PipenoUI.FlashFileUploadUtils.uploadError = function(fileName, error) {
            if (showProgress) {
                var parentContainer = $("#container-" + fileName.split(".")[0]);

                parentContainer.fadeOut();
            }

            $(self).trigger("uploadError", [fileName, error]);
        };
        /*#endregion*/

        this.startUpload = function() {

            uploader.startUpload();

            PipenoUI.FlashFileUpload.autoStartUpload();
        };

        /**
        *   Initialize the control and all its 
        *   inner elements. 
        */
        (this.Init = function() {

            swfContainer = $("<div/>", { "class": "swf-container" });
            swfDiv = $("<div/>", { id: "pipeno-uploader" });

            swfContainer.append(swfDiv);

            self.container.append(swfContainer);


            swfobject.embedSWF(swfUrl, "pipeno-uploader", dimensions.width.toString(), dimensions.height.toString(), "9.0.0", null, settings, params, attributes);

            uploader = document.getElementById("flash-uploader");

        })();

    };
    /*#endregion*/

    /*#region SubmitForm*/

    PipenoUI.SubmitForm = function(actionLink, settings) {

        var inputs = new Object();

        settings = $.extend({
            method: 'POST'
        }, settings);

        if (actionLink && actionLink.length) {
            settings.action = actionLink;
        }

        this.AddItem = function(key, value) {
            inputs[key] = value;
        }

        this.RemoveItem = function(key) {
            inputs[key] = null;
        }

        this.GetItem = function(key) {
            return inputs[key];
        }

        this.Submit = function(actionLink) {
            if (!inputs) {
                return false;
            }
            if (actionLink && actionLink.length) {
                settings.action = actionLink;
            }

            var _method = settings.method.toLowerCase() == "get" ? "get" : "post";

            var form = $("<form/>", { action: settings.action, method: _method }).appendTo($("body"));
            for (var i in inputs) {
                if (inputs[i] != null && inputs[i] != undefined) {
                    $("<input/>", { type: "hidden", name: i, value: inputs[i] }).appendTo(form);
                }
            }

            form.submit();
        }
    }

    /*#endregion*/

    /*#region TextEditor*/
    /**
    * TextEditor class - it's the initialization and setup of the TinyMCE editor
    * 
    * XHTML:
    * <textarea class="[your-class]"></textarea>
    * 
    * JS:
    * var textEditor = new PipenoUI.TextEditor('your-class', settings, editorSetupFunction);
    * 
    * NOTES:
    * - this class contains the basic settings we need for tinyMCE. For more settings check http://wiki.moxiecode.com/index.php
    * - editorSetupFunction represents a function where you can add more events for the current editor
    * 
    */
    PipenoUI.TextEditor = function(container, settings, editorSetupFunction) {
       
        settings = $.extend({

            theme: "advanced",
            mode: "exact",
            elements: container,
            width: "573",           
            theme_advanced_buttons1: "bold,italic,underline,bullist,numlist,undo,redo,custom_link",
            theme_advanced_buttons2: "",
            theme_advanced_buttons3: "",
            theme_advanced_toolbar_location: "top",
            theme_advanced_toolbar_align: "left",            
            theme_advanced_resizing: true,
            theme_advanced_resize_horizontal: false,
            theme_advanced_resizing_min_height: 400,
            theme_advanced_path: false,
            entity_encoding: "raw",            
            inline_styles: true,
            paste_auto_cleanup_on_paste: true,
            paste_remove_styles: true,
            paste_remove_spans:true,
            //setting to remove the extra white space 
            //between paragraphs in firefox
            fix_list_elements: true,
            remove_linebreaks: true,
            apply_source_formatting: false,
            force_br_newlines: false,
            force_p_newlines: false,
            valid_elements : "a[href|target=_blank],b,i,strong,em,li,ul,ol,u,p,br,img[src|alt|class]",
            
            setup: function(ed) {
                setupFunction(ed);
            }

        }, settings);

        this.getEditor = function(editor) {

            return tinyMCE.get(editor);
        };

        var setupFunction = function(ed) {

            if (editorSetupFunction != null) {
                editorSetupFunction(ed);
            }

            ed.onNodeChange.add(function(ed, cm, n) {
                cm.get('custom_link').setDisabled(ed.selection.isCollapsed());
            });
           
        };
   
        /**
        *   Initialize the control and all its 
        *   inner elements. 
        */
        (this.Init = function() {

            tinyMCE.init(settings);

        })();

    };

    /*#endregion*/

    /*#region ChartSettings*/
    /**
    * Analytics chart settings
    * 
    */
     PipenoUI.Chart = function(settings) {
             
        settings = $.extend({
            title: "Total Views", 
            values: [],
            labels: [],
            unique_values: [],    
            steps: 1,
            visible_steps: 1       
        }, settings);
        
        var steps;
        var maxValue;

        var orderOfMagnitude = Math.pow(10, Math.floor(Math.log(settings.max)/Math.log(10)));
        var unit = Math.floor(settings.max / orderOfMagnitude);
        var steps = orderOfMagnitude;
        if(unit < 2) {
          steps /= 2;
        } else {
          if (unit > 5) {steps *= 2};
        }
        maxValue = Math.ceil(settings.max / steps) * steps; 
                   
        this.data = {              

        "elements": [
                    {
                        "type": "line",
                        "alpha": 0.5,
                        "colour": "#007681",
                        "text": "Page views",
                        "font-size": 9,  
                        "dot-style": {
				            "type":"solid-dot","dot-size": 4,
				            "tip":"#val# Views<br>#x_label#" },                      
                        "values": settings.values
                    }, {
                        "type": "line",
                        "alpha": 0.5,
                        "colour": "#EC0000",
                        "text": "Unique views",
                        "font-size": 9, 
                         "dot-style": {
				            "type":"solid-dot", "dot-size": 4,
				            "tip":"<strong>#val#</strong> Unique views<br>#x_label#" },                        
                        "values": settings.unique_values
                    }
                  ],

            "x_axis": {
                "stroke": 1,
                "tick_height": 5,
                "colour": "#BDBDBD",
                "grid-colour": "#BDBDBD",                                                              
                "labels": {
                    "labels": settings.labels,
                    "steps": settings.steps,
                    "visible_steps": settings.visible_steps,  
                    "rotate": "-45"                   
                }
            },

            "y_axis": {
                "stroke": 1,
                "tick_length": 5,
                "colour": "#BDBDBD",
                "grid-colour": "#BDBDBD",
                "max": maxValue,            
                "steps": steps/2                                                
                                          
            },          
             "bg_colour": "#F9F9F9", 
             "tooltip" :{
                "background": "#F7F08E",
                "title":"{font-size: 12px; color: #000000;font-weight: bold; text-align: center;}",
                "body":"{font-size: 11px; color: #000000; text-align: center;}",
                "stroke": 1,
                "rounded": 0
            }
    };       

    }
    /*#endregion*/
})(window);