Create a very basic time off requests SharePoint application with Microsoft Access

Long time ago Microsoft Access was frequently used for small application, being also known as a desktop database. After a while being in the dark, Microsoft decided to bring Access back to life, which is not a bad idea. Let’s have a quick look on how a regular user can create a time off requests application without writing a single line of code.

For the beginning, we need to start Microsoft Access and select custom web app from screen menu. In the dialogue screen we need to put the name of our application and URL of the SharePoint where we are going to host it.

2014-10-25 18_56_54-Access

Please note validation rule from the screenshot below marked in red. This is telling you how to make a field mandatory and how to set your validation message for a field.
2014-10-25 19_12_10-Access - HR TimeOff _ Database- https___hp314.sharepoint.com_sites_SPRIntPublish
The final step is to create the table containing the requests with a lookup field to the table we created on the previous step.
2014-10-25 19_17_48-Access - HR TimeOff _ Database- https___hp314.sharepoint.com_sites_SPRIntPublish
Please note again settings for “End Date” field, having a more complex validation rule, as it is compared the value with “Start Date” and throw an error is is not greater than it.
2014-10-25 19_21_31-Access
Everybody can now developed small application without advanced programming language. Of course this won’t replace big and complex application, but for a normal employee this can be a real help. And application looks great when it comes to design. 🙂
2014-10-25 19_26_15-HR TimeOff

Advertisements

Lazy loading files in SharePoint

A lot of SharePoint developers are moving now to JavaScript as Microsoft intention is to concentrate as much as possible on client side development. In most of the cases, because they are coming from client side development, project is delivered with a lot of un-organized JavaScript code, loaded with scripts tags placed inside various web parts.

To organize the files, developers can now use some JavaScript loaders libraries like requirejs, and the next point is about how to use it in SharePoint development environment.

In general, when I need to work in client side, I used scriptmanager control to load the files I need. The reason I am doing this is because SharePoint client side development requires Microsoft Ajax library to be loaded and scriptmanager is doing this before specified files are loaded into the page.

<asp:ScriptManager id="ScriptManager" runat="server" EnablePageMethods="false" LoadScriptsBeforeUI="false" EnablePartialRendering="true" EnableScriptGlobalization="false" EnableScriptLocalization="true">
	<Scripts>
	<asp:ScriptReference Path="../../Scripts/require.js" />
	<asp:ScriptReference Path="../../Scripts/requireconfig.js" />
	<asp:ScriptReference Path="../../Scripts/master.js" />
	</Scripts>
</asp:ScriptManager>

Pay attention how to use LoadScriptsBeforeUI property. I would suggest you to have it set on false, otherwise you can affect SharePoint functionality.

Using scriptmanager we load require.js library, requireconfig.js and master.js. Requireconfig.js is our file containing common configuration for loading files. For example we can set loading orders for a calendar which is making use of a date and time utility file.

(function() {
	require.config({ 
	
		baseUrl: 'https://siteurl',
		
		// Paths
		paths: {
			DateTime: 'Utils/DateTime/DateTime',
			Calendar: 'Apps/Calendar/Calendar',
		},
		
		// Set dependendecies for js files
		shim: {
			'Calendar': {
				deps: ['DateTime']
			} 
		}
		
	});

}());

Master.js contains our custom code which should be a sort of dispatcher for loading files. Depending on page name, we might want to load some specific JavaScript files when page is loaded.

function pageLoad() {

	// Get the page name (making use of SharePoint builtin functionality here)
	JSRequest.EnsureSetup();
	var pageName = JSRequest['FileName'];
	
	if(pageName === 'AllPages.aspx') {
		
		// Additional configure requirejs for AllPages.aspx page
		require.config({
			paths: {
				AllPages: 'Pages/AllPages',
			},
			shim: {
				'AllPages': {
					deps: ['Calendar']
				} 
			}
		});
	}
	
	// Load required files for AllPages.aspx
	require(['AllPages'],function() {
		// Some logic can also be implemented here or in AllPages.js
	});

}

You can also combine this logic with built-in registerCssLink function (available in SharePoint 2013), which I wrote about it here, to load CSS file specific to current page.

Happy conding!!! 🙂

Singleton pattern using Microsoft Ajax JavaScript library

Microsoft Ajax JavaScript library is one the powerful libraries on the market, even is not so popular any more. More and more developers are going into jQuery direction, but I still continue to use on daily basis, as I am working in SharePoint environment.
For today, I have created a very simple example of singleton pattern based on this library. In essence, is not different at all than pure JavaScript, but the purpose of this is to show to other Microsoft Ajax enthusiastic developers how can integrate this pattern into their code.

/// <Reference Name="MicrosoftAjax.js" />

Type.registerNamespace('NS');

NS.Singleton = function () {
    /// <summary>Constructor for our singleton class</summary>
    if (NS.Singleton._instance !== null) {
        throw "An instance of NS.Singleton already exists"
    }
    NS.Singleton._instance = this;
};

// Stores the instance of Singleton class
NS.Singleton._instance = null;

NS.Singleton.get_instance = function () {
    /// <summary>Return single instance of NS.Singleton</summary>
    if (NS.Singleton._instance === null) {
        return new NS.Singleton();
    }
    else {
        MS.Singleton._instance;
    }
};

NS.Singleton.prototype = {
    firstMethod: function () {
        alert('This is first method');
    },
    secondMethod: function () {
        alert('This is second method');
    }
};

NS.Singleton.registerClass('NS.Singleton');

So, we have a class called “Singleton”, included into “NS” namespace, and we want to have only one instance of this class in the application. For this I have created a static property “_instance” of this class where we store the instance. I used “_” in the name as, by convention, private properties or method should start with this sign in the absence of access modifiers in JavaScript. Visual Studio is quite good on the intellisense and won’t trigger the auto complete for private properties.

Besides this, we need also a static method called “get_instance” to create an instance or return existing one.

var instance_one = NS.Singleton.get_instance();
instance_one.firstMethod();
var instance_two = NS.Singleton.get_instance();
instance_two.secondMethod();

In this example, instance_one and instance_two variable contains the same reference to the instance of NS.Singleton.

It might appear for you a little bit different than classical examples, but in essence is doing the same thing. In any case, if you decide to use Microsoft Ajax library, this is a working example.

Microsoft Ajax rounded corners behaviour in SharePoint

As my favourite library is still Microsoft Ajax, I am still working to move some Ajax Toolkit functionality to SharePoint. This is example is about how to adapt rounded corner behaviour from Ajax Too;kit to SharePoint pages. As you probably now, MicrosoftAjax.js library contains less code on SharePoint compared with version existing from the Toolkit. So, I had to remove register components functionality to make it work.

To enable the behaviour you will need some JavaScript files. MicrosoftAjax.js is normally loaded by ScriptManager control, so you don’t need to include it. I also recommend to include all the others using ScriptManager control. I did different because I used a normal HTML application created by NetBeans, which is one the best JavaScript editors.

        <script type="text/javascript" src="scripts/MicrosoftAjax.js"></script>
        <script type="text/javascript" src="scripts/extended/Common/Common.js"></script>
        <script type="text/javascript" src="scripts/extended/ExtendedBase/ExtendedBase.js"></script>
        <script type="text/javascript" src="scripts/extended/RoundedCorners/RoundedCorners.js"></script>

The other 3 files are taken from Ajax Toolkit and modified by me to make them work in SharePoint environment.

/// Common.js

var $common, CommonToolkitScripts;


Type.registerNamespace('Sys.Extended.UI');

Sys.Extended.UI.BoxSide = function() {
    /// <summary>
    /// The BoxSide enumeration describes the sides of a DOM element
    /// </summary>
    /// 
    /// 
    /// 
    /// 
};

Sys.Extended.UI.BoxSide.prototype = {
    Top : 0,
    Right : 1,
    Bottom : 2,
    Left : 3
};

Sys.Extended.UI.BoxSide.registerEnum("Sys.Extended.UI.BoxSide", false);



Sys.Extended.UI._CommonToolkitScripts = function() {
    /// <summary>
    /// The _CommonToolkitScripts class contains functionality utilized across a number
    /// of controls (but not universally)
    /// </summary>
    /// 
    /// You should not create new instances of _CommonToolkitScripts.  Instead you should use the shared instance CommonToolkitScripts (or Sys.Extended.UI.CommonToolkitScripts).
    /// 
};

Sys.Extended.UI._CommonToolkitScripts.prototype = {
    _borderStyleNames: ["borderTopStyle", "borderRightStyle", "borderBottomStyle", "borderLeftStyle"],
    _borderWidthNames: ["borderTopWidth", "borderRightWidth", "borderBottomWidth", "borderLeftWidth"],
    _paddingWidthNames: ["paddingTop", "paddingRight", "paddingBottom", "paddingLeft"],
    _marginWidthNames: ["marginTop", "marginRight", "marginBottom", "marginLeft"],

    getCurrentStyle: function(element, attribute, defaultValue) {
        /// <summary>
        /// CommonToolkitScripts.getCurrentStyle is used to compute the value of a style attribute on an
        /// element that is currently being displayed.  This is especially useful for scenarios where
        /// several CSS classes and style attributes are merged, or when you need information about the
        /// size of an element (such as its padding or margins) that is not exposed in any other fashion.
        /// </summary>
        /// 
        /// Live DOM element to check style of
        /// 
        /// 
        /// The style attribute's name is expected to be in a camel-cased form that you would use when
        /// accessing a JavaScript property instead of the hyphenated form you would use in a CSS
        /// stylesheet (i.e. it should be "backgroundColor" and not "background-color").
        /// 
        /// 
        /// In the event of a problem (i.e. a null element or an attribute that cannot be found) we
        /// return this object (or null if none if not specified).
        /// 
        /// 
        /// Current style of the element's attribute
        /// 

        var currentValue = null;
        if (element) {
            if (element.currentStyle) {
                currentValue = element.currentStyle[attribute];
            } else if (document.defaultView &amp;&amp; document.defaultView.getComputedStyle) {
                var style = document.defaultView.getComputedStyle(element, null);
                if (style) {
                    currentValue = style[attribute];
                }
            }

            if (!currentValue &amp;&amp; element.style.getPropertyValue) {
                currentValue = element.style.getPropertyValue(attribute);
            }
            else if (!currentValue &amp;&amp; element.style.getAttribute) {
                currentValue = element.style.getAttribute(attribute);
            }
        }

        if ((!currentValue || currentValue == "" || typeof (currentValue) === 'undefined')) {
            if (typeof (defaultValue) != 'undefined') {
                currentValue = defaultValue;
            }
            else {
                currentValue = null;
            }
        }
        return currentValue;
    },

    getInheritedBackgroundColor: function(element) {
        /// <summary>
        /// CommonToolkitScripts.getInheritedBackgroundColor provides the ability to get the displayed
        /// background-color of an element.  In most cases calling CommonToolkitScripts.getCurrentStyle
        /// won't do the job because it will return "transparent" unless the element has been given a
        /// specific background color.  This function will walk up the element's parents until it finds
        /// a non-transparent color.  If we get all the way to the top of the document or have any other
        /// problem finding a color, we will return the default value '#FFFFFF'.  This function is
        /// especially important when we're using opacity in IE (because ClearType will make text look
        /// horrendous if you fade it with a transparent background color).
        /// </summary>
        /// 
        /// Live DOM element to get the background color of
        /// 
        /// 
        /// Background color of the element
        /// 

        if (!element) return '#FFFFFF';
        var background = this.getCurrentStyle(element, 'backgroundColor');
        try {
            while (!background || background == '' || background == 'transparent' || background == 'rgba(0, 0, 0, 0)') {
                element = element.parentNode;
                if (!element) {
                    background = '#FFFFFF';
                } else {
                    background = this.getCurrentStyle(element, 'backgroundColor');
                }
            }
        } catch (ex) {
            background = '#FFFFFF';
        }
        return background;
    },

    getLocation: function(element) {
        /// <summary>Gets the coordinates of a DOM element.</summary>
        /// 
        /// 
        ///   A Point object with two fields, x and y, which contain the pixel coordinates of the element.
        /// 
        return Sys.UI.DomElement.getLocation(element);
    },

    setLocation: function(element, point) {
        /// <summary>
        /// Sets the current location for an element.
        /// </summary>
        /// 
        /// DOM element
        /// 
        /// 
        /// Point object (of the form {x,y})
        /// 
        /// 
        /// This method does not attempt to set the positioning mode of an element.
        /// The position is relative from the elements nearest position:relative or
        /// position:absolute element.
        /// 
        Sys.UI.DomElement.setLocation(element, point.x, point.y);
    },

    getContentSize: function(element) {
        /// <summary>
        /// Gets the "content-box" size of an element.
        /// </summary>
        /// 
        /// DOM element
        /// 
        /// 
        /// Size of the element (in the form {width,height})
        /// 
        /// 
        /// The "content-box" is the size of the content area *inside* of the borders and
        /// padding of an element. The "content-box" size does not include the margins around
        /// the element.
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }
        var size = this.getSize(element);
        var borderBox = this.getBorderBox(element);
        var paddingBox = this.getPaddingBox(element);
        return {
            width: size.width - borderBox.horizontal - paddingBox.horizontal,
            height: size.height - borderBox.vertical - paddingBox.vertical
        }
    },

    getSize: function(element) {
        /// <summary>
        /// Gets the "border-box" size of an element.
        /// </summary>
        /// 
        /// DOM element
        /// 
        /// 
        /// Size of the element (in the form {width,height})
        /// 
        /// 
        /// The "border-box" is the size of the content area *outside* of the borders and
        /// padding of an element.  The "border-box" size does not include the margins around
        /// the element.
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }
        return {
            width: element.offsetWidth,
            height: element.offsetHeight
        };
    },

    setContentSize: function(element, size) {
        /// <summary>
        /// Sets the "content-box" size of an element.
        /// </summary>
        /// 
        /// DOM element
        /// 
        /// 
        /// Size of the element (in the form {width,height})
        /// 
        /// 
        /// The "content-box" is the size of the content area *inside* of the borders and
        /// padding of an element. The "content-box" size does not include the margins around
        /// the element.
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }
        if (!size) {
            throw Error.argumentNull('size');
        }
        if (this.getCurrentStyle(element, 'MozBoxSizing') == 'border-box' || this.getCurrentStyle(element, 'BoxSizing') == 'border-box') {
            var borderBox = this.getBorderBox(element);
            var paddingBox = this.getPaddingBox(element);
            size = {
                width: size.width + borderBox.horizontal + paddingBox.horizontal,
                height: size.height + borderBox.vertical + paddingBox.vertical
            };
        }
        element.style.width = size.width.toString() + 'px';
        element.style.height = size.height.toString() + 'px';
    },

    setSize: function(element, size) {
        /// <summary>
        /// Sets the "border-box" size of an element.
        /// </summary>
        /// 
        /// The "border-box" is the size of the content area *outside* of the borders and 
        /// padding of an element.  The "border-box" size does not include the margins around
        /// the element.
        /// 
        /// DOM element
        /// Size of the element (in the form {width,height})
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }
        if (!size) {
            throw Error.argumentNull('size');
        }
        var borderBox = this.getBorderBox(element);
        var paddingBox = this.getPaddingBox(element);
        var contentSize = {
            width: size.width - borderBox.horizontal - paddingBox.horizontal,
            height: size.height - borderBox.vertical - paddingBox.vertical
        };
        this.setContentSize(element, contentSize);
    },

    getBounds: function(element) {
        /// <summary>Gets the coordinates, width and height of an element.</summary>
        /// 
        /// 
        ///   A Bounds object with four fields, x, y, width and height, which contain the pixel coordinates,
        ///   width and height of the element.
        /// 
        return Sys.UI.DomElement.getBounds(element);
    },

    setBounds: function(element, bounds) {
        /// <summary>
        /// Sets the "border-box" bounds of an element
        /// </summary>
        /// 
        /// DOM element
        /// 
        /// 
        /// Bounds of the element (of the form {x,y,width,height})
        /// 
        /// 
        /// The "border-box" is the size of the content area *outside* of the borders and
        /// padding of an element.  The "border-box" size does not include the margins around
        /// the element.
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }
        if (!bounds) {
            throw Error.argumentNull('bounds');
        }
        this.setSize(element, bounds);
        $common.setLocation(element, bounds);
    },

    getClientBounds: function() {
        /// <summary>
        /// Gets the width and height of the browser client window (excluding scrollbars)
        /// </summary>
        /// 
        /// Browser's client width and height
        /// 

        var clientWidth;
        var clientHeight;

        if (document.compatMode == "CSS1Compat") {
            clientWidth = document.documentElement.clientWidth;
            clientHeight = document.documentElement.clientHeight;
        }
        else {
            clientWidth = document.body.clientWidth;
            clientHeight = document.body.clientHeight;
        }
        return new Sys.UI.Bounds(0, 0, clientWidth, clientHeight);
    },

    getMarginBox: function(element) {
        /// <summary>
        /// Gets the entire margin box sizes.
        /// </summary>
        /// 
        /// DOM element
        /// 
        /// 
        /// Element's margin box sizes (of the form {top,left,bottom,right,horizontal,vertical})
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }
        var box = {
            top: this.getMargin(element, Sys.Extended.UI.BoxSide.Top),
            right: this.getMargin(element, Sys.Extended.UI.BoxSide.Right),
            bottom: this.getMargin(element, Sys.Extended.UI.BoxSide.Bottom),
            left: this.getMargin(element, Sys.Extended.UI.BoxSide.Left)
        };
        box.horizontal = box.left + box.right;
        box.vertical = box.top + box.bottom;
        return box;
    },

    getBorderBox: function(element) {
        /// <summary>
        /// Gets the entire border box sizes.
        /// </summary>
        /// 
        /// DOM element
        /// 
        /// 
        /// Element's border box sizes (of the form {top,left,bottom,right,horizontal,vertical})
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }
        var box = {
            top: this.getBorderWidth(element, Sys.Extended.UI.BoxSide.Top),
            right: this.getBorderWidth(element, Sys.Extended.UI.BoxSide.Right),
            bottom: this.getBorderWidth(element, Sys.Extended.UI.BoxSide.Bottom),
            left: this.getBorderWidth(element, Sys.Extended.UI.BoxSide.Left)
        };
        box.horizontal = box.left + box.right;
        box.vertical = box.top + box.bottom;
        return box;
    },

    getPaddingBox: function(element) {
        /// <summary>
        /// Gets the entire padding box sizes.
        /// </summary>
        /// 
        /// DOM element
        /// 
        /// 
        /// Element's padding box sizes (of the form {top,left,bottom,right,horizontal,vertical})
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }
        var box = {
            top: this.getPadding(element, Sys.Extended.UI.BoxSide.Top),
            right: this.getPadding(element, Sys.Extended.UI.BoxSide.Right),
            bottom: this.getPadding(element, Sys.Extended.UI.BoxSide.Bottom),
            left: this.getPadding(element, Sys.Extended.UI.BoxSide.Left)
        };
        box.horizontal = box.left + box.right;
        box.vertical = box.top + box.bottom;
        return box;
    },

    isBorderVisible: function(element, boxSide) {
        /// <summary>
        /// Gets whether the current border style for an element on a specific boxSide is not 'none'.
        /// </summary>
        /// 
        /// DOM element
        /// 
        /// 
        /// Side of the element
        /// 
        /// 
        /// Whether the current border style for an element on a specific boxSide is not 'none'.
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }
        if (boxSide  Sys.Extended.UI.BoxSide.Left) {
            throw Error.argumentOutOfRange(String.format(Sys.Res.enumInvalidValue, boxSide, 'Sys.Extended.UI.BoxSide'));
        }
        var styleName = this._borderStyleNames[boxSide];
        var styleValue = this.getCurrentStyle(element, styleName);
        return styleValue != "none";
    },

    getMargin: function(element, boxSide) {
        /// <summary>
        /// Gets the margin thickness of an element on a specific boxSide.
        /// </summary>
        /// 
        /// DOM element
        /// 
        /// 
        /// Side of the element
        /// 
        /// 
        /// Margin thickness on the element's specified side
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }
        if (boxSide  Sys.Extended.UI.BoxSide.Left) {
            throw Error.argumentOutOfRange(String.format(Sys.Res.enumInvalidValue, boxSide, 'Sys.Extended.UI.BoxSide'));
        }
        var styleName = this._marginWidthNames[boxSide];
        var styleValue = this.getCurrentStyle(element, styleName);
        try { return this.parsePadding(styleValue); } catch (ex) { return 0; }
    },

    getBorderWidth: function(element, boxSide) {
        /// <summary>
        /// Gets the border thickness of an element on a specific boxSide.
        /// </summary>
        /// 
        /// DOM element
        /// 
        /// 
        /// Side of the element
        /// 
        /// 
        /// Border thickness on the element's specified side
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }
        if (boxSide  Sys.Extended.UI.BoxSide.Left) {
            throw Error.argumentOutOfRange(String.format(Sys.Res.enumInvalidValue, boxSide, 'Sys.Extended.UI.BoxSide'));
        }
        if (!this.isBorderVisible(element, boxSide)) {
            return 0;
        }
        var styleName = this._borderWidthNames[boxSide];
        var styleValue = this.getCurrentStyle(element, styleName);
        return this.parseBorderWidth(styleValue);
    },

    getPadding: function(element, boxSide) {
        /// <summary>
        /// Gets the padding thickness of an element on a specific boxSide.
        /// </summary>
        /// 
        /// DOM element
        /// 
        /// 
        /// Side of the element
        /// 
        /// 
        /// Padding on the element's specified side
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }
        if (boxSide  Sys.Extended.UI.BoxSide.Left) {
            throw Error.argumentOutOfRange(String.format(Sys.Res.enumInvalidValue, boxSide, 'Sys.Extended.UI.BoxSide'));
        }
        var styleName = this._paddingWidthNames[boxSide];
        var styleValue = this.getCurrentStyle(element, styleName);
        return this.parsePadding(styleValue);
    },

    parseBorderWidth: function(borderWidth) {
        /// <summary>
        /// Parses a border-width string into a pixel size
        /// </summary>
        /// 
        /// Type of border ('thin','medium','thick','inherit',px unit,null,'')
        /// 
        /// 
        /// Number of pixels in the border-width
        /// 
        if (!this._borderThicknesses) {

            var borderThicknesses = {};
            var div0 = document.createElement('div');
            div0.style.visibility = 'hidden';
            div0.style.position = 'absolute';
            div0.style.fontSize = '1px';
            document.body.appendChild(div0)
            var div1 = document.createElement('div');
            div1.style.height = '0px';
            div1.style.overflow = 'hidden';
            div0.appendChild(div1);
            var base = div0.offsetHeight;
            div1.style.borderTop = 'solid black';
            div1.style.borderTopWidth = 'thin';
            borderThicknesses['thin'] = div0.offsetHeight - base;
            div1.style.borderTopWidth = 'medium';
            borderThicknesses['medium'] = div0.offsetHeight - base;
            div1.style.borderTopWidth = 'thick';
            borderThicknesses['thick'] = div0.offsetHeight - base;
            div0.removeChild(div1);
            document.body.removeChild(div0);
            this._borderThicknesses = borderThicknesses;
        }

        if (borderWidth) {
            switch (borderWidth) {
                case 'thin':
                case 'medium':
                case 'thick':
                    return this._borderThicknesses[borderWidth];
                case 'inherit':
                    return 0;
            }
            var unit = this.parseUnit(borderWidth);
            Sys.Debug.assert(unit.type == 'px', String.format(Sys.Extended.UI.Resources.Common_InvalidBorderWidthUnit, unit.type));
            return unit.size;
        }
        return 0;
    },

    parsePadding: function(padding) {
        /// <summary>
        /// Parses a padding string into a pixel size
        /// </summary>
        /// 
        /// Padding to parse ('inherit',px unit,null,'')
        /// 
        /// 
        /// Number of pixels in the padding
        /// 

        if (padding) {
            if (padding == 'inherit') {
                return 0;
            }
            var unit = this.parseUnit(padding);
            if (unit.type !== 'px') {
                Sys.Debug.fail(String.format(Sys.Extended.UI.Resources.Common_InvalidPaddingUnit, unit.type));
            }
            return unit.size;
        }
        return 0;
    },

    parseUnit: function(value) {
        /// <summary>
        /// Parses a unit string into a unit object
        /// </summary>
        /// 
        /// Value to parse (of the form px unit,% unit,em unit,...)
        /// 
        /// 
        /// Parsed unit (of the form {size,type})
        /// 

        if (!value) {
            throw Error.argumentNull('value');
        }

        value = value.trim().toLowerCase();
        var l = value.length;
        var s = -1;
        for (var i = 0; i &lt; l; i++) {
            var ch = value.substr(i, 1);
            if ((ch  '9') &amp;&amp; ch != '-' &amp;&amp; ch != '.' &amp;&amp; ch != ',') {
                break;
            }
            s = i;
        }
        if (s == -1) {
            throw Error.create(Sys.Extended.UI.Resources.Common_UnitHasNoDigits);
        }
        var type;
        var size;
        if (s &lt; (l - 1)) {
            type = value.substring(s + 1).trim();
        } else {
            type = 'px';
        }
        size = parseFloat(value.substr(0, s + 1));
        if (type == 'px') {
            size = Math.floor(size);
        }
        return {
            size: size,
            type: type
        };
    },

    getElementOpacity: function(element) {
        /// <summary>
        /// Get the element's opacity
        /// </summary>
        /// 
        /// Element
        /// 
        /// 
        /// Opacity of the element
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }

        var hasOpacity = false;
        var opacity;

        if (element.filters) {
            var filters = element.filters;
            if (filters.length !== 0) {
                var alphaFilter = filters['DXImageTransform.Microsoft.Alpha'];
                if (alphaFilter) {
                    opacity = alphaFilter.opacity / 100.0;
                    hasOpacity = true;
                }
            }
        }
        else {
            opacity = this.getCurrentStyle(element, 'opacity', 1);
            hasOpacity = true;
        }

        if (hasOpacity === false) {
            return 1.0;
        }
        return parseFloat(opacity);
    },

    setElementOpacity: function(element, value) {
        /// <summary>
        /// Set the element's opacity
        /// </summary>
        /// 
        /// Element
        /// 
        /// 
        /// Opacity of the element
        /// 

        if (!element) {
            throw Error.argumentNull('element');
        }

        if (element.filters) {
            var filters = element.filters;
            var createFilter = true;
            if (filters.length !== 0) {
                var alphaFilter = filters['DXImageTransform.Microsoft.Alpha'];
                if (alphaFilter) {
                    createFilter = false;
                    alphaFilter.opacity = value * 100;
                }
            }
            if (createFilter) {
                element.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity=' + (value * 100) + ')';
            }
        }
        else {
            element.style.opacity = value;
        }
    },

    getVisible: function(element) {
        /// <summary>
        /// Check if an element is visible
        /// </summary>
        /// 
        /// Element
        /// 
        /// 
        /// True if the element is visible, false otherwise
        /// 


        return (element &amp;&amp;
                ("none" != $common.getCurrentStyle(element, "display")) &amp;&amp;
                ("hidden" != $common.getCurrentStyle(element, "visibility")));
    },

    setVisible: function(element, value) {
        /// <summary>
        /// Check if an element is visible
        /// </summary>
        /// 
        /// Element
        /// 
        /// 
        /// True to make the element visible, false to hide it
        /// 


        if (element &amp;&amp; value != $common.getVisible(element)) {
            if (value) {
                if (element.style.removeAttribute) {
                    element.style.removeAttribute("display");
                } else {
                    element.style.removeProperty("display");
                }
            } else {
                element.style.display = 'none';
            }
            element.style.visibility = value ? 'visible' : 'hidden';
        }
    },

    resolveFunction: function(value) {
        /// <summary>
        /// Returns a function reference that corresponds to the provided value
        /// </summary>
        /// 
        /// The value can either be a Function, the name of a function (that can be found using window['name']),
        /// or an expression that evaluates to a function.
        /// 
        /// 
        /// Reference to the function, or null if not found
        /// 

        if (value) {
            if (value instanceof Function) {
                return value;
            } else if (String.isInstanceOfType(value) &amp;&amp; value.length &gt; 0) {
                var func;
                if ((func = window[value]) instanceof Function) {
                    return func;
                } else if ((func = eval(value)) instanceof Function) {
                    return func;
                }
            }
        }
        return null;
    },

    addCssClasses: function(element, classNames) {
        /// <summary>
        /// Adds multiple css classes to a DomElement
        /// </summary>
        /// The element to modify
        /// The class names to add

        for (var i = 0; i &lt; classNames.length; i++) {
            Sys.UI.DomElement.addCssClass(element, classNames[i]);
        }
    },
    removeCssClasses: function(element, classNames) {
        /// <summary>
        /// Removes multiple css classes to a DomElement
        /// </summary>
        /// The element to modify
        /// The class names to remove

        for (var i = 0; i &lt; classNames.length; i++) {
            Sys.UI.DomElement.removeCssClass(element, classNames[i]);
        }
    },
    setStyle: function(element, style) {
        /// <summary>
        /// Sets the style of the element using the supplied style template object
        /// </summary>
        /// The element to modify
        /// The template

        $common.applyProperties(element.style, style);
    },
    removeHandlers: function(element, events) {
        /// <summary>
        /// Removes a set of event handlers from an element
        /// </summary>
        /// The element to modify
        /// The template object that contains event names and delegates
        /// 
        /// This is NOT the same as $clearHandlers which removes all delegates from a DomElement.  This rather removes select delegates 
        /// from a specified element and has a matching signature as $addHandlers
        /// 
        for (var name in events) {
            $removeHandler(element, name, events[name]);
        }
    },

    overlaps: function(r1, r2) {
        /// <summary>
        /// Determine if two rectangles overlap
        /// </summary>
        /// 
        /// Rectangle
        /// 
        /// 
        /// Rectangle
        /// 
        /// 
        /// True if the rectangles overlap, false otherwise
        /// 

        return r1.x &lt; (r2.x + r2.width)
                &amp;&amp; r2.x &lt; (r1.x + r1.width)
                &amp;&amp; r1.y &lt; (r2.y + r2.height)
                &amp;&amp; r2.y &lt; (r1.y + r1.height);
    },

    containsPoint: function(rect, x, y) {
        /// <summary>
        /// Tests whether a point (x,y) is contained within a rectangle
        /// </summary>
        /// The rectangle
        /// The x coordinate of the point
        /// The y coordinate of the point

        return x &gt;= rect.x &amp;&amp; x = rect.y &amp;&amp; y &lt; (rect.y + rect.height);
    },

    isKeyDigit: function(keyCode) {
        /// <summary>
        /// Gets whether the supplied key-code is a digit
        /// </summary>
        /// The key code of the event (from Sys.UI.DomEvent)
        /// 

        return (0x30 &lt;= keyCode &amp;&amp; keyCode &lt;= 0x39);
    },

    isKeyNavigation: function(keyCode) {
        /// <summary>
        /// Gets whether the supplied key-code is a navigation key
        /// </summary>
        /// The key code of the event (from Sys.UI.DomEvent)
        /// 

        return (Sys.UI.Key.left &lt;= keyCode &amp;&amp; keyCode &lt;= Sys.UI.Key.down);
    },

    padLeft: function(text, size, ch, truncate) {
        /// <summary>
        /// Pads the left hand side of the supplied text with the specified pad character up to the requested size
        /// </summary>
        /// The text to pad
        /// The size to pad the text (default is 2)
        /// The single character to use as the pad character (default is ' ')
        /// Whether to truncate the text to size (default is false)

        return $common._pad(text, size || 2, ch || ' ', 'l', truncate || false);
    },

    padRight: function(text, size, ch, truncate) {
        /// <summary>
        /// Pads the right hand side of the supplied text with the specified pad character up to the requested size
        /// </summary>
        /// The text to pad
        /// The size to pad the text (default is 2)
        /// The single character to use as the pad character (default is ' ')
        /// Whether to truncate the text to size (default is false)

        return $common._pad(text, size || 2, ch || ' ', 'r', truncate || false);
    },

    _pad: function(text, size, ch, side, truncate) {
        /// <summary>
        /// Pads supplied text with the specified pad character up to the requested size
        /// </summary>
        /// The text to pad
        /// The size to pad the text
        /// The single character to use as the pad character
        /// Either 'l' or 'r' to siginfy whether to pad the Left or Right side respectively
        /// Whether to truncate the text to size

        text = text.toString();
        var length = text.length;
        var builder = new Sys.StringBuilder();
        if (side == 'r') {
            builder.append(text);
        }
        while (length  size) {
            if (side == 'l') {
                result = result.substr(result.length - size, size);
            } else {
                result = result.substr(0, size);
            }
        }
        return result;
    },

    __DOMEvents: {
        focusin: { eventGroup: "UIEvents", init: function(e, p) { e.initUIEvent("focusin", true, false, window, 1); } },
        focusout: { eventGroup: "UIEvents", init: function(e, p) { e.initUIEvent("focusout", true, false, window, 1); } },
        activate: { eventGroup: "UIEvents", init: function(e, p) { e.initUIEvent("activate", true, true, window, 1); } },
        focus: { eventGroup: "UIEvents", init: function(e, p) { e.initUIEvent("focus", false, false, window, 1); } },
        blur: { eventGroup: "UIEvents", init: function(e, p) { e.initUIEvent("blur", false, false, window, 1); } },
        click: { eventGroup: "MouseEvents", init: function(e, p) { e.initMouseEvent("click", true, true, window, 1, p.screenX || 0, p.screenY || 0, p.clientX || 0, p.clientY || 0, p.ctrlKey || false, p.altKey || false, p.shiftKey || false, p.metaKey || false, p.button || 0, p.relatedTarget || null); } },
        dblclick: { eventGroup: "MouseEvents", init: function(e, p) { e.initMouseEvent("click", true, true, window, 2, p.screenX || 0, p.screenY || 0, p.clientX || 0, p.clientY || 0, p.ctrlKey || false, p.altKey || false, p.shiftKey || false, p.metaKey || false, p.button || 0, p.relatedTarget || null); } },
        mousedown: { eventGroup: "MouseEvents", init: function(e, p) { e.initMouseEvent("mousedown", true, true, window, 1, p.screenX || 0, p.screenY || 0, p.clientX || 0, p.clientY || 0, p.ctrlKey || false, p.altKey || false, p.shiftKey || false, p.metaKey || false, p.button || 0, p.relatedTarget || null); } },
        mouseup: { eventGroup: "MouseEvents", init: function(e, p) { e.initMouseEvent("mouseup", true, true, window, 1, p.screenX || 0, p.screenY || 0, p.clientX || 0, p.clientY || 0, p.ctrlKey || false, p.altKey || false, p.shiftKey || false, p.metaKey || false, p.button || 0, p.relatedTarget || null); } },
        mouseover: { eventGroup: "MouseEvents", init: function(e, p) { e.initMouseEvent("mouseover", true, true, window, 1, p.screenX || 0, p.screenY || 0, p.clientX || 0, p.clientY || 0, p.ctrlKey || false, p.altKey || false, p.shiftKey || false, p.metaKey || false, p.button || 0, p.relatedTarget || null); } },
        mousemove: { eventGroup: "MouseEvents", init: function(e, p) { e.initMouseEvent("mousemove", true, true, window, 1, p.screenX || 0, p.screenY || 0, p.clientX || 0, p.clientY || 0, p.ctrlKey || false, p.altKey || false, p.shiftKey || false, p.metaKey || false, p.button || 0, p.relatedTarget || null); } },
        mouseout: { eventGroup: "MouseEvents", init: function(e, p) { e.initMouseEvent("mousemove", true, true, window, 1, p.screenX || 0, p.screenY || 0, p.clientX || 0, p.clientY || 0, p.ctrlKey || false, p.altKey || false, p.shiftKey || false, p.metaKey || false, p.button || 0, p.relatedTarget || null); } },
        load: { eventGroup: "HTMLEvents", init: function(e, p) { e.initEvent("load", false, false); } },
        unload: { eventGroup: "HTMLEvents", init: function(e, p) { e.initEvent("unload", false, false); } },
        select: { eventGroup: "HTMLEvents", init: function(e, p) { e.initEvent("select", true, false); } },
        change: { eventGroup: "HTMLEvents", init: function(e, p) { e.initEvent("change", true, false); } },
        submit: { eventGroup: "HTMLEvents", init: function(e, p) { e.initEvent("submit", true, true); } },
        reset: { eventGroup: "HTMLEvents", init: function(e, p) { e.initEvent("reset", true, false); } },
        resize: { eventGroup: "HTMLEvents", init: function(e, p) { e.initEvent("resize", true, false); } },
        scroll: { eventGroup: "HTMLEvents", init: function(e, p) { e.initEvent("scroll", true, false); } }
    },

    tryFireRawEvent: function(element, rawEvent) {
        /// <summary>
        /// Attempts to fire a raw DOM event on an element
        /// </summary>
        /// The element to fire the event
        /// The raw DOM event object to fire. Must not be Sys.UI.DomEvent
        /// True if the event was successfully fired, otherwise false

        try {
            if (element.fireEvent) {
                element.fireEvent("on" + rawEvent.type, rawEvent);
                return true;
            } else if (element.dispatchEvent) {
                element.dispatchEvent(rawEvent);
                return true;
            }
        } catch (e) {
        }
        return false;
    },

    tryFireEvent: function(element, eventName, properties) {
        /// <summary>
        /// Attempts to fire a DOM event on an element
        /// </summary>
        /// The element to fire the event
        /// The name of the event to fire (without an 'on' prefix)
        /// Properties to add to the event
        /// True if the event was successfully fired, otherwise false

        try {
            if (document.createEventObject) {
                var e = document.createEventObject();
                $common.applyProperties(e, properties || {});
                element.fireEvent("on" + eventName, e);
                return true;
            } else if (document.createEvent) {
                var def = $common.__DOMEvents[eventName];
                if (def) {
                    var e = document.createEvent(def.eventGroup);
                    def.init(e, properties || {});
                    element.dispatchEvent(e);
                    return true;
                }
            }
        } catch (e) {
        }
        return false;
    },

    wrapElement: function(innerElement, newOuterElement, newInnerParentElement) {
        /// <summary>
        /// Wraps an inner element with a new outer element at the same DOM location as the inner element
        /// </summary>
        /// The element to be wrapped
        /// The new parent for the element
        /// 

        var parent = innerElement.parentNode;
        parent.replaceChild(newOuterElement, innerElement);
        (newInnerParentElement || newOuterElement).appendChild(innerElement);
    },

    unwrapElement: function(innerElement, oldOuterElement) {
        /// <summary>
        /// Unwraps an inner element from an outer element at the same DOM location as the outer element
        /// </summary>
        /// The element to be wrapped
        /// The new parent for the element
        /// 

        var parent = oldOuterElement.parentNode;
        if (parent != null) {
            $common.removeElement(innerElement);
            parent.replaceChild(innerElement, oldOuterElement);
        }
    },

    removeElement: function(element) {
        /// <summary>
        /// Removes an element from the DOM tree
        /// </summary>
        /// The element to be removed
        /// 

        var parent = element.parentNode;
        if (parent != null) {
            parent.removeChild(element);
        }
    },

    applyProperties: function(target, properties) {
        /// <summary>
        /// Quick utility method to copy properties from a template object to a target object
        /// </summary>
        /// The object to apply to
        /// The template to copy values from

        for (var p in properties) {
            var pv = properties[p];
            if (pv != null &amp;&amp; Object.getType(pv) === Object) {
                var tv = target[p];
                $common.applyProperties(tv, pv);
            } else {
                target[p] = pv;
            }
        }
    },

    createElementFromTemplate: function(template, appendToParent, nameTable) {
        /// <summary>
        /// Creates an element for the current document based on a template object
        /// </summary>
        /// The template from which to create the element
        /// A DomElement under which to append this element
        /// An object to use as the storage for the element using template.name as the key
        /// 
        /// 
        /// This method is useful if you find yourself using the same or similar DomElement constructions throughout a class.  You can even set the templates
        /// as static properties for a type to cut down on overhead.  This method is often called with a JSON style template:
        /// <code>
        /// var elt = $common.createElementFromTemplate({
        ///     nodeName : "div",
        ///     properties : {
        ///         style : {
        ///             height : "100px",
        ///             width : "100px",
        ///             backgroundColor : "white"
        ///         },
        ///         expandoAttribute : "foo"
        ///     },
        ///     events : {
        ///         click : function() { alert("foo"); },
        ///         mouseover : function() { elt.backgroundColor = "silver"; },
        ///         mouseout : function() { elt.backgroundColor = "white"; }
        ///     },
        ///     cssClasses : [ "class0", "class1" ],
        ///     visible : true,
        ///     opacity : .5
        /// }, someParent);
        /// </code>
        /// 

        if (typeof (template.nameTable) != 'undefined') {
            var newNameTable = template.nameTable;
            if (String.isInstanceOfType(newNameTable)) {
                newNameTable = nameTable[newNameTable];
            }
            if (newNameTable != null) {
                nameTable = newNameTable;
            }
        }

        var elementName = null;
        if (typeof (template.name) !== 'undefined') {
            elementName = template.name;
        }

        var elt = document.createElement(template.nodeName);

        if (typeof (template.name) !== 'undefined' &amp;&amp; nameTable) {
            nameTable[template.name] = elt;
        }

        if (typeof (template.parent) !== 'undefined' &amp;&amp; appendToParent == null) {
            var newParent = template.parent;
            if (String.isInstanceOfType(newParent)) {
                newParent = nameTable[newParent];
            }
            if (newParent != null) {
                appendToParent = newParent;
            }
        }

        if (typeof (template.properties) !== 'undefined' &amp;&amp; template.properties != null) {
            $common.applyProperties(elt, template.properties);
        }

        if (typeof (template.cssClasses) !== 'undefined' &amp;&amp; template.cssClasses != null) {
            $common.addCssClasses(elt, template.cssClasses);
        }

        if (typeof (template.events) !== 'undefined' &amp;&amp; template.events != null) {
            $addHandlers(elt, template.events);
        }

        if (typeof (template.visible) !== 'undefined' &amp;&amp; template.visible != null) {
            this.setVisible(elt, template.visible);
        }

        if (appendToParent) {
            appendToParent.appendChild(elt);
        }

        if (typeof (template.opacity) !== 'undefined' &amp;&amp; template.opacity != null) {
            $common.setElementOpacity(elt, template.opacity);
        }

        if (typeof (template.children) !== 'undefined' &amp;&amp; template.children != null) {
            for (var i = 0; i &lt; template.children.length; i++) {
                var subtemplate = template.children[i];
                $common.createElementFromTemplate(subtemplate, elt, nameTable);
            }
        }

        var contentPresenter = elt;
        if (typeof (template.contentPresenter) !== 'undefined' &amp;&amp; template.contentPresenter != null) {
            contentPresenter = nameTable[contentPresenter];
        }

        if (typeof (template.content) !== 'undefined' &amp;&amp; template.content != null) {
            var content = template.content;
            if (String.isInstanceOfType(content)) {
                content = nameTable[content];
            }
            if (content.parentNode) {
                $common.wrapElement(content, elt, contentPresenter);
            } else {
                contentPresenter.appendChild(content);
            }
        }

        return elt;
    },

    prepareHiddenElementForATDeviceUpdate: function() {
        /// <summary>
        /// JAWS, an Assistive Technology device responds to updates to form elements 
        /// and refreshes its document buffer to what is showing live
        /// in the browser. To ensure that Toolkit controls that make XmlHttpRequests to
        /// retrieve content are useful to users with visual disabilities, we update a
        /// hidden form element to ensure that JAWS conveys what is in
        /// the browser. See this article for more details: 
        /// http://juicystudio.com/article/improving-ajax-applications-for-jaws-users.php
        /// This method creates a hidden input on the screen for any page that uses a Toolkit
        /// control that will perform an XmlHttpRequest.
        /// </summary>   
        var objHidden = document.getElementById('hiddenInputToUpdateATBuffer_CommonToolkitScripts');
        if (!objHidden) {
            var objHidden = document.createElement('input');
            objHidden.setAttribute('type', 'hidden');
            objHidden.setAttribute('value', '1');
            objHidden.setAttribute('id', 'hiddenInputToUpdateATBuffer_CommonToolkitScripts');
            objHidden.setAttribute('name', 'hiddenInputToUpdateATBuffer_CommonToolkitScripts');
            if (document.forms[0]) {
                document.forms[0].appendChild(objHidden);
            }
        }
    },

    updateFormToRefreshATDeviceBuffer: function() {
        /// <summary>
        /// Updates the hidden buffer to ensure that the latest document stream is picked up
        /// by the screen reader.
        /// </summary>
        var objHidden = document.getElementById('hiddenInputToUpdateATBuffer_CommonToolkitScripts');

        if (objHidden) {
            if (objHidden.getAttribute('value') == '1') {
                objHidden.setAttribute('value', '0');
            } else {
                objHidden.setAttribute('value', '1');
            }
        }
    },

    appendElementToFormOrBody: function(element) {
        /// <summary>
        /// Tries to append an element to the current form. If no form exists, the element will be appended to the body element.
        /// </summary>
        /// The element to append.
        if (document.forms &amp;&amp; document.forms[0]) {
            document.forms[0].appendChild(element);
        } else {
            document.body.appendChild(element);
        }
    }
};

CommonToolkitScripts = Sys.Extended.UI.CommonToolkitScripts = new Sys.Extended.UI._CommonToolkitScripts();
$common = CommonToolkitScripts;

Sys.UI.DomElement.getVisible = $common.getVisible;
Sys.UI.DomElement.setVisible = $common.setVisible;
Sys.UI.Control.overlaps = $common.overlaps;

Sys.Extended.UI._DomUtility = function() {
    /// <summary>
    /// Utility functions for manipulating the DOM
    /// </summary>
}
Sys.Extended.UI._DomUtility.prototype = {
    isDescendant : function(ancestor, descendant) {
        /// <summary>
        /// Whether the specified element is a descendant of the ancestor
        /// </summary>
        /// Ancestor node
        /// Possible descendant node
        /// 
        
        for (var n = descendant.parentNode; n != null; n = n.parentNode) {
            if (n == ancestor) return true;
        }
        return false;
    },
    isDescendantOrSelf : function(ancestor, descendant) {
        /// <summary>
        /// Whether the specified element is a descendant of the ancestor or the same as the ancestor
        /// </summary>
        /// Ancestor node
        /// Possible descendant node
        /// 

        if (ancestor === descendant) 
            return true;
        return Sys.Extended.UI.DomUtility.isDescendant(ancestor, descendant);
    },
    isAncestor : function(descendant, ancestor) {
        /// <summary>
        /// Whether the specified element is an ancestor of the descendant
        /// </summary>
        /// Descendant node
        /// Possible ancestor node
        /// 

        return Sys.Extended.UI.DomUtility.isDescendant(ancestor, descendant);
    },
    isAncestorOrSelf : function(descendant, ancestor) {
        /// <summary>
        /// Whether the specified element is an ancestor of the descendant or the same as the descendant
        /// </summary>
        /// Descendant node
        /// Possible ancestor node
        /// 
        
        if (descendant === ancestor)
            return true;
            
        return Sys.Extended.UI.DomUtility.isDescendant(ancestor, descendant);
    },
    isSibling : function(self, sibling) {
        /// <summary>
        /// Whether the specified element is a sibling of the self element
        /// </summary>
        /// Self node
        /// Possible sibling node
        /// 
        
        var parent = self.parentNode;
        for (var i = 0; i &lt; parent.childNodes.length; i++) {
            if (parent.childNodes[i] == sibling) return true;
        }
        return false;
    }
}
Sys.Extended.UI._DomUtility.registerClass(&quot;Sys.Extended.UI._DomUtility&quot;);
Sys.Extended.UI.DomUtility = new Sys.Extended.UI._DomUtility();


Sys.Extended.UI.TextBoxWrapper = function(element) {
    /// <summary>
    /// Class that wraps a TextBox (INPUT type="text") to abstract-out the
    /// presence of a watermark (which may be visible to the user but which
    /// should never be read by script.
    /// </summary>
    /// 
    /// The DOM element the behavior is associated with
    /// 
    Sys.Extended.UI.TextBoxWrapper.initializeBase(this, [element]);
    this._current = element.value;
    this._watermark = null;
    this._isWatermarked = false;
}

Sys.Extended.UI.TextBoxWrapper.prototype = {

    dispose : function() {
        /// <summary>
        /// Dispose the behavior
        /// </summary>
        this.get_element().TextBoxWrapper = null;
        Sys.Extended.UI.TextBoxWrapper.callBaseMethod(this, 'dispose');
    },

    get_Current : function() {
        /// 
        /// Current value actually in the TextBox (i.e., TextBox.value)
        /// 
        this._current = this.get_element().value;
        return this._current;
    },
    set_Current : function(value) {
        this._current = value;
        this._updateElement();
    },

    get_Value : function() {
        /// 
        /// Conceptual "value" of the TextBox - its contents if no watermark is present
        /// or "" if one is
        /// 
        if (this.get_IsWatermarked()) {
            return "";
        } else {
            return this.get_Current();
        }
    },
    set_Value : function(text) {
        this.set_Current(text);
        if (!text || (0 == text.length)) {
            if (null != this._watermark) {
                this.set_IsWatermarked(true);
            }
        } else {
            this.set_IsWatermarked(false);
        }
    },

    get_Watermark : function() {
        /// 
        /// Text of the watermark for the TextBox
        /// 
        return this._watermark;
    },
    set_Watermark : function(value) {
        this._watermark = value;
        this._updateElement();
    },

    get_IsWatermarked : function() {
        /// 
        /// true iff the TextBox is watermarked
        /// 
        return this._isWatermarked;
    },
    set_IsWatermarked : function(isWatermarked) {
        if (this._isWatermarked != isWatermarked) {
            this._isWatermarked = isWatermarked;
            this._updateElement();
            this._raiseWatermarkChanged();
        }
    },

    _updateElement : function() {
        /// <summary>
        /// Updates the actual contents of the TextBox according to what should be there
        /// </summary>
        var element = this.get_element();
        if (this._isWatermarked) {
            if (element.value != this._watermark) {
                element.value = this._watermark;
            }
        } else {
            if (element.value != this._current) {
                element.value = this._current;
            }
        }
    },

    add_WatermarkChanged : function(handler) {
        /// <summary>
        /// Adds a handler for the WatermarkChanged event
        /// </summary>
        /// 
        /// Handler
        /// 
        this.get_events().addHandler("WatermarkChanged", handler);
    },
    remove_WatermarkChanged : function(handler) {
        /// <summary>
        /// Removes a handler for the WatermarkChanged event
        /// </summary>
        /// 
        /// Handler
        /// 
        this.get_events().removeHandler("WatermarkChanged", handler);
    },
    _raiseWatermarkChanged : function() {
        /// <summary>
        /// Raises the WatermarkChanged event
        /// </summary>
        var onWatermarkChangedHandler = this.get_events().getHandler("WatermarkChanged");
        if (onWatermarkChangedHandler) {
            onWatermarkChangedHandler(this, Sys.EventArgs.Empty);
        }
    }
}
Sys.Extended.UI.TextBoxWrapper.get_Wrapper = function(element) {
    /// <summary>
    /// Gets (creating one if necessary) the TextBoxWrapper for the specified TextBox
    /// </summary>
    /// 
    /// TextBox for which to get the wrapper
    /// 
    /// 
    /// TextBoxWrapper instance
    /// 
    if (null == element.TextBoxWrapper) {
        element.TextBoxWrapper = new Sys.Extended.UI.TextBoxWrapper(element);
    }
    return element.TextBoxWrapper;
}
Sys.Extended.UI.TextBoxWrapper.registerClass('Sys.Extended.UI.TextBoxWrapper', Sys.UI.Behavior);

Sys.Extended.UI.TextBoxWrapper.validatorGetValue = function(id) {i
    /// <summary>
    /// Wrapper for ASP.NET's validatorGetValue to return the value from the wrapper if present
    /// </summary>
    /// 
    /// id of the element
    /// 
    /// 
    /// Value from the wrapper or result of original ValidatorGetValue
    /// 
    var control = $get(id);
    if (control &amp;&amp; control.TextBoxWrapper) {
        return control.TextBoxWrapper.get_Value();
    }
    return Sys.Extended.UI.TextBoxWrapper._originalValidatorGetValue(id);
}

if (typeof(ValidatorGetValue) == 'function') {
    Sys.Extended.UI.TextBoxWrapper._originalValidatorGetValue = ValidatorGetValue;
    ValidatorGetValue = Sys.Extended.UI.TextBoxWrapper.validatorGetValue;
}


if (Sys.CultureInfo &amp;&amp; Sys.CultureInfo.prototype._getAbbrMonthIndex) {
    Sys.CultureInfo.prototype._getAbbrMonthIndex = function(value) {
        if (!this._upperAbbrMonths) {
            this._upperAbbrMonths = this._toUpperArray(this.dateTimeFormat.AbbreviatedMonthNames);
        }
        return Array.indexOf(this._upperAbbrMonths, this._toUpper(value));
    }
    Sys.CultureInfo.CurrentCulture._getAbbrMonthIndex = Sys.CultureInfo.prototype._getAbbrMonthIndex;
    Sys.CultureInfo.InvariantCulture._getAbbrMonthIndex = Sys.CultureInfo.prototype._getAbbrMonthIndex;
}
/// ExtendedBase.js

Type.registerNamespace('Sys.Extended.UI');

// This is the base behavior for all extender behaviors
Sys.Extended.UI.BehaviorBase = function(element) {
    /// <summary>
    /// Base behavior for all extender behaviors
    /// </summary>
    /// <param name="element" type="Sys.UI.DomElement" domElement="true">
    /// Element the behavior is associated with
    /// </param>
    Sys.Extended.UI.BehaviorBase.initializeBase(this,[element]);
    
    this._clientStateFieldID = null;
    this._pageRequestManager = null;
    this._partialUpdateBeginRequestHandler = null;
    this._partialUpdateEndRequestHandler = null;
};

Sys.Extended.UI.BehaviorBase.prototype = {
    initialize : function() {
        /// <summary>
        /// Initialize the behavior
        /// </summary>

        // TODO: Evaluate necessity
        Sys.Extended.UI.BehaviorBase.callBaseMethod(this, 'initialize');
    },

    dispose : function() {
        /// <summary>
        /// Dispose the behavior
        /// </summary>
        Sys.Extended.UI.BehaviorBase.callBaseMethod(this, 'dispose');

        if (this._pageRequestManager) {
            if (this._partialUpdateBeginRequestHandler) {
                this._pageRequestManager.remove_beginRequest(this._partialUpdateBeginRequestHandler);
                this._partialUpdateBeginRequestHandler = null;
            }
            if (this._partialUpdateEndRequestHandler) {
                this._pageRequestManager.remove_endRequest(this._partialUpdateEndRequestHandler);
                this._partialUpdateEndRequestHandler = null;
            }
            this._pageRequestManager = null;
        }
    },

    get_ClientStateFieldID : function() {
        /// <value type="String">
        /// ID of the hidden field used to store client state
        /// </value>
        return this._clientStateFieldID;
    },
    set_ClientStateFieldID : function(value) {
        if (this._clientStateFieldID !== value) {
            this._clientStateFieldID = value;
            this.raisePropertyChanged('ClientStateFieldID');
        }
    },

    get_ClientState : function() {
        /// <value type="String">
        /// Client state
        /// </value>
        if (this._clientStateFieldID) {
            var input = document.getElementById(this._clientStateFieldID);
            if (input) {
                return input.value;
            }
        }
        return null;
    },
    set_ClientState : function(value) {
        if (this._clientStateFieldID) {
            var input = document.getElementById(this._clientStateFieldID);
            if (input) {
                input.value = value;
            }
        }
    },

    registerPartialUpdateEvents : function() {
        /// <summary>
        /// Register for beginRequest and endRequest events on the PageRequestManager,
        /// (which cause _partialUpdateBeginRequest and _partialUpdateEndRequest to be
        /// called when an UpdatePanel refreshes)
        /// </summary>

        if (Sys && Sys.WebForms && Sys.WebForms.PageRequestManager){
            this._pageRequestManager = Sys.WebForms.PageRequestManager.getInstance();
            if (this._pageRequestManager) {
                this._partialUpdateBeginRequestHandler = Function.createDelegate(this, this._partialUpdateBeginRequest);
                this._pageRequestManager.add_beginRequest(this._partialUpdateBeginRequestHandler);
                this._partialUpdateEndRequestHandler = Function.createDelegate(this, this._partialUpdateEndRequest);
                this._pageRequestManager.add_endRequest(this._partialUpdateEndRequestHandler);
            }
        }
    },

    _partialUpdateBeginRequest : function(sender, beginRequestEventArgs) {
        /// <summary>
        /// Method that will be called when a partial update (via an UpdatePanel) begins,
        /// if registerPartialUpdateEvents() has been called.
        /// </summary>
        /// <param name="sender" type="Object">
        /// Sender
        /// </param>
        /// <param name="beginRequestEventArgs" type="Sys.WebForms.BeginRequestEventArgs">
        /// Event arguments
        /// </param>

        // Nothing done here; override this method in a child class
    },
    
    _partialUpdateEndRequest : function(sender, endRequestEventArgs) {
        /// <summary>
        /// Method that will be called when a partial update (via an UpdatePanel) finishes,
        /// if registerPartialUpdateEvents() has been called.
        /// </summary>
        /// <param name="sender" type="Object">
        /// Sender
        /// </param>
        /// <param name="endRequestEventArgs" type="Sys.WebForms.EndRequestEventArgs">
        /// Event arguments
        /// </param>

        // Nothing done here; override this method in a child class
    }
};
Sys.Extended.UI.BehaviorBase.registerClass('Sys.Extended.UI.BehaviorBase', Sys.UI.Behavior);
/// RoundedCorners.js

Type.registerNamespace('Sys.Extended.UI');

Sys.Extended.UI.BoxCorners = function () {
    throw Error.invalidOperation();
};


Sys.Extended.UI.BoxCorners.prototype = {
    None: 0x00,

    TopLeft: 0x01,
    TopRight: 0x02,
    BottomRight: 0x04,
    BottomLeft: 0x08,

    Top: 0x01 | 0x02,
    Right: 0x02 | 0x04,
    Bottom: 0x04 | 0x08,
    Left: 0x08 | 0x01,
    All: 0x01 | 0x02 | 0x04 | 0x08
};

Sys.Extended.UI.BoxCorners.registerEnum("Sys.Extended.UI.BoxCorners", true);



Sys.Extended.UI.RoundedCornersBehavior = function (element) {
    /// <summary>
    /// The RoundedCornersBehavior rounds the corners of its target element
    /// </summary>
    /// <param name="element" type="Sys.UI.DomElement" domElement="true">
    /// DOM element associated with the behavior
    /// </param>
    Sys.Extended.UI.RoundedCornersBehavior.initializeBase(this, [element]);

    this._corners = Sys.Extended.UI.BoxCorners.All;
    this._radius = 5;
    this._color = null;
    this._parentDiv = null;
    this._originalStyle = null;
    this._borderColor = null;
    this._isDirty = true;
};
Sys.Extended.UI.RoundedCornersBehavior.prototype = {
    initialize: function () {
        /// <summary>
        /// Initialize the behavior
        /// </summary>
        Sys.Extended.UI.RoundedCornersBehavior.callBaseMethod(this, 'initialize');
        this.update();
    },

    dispose: function () {
        /// <summary>
        /// Dispose the behavior
        /// </summary>

        this.disposeParentDiv();
        Sys.Extended.UI.RoundedCornersBehavior.callBaseMethod(this, 'dispose');
    },

    update: function () {
        /// <summary>
        /// Create the surrounding div that will have rounded corners
        /// </summary>
        var e = this.get_element();

        if (!e || !this._isDirty || this.get_isUpdating()) return;

        this.disposeParentDiv();

        // Check if current browser supports css3 or not
        //                if (e.style.borderRadius != undefined)
        //                    alert('support css3 for IE/Chrome');
        //                else if (e.style.MozBorderRadius != undefined)
        //                    alert('support css3 MoZ');
        //                else if (e.style.WebkitBorderRadius != undefined)
        //                    alert('support css3 Chrome');
        //                else
        //                    alert('not support css3');

        // This works with IE and latest versions of Chrome
        if (e.style.borderRadius !== undefined) {
            e.style.borderRadius = this._radius + "px";

            // if border color is not none then set 
            // border style to solid and border width to 1px
            if (this._borderColor) {
                e.style.border = "solid";
                e.style.borderWidth = "1px";
                e.style.borderColor = this._borderColor;
            }
            else {
                e.style.border = "none";
                e.style.borderWidth = "0px";
            }

            // if TopLeft corner is unchecked then Top left corner will not be round
            if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.TopLeft)) {
                e.style.borderTopLeftRadius = "0px";
            }

            // if BottomLeft corner is unchecked then Bottom left corner will not be round
            if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.BottomLeft)) {
                e.style.borderBottomLeftRadius = "0px";
            }

            // if TopRight corner is unchecked then Top right corner will not be round
            if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.TopRight)) {
                e.style.borderTopRightRadius = "0px";
            }

            // if BottomRight corner is unchecked then Bottom Right corner will not be round
            if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.BottomRight)) {
                e.style.borderBottomRightRadius = "0px";
            }

        }
            // This works with FireFox
        else if (e.style.MozBorderRadius !== undefined) {
            e.style.MozBorderRadius = this._radius + "px";

            // if border color is not none then set 
            // border style to solid and border width to 1px
            if (this._borderColor) {
                e.style.border = "solid";
                e.style.borderWidth = "1px";
                e.style.borderColor = this._borderColor;
            }
            else {
                e.style.border = "none";
                e.style.borderWidth = "0px";
            }

            // if TopLeft corner is unchecked then Top left corner will not be round                                        
            if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.TopLeft)) {
                e.style.MozBorderRadiusTopleft = "0px";
            }

            // if BottomLeft corner is unchecked then Bottom left corner will not be round
            if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.BottomLeft)) {
                e.style.MozBorderRadiusBottomleft = "0px";
            }

            // if TopRight corner is unchecked then Top right corner will not be round
            if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.TopRight)) {
                e.style.MozBorderRadiusTopright = "0px";
            }

            // if BottomRight corner is unchecked then Bottom Right corner will not be round
            if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.BottomRight)) {
                e.style.MozBorderRadiusBottomright = "0px";
            }

        }
            // This works with older versions of google Chrome
        else if (e.style.WebkitBorderRadius !== undefined) {
            e.style.WebkitBorderRadius = this._radius + "px";

            // if border color is not none then set 
            // border style to solid and border width to 1px
            if (this._borderColor) {
                e.style.border = "solid";
                e.style.borderWidth = "1px";
                e.style.borderColor = this._borderColor;
            }
            else {
                e.style.border = "none";
                e.style.borderWidth = "0px";
            }

            // if TopLeft corner is unchecked then Top left corner will not be round                                        
            if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.TopLeft)) {
                e.style.WebkitBorderRadiusTopLeft = "0px";
            }

            // if BottomLeft corner is unchecked then Bottom left corner will not be round
            if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.BottomLeft)) {
                e.style.WebkitBorderRadiusBottomLeft = "0px";
            }

            // if TopRight corner is unchecked then Top right corner will not be round
            if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.TopRight)) {
                e.style.WebkitBorderRadiusTopRight = "0px";
            }

            // if BottomRight corner is unchecked then Bottom Right corner will not be round
            if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.BottomRight)) {
                e.style.WebkitBorderRadiusBottomRight = "0px";
            }
        }
            // if browser does not support css3 then continue old way to make corners round
        else {

            var color = this.getBackgroundColor();
            var originalWidth = e.offsetWidth;
            var newParent = e.cloneNode(false);

            // move all children into the new div.
            this.moveChildren(e, newParent);

            // modify the target element to be transparent
            // and set up the new parent
            this._originalStyle = e.style.cssText;
            e.style.backgroundColor = "transparent";
            e.style.verticalAlign = "top";
            e.style.padding = "0";
            e.style.overflow = "";
            e.style.className = "";
            // Don't assume there is a numerical value for height.  A height of "auto" is possible.
            if (e.style.height && e.style.height !== "auto") {
                // Increase the height to account for the rounded corners
                e.style.height = parseInt($common.getCurrentStyle(e, 'height')) + (this._radius * 2) + "px";
            } else {
                // Note: Do NOT use $common.getCurrentStyle in the check below
                // because that breaks the work-around
                if (!e.style.width && (0 < originalWidth)) {
                    // The following line works around a problem where IE renders the first
                    // rounded DIV about 6 pixels too high if e doesn't have a width or height
                    e.style.width = originalWidth + "px";
                }
            }

            // these are properties we don't want cloned down to the new parent
            newParent.style.position = "";
            newParent.style.border = "";
            newParent.style.margin = "";
            newParent.style.width = "100%";
            if ((newParent.style.overflow === "") && ($common.getCurrentStyle(e, "overflow") === "visible")) {
                // If the style of the target element has not been set inline, 
                // and has not been set in a stylesheet to something other than "visible" 
                // (which is the default), then set it to "auto", so that any child block elements
                // do not cause the rounded corner divs to display as separated from the target div  
                newParent.style.overflow = "auto";
            }
            newParent.id = "";
            newParent.removeAttribute("control");

            if (this._borderColor) {
                newParent.style.borderTopStyle = "none";
                newParent.style.borderBottomStyle = "none";
                newParent.style.borderLeftStyle = "solid";
                newParent.style.borderRightStyle = "solid";
                newParent.style.borderLeftColor = this._borderColor;
                newParent.style.borderRightColor = this._borderColor;
                newParent.style.borderLeftWidth = "1px";
                newParent.style.borderRightWidth = "1px";
                if (this._radius === 0) {
                    newParent.style.borderTopStyle = "solid";
                    newParent.style.borderBottomStyle = "solid";
                    newParent.style.borderTopColor = this._borderColor;
                    newParent.style.borderBottomColor = this._borderColor;
                    newParent.style.borderTopWidth = "1px";
                    newParent.style.borderBottomWidth = "1px";
                }
            } else {
                newParent.style.borderTopStyle = "none";
                newParent.style.borderBottomStyle = "none";
                newParent.style.borderLeftStyle = "none";
                newParent.style.borderRightStyle = "none";
            }

            // build a set of steps on each end to fake the corners.
            //  ------- (step 0)
            //  -------- (step n-1)
            //  --------- (step n)
            //  XXXXXXXXX (inner div)
            //  XXXXXXXXX
            //  --------- (bottom step n)
            //  --------  (bottom step n-1)
            //  ------    (bottom step 0)

            var lastDiv = null;
            var radius = this._radius;
            var lines = this._radius;
            var lastDelta = 0;

            for (var i = lines; i > 0; i--) {

                // figure out how much we'll need to subtract from each item
                var angle = Math.acos(i / radius);
                var delta = radius - Math.round(Math.sin(angle) * radius);

                // build a 1 pixel tall div
                // that's delta pixels shorter on each end.

                // add the top one
                var newDiv = document.createElement("DIV");
                newDiv.__roundedDiv = true;
                newDiv.style.backgroundColor = color;
                newDiv.style.marginLeft = delta + "px";
                newDiv.style.marginRight = (delta - (this._borderColor ? 2 : 0)) + "px";
                newDiv.style.height = "1px";
                newDiv.style.fontSize = "1px"; // workaround for IE wierdness with 1px divs.
                newDiv.style.overflow = "hidden";

                if (this._borderColor) {
                    newDiv.style.borderLeftStyle = "solid";
                    newDiv.style.borderRightStyle = "solid";
                    newDiv.style.borderLeftColor = this._borderColor;
                    newDiv.style.borderRightColor = this._borderColor;

                    var offset = Math.max(0, lastDelta - delta - 1);
                    newDiv.style.borderLeftWidth = (offset + 1) + "px";
                    newDiv.style.borderRightWidth = (offset + 1) + "px";

                    if (i === lines) {
                        newDiv.__roundedDivNoBorder = true;
                        newDiv.style.backgroundColor = this._borderColor;
                    }
                }

                e.insertBefore(newDiv, lastDiv);

                var topDiv = newDiv;

                // add the bottom one one
                newDiv = newDiv.cloneNode(true);
                newDiv.__roundedDiv = true;

                e.insertBefore(newDiv, lastDiv);

                var bottomDiv = newDiv;

                lastDiv = newDiv;
                lastDelta = delta;

                if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.TopLeft)) {
                    topDiv.style.marginLeft = "0";
                    if (this._borderColor) {
                        topDiv.style.borderLeftWidth = "1px";
                    }
                }
                if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.TopRight)) {
                    topDiv.style.marginRight = "0";
                    if (this._borderColor) {
                        topDiv.style.borderRightWidth = "1px";
                        topDiv.style.marginRight = "-2px";
                    }
                }
                if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.BottomLeft)) {
                    bottomDiv.style.marginLeft = "0";
                    if (this._borderColor) {
                        bottomDiv.style.borderLeftWidth = "1px";
                    }
                }
                if (!this.isCornerSet(Sys.Extended.UI.BoxCorners.BottomRight)) {
                    bottomDiv.style.marginRight = "0";
                    if (this._borderColor) {
                        bottomDiv.style.borderRightWidth = "1px";
                        bottomDiv.style.marginRight = "-2px";
                    }
                }
            }

            // finally, add the newParent (which has all the original content)
            // into the div.
            e.insertBefore(newParent, lastDiv);
            this._parentDiv = newParent;
            this._isDirty = false;
        }
    },

    disposeParentDiv: function () {
        /// <summary>
        /// Dispose the surrounding div with rounded corners
        /// </summary>

        if (this._parentDiv) {
            // clean up the divs we added.
            var e = this.get_element();
            var children = e.childNodes;
            for (var i = children.length - 1; i >= 0; i--) {
                var child = children[i];
                if (child) {
                    if (child === this._parentDiv) {
                        this.moveChildren(child, e);
                    }
                    try {
                        e.removeChild(child);
                    } catch (e) {
                        // Safari likes to throw NOT_FOUND_ERR (DOMException 8)
                        // but it seems to work fine anyway.
                    }
                }
            }

            // restore the original style
            if (this._originalStyle) {
                e.style.cssText = this._originalStyle;
                this._originalStyle = null;
            }
            this._parentDiv = null;
        }
    },

    getBackgroundColor: function () {
        /// <summary>
        /// Get the background color of the target element
        /// </summary>
        if (this._color) {
            return this._color;
        }
        return $common.getCurrentStyle(this.get_element(), 'backgroundColor');
    },

    moveChildren: function (src, dest) {
        /// <summary>
        /// Move the child nodes from one element to another
        /// </summary>
        /// <param name="src" type="Sys.UI.DomElement" domElement="true">
        /// DOM Element
        /// </param>
        /// <param name="dest" type="Sys.UI.DomElement" domElement="true">
        /// DOM Element
        /// </param>

        var moveCount = 0;
        while (src.hasChildNodes()) {
            var child = src.childNodes[0];
            child = src.removeChild(child);
            dest.appendChild(child);
            moveCount++;
        }
        return moveCount;
    },

    isCornerSet: function (corner) {
        /// <summary>
        /// Check whether the a flag for this corner has been set
        /// </summary>
        /// <param name="corner" type="AjaxControlTooolkit.BoxCorners">
        /// Corner to check
        /// </param>
        /// <returns type="Boolean">
        /// True if it is included in the flags, false otherwise
        /// </returns>
        return (this._corners & corner) !== Sys.Extended.UI.BoxCorners.None;
    },

    setCorner: function (corner, value) {
        /// <summary>
        /// Set a corner as one that should be rounded
        /// </summary>
        /// <param name="corner" type="Sys.Extended.UI.BoxCorners">
        /// Corner to set
        /// </param>
        /// <param name="value" type="Boolean">
        /// True to set the value, False to clear it
        /// </param>
        if (value) {
            this.set_Corners(this._corners | corner);
        } else {
            this.set_Corners(this._corners & ~corner);
        }
    },

    get_Color: function () {
        /// <value type="String">
        /// The background color of the rounded area an corners.  By default this picks up the background color of the panel that it is attached to.
        /// </value>
        return this._color;
    },
    set_Color: function (value) {
        if (value !== this._color) {
            this._color = value;
            this._isDirty = true;
            this.update();
            this.raisePropertyChanged('Color');
        }
    },

    get_Radius: function () {
        /// <value type="Number" integer="true">
        /// The radius of the corners (and height of the added area).  Default is 5.
        /// </value>
        return this._radius;
    },
    set_Radius: function (value) {
        if (value !== this._radius) {
            this._radius = value;
            this._isDirty = true;
            this.update();
            this.raisePropertyChanged('Radius');
        }
    },

    get_Corners: function () {
        /// <value type="Sys.Extended.UI.BoxCorners">
        /// Corners that should be rounded
        /// </value>
        return this._corners;
    },
    set_Corners: function (value) {
        if (value !== this._corners) {
            this._corners = value;
            this._isDirty = true;
            this.update();
            this.raisePropertyChanged("Corners");
        }
    },

    get_BorderColor: function () {
        /// <value type="String">
        /// Color of the border (and hence the rounded corners)
        /// </value>
        return this._borderColor;
    },
    set_BorderColor: function (value) {
        if (value !== this._borderColor) {
            this._borderColor = value;
            this._isDirty = true;
            this.update();
            this.raisePropertyChanged("BorderColor");
        }
    }
};
Sys.Extended.UI.RoundedCornersBehavior.registerClass('Sys.Extended.UI.RoundedCornersBehavior', Sys.Extended.UI.BehaviorBase);
 

Now let me give a little bit of information about how to use it. Assuming you have a div with the ID “roundedDiv”, you can round it’s corners by adding below line of code.

            var roundedControl  = $create(Sys.Extended.UI.RoundedCornersBehavior,{ Radius: 20, BorderColor: 'red' },null, null, $get('roundedDiv'));

You can also specify what corners to round by adding Corners property to the second parameter. Possible values for this property are: Sys.Extended.UI.BoxCorners.None. Sys.Extended.UI.BoxCorners.TopLeft, Sys.Extended.UI.BoxCorners.TopRight, Sys.Extended.UI.BoxCorners.BottomLeft, Sys.Extended.UI.BoxCorners.BottomRight, Sys.Extended.UI.BoxCorners.Top, Sys.Extended.UI.BoxCorners.Bottom, Sys.Extended.UI.BoxCorners.Left, Sys.Extended.UI.BoxCorners.Right, Sys.Extended.UI.BoxCorners.All (default value if not specified).

     var roundedControl  = $create(Sys.Extended.UI.RoundedCornersBehavior,{ Corners: Sys.Extended.UI.BoxCorners.BottomRight, Radius: 20, BorderColor: 'red' },null, null, $get('roundedDiv'));

It seems to work in Chrome and Internet Explorer as well. The only issue I could see are related to rounded an image tag in Internet Explorer 7 and 8, but it seems is a problem related to insertBefore operation which also occur in other libraries like jQuery.

JavaScript browser detection in SharePoint Online

Again, this article is concentrated on SharePoint Online, but is possible to work in SharePoint 2013 as well. So, in case you tested this, please let me know if is working or not. I assume will be a yes as I can recognize some of the feature from SharePoint 2007.

Detecting browser

As a SharePoint Developer, making use of web technologies, you might be requested to identify browser and develop separate code depending on what browser customer is using.

if (browseris.ie === true) {
    // IE implementation
};

if (browseris.firefox === true) {
    // Firefox implementation
};

if (browseris.chrome === true) {
    // Chrome implementation
};

Detecting browser version

Sometimes, detecting browser is not sufficient and as a developer you need to identify browser version. And again, some properties of “browseris” object can help you (values should be compare with Boolean value).

// Chrome
var chrome7up = browseris.chrome7up;
var chrome8up = browseris.chrome8up;
var chrome9up = browseris.chrome9up;

// Firefox
var firefox3up = browseris.firefox3up;
var firefox36up = browseris.firefox36up;
var firefox4up = browseris.firefox4up;

// IE
var ie7down = browseris.ie7down;
var ie8down = browseris.ie8down;
var ie9down = browseris.ie9down;
var ie7up = browseris.ie7up;

I have to admit IE logic is a little bit strange, but you can still get full Internet Explorer version using this code:

var ieversion  = browseris.verIEFull;

Unfortunately, it is returning undefined in IE11 with document mode set to edge, because code is actually do not identified browser as being Internet Explorer.

// For IE11, this is false
browseris.ie === false;

Manipulating URL with JavaScript in SharePoint Online

Init.js is included in JavaScript core files for SharePoint environment. From 2007 till 2013 it suffered some changes, new functionalities being added. What I am going to present was tested in SharePoint Online environment, but I have a feeling exists also in SharePoint 2013 on premise.

When you work with URL, you have all the functions you need included. No need to go to other libraries or creating your own code for this. For example, here is how you can decode full URL of a page.

var url = window.location.href;
CompleteDecode(url);

If you want to get the URL, but without a specified query string parameter, the function RemoveQueryParameterFromUrl is your friend;

RemoveQueryParameterFromUrl(url,'query string parameter name');

In an opposite way you can change a query string parameter:

StURLSetVar2(url,'query string parameter name','query string parameter value');

In case you have the specified query string parameter in the URL, it will take the specified value. If not, it will be added to the URL.
To get the URL, without query string parameters, you can simply remove them all using RemoveParametersFromUrl:

RemoveParametersFromUrl(url);

Even init.js is a SharePoint library, I think it can be used also for other technologies or at least it can be adapted. I really believe we can learn something from Microsoft analysing the provided code.

Create tooltips for SharePoint

There are a lot of SharePoint tooltips plugins, but I am not aware of anyone close to SharePoint environment. So, I said, why not try to create one and this is how I created code below. It is presented for didactic purpose, but I have a plan to extend.

First thing we need to do is to create a communication between the tooltip callout and SharePoint, because it is almost sure we will need to get the data dynamically from SharePoint lists.

Shp.Data = function () {
    /// <summary>SharePoint Data static class</summary>
}

Shp.Data.getListItems = function (getListItemsOptions, success, fail) {
    /// <summary>Get items from SharePoint using client object model</summary>
    /// <param name="getListItemsOptions" mayBeNull="false" optional="false" type="Object"></summary>
    /// <param name="success" mayBeNull="false" optional="false" type="Function"></summary>
    /// <param name="fail" mayBeNull="false" optional="true" type="Function"></summary>
    var e = Function.validateParameters(arguments, [{ name: 'getListItemsOptions', mayBeNull: false, optional: false, type: Object },
                                                    { name: 'success', mayBeNull: false, optional: false, type: Function },
                                                    { name: 'fail', mayBeNull: false, optional: true, type: Function }], true);
    if (e) throw 'Shp.DOM.getListItems was called with invalid paramater.';

    var caml = caml || '<View></View>';
    var fail = fail || function (sender, args) {
        alert('Error getting data from ' + getListItemsOptions[listName]);
    }
    var ctx = getListItemsOptions['context'] || SP.ClientContext.get_current();
    var oList = ctx.get_web().get_lists().getByTitle(getListItemsOptions['listName']);
    var camlQuery = new SP.CamlQuery();
    camlQuery.set_viewXml(caml);
    var oListItems = oList.getItems(camlQuery);
    ctx.load(oListItems);
    ctx.executeQueryAsync(function () {
        success(oListItems);
    }, fail);
}

Shp.Data.registerClass('Shp.Data');

The second step is to create code for callout. Shp.Data will be used to connect to the SharePoint and Shp.DOM will present it into tooltips.

/// <Reference Name="MicrosoftAjax.js" />
/// <Reference Name="MicrosoftAjaxWebForms.js" />
Type.registerNamespace('Shp');

Shp.DOM = function () {
    /// <summary>SharePoint DOM static class</summary>
}

Shp.DOM.addCallout = function (el, calloutOptions) {
    /// <summary>Addd callout for an element</summary>
    /// <param name="el" mayBeNull="false" optional="false" domElement="true"></summary>
    /// <param name="calloutOptions" mayBeNull="false" optional="false" type="Object"></summary>
    var e = Function.validateParameters(arguments, [{ name: 'el', mayBeNull: false, optional: false, domElement: true },
                                                    { name: 'calloutOptions', mayBeNull: false, optional: false, type: Object }], true);
    if (e) throw 'Shp.DOM.addCallout was called with invalid paramaters.';

	// build variable required for tooltip creation
    var html = calloutOptions['html'] || el.title;
    var position = calloutOptions['position'] || 'right';
    var sharepoint = calloutOptions['sharepoint'] || null;
    var callout = callout || document.createElement('DIV');
    var timer = null;

	// add loader image if data is retrieved from SharePoint
    var addLoaderImage = function () {
        var img = '<div class="callout-loader-container">' +
    			   '<img class="callout-loader" src="https://hp314.sharepoint.com/sites/sprint/SitePages/ajax-loader.gif" />' +
    			   '</div>';
        callout.innerHTML = img;
    }

	// show tooltip callout
    var showCallout = function () {

        if (timer !== null) {
            clearTimeout(timer);
        }
        callout.innerHTML = html;
        callout.style.position = 'absolute';
        callout.className = 'callout ' + position;
        if (sharepoint !== null) {
            addLoaderImage();
        }
        document.body.appendChild(callout);

        // Set the position of the callout
        var top;
        var left;
        switch (position) {
            case 'right':
                top = Shp.DOM.asbTop(el) - 25 - Math.floor(callout.offsetHeight / 2) + Math.floor(el.offsetHeight / 2);
                left = Shp.DOM.asbLeft(el) + el.offsetWidth - 10;
                break;
            case 'left':
                top = Shp.DOM.asbTop(el) - 25 - Math.floor(callout.offsetHeight / 2) + Math.floor(el.offsetHeight / 2);
                left = Shp.DOM.asbLeft(el) - callout.offsetWidth - 40;
                break;
            case 'top':
                top = Shp.DOM.asbTop(el) - 25 - callout.offsetHeight - 10;
                left = Shp.DOM.asbLeft(el) - 25 - Math.floor(callout.offsetWidth / 2) + Math.floor(el.offsetWidth / 2);
                break;
            case 'bottom':
                top = Shp.DOM.asbTop(el) + 10;
                left = Shp.DOM.asbLeft(el) - 25 - Math.floor(callout.offsetWidth / 2) + Math.floor(el.offsetWidth / 2);
                break;
        }

        callout.style.top = top + 'px';
        callout.style.left = left + 'px';

        // If sharepoint option is specified get data from sharepoint
        if (sharepoint !== null) {
            Shp.Data.getListItems({ listName: sharepoint['listName'] },
            	// Items are get from server (succes)
	            function (items) {
	                if (timer == null) {
	                    timer = setTimeout(hideCallout, 1000);
	                }
	                var calloutHtmlProvider = sharepoint['markupFactory'];
	                callout.innerHTML = calloutHtmlProvider(items, callout);
	            },
	            // Items are not retrieved from server (error)
	            function(sender, args) {
	            	callout.innerHTML = 'Error getting data from ' + sharepoint['listName'] +
	            						'<br />' + sender.get_message();
	            });
        }

    }

    var hideCallout = function () {
        try {
            document.body.removeChild(callout);
        }
        catch (e) {
        }
    }

    Sys.UI.DomEvent.addHandler(callout, 'mouseover', function () {
        if (timer !== null) {
            clearTimeout(timer);
        }
    });

    Sys.UI.DomEvent.addHandler(callout, 'mouseout', function () {
        timer = setTimeout(hideCallout, 1000);
    });

    Sys.UI.DomEvent.addHandler(el, 'mouseover', showCallout);
    Sys.UI.DomEvent.addHandler(el, 'mouseout', function () {
        timer = setTimeout(hideCallout, 1000);
    });

}

Shp.DOM._getPos = function (el, prop) {
    /// <summary>Get position from left or top an element (internal use)</summary>
    /// <param name="el" mayBeNull="false" optional="false" domElement="true"></param>
    /// <param name="prop" mayBeNull="false" optional="false" type="String"></param>
    var c = el[prop];
    var parent = el.offsetParent;
    while (parent !== null && parent.tagName != 'BODY') {
        c += parent[prop];
        parent = parent.offsetParent;
    }
    if (parent !== null) {
        c += parent[prop];
    }
    return c;
}

Shp.DOM.asbLeft = function (el) {
    /// <summary>Get position from left of an element</summary>
    /// <param name="el" mayBeNull="false" optional="false" domElement="true"></param>
    var e = Function.validateParameters(arguments, [{ name: 'el', domElement: true, mayBeNull: false, optional: false }], false);
    if (e) throw 'Shp.DOM.asbLeft was called with invalid parameter';
    return Shp.DOM._getPos(el, "offsetLeft") - Shp.DOM._getPos(el, "scrollLeft");

}

Shp.DOM.asbTop = function (el) {
    /// <summary>Get position from top of an element</summary>
    /// <param name="el" mayBeNull="false" optional="false" domElement="true"></param>
    var e = Function.validateParameters(arguments, [{ name: 'el', domElement: true, mayBeNull: false, optional: false }], false);
    if (e) throw 'Shp.DOM.asbTop was called with invalid parameter';
    return Shp.DOM._getPos(el, "offsetTop") - Shp.DOM._getPos(el, "scrollTop");
}

Shp.DOM.getInnerText = function (el) {
    /// <summary>Get innerText from a DOM element, if property is available</summary>
    /// <param name="el" mayBeNull="false" optional="false" domElement="true"></param>
    /// <returns type="String"></returns>
    var e = Function.validateParameters(arguments, [{ name: 'el', domElement: true, mayBeNull: false, optional: false }], false);
    if (e) throw 'Shp.DOM.getInnerText was called with invalid parameter';

    // Try returning textContent (applicable for Mozilla)
    if (typeof el.textContent !== 'undefined' && el.textContent !== null) {
        return el.textContent;
    }

    // Try returning innerText (applicable for Chrome and IE)
    if (typeof el.innerText != 'undefined') {
        return el.innerText;
    }

    return undefined;
}
Shp.DOM.registerClass('Shp.DOM');

The last thing is to include some CSS into the page. This is not created by me, but you can find it here

And now, let me explain how to use it. The simple way is to create the callout is this:

	var element = $get('elementId');
	Shp.DOM.addCallout(element ,{ position: 'top' });

Content of the callout box will be taken from title property of the element. Also, position property can have following values: top, left, right, bottom. But if you can to specify your own html, you can do this easily.

	var element = $get('elementId');
	Shp.DOM.addCallout(element ,{ position: 'top', html: 'callout html content goes here' });

The last scenario is to get content of callout from a SharePoint list. For this, you need to specify a new property called “sharepoint”, which is actually an object containing these properties.

listName (required) Name of the SharePoint list
markupFactory (required) A function which accepts obtained list items and DOM element to which callout is attached. It should return a string which will become callout content.
caml (optional) CAML query string.

More concrete, here is how you can use it.

var element = $get('elementId');
var opt = {
    listName: 'test',
    markupFactory: function (items, callout) {
        var html = '';
        for (var i = 0; i < items.get_count() ; i++) {
            html += items.itemAt(i).get_item('Title');
            html += '<br />';
        }
        return html;
    }
}

Shp.DOM.addCallout(otherElement, {
    position: 'top',
    sharepoint: opt
});

Code is still under development, but you can have an idea about how it is supposed to work. If you want to contribute to it, you are most welcome!

Creating tooltips in a view (a practical implementation)

For this example all I needed is to create a list containing the default column Title, which will serve as trigger element for our tooltips, and Description (multiple lines of text) which will contain callouts content.

1. Adding required files into the page.

Open the master in page with SharePoint Designer and add required JavaScript files using asp:ScriptManager.

<asp:ScriptManager id="ScriptManager" runat="server" EnablePageMethods="false" EnablePartialRendering="true" EnableScriptGlobalization="false" EnableScriptLocalization="true" LoadScriptsBeforeUI="false">
	<Scripts>
	<asp:ScriptReference Path="../../Style Library/Sprint/js/sharepoint.data.js" />
	<asp:ScriptReference Path="../../Style Library/Sprint/js/sharepoint.dom.js" />
	</Scripts>
</asp:ScriptManager>

Add also reference to callout CSS file.

<SharePoint:CssRegistration Name="/sites/sprint/Style Library/Sprint/css/callout.css" runat="server" EnableCssTheming="false" />

2. Make required settings in the view.

Go to the “All Items” view or any other if you prefer so, edit the page, edit the webpart and tick “Server Render” checkbox. In the same time in “XSL Link” add a link to a XSL with the following content.

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal" xmlns:o="urn:schemas-microsoft-com:office:office">
<xsl:include href="/_layouts/xsl/main.xsl"/>
<xsl:include href="/_layouts/xsl/internal.xsl"/>
 
<xsl:template name="ShowToolip" ddwrt:dvt_mode="body" match="FieldRef[@Name='Title']" mode="Text_body" ddwrt:ghost="show">
<xsl:param name="thisNode" select="." />
<xsl:param name="TooltipElement" select="$thisNode/@Title" />
<xsl:param name="TooltipContent" select="$thisNode/@Description" />
<xsl:if test="$TooltipElement != ''">
    <a title="{$TooltipContent}" href="#" class="jstooltip"><xsl:value-of select="$TooltipElement" disable-output-escaping="yes" /></a>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

2. Create tooltips.

Add into your page view a script editor control. This should contain the following code.

function pageLoad (sender, args) {
	var elements = document.querySelectorAll('a.jstooltip');
	for(var i = 0; i &lt; elements.length; i++) {
		Shp.DOM.addCallout( elements[i],{ position: 'right' });
	}
}

You can use jQuery.document(ready) or any other way to attach a method to page load event. I used MicrosoftAjax specific way because is trigger for complete postback as well as for partial postback. Content of description column should appear also in callout content for the Title (be sure you included in the view Title column, not TitleLink).

For more details, if you have a SharePoint Online account, drop me a message to share the page with you.

2014-10-04 09_53_52-Tooltips - All Items