/*
 *
 * SpreadJS Library 1.0.0
 * http://wijmo.com/
 *
 * Copyright(c) GrapeCity, Inc.  All rights reserved.
 *
 * Licensed under the Wijmo Commercial License. Also available under the GNU GPL Version 3 license.
 * licensing@wijmo.com
 * http://wijmo.com/widgets/license/
 *
 *
 **/
(function(window, $)
{
    "use strict";;
    var version = $().jquery ? $().jquery.split('.') : "";
    if(parseInt(version[0],10) > 1 || parseInt(version[0],10) === 1 && parseInt(version[1],10) >= 9)
    {
        $.event.handle = $.event.dispatch;
        var matched,
            browser;
        $.uaMatch = function(ua)
        {
            ua = ua.toLowerCase();
            var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
            return{
                    browser: match[1] || "",
                    version: match[2] || "0"
                }
        };
        if(!$.browser)
        {
            matched = $.uaMatch(navigator.userAgent);
            browser = {};
            if(matched.browser)
            {
                browser[matched.browser] = true;
                browser.version = matched.version
            }
            if(browser.chrome)
                browser.webkit = true;
            else if(browser.webkit)
                browser.safari = true;
            $.browser = browser
        }
        var oldToggle = $.fn.toggle;
        $.fn.toggle = function(fn, fn2)
        {
            if(!$.isFunction(fn) || !$.isFunction(fn2))
                return oldToggle.apply(this,arguments);
            var args = arguments,
                guid = fn.guid || $.guid++,
                i = 0,
                toggler = function(event)
                {
                    var lastToggle = ($._data(this,"lastToggle" + fn.guid) || 0) % i;
                    $._data(this,"lastToggle" + fn.guid,lastToggle + 1);
                    event.preventDefault();
                    return args[lastToggle].apply(this,arguments) || false
                };
            toggler.guid = guid;
            while(i < args.length)
                args[i++].guid = guid;
            return this.click(toggler)
        };
        var oldSelf = $.fn.andSelf || $.fn.addBack;
        $.fn.andSelf = function()
        {
            return oldSelf.apply(this,arguments)
        }
    }
    version = $.ui.dialog.version ? $.ui.dialog.version.split('.') : "";
    if(parseInt(version[0],10) > 1 || parseInt(version[0],10) === 1 && parseInt(version[1],10) >= 10)
    {
        $.extend($.ui.dialog,{
            uuid: 0,
            maxZ: 0,
            getTitleId: function($el)
            {
                var id = $el.attr("id");
                if(!id)
                {
                    this.uuid += 1;
                    id = this.uuid
                }
                return"ui-dialog-title-" + id
            },
            overlay: function(dialog)
            {
                this.$el = $.ui.dialog.overlay.create(dialog)
            }
        });
        $.extend($.ui.dialog.overlay,{
            instances: [],
            oldInstances: [],
            maxZ: 0,
            events: $.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(event)
            {
                return event + ".dialog-overlay"
            }).join(" "),
            create: function(dialog)
            {
                if(this.instances.length === 0)
                {
                    setTimeout(function()
                    {
                        if($.ui.dialog.overlay.instances.length)
                            $(document).bind($.ui.dialog.overlay.events,function(event)
                            {
                                if($(event.target).zIndex() < $.ui.dialog.overlay.maxZ)
                                    return false
                            })
                    },1);
                    $(window).bind("resize.dialog-overlay",$.ui.dialog.overlay.resize)
                }
                var $el = this.oldInstances.pop() || $("<div>").addClass("ui-widget-overlay");
                $(document).bind("keydown.dialog-overlay",function(event)
                {
                    var instances = $.ui.dialog.overlay.instances;
                    if(instances.length !== 0 && instances[instances.length - 1] === $el && dialog.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode && event.keyCode === $.ui.keyCode.ESCAPE)
                    {
                        dialog.close(event);
                        event.preventDefault()
                    }
                });
                $el.appendTo(document.body).css({
                    width: this.width(),
                    height: this.height()
                });
                if($.fn.bgiframe)
                    $el.bgiframe();
                this.instances.push($el);
                return $el
            },
            destroy: function($el)
            {
                var indexOf = $.inArray($el,this.instances),
                    maxZ = 0;
                if(indexOf !== -1)
                    this.oldInstances.push(this.instances.splice(indexOf,1)[0]);
                if(this.instances.length === 0)
                    $([document,window]).unbind(".dialog-overlay");
                $el.height(0).width(0).remove();
                $.each(this.instances,function()
                {
                    maxZ = Math.max(maxZ,this.css("z-index"))
                });
                this.maxZ = maxZ
            },
            height: function()
            {
                var scrollHeight,
                    offsetHeight;
                if($.ui.ie)
                {
                    scrollHeight = Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);
                    offsetHeight = Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);
                    if(scrollHeight < offsetHeight)
                        return $(window).height() + "px";
                    else
                        return scrollHeight + "px"
                }
                else
                    return $(document).height() + "px"
            },
            width: function()
            {
                var scrollWidth,
                    offsetWidth;
                if($.ui.ie)
                {
                    scrollWidth = Math.max(document.documentElement.scrollWidth,document.body.scrollWidth);
                    offsetWidth = Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);
                    if(scrollWidth < offsetWidth)
                        return $(window).width() + "px";
                    else
                        return scrollWidth + "px"
                }
                else
                    return $(document).width() + "px"
            },
            resize: function()
            {
                var $overlays = $([]);
                $.each($.ui.dialog.overlay.instances,function()
                {
                    $overlays = $overlays.add(this)
                });
                $overlays.css({
                    width: 0,
                    height: 0
                }).css({
                    width: $.ui.dialog.overlay.width(),
                    height: $.ui.dialog.overlay.height()
                })
            }
        });
        $.extend($.ui.dialog.overlay.prototype,{destroy: function()
            {
                $.ui.dialog.overlay.destroy(this.$el)
            }});
        if($.uiBackCompat !== false)
            (function($)
            {
                var _position = $.fn.position;
                $.fn.position = function(options)
                {
                    if(!options || !options.offset)
                        return _position.call(this,options);
                    var offset = options.offset.split(" "),
                        at = options.at.split(" ");
                    if(offset.length === 1)
                        offset[1] = offset[0];
                    if(/^\d/.test(offset[0]))
                        offset[0] = "+" + offset[0];
                    if(/^\d/.test(offset[1]))
                        offset[1] = "+" + offset[1];
                    if(at.length === 1)
                        if(/left|center|right/.test(at[0]))
                            at[1] = "center";
                        else
                        {
                            at[1] = at[0];
                            at[0] = "center"
                        }
                    return _position.call(this,$.extend(options,{
                            at: at[0] + offset[0] + " " + at[1] + offset[1],
                            offset: undefined
                        }))
                }
            })(jQuery)
    }
})(window,jQuery);
(function(window, $)
{
    "use strict";;
    var const_undefined = "undefined";
    var const_function = "function";
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === const_undefined)
        GrapeCity = window.GrapeCity = {Version: "1.0.0"};
    var UI = GrapeCity.UI;
    if(typeof UI === const_undefined)
        UI = GrapeCity.UI = {};
    var document = window.document;
    if(typeof $.event.special.gcmousewheel === const_undefined)
        (function($)
        {
            var domMS = 'DOMMouseScroll',
                ms = 'mousewheel';
            var types = [domMS,ms];
            function handler(event)
            {
                var args = [].slice.call(arguments,1),
                    delta = 0,
                    returnValue = true;
                event = $.event.fix(event || window.event);
                event.type = "gc" + ms;
                if((event.wheelDelta === undefined || event.wheelDelta === null) && (event.detail === undefined || event.detail === null))
                {
                    event.wheelDelta = event.originalEvent.wheelDelta;
                    event.detail = event.originalEvent.detail
                }
                if(event.wheelDelta)
                    delta = event.wheelDelta / 120;
                if(event.detail)
                    delta = -event.detail / 3;
                args.unshift(event,delta);
                return $.event.handle.apply(event.target || event.srcElement,args)
            }
            $.event.special.gcmousewheel = {
                setup: function()
                {
                    if(this.addEventListener)
                        for(var i = types.length; i; )
                        {
                            var t = types[--i];
                            if(t === ms)
                                this.addEventListener(domMS,handler,false);
                            this.addEventListener(t,handler,false)
                        }
                    else
                        this.attachEvent("on" + ms,handler)
                },
                teardown: function()
                {
                    if(this.removeEventListener)
                        for(var i = types.length; i; )
                        {
                            var t = types[--i];
                            if(t === ms)
                                this.removeEventListener(domMS,handler,false);
                            this.removeEventListener(t,handler,false)
                        }
                    else
                        this.detachEvent("on" + ms,handler)
                }
            };
            $.fn.extend({
                handlegcmousewheel: function(fn)
                {
                    return fn ? this.bind(ms,fn) : this.trigger(ms)
                },
                unhandlegcmousewheel: function(fn)
                {
                    return this.unbind(ms,fn)
                }
            })
        })(jQuery);
    var gcUIElement = "gcUIElement";
    UI.Global = function()
    {
        if(typeof window.gcGlobal === const_undefined)
            this._init()
    };
    UI.Global.prototype = {
        activeElement: null,
        _eventSuspended: 0,
        keyDown: function(event)
        {
            var gcGlobal = window.gcGlobal;
            if(gcGlobal._eventSuspended > 0)
                return;
            var activeElement = gcGlobal.activeElement;
            if(activeElement && activeElement._validationSelect)
            {
                var $select = $(activeElement._validationSelect);
                if($select.is(":visible"))
                    return
            }
            if(activeElement && activeElement.getCellType && activeElement.getActiveRowIndex && activeElement.getActiveColumnIndex)
            {
                var cellType = activeElement.getCellType(activeElement.getActiveRowIndex(),activeElement.getActiveColumnIndex());
                if(cellType && cellType.isReservedKey(event))
                    return
            }
            if(activeElement && activeElement.doKeyDown)
            {
                activeElement.doKeyDown(event);
                if((event.keyCode === UI.Key.z || event.keyCode === UI.Key.y) && event.ctrlKey && !event.altKey)
                    UI.cancelDefault(event)
            }
        },
        keyUp: function(event)
        {
            var gcGlobal = window.gcGlobal;
            if(gcGlobal._eventSuspended > 0)
                return;
            var activeElement = gcGlobal.activeElement;
            if(activeElement && activeElement.getCellType && activeElement.getActiveRowIndex && activeElement.getActiveColumnIndex)
            {
                var cellType = activeElement.getCellType(activeElement.getActiveRowIndex(),activeElement.getActiveColumnIndex());
                if(cellType && cellType.isReservedKey(event))
                    return
            }
            if(activeElement && activeElement.doKeyUp)
                activeElement.doKeyUp(event)
        },
        docSelectStart: function(event)
        {
            if(!document.all && window.gcGlobal && window.gcGlobal.activeElement)
                UI.cancelDefault(event);
            return false
        },
        getUIElement: function(e)
        {
            var w = e;
            while(w && w.tagName !== "BODY")
            {
                if(typeof w.getAttribute !== const_function)
                    break;
                var t = w.getAttribute(gcUIElement);
                if(!t)
                    t = w.gcUIElement;
                if(t)
                    return w;
                w = w.parentNode
            }
            return null
        },
        _init: function()
        {
            window.gcGlobal = this;
            this._eventSuspended = 0;
            var global = UI.Global;
            if($.browser.msie && document.documentMode !== undefined && document.documentMode !== null && document.documentMode < 9)
            {
                window.attachEvent('keydown',global.prototype.keyDown);
                window.attachEvent('keyup',global.prototype.keyUp);
                document.attachEvent('selectstart',global.prototype.docSelectStart)
            }
            else
            {
                window.addEventListener('keydown',global.prototype.keyDown,true);
                window.addEventListener('keyup',global.prototype.keyUp,true);
                document.addEventListener('selectstart',global.prototype.docSelectStart,true)
            }
            $(document).mousedown(function(e)
            {
                var ae = window.gcGlobal.activeElement;
                if(ae)
                {
                    var hitElement = global.prototype.getUIElement(e.target);
                    if(!hitElement && ae.endEdit)
                    {
                        ae.endEdit();
                        ae.repaint()
                    }
                    if(!hitElement && ae._disposeValidationUI)
                        ae._disposeValidationUI();
                    if(!hitElement)
                        window.gcGlobal.activeElement = null
                }
            });
            $(document).ready(function()
            {
                GrapeCity.UI.Global.prototype._createDummyObjects()
            })
        },
        _createDummyObjects: function()
        {
            var global = GrapeCity.UI.Global;
            if(!global.prototype._dummyContent)
            {
                var createSpan = function(className)
                    {
                        var t = document.createElement("span");
                        t.className = className;
                        t.style.display = "none";
                        document.body.insertBefore(t,null);
                        return t
                    };
                global.prototype._dummyHeader = createSpan("ui-widget-header ui-state-default");
                global.prototype._dummyContent = createSpan("ui-widget-content");
                global.prototype._dummyHover = createSpan("ui-state-hover");
                global.prototype._dummyHighlight = createSpan("ui-state-highlight")
            }
        },
        getWijmoThemeStyle: function(visualState)
        {
            var global = UI.Global,
                vs = UI.VisualState;
            var t = global.prototype._dummyHeader;
            if(visualState === vs.Highlight || visualState === vs.Selected)
                t = global.prototype._dummyHighlight;
            else if(visualState === vs.Hover)
                t = global.prototype._dummyHover;
            var ts = t.currentStyle;
            if(document.defaultView && document.defaultView.getComputedStyle)
                ts = document.defaultView.getComputedStyle(t,'');
            return ts
        },
        suspendEvent: function()
        {
            var gcGlobal = window.gcGlobal;
            gcGlobal._eventSuspended++
        },
        resumeEvent: function()
        {
            var gcGlobal = window.gcGlobal;
            gcGlobal._eventSuspended--;
            if(gcGlobal._eventSuspended < 0)
                gcGlobal._eventSuspended = 0
        }
    };
    window.gcGlobal = new UI.Global;
    UI.Key = {
        left: 37,
        right: 39,
        up: 38,
        down: 40,
        tab: 9,
        enter: 13,
        shift: 16,
        ctrl: 17,
        space: 32,
        altkey: 18,
        home: 36,
        end: 35,
        pup: 33,
        pdn: 34,
        backspace: 8,
        del: 46,
        esc: 27,
        c: 67,
        v: 86,
        x: 88,
        z: 90,
        y: 89
    };
    UI.KeyMap = function(key, ctrl, shift, alt, action)
    {
        this.key = key;
        this.ctrl = ctrl;
        this.shift = shift;
        this.alt = alt;
        this.action = action
    };
    Array.prototype.remove = function(item)
    {
        for(var i = 0; i < this.length; i++)
            if(this[i] === item)
            {
                this.splice(i,1);
                return
            }
    };
    Array.prototype.contains = function(item)
    {
        for(var i = 0; i < this.length; i++)
            if(this[i] === item)
                return true;
        return false
    };
    Array.prototype.indexOf = function(item, index)
    {
        if(index === undefined || index === null || isNaN(index))
            index = 0;
        for(var i = index; i < this.length; i++)
            if(this[i] === item)
                return i;
        return-1
    };
    if(!Array.prototype.map)
        Array.prototype.map = function(callback, thisArg)
        {
            var T,
                A,
                k;
            var O = window.Object(this);
            var len = O.length >>> 0;
            if(typeof callback !== "function")
                throw new TypeError(callback + " is not a function");
            if(thisArg)
                T = thisArg;
            A = new Array(len);
            k = 0;
            while(k < len)
            {
                var kValue,
                    mappedValue;
                if(k in O)
                {
                    kValue = O[k];
                    mappedValue = callback.call(T,kValue,k,O);
                    A[k] = mappedValue
                }
                k++
            }
            return A
        };
    UI.Point = function(x, y)
    {
        this.x = x;
        this.y = y;
        this.clone = function()
        {
            return new UI.Point(this.x,this.y)
        }
    };
    UI.Rect = function(x, y, w, h)
    {
        this.x = x;
        this.y = y;
        this.width = w;
        this.height = h
    };
    UI.Rect.prototype.intersect = function(x, y, width, height)
    {
        return x < this.x + this.width && this.x < x + width && y < this.y + this.height && this.y < y + height
    };
    UI.Rect.prototype.intersectRect = function(rect)
    {
        return rect.x < this.x + this.width && this.x < rect.x + rect.width && rect.y < this.y + this.height && this.y < rect.y + rect.height
    };
    UI.Rect.prototype.contains = function(x, y)
    {
        return x < this.x + this.width && this.x < x && y < this.y + this.height && this.y < y
    };
    UI.Rect.prototype.getIntersectRect = function(rect)
    {
        if(this.intersectRect(rect))
        {
            var x1_s = this.x;
            var y1_s = this.y;
            var x1_e = this.x + this.width;
            var y1_e = this.y + this.height;
            var x2_s = rect.x;
            var y2_s = rect.y;
            var x2_e = rect.x + rect.width;
            var y2_e = rect.y + rect.height;
            var intersectX_s = Math.max(x1_s,x2_s);
            var intersectY_s = Math.max(y1_s,y2_s);
            var intersectX_e = Math.min(x1_e,x2_e);
            var intersectY_e = Math.min(y1_e,y2_e);
            var newX = intersectX_s;
            var newY = intersectY_s;
            var newWidth = intersectX_e - intersectX_s;
            var newHeight = intersectY_e - intersectY_s;
            if(newWidth > 0 && newHeight > 0)
                return new UI.Rect(newX,newY,newWidth,newHeight)
        }
        return null
    };
    UI.Direction = {
        up: 1,
        down: 2,
        left: 3,
        right: 4
    };
    UI.HorizontalAlign = {
        left: 0,
        center: 1,
        right: 2,
        general: 3
    };
    UI.VerticalAlign = {
        top: 0,
        center: 1,
        bottom: 2
    };
    UI.HorizontalPosition = {
        left: 0,
        center: 1,
        right: 2,
        nearest: 3
    };
    UI.VerticalPosition = {
        top: 0,
        center: 1,
        bottom: 2,
        nearest: 3
    };
    UI.HeaderAutoText = {
        blank: 0,
        numbers: 1,
        letters: 2
    };
    UI.ClipboardPasteOptions = {
        All: 0,
        Values: 1,
        Formatting: 2,
        Formulas: 3
    };
    UI.TextFileOpenFlags = {
        None: 0,
        IncludeRowHeader: 1,
        IncludeColumnHeader: 2,
        UnFormatted: 8,
        ImportFormula: 16
    };
    UI.VisualState = {
        Normal: 0,
        Highlight: 1,
        Selected: 2,
        Active: 3,
        Hover: 4
    };
    UI.ReferenceStyle = {
        A1: 0,
        R1C1: 1
    };
    UI.LineStyle = {
        empty: 0,
        thin: 1,
        medium: 2,
        dashed: 3,
        dotted: 4,
        thick: 5,
        double: 6,
        hair: 7,
        mediumDashed: 8,
        dashDot: 9,
        mediumDashDot: 10,
        dashDotDot: 11,
        mediumDashDotDot: 12,
        slantedDashDot: 13
    };
    UI.LineBorder = function(color, style)
    {
        this.color = color;
        this.style = style
    };
    UI.LineBorder.prototype = {
        color: "black",
        style: UI.LineStyle.empty,
        width: function(border)
        {
            if(border && border.style)
            {
                var LineStyle = UI.LineStyle;
                switch(border.style)
                {
                    case LineStyle.dashDot:
                    case LineStyle.thin:
                    case LineStyle.dashed:
                    case LineStyle.dotted:
                    case LineStyle.hair:
                    case LineStyle.dashDotDot:
                        return 1;
                    case LineStyle.medium:
                    case LineStyle.mediumDashDot:
                    case LineStyle.mediumDashDotDot:
                    case LineStyle.mediumDashed:
                    case LineStyle.slantedDashDot:
                        return 2;
                    case LineStyle.thick:
                    case LineStyle.double:
                        return 3
                }
            }
            return 0
        },
        _weight: function(border)
        {
            if(border && border.style)
            {
                var LineStyle = UI.LineStyle;
                switch(border.style)
                {
                    case LineStyle.dashDot:
                    case LineStyle.dashed:
                    case LineStyle.dotted:
                    case LineStyle.dashDotDot:
                    case LineStyle.hair:
                        return 100;
                    case LineStyle.thin:
                        return 101;
                    case LineStyle.slantedDashDot:
                    case LineStyle.mediumDashed:
                    case LineStyle.mediumDashDot:
                    case LineStyle.mediumDashDotDot:
                        return 198;
                    case LineStyle.medium:
                        return 199;
                    case LineStyle.thick:
                        return 300;
                    case LineStyle.double:
                        return 90;
                    case LineStyle.empty:
                        return 0
                }
            }
            return-2
        }
    };
    var _fontProps = function()
        {
            var dict = {};
            dict['font-style'] = function(value, domElement)
            {
                if(value)
                    domElement.style.fontStyle = value
            };
            dict['font-variant'] = function(value, domElement)
            {
                if(value)
                    domElement.style.fontVariant = value
            };
            dict['font-weight'] = function(value, domElement)
            {
                if(value)
                    domElement.style.fontWeight = value
            };
            dict['font-size'] = function(value, domElement)
            {
                if(value)
                    domElement.style.fontSize = value
            };
            dict['line-height'] = function(value, domElement)
            {
                if(value)
                    domElement.style.lineHeight = value
            };
            dict['font-family'] = function(value, domElement)
            {
                if(value)
                    domElement.style.fontFamily = value
            };
            return dict
        }();
    function buildFontString(cssStyle)
    {
        if($.browser.safari)
            return cssStyle.font;
        var f = cssStyle.fontStyle;
        f += " " + cssStyle.fontVariant;
        f += " " + cssStyle.fontWeight;
        f += " " + cssStyle.fontSize;
        f += "/" + cssStyle.lineHeight;
        f += " " + cssStyle.fontFamily;
        return f
    }
    function beginBulidFont(sheet)
    {
        var span = document.createElement("span");
        span.style.visibility = "hidden";
        span.style.top = "-10000px";
        span.style.left = "-10000px";
        span.style.position = "absolute";
        span.className = "gcFontDetectSpanStyle";
        span.setAttribute(gcUIElement,"gcFontDetectSpan");
        document.body.insertBefore(span,null);
        return{
                span: span,
                dispose: function()
                {
                    document.body.removeChild(span);
                    delete this.span
                }
            }
    }
    UI.FontFactory = function(sheet)
    {
        this._sheet = sheet
    };
    UI.FontFactory.prototype = {buildFont: function(opt, font)
        {
            var f = '';
            if(opt)
            {
                var res = beginBulidFont(this.sheet);
                try
                {
                    if(font)
                        res.span.style.font = font;
                    $.each(_fontProps,function(k, fn)
                    {
                        fn(opt.hasOwnProperty(k) ? opt[k] : null,res.span)
                    })
                }
                finally
                {
                    var fs = res.span.currentStyle;
                    if(document.defaultView && document.defaultView.getComputedStyle)
                        fs = document.defaultView.getComputedStyle(res.span,'');
                    f = buildFontString(fs);
                    res.dispose()
                }
            }
            return f
        }};
    UI.Style = function(backColor, foreColor, hAlign, vAlign, font, themeFont, formatter, borderLeft, borderTop, borderRight, borderBottom, locked, textIndent, wordWrap, shrinkToFit, backgroundImage, cellType)
    {
        this.backgroundImage = backgroundImage;
        this.backColor = backColor;
        this.foreColor = foreColor;
        this.hAlign = hAlign;
        this.vAlign = vAlign;
        this.font = font;
        this.themeFont = themeFont;
        this.formatter = formatter;
        this.borderLeft = borderLeft;
        this.borderTop = borderTop;
        this.borderRight = borderRight;
        this.borderBottom = borderBottom;
        this.locked = locked;
        this.textIndent = textIndent;
        this.wordWrap = wordWrap;
        this.shrinkToFit = shrinkToFit;
        this.validator = undefined;
        this.cellType = cellType
    };
    UI.Style.prototype._initDefault = function()
    {
        this.backgroundImage = undefined;
        this.backColor = undefined;
        this.foreColor = undefined;
        this.hAlign = undefined;
        this.vAlign = undefined;
        this.font = undefined;
        this.themeFont = undefined;
        this.formatter = undefined;
        this.validator = undefined;
        this.borderLeft = undefined;
        this.borderTop = undefined;
        this.borderRight = undefined;
        this.borderBottom = undefined;
        this.locked = undefined;
        this.textIndent = undefined;
        this.wordWrap = undefined;
        this.shrinkToFit = undefined;
        this.cellType = undefined
    };
    UI.Style.prototype.copyFrom = function(style)
    {
        this.backgroundImage = style.backgroundImage;
        this.backColor = style.backColor;
        this.foreColor = style.foreColor;
        this.hAlign = style.hAlign;
        this.vAlign = style.vAlign;
        this.font = style.font;
        this.themeFont = style.themeFont;
        this.formatter = style.formatter;
        this._autoFormatter = style._autoFormatter;
        this.validator = style.validator;
        this.borderLeft = style.borderLeft;
        this.borderTop = style.borderTop;
        this.borderRight = style.borderRight;
        this.borderBottom = style.borderBottom;
        this.locked = style.locked;
        this.textIndent = style.textIndent;
        this.wordWrap = style.wordWrap;
        this.shrinkToFit = style.shrinkToFit;
        this.cellType = style.cellType
    };
    function _cloneLineBorder(border)
    {
        if(border && border instanceof UI.LineBorder)
            return new UI.LineBorder(border.color,border.style);
        return border
    }
    UI.Style.prototype.compose = function(style, force)
    {
        if(force || typeof this.backgroundImage === const_undefined)
            this.backgroundImage = style.backgroundImage;
        if(force || typeof this.backColor === const_undefined)
            this.backColor = style.backColor;
        if(force || typeof this.foreColor === const_undefined)
            this.foreColor = style.foreColor;
        if(force || typeof this.hAlign === const_undefined)
            this.hAlign = style.hAlign;
        if(force || typeof this.vAlign === const_undefined)
            this.vAlign = style.vAlign;
        if(force || typeof this.font === const_undefined)
            this.font = style.font;
        if(force || typeof this.themeFont === const_undefined)
            this.themeFont = style.themeFont;
        if(force || typeof this.formatter === const_undefined)
            this.formatter = style.formatter;
        if(force || typeof this.validator === const_undefined)
            this.validator = style.validator;
        if(force || typeof this._autoFormatter === const_undefined)
            this._autoFormatter = style._autoFormatter;
        if(force || typeof this.borderLeft === const_undefined)
            this.borderLeft = _cloneLineBorder(style.borderLeft);
        if(force || typeof this.borderTop === const_undefined)
            this.borderTop = _cloneLineBorder(style.borderTop);
        if(force || typeof this.borderRight === const_undefined)
            this.borderRight = _cloneLineBorder(style.borderRight);
        if(force || typeof this.borderBottom === const_undefined)
            this.borderBottom = _cloneLineBorder(style.borderBottom);
        if(force || typeof this.locked === const_undefined)
            this.locked = style.locked;
        if(force || typeof this.textIndent === const_undefined)
            this.textIndent = style.textIndent;
        if(force || typeof this.wordWrap === const_undefined)
            this.wordWrap = style.wordWrap;
        if(force || typeof this.shrinkToFit === const_undefined)
            this.shrinkToFit = style.shrinkToFit;
        if(force || typeof this.cellType === const_undefined)
            this.cellType = style.cellType
    };
    UI.Style.prototype.clear = function(propertyName)
    {
        if(arguments.length === 0)
        {
            this._initDefault();
            return
        }
        if(propertyName === "dataValidator")
            propertyName = "validator";
        this[propertyName] = undefined
    };
    var measureSpan;
    function composeFont(font, fontFamily)
    {
        if(!fontFamily)
            return font;
        if(!measureSpan)
        {
            var span = document.createElement("span");
            span.style.visibility = "hidden";
            span.style.top = "-10000px";
            span.style.left = "-10000px";
            document.body.insertBefore(span,null);
            measureSpan = span
        }
        if(font)
            measureSpan.style.font = font;
        else
            measureSpan.style.fontSize = '10pt';
        measureSpan.style.fontFamily = fontFamily;
        var fs = measureSpan.currentStyle;
        if(document.defaultView && document.defaultView.getComputedStyle)
            fs = document.defaultView.getComputedStyle(measureSpan,'');
        return buildFontString(fs)
    }
    UI.Style.prototype._normalize = function(theme)
    {
        var v,
            c;
        for(var p in this)
            if(theme && (p === 'foreColor' || p === 'backColor'))
            {
                v = this[p];
                if(v && theme.getColor)
                {
                    c = theme.getColor(v);
                    if(c)
                        this[p] = c
                }
            }
            else if(theme && (p === 'borderLeft' || p === 'borderTop' || p === 'borderRight' || p === 'borderBottom'))
            {
                var border = this[p];
                if(border)
                {
                    v = border.color;
                    if(v && theme.getColor)
                    {
                        c = theme.getColor(v);
                        if(c)
                            this[p].color = c
                    }
                }
            }
        if(this.themeFont && theme && theme.getFont)
            this.font = composeFont(this.font,theme.getFont(this.themeFont));
        return this
    };
    UI.NameInfo = function(name, expr, row, column)
    {
        this._name = name;
        this._baseRow = row;
        this._baseColumn = column;
        this._expr = expr
    };
    UI.NameInfo.prototype = {
        getName: function()
        {
            return this._name
        },
        getRow: function()
        {
            return this._baseRow
        },
        getColumn: function()
        {
            return this._baseColumn
        },
        getExpression: function()
        {
            return this._expr
        }
    };
    UI.createEventHandler = function(element, method)
    {
        return function()
            {
                return method.apply(element,arguments)
            }
    };
    UI.cancelDefault = function(e)
    {
        if(e.preventDefault)
        {
            e.preventDefault();
            e.stopPropagation()
        }
        else
        {
            e.cancelBubble = false;
            e.returnValue = false
        }
        return false
    };
    var canvasApiFound,
        slCanvasApiFound;
    UI._isStandardCanvas = function()
    {
        if(typeof canvasApiFound === const_undefined)
            canvasApiFound = typeof document.createElement("canvas").getContext !== const_undefined;
        return canvasApiFound
    };
    UI._isSilverlightCanvas = function()
    {
        if(UI._isStandardCanvas())
            return true;
        if(typeof slCanvasApiFound === const_undefined)
            slCanvasApiFound = typeof window.slcanvas !== const_undefined;
        return slCanvasApiFound
    };
    UI._useDoubleBuffer = function()
    {
        return UI._isStandardCanvas() || UI._isSilverlightCanvas()
    };
    UI.GcUIElement = function(x, y, w, h, name)
    {
        this._init(x,y,w,h,name)
    };
    UI.GcUIElement.prototype = {
        _init: function(x, y, w, h, name)
        {
            this._bounds = new UI.Rect(x,y,w,h);
            this._name = name
        },
        getBounds: function()
        {
            return this._bounds
        },
        setBounds: function(rect)
        {
            this._bounds.x = rect.x;
            this._bounds.y = rect.y;
            this._bounds.width = rect.width;
            this._bounds.height = rect.height
        },
        _draw: function(ctx)
        {
            if(!ctx)
                return;
            ctx.save();
            var lingrad = ctx.createLinearGradient(this._bounds.x,this._bounds.y,this._bounds.x,this._bounds.y + 40);
            lingrad.addColorStop(0,'#00ABEB');
            lingrad.addColorStop(1,'#fff');
            ctx.fillStyle = lingrad;
            ctx.fillRect(this._bounds.x,this._bounds.y,this._bounds.width,this._bounds.height);
            var t = this._name;
            if(t && t.length > 0)
            {
                ctx.fillStyle = "darkblue";
                ctx.font = "20pt Arial";
                ctx.fillText(t,this._bounds.x + 36,this._bounds.y + 28)
            }
            ctx.strokeStyle = "black";
            ctx.strokeRect(this._bounds.x,this._bounds.y,this._bounds.width,this._bounds.height);
            ctx.restore()
        },
        size: function(width, height)
        {
            this._bounds.width = width;
            this._bounds.height = height
        },
        move: function(x, y)
        {
            this._bounds.x = x;
            this._bounds.y = y
        }
    };
    UI._ThemeContext = function(owner)
    {
        this._owner = owner;
        if(owner)
            this._currentTheme = owner.currentTheme()
    };
    UI._ThemeContext.prototype = {
        owner: function(value)
        {
            if(arguments.length === 0)
                return this._owner;
            this._owner = value;
            if(this._owner)
                this._currentTheme = this._owner.currentTheme();
            return this
        },
        getColor: function(name)
        {
            if(this._currentTheme)
            {
                var colors = this._currentTheme.colors();
                if(colors)
                    return colors.getColor(name)
            }
            return name
        },
        getFont: function(name)
        {
            if(name)
            {
                if(this._currentTheme)
                {
                    var lowercaseName = name;
                    if(lowercaseName === "Body")
                        return this._currentTheme.bodyFont();
                    else if(lowercaseName === "Headings")
                        return this._currentTheme.headerFont()
                }
                return name
            }
            return null
        }
    };
    UI.Timer = function(host)
    {
        this.host = host;
        this.interval = null;
        this.action = null;
        this.intervalId = null;
        this.result = null;
        this.working = false
    };
    UI.Timer.prototype = {
        setAction: function(action)
        {
            if(typeof action === const_function)
                this.action = action
        },
        setInterval: function(interval)
        {
            if(!isNaN(interval) && interval > 0)
            {
                var oldInterval = this.interval;
                this.interval = interval;
                if(oldInterval !== interval)
                    this.start()
            }
            else
                this.stop()
        },
        start: function()
        {
            this.clear();
            if(!isNaN(this.interval))
            {
                var self = this;
                this.intervalId = window.setInterval(function()
                {
                    self.run()
                },this.interval)
            }
        },
        run: function()
        {
            this.working = true;
            if(typeof this.action === const_function)
                this.result = this.action.call(this.host)
        },
        stop: function()
        {
            this.clear();
            this.interval = null;
            this.action = null;
            this.intervalId = null;
            this.result = null;
            this.working = false
        },
        clear: function()
        {
            if(this.intervalId)
                window.clearInterval(this.intervalId)
        },
        _dispose: function()
        {
            this.stop();
            this.host = null
        }
    }
})(window,jQuery);
(function(window, $)
{
    "use strict";;
    var const_undefined = "undefined",
        const_function = "function",
        const_number = "number",
        const_string = "string",
        const_boolean = "boolean";
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === const_undefined)
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === const_undefined)
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    var document = window.document;
    var ko = window.ko;
    var ImportExportOptions = function(flags)
        {
            var TextFileOpenFlags = UI.TextFileOpenFlags;
            var IncludeRowHeader = TextFileOpenFlags.IncludeRowHeader,
                IncludeColumnHeader = TextFileOpenFlags.IncludeColumnHeader,
                UnFormatted = TextFileOpenFlags.UnFormatted,
                ImportFormula = TextFileOpenFlags.ImportFormula;
            this.rowHeader = (flags & IncludeRowHeader) === IncludeRowHeader;
            this.columnHeader = (flags & IncludeColumnHeader) === IncludeColumnHeader;
            this.unFormatted = (flags & UnFormatted) === UnFormatted;
            this.formula = (flags & ImportFormula) === ImportFormula;
            this.expandRows = true;
            this.expandColumns = true
        };
    ImportExportOptions.prototype = {fixOptions: function(sheet)
        {
            var SheetArea = UI.SheetArea;
            if(!sheet)
                return;
            if(sheet.getColumnCount(SheetArea.rowHeader) <= 0)
                this.rowHeader = false;
            if(sheet.getRowCount(SheetArea.colHeader) <= 0)
                this.columnHeader = false
        }};
    function raiseInvalidRangeException(item, value, start, end)
    {
        throw new Error("Invalid " + item + ": " + value + " (must be between " + start + " and " + end + ").");
    }
    var staticMembers = {
            copyTo: function(src, srcRow, srcColumn, dest, destRow, destColumn, copyRowCount, copyColumnCount, option)
            {
                staticMembers.checkArguments(src,srcRow,srcColumn,dest,destRow,destColumn,copyRowCount,copyColumnCount);
                var CopyToOption = UI.CopyToOption;
                src.suspendCalcService();
                dest.suspendCalcService();
                var savedValues = null;
                if((option & CopyToOption.BindingPath) > 0)
                    savedValues = staticMembers.copyBindingPath(src,srcRow,srcColumn,dest,destRow,destColumn,copyRowCount,copyColumnCount);
                try
                {
                    if((option & CopyToOption.Value) > 0)
                    {
                        staticMembers.copyValue(src,srcRow,srcColumn,dest,destRow,destColumn,copyRowCount,copyColumnCount);
                        if((option & CopyToOption.Formula) === 0)
                            staticMembers.clearFormula(dest,destRow,destColumn,copyRowCount,copyColumnCount);
                        if((option & CopyToOption.BindingPath) > 0)
                        {
                            var count = savedValues.length;
                            var baseRow = destRow < 0 ? 0 : destRow;
                            var baseCol = destColumn < 0 ? 0 : destColumn;
                            for(var i = 0; i < count; i++)
                            {
                                var cv = savedValues[i];
                                dest.setValue(baseRow + cv.row,baseCol + cv.col,cv.value)
                            }
                        }
                    }
                    if((option & CopyToOption.Formula) > 0)
                        staticMembers.copyFormula(src,srcRow,srcColumn,dest,destRow,destColumn,copyRowCount,copyColumnCount)
                }
                finally
                {
                    src.resumeCalcService();
                    dest.resumeCalcService()
                }
                if((option & CopyToOption.Style) > 0)
                    staticMembers.copyStyle(src,srcRow,srcColumn,dest,destRow,destColumn,copyRowCount,copyColumnCount);
                if((option & CopyToOption.Sparkline) > 0)
                    staticMembers.copySparkline(src,srcRow,srcColumn,dest,destRow,destColumn,copyRowCount,copyColumnCount);
                if((option & CopyToOption.RangeGroup) > 0)
                {
                    if(srcRow < 0)
                        staticMembers.copyColumnRangeGroup(src,srcColumn,dest,destColumn,copyColumnCount);
                    if(srcColumn < 0)
                        staticMembers.copyRowRangeGroup(src,srcRow,dest,destRow,copyRowCount)
                }
                if((option & CopyToOption.Span) > 0)
                    staticMembers.copySpan(src,srcRow,srcColumn,dest,destRow,destColumn,copyRowCount,copyColumnCount);
                if(srcRow < 0)
                    staticMembers.copyColumnAxis(src,srcColumn,dest,destColumn,copyColumnCount,option);
                if(srcColumn < 0)
                    staticMembers.copyRowAxis(src,srcRow,dest,destRow,copyRowCount,option);
                if(srcRow < 0 && destRow < 0 && srcColumn < 0 && destColumn < 0)
                    staticMembers.copySheetInfo(src,dest,option);
                dest._raisePropertyChanged("[CopyTo]")
            },
            checkArguments: function(src, fromRow, fromColumn, dest, toRow, toColumn, rowCount, columnCount)
            {
                if(!src)
                    throw new Error("src");
                if(!dest)
                    throw new Error("dest");
                if(fromRow < -1 || fromRow >= src.getRowCount())
                    raiseInvalidRangeException("from row index",fromRow,"-1",src.getRowCount() - 1);
                if(fromColumn < -1 || fromColumn >= src.getColumnCount())
                    raiseInvalidRangeException("from column index",fromColumn,"-1",src.getColumnCount() - 1);
                if(toRow < -1 || toRow >= dest.getRowCount())
                    raiseInvalidRangeException("to row index",toRow,"-1",dest.getRowCount() - 1);
                if(toColumn < -1 || toColumn >= dest.getColumnCount())
                    raiseInvalidRangeException("to column index",toColumn,"-1",dest.getColumnCount() - 1);
                var fromColumn2 = fromColumn;
                if(fromColumn < 0)
                {
                    fromColumn2 = 0;
                    columnCount = src.getColumnCount()
                }
                var toColumn2 = toColumn < 0 ? 0 : toColumn;
                if(columnCount < 1 || fromColumn2 + columnCount > src.getColumnCount() || toColumn2 + columnCount > dest.getColumnCount())
                    raiseInvalidRangeException("column count",columnCount,"1",Math.min(src.getColumnCount() - fromColumn2,dest.getColumnCount() - toColumn2));
                var fromRow2 = fromRow;
                if(fromRow < 0)
                {
                    fromRow2 = 0;
                    rowCount = src.getRowCount()
                }
                var toRow2 = toRow < 0 ? 0 : toRow;
                if(rowCount < 1 || fromRow2 + rowCount > src.getRowCount() || toRow2 + rowCount > dest.getRowCount())
                    raiseInvalidRangeException("row count",rowCount,"1",Math.min(src.getRowCount() - fromRow2,dest.getRowCount() - toRow2))
            },
            copyValue: function(src, srcRow, srcColumn, dest, destRow, destColumn, copyRowCount, copyColumnCount)
            {
                var crossSheet = !(src === dest && src._name === dest._name);
                var SheetArea = UI.SheetArea,
                    hRowCount,
                    hColumnCount,
                    r,
                    c,
                    value;
                if(srcRow < 0)
                {
                    var fColumn = srcColumn;
                    var tColumn = destColumn;
                    hRowCount = Math.min(src.getRowCount(SheetArea.colHeader),dest.getRowCount(SheetArea.colHeader));
                    hColumnCount = copyColumnCount;
                    if(srcColumn < 0)
                    {
                        fColumn = 0;
                        hColumnCount = src.getColumnCount()
                    }
                    if(destColumn < 0)
                        tColumn = 0;
                    if(crossSheet)
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                value = src.getValue(r,fColumn + c,SheetArea.colHeader);
                                if(value === undefined || value === null)
                                    if(src.isColumnBound(fColumn + c) && (r === src.colHeaderAutoTextIndex || r === hRowCount - 1 && src.colHeaderAutoTextIndex === -1))
                                        value = src.getDataColumnName(fColumn + c);
                                if(value === undefined || value === null)
                                    dest._setValueInternal(r,tColumn + c,SheetArea.colHeader,null,true);
                                else
                                    dest._setValueInternal(r,tColumn + c,SheetArea.colHeader,staticMembers.cloneObject(value),true)
                            }
                    else
                    {
                        var savedColumnHeaderValues = new UI._GcSheetModel(hRowCount,hColumnCount,null);
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                value = src._getModel(SheetArea.colHeader).getValue(r,fColumn + c);
                                if(value === undefined || value === null)
                                    if(src.isColumnBound(fColumn + c) && (r === src.colHeaderAutoTextIndex || r === hRowCount - 1 && src.colHeaderAutoTextIndex === -1))
                                        value = src.getDataColumnName(fColumn + c);
                                if(value !== undefined && value !== null)
                                    savedColumnHeaderValues.setValue(r,c,staticMembers.cloneObject(value))
                            }
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                                dest._setValueInternal(r,tColumn + c,SheetArea.colHeader,savedColumnHeaderValues.getValue(r,c),true)
                    }
                }
                if(srcColumn < 0)
                {
                    var fRow = srcRow;
                    var tRow = destRow;
                    hRowCount = copyRowCount;
                    hColumnCount = Math.min(src.getColumnCount(SheetArea.rowHeader),dest.getColumnCount(SheetArea.rowHeader));
                    if(srcRow < 0)
                    {
                        fRow = 0;
                        hRowCount = src.getRowCount()
                    }
                    if(destRow < 0)
                        tRow = 0;
                    if(crossSheet)
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                value = src.getValue(fRow + r,c,SheetArea.rowHeader);
                                if(value === undefined || value === null)
                                    dest._setValueInternal(tRow + r,c,SheetArea.rowHeader,null,true);
                                else
                                    dest._setValueInternal(tRow + r,c,SheetArea.rowHeader,staticMembers.cloneObject(value),true)
                            }
                    else
                    {
                        var savedRowHeaderValues = new UI._GcSheetModel(hRowCount,hColumnCount,null);
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                value = src._getModel(SheetArea.rowHeader).getValue(fRow + r,c);
                                if(value !== undefined && value !== null)
                                    savedRowHeaderValues.setValue(r,c,staticMembers.cloneObject(value))
                            }
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                                dest._setValueInternal(tRow + r,c,SheetArea.rowHeader,savedRowHeaderValues.getValue(r,c),true)
                    }
                }
                var fromRow = srcRow;
                var fromColumn = srcColumn;
                var toRow = destRow;
                var toColumn = destColumn;
                var rowCount = copyRowCount;
                var columnCount = copyColumnCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(srcColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destRow < 0)
                    toRow = 0;
                if(destColumn < 0)
                    toColumn = 0;
                if(crossSheet)
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                        {
                            value = src.getValue(fromRow + r,fromColumn + c,SheetArea.viewport);
                            if(value === undefined || value === null)
                                dest._setValueInternal(toRow + r,toColumn + c,SheetArea.viewport,null,true);
                            else
                                dest._setValueInternal(toRow + r,toColumn + c,SheetArea.viewport,staticMembers.cloneObject(value),true)
                        }
                else
                {
                    var savedValues = new UI._GcSheetModel(rowCount,columnCount,null);
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                        {
                            value = src.getValue(fromRow + r,fromColumn + c,SheetArea.viewport);
                            if(value !== undefined && value !== null)
                                savedValues.setValue(r,c,staticMembers.cloneObject(value))
                        }
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                            dest._setValueInternal(toRow + r,toColumn + c,SheetArea.viewport,savedValues.getValue(r,c),true)
                }
            },
            copyBindingPath: function(src, srcRow, srcColumn, dest, destRow, destColumn, copyRowCount, copyColumnCount)
            {
                var fromRow = srcRow;
                var fromColumn = srcColumn;
                var toRow = destRow;
                var toColumn = destColumn;
                var rowCount = copyRowCount;
                var columnCount = copyColumnCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(srcColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destRow < 0)
                    toRow = 0;
                if(destColumn < 0)
                    toColumn = 0;
                var savedValues = [];
                var crossSheet = !(src === dest && src._name === dest._name);
                if(crossSheet)
                {
                    var path;
                    for(var r = 0; r < rowCount; r++)
                        for(var c = 0; c < columnCount; c++)
                        {
                            path = src.getBindingPath(fromRow + r,fromColumn + c);
                            dest.setBindingPath(toRow + r,toColumn + c,path);
                            if(path)
                                savedValues.push({
                                    row: r,
                                    col: c,
                                    value: src.getValue(fromRow + r,fromColumn + c)
                                })
                        }
                }
                else
                {
                    var savedPaths = new UI._GcSheetModel(rowCount,columnCount,null);
                    var path;
                    for(var r = 0; r < rowCount; r++)
                        for(var c = 0; c < columnCount; c++)
                        {
                            path = src.getBindingPath(fromRow + r,fromColumn + c);
                            savedPaths.setBindingPath(r,c,path);
                            if(path)
                                savedValues.push({
                                    row: r,
                                    col: c,
                                    value: src.getValue(fromRow + r,fromColumn + c)
                                })
                        }
                    for(var r = 0; r < rowCount; r++)
                        for(var c = 0; c < columnCount; c++)
                            dest.setBindingPath(toRow + r,toColumn + c,savedPaths.getBindingPath(r,c))
                }
                return savedValues
            },
            cloneObject: function(obj)
            {
                var objClone;
                if(typeof obj === const_number || typeof obj === const_string || typeof obj === const_boolean || typeof obj === const_undefined)
                {
                    objClone = obj;
                    return objClone
                }
                else if(obj instanceof Date)
                {
                    //objClone = new Date(obj);
                    objClone = new Date(obj.toUTCString());
                    return objClone
                }
                else if(obj instanceof Object)
                    objClone = new obj.constructor;
                else
                    objClone = new obj.constructor(obj.valueOf());
                for(var key in obj)
                    if(obj.hasOwnProperty(key) && objClone[key] !== obj[key])
                        if(typeof this[key] === 'object')
                            objClone[key] = staticMembers.cloneObject(obj[key]);
                        else
                            objClone[key] = obj[key];
                objClone.toString = obj.toString;
                objClone.valueOf = obj.valueOf;
                return objClone
            },
            clearFormula: function(sheet, row, column, rowCount, columnCount)
            {
                if(row < 0)
                {
                    row = 0;
                    rowCount = sheet.getRowCount()
                }
                if(column < 0)
                {
                    column = 0;
                    columnCount = sheet.getColumnCount()
                }
                for(var r = row; r < rowCount; r++)
                    for(var c = column; c < columnCount; c++)
                        sheet.setFormula(r,c,null)
            },
            _copyExpression: function(expr, row, column)
            {
                var newExpr = expr;
                var Calc = GrapeCity.Calc;
                var Expressions = Calc.Expressions;
                var Functions = Calc.Functions;
                var Parser = Calc.Parser;
                var Reference = Calc.Errors.Reference;
                var ErrorExpression = Expressions.ErrorExpression,
                    ExternalErrorExpression = Expressions.ExternalErrorExpression;
                if(expr instanceof Expressions.ParenthesesExpression)
                {
                    newExpr = $.extend(true,new Expressions.ParenthesesExpression,expr);
                    newExpr.argument = staticMembers._copyExpression(expr.argument,row,column)
                }
                else if(expr instanceof Expressions.CellExpression)
                {
                    newExpr = $.extend(true,new Expressions.CellExpression,expr);
                    if(expr.rowRelative && (expr.row + row < 0 || expr.row + row > Parser.maxRowCount))
                        return new ErrorExpression(Reference);
                    if(expr.columnRelative && (expr.column + column < 0 || expr.column + column > Parser.maxColumnCount))
                        return new ErrorExpression(Reference)
                }
                else if(expr instanceof Expressions.ExternalCellExpression)
                {
                    newExpr = $.extend(true,new Expressions.ExternalCellExpression,expr);
                    if(expr.rowRelative && (expr.row + row < 0 || expr.row + row > Parser.maxRowCount))
                        return new ExternalErrorExpression(expr.source,Reference);
                    if(expr.columnRelative && (expr.column + column < 0 || expr.column + column > Parser.maxColumnCount))
                        return new ExternalErrorExpression(expr.source,Reference)
                }
                else if(expr instanceof Expressions.RangeExpression)
                {
                    newExpr = $.extend(true,new Expressions.RangeExpression,expr);
                    if(expr.startRowRelative && (expr.startRow + row < 0 || expr.startRow + row > Parser.maxRowCount))
                        return new ErrorExpression(Reference);
                    if(expr.startColumnRelative && (expr.startColumn + column < 0 || expr.startColumn + column > Parser.maxColumnCount))
                        return new ErrorExpression(Reference);
                    if(expr.endRowRelative && (expr.endRow + row < 0 || expr.endRow + row > Parser.maxRowCount))
                        return new ErrorExpression(Reference);
                    if(expr.endColumnRelative && (expr.endColumn + column < 0 || expr.endColumn + column > Parser.maxColumnCount))
                        return new ErrorExpression(Reference)
                }
                else if(expr instanceof Expressions.ExternalRangeExpression)
                {
                    newExpr = $.extend(true,new Expressions.ExternalRangeExpression,expr);
                    if(expr.startRowRelative && (expr.startRow + row < 0 || expr.startRow + row > Parser.maxRowCount))
                        return new ExternalErrorExpression(expr.source,Reference);
                    if(expr.startColumnRelative && (expr.startColumn + column < 0 || expr.startColumn + column > Parser.maxColumnCount))
                        return new ExternalErrorExpression(expr.source,Reference);
                    if(expr.endRowRelative && (expr.endRow + row < 0 || expr.endRow + row > Parser.maxRowCount))
                        return new ExternalErrorExpression(expr.source,Reference);
                    if(expr.endColumnRelative && (expr.endColumn + column < 0 || expr.endColumn + column > Parser.maxColumnCount))
                        return new ExternalErrorExpression(expr.source,Reference)
                }
                else if(expr instanceof Expressions.UnaryOperatorExpression)
                {
                    newExpr = $.extend(true,new Expressions.UnaryOperatorExpression,expr);
                    newExpr.operand = staticMembers._copyExpression(expr.operand,row,column)
                }
                else if(expr instanceof Expressions.BinaryOperatorExpression)
                {
                    newExpr = $.extend(true,new Expressions.BinaryOperatorExpression,expr);
                    newExpr.left = staticMembers._copyExpression(expr.left,row,column);
                    newExpr.right = staticMembers._copyExpression(expr.right,row,column)
                }
                else if(expr instanceof Expressions.FunctionExpression)
                {
                    newExpr = $.extend(true,new Expressions.FunctionExpression(new Functions.Function),expr);
                    if(expr.args && expr.args.length > 0)
                        for(var i = 0; i < expr.args.length; i++)
                            newExpr.args[i] = staticMembers._copyExpression(expr.args[i],row,column)
                }
                return newExpr
            },
            copyFormula: function(src, srcRow, srcColumn, dest, destRow, destColumn, copyRowCount, copyColumnCount)
            {
                var fromRow = srcRow;
                var fromColumn = srcColumn;
                var toRow = destRow;
                var toColumn = destColumn;
                var rowCount = copyRowCount;
                var columnCount = copyColumnCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(srcColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destRow < 0)
                    toRow = 0;
                if(destColumn < 0)
                    toColumn = 0;
                var crossSheet = !(src === dest && src._name === dest._name);
                var destSource = dest._getSheetSource();
                if(destSource)
                    destSource.unlinkCellExpression(toRow,toColumn,rowCount,columnCount);
                var m = new UI._GcSheetModel(rowCount,columnCount,null);
                var mc = new UI._GcSheetModel(rowCount,columnCount,null);
                var r,
                    c;
                for(r = 0; r < rowCount; r++)
                    for(c = 0; c < columnCount; c++)
                        mc.setFormula(r,c,src._calcDataModel.getFormula(r + fromRow,c + fromColumn));
                for(r = 0; r < rowCount; r++)
                    for(c = 0; c < columnCount; c++)
                    {
                        var expr = staticMembers._copyExpression(mc.getFormula(r,c),r + toRow,c + toColumn);
                        dest._dataModel.setFormula(r + toRow,c + toColumn,dest.getCalcService().unparse(expr,r + toRow,c + toColumn));
                        dest._calcDataModel.setFormula(r + toRow,c + toColumn,expr)
                    }
                if(destSource)
                {
                    destSource.linkCellExpression(toRow,toColumn,rowCount,columnCount);
                    destSource._addCellsToDirty(toRow,toColumn,rowCount,columnCount)
                }
            },
            copyStyle: function(src, srcRow, srcColumn, dest, destRow, destColumn, copyRowCount, copyColumnCount)
            {
                var crossSheet = !(src === dest && src._name === dest._name);
                var SheetArea = UI.SheetArea;
                var hRowCount,
                    hColumnCount,
                    r,
                    c,
                    style;
                if(srcRow < 0)
                {
                    var fColumn = srcColumn;
                    var tColumn = destColumn;
                    hRowCount = Math.min(src.getRowCount(SheetArea.colHeader),dest.getRowCount(SheetArea.colHeader));
                    hColumnCount = copyColumnCount;
                    if(srcColumn < 0)
                    {
                        fColumn = 0;
                        hColumnCount = src.getColumnCount()
                    }
                    if(destColumn < 0)
                        tColumn = 0;
                    if(crossSheet)
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                style = src.getActualStyle(r,fColumn + c,SheetArea.colHeader);
                                if(!style)
                                    dest.setStyle(r,tColumn + c,null,SheetArea.colHeader);
                                else
                                    dest.setStyle(r,tColumn + c,staticMembers.cloneObject(style),SheetArea.colHeader)
                            }
                    else
                    {
                        var savedColumnHeaderStyles = new UI._GcSheetModel(hRowCount,hColumnCount,null);
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                style = src.getActualStyle(r,fColumn + c,SheetArea.colHeader);
                                if(style)
                                    savedColumnHeaderStyles.setValue(r,c,staticMembers.cloneObject(style))
                            }
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                                dest.setStyle(r,tColumn + c,savedColumnHeaderStyles.getValue(r,c),SheetArea.colHeader)
                    }
                }
                if(srcColumn < 0)
                {
                    var fRow = srcRow;
                    var tRow = destRow;
                    hRowCount = copyRowCount;
                    hColumnCount = Math.min(src.getColumnCount(SheetArea.rowHeader),dest.getColumnCount(SheetArea.rowHeader));
                    if(srcRow < 0)
                    {
                        fRow = 0;
                        hRowCount = src.getRowCount()
                    }
                    if(destRow < 0)
                        tRow = 0;
                    if(crossSheet)
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                style = src.getActualStyle(fRow + r,c,SheetArea.rowHeader);
                                if(!style)
                                    dest.setStyle(tRow + r,c,null,SheetArea.rowHeader);
                                else
                                    dest.setStyle(tRow + r,c,staticMembers.cloneObject(style),SheetArea.rowHeader)
                            }
                    else
                    {
                        var savedRowHeaderStyles = new UI._GcSheetModel(hRowCount,hColumnCount,null);
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                style = src.getActualStyle(fRow + r,c,SheetArea.rowHeader);
                                if(style)
                                    savedRowHeaderStyles.setValue(r,c,staticMembers.cloneObject(style))
                            }
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                                dest.setStyle(tRow + r,c,savedRowHeaderStyles.getValue(r,c),SheetArea.rowHeader)
                    }
                }
                var fromRow = srcRow;
                var fromColumn = srcColumn;
                var toRow = destRow;
                var toColumn = destColumn;
                var rowCount = copyRowCount;
                var columnCount = copyColumnCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(srcColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destRow < 0)
                    toRow = 0;
                if(destColumn < 0)
                    toColumn = 0;
                if(crossSheet)
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                        {
                            style = src.getActualStyle(fromRow + r,fromColumn + c,SheetArea.viewport);
                            if(!style)
                                dest.setStyle(toRow + r,toColumn + c,null,SheetArea.viewport);
                            else
                                dest.setStyle(toRow + r,toColumn + c,staticMembers.cloneObject(style),SheetArea.viewport)
                        }
                else
                {
                    var savedStyles = new UI._GcSheetModel(rowCount,columnCount,null);
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                        {
                            style = src.getActualStyle(fromRow + r,fromColumn + c,SheetArea.viewport);
                            if(style)
                                savedStyles.setValue(r,c,staticMembers.cloneObject(style))
                        }
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                            dest.setStyle(toRow + r,toColumn + c,savedStyles.getValue(r,c),SheetArea.viewport)
                }
            },
            copyColumnRangeGroup: function(src, srcColumn, dest, destColumn, copyColumnCount)
            {
                var fromColumn = srcColumn;
                var toColumn = destColumn;
                var columnCount = copyColumnCount;
                if(fromColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destColumn < 0)
                    toColumn = 0;
                var crossSheet = !(src === dest && src._name === dest._name);
                if(crossSheet)
                    staticMembers.crossSheetCopyRangeGroup(src.colRangeGroup,fromColumn,dest.colRangeGroup,toColumn,columnCount);
                else
                {
                    var columnGroup = src.colRangeGroup;
                    if(columnGroup)
                        columnGroup._copy(fromColumn,toColumn,columnCount)
                }
            },
            crossSheetCopyRangeGroup: function(srcGroup, from, destGroup, to, count)
            {
                if(from < 0)
                    from = 0;
                if(to < 0)
                    to = 0;
                var clonedItems = {};
                if(srcGroup)
                {
                    var index = srcGroup.items.nextNonEmptyIndex(from - 1);
                    while(index >= 0 && index < from + count)
                    {
                        clonedItems[index - from] = new UI.RangeGroupItemInfo(srcGroup.items[index]);
                        index = srcGroup.items.nextNonEmptyIndex(index)
                    }
                }
                if(destGroup)
                {
                    destGroup.items.clear(to,count);
                    if(clonedItems.length > 0)
                        for(var key in clonedItems)
                            if(clonedItems.hasOwnProperty(key))
                            {
                                var value = clonedItems[key];
                                destGroup.items[to + key] = value
                            }
                }
            },
            copySparkline: function(src, srcRow, srcColumn, dest, destRow, destColumn, copyRowCount, copyColumnCount)
            {
                var fromRow = srcRow;
                var fromColumn = srcColumn;
                var toRow = destRow;
                var toColumn = destColumn;
                var rowCount = copyRowCount;
                var columnCount = copyColumnCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(srcColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destRow < 0)
                    toRow = 0;
                if(destColumn < 0)
                    toColumn = 0;
                var crossSheet = !(src === dest && src._name === dest._name);
                var sparkline;
                if(crossSheet)
                {
                    sparkline = dest._sparklineGroupManager;
                    if(sparkline)
                        sparkline._exCopy(src,fromRow,fromColumn,toRow,toColumn,rowCount,columnCount)
                }
                else
                {
                    sparkline = src._sparklineGroupManager;
                    if(sparkline)
                        sparkline._copy(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount)
                }
            },
            copyRowRangeGroup: function(src, srcRow, dest, destRow, copyRowCount)
            {
                var fromRow = srcRow;
                var toRow = destRow;
                var rowCount = copyRowCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(destRow < 0)
                    toRow = 0;
                var crossSheet = !(src === dest && src._name === dest._name);
                if(crossSheet)
                    staticMembers.crossSheetCopyRangeGroup(src.rowRangeGroup,fromRow,dest.rowRangeGroup,toRow,rowCount);
                else
                {
                    var rowGroup = src.rowRangeGroup;
                    if(rowGroup)
                        rowGroup._copy(fromRow,toRow,rowCount)
                }
            },
            copySpan: function(src, srcRow, srcColumn, dest, destRow, destColumn, copyRowCount, copyColumnCount)
            {
                var SheetArea = UI.SheetArea;
                var fromRow = srcRow;
                var fromColumn = srcColumn;
                var toRow = destRow;
                var toColumn = destColumn;
                var rowCount = copyRowCount;
                var columnCount = copyColumnCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(fromColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destRow < 0)
                    toRow = 0;
                if(destColumn < 0)
                    toColumn = 0;
                var crossSheet = !(src === dest && src._name === dest._name);
                if(srcRow < 0)
                    if(crossSheet)
                        staticMembers.crossSheetCopySpans(src._colHeaderSpanModel,-1,fromColumn,dest._colHeaderSpanModel,-1,toColumn,-1,columnCount);
                    else
                    {
                        var csm = src._getSpanModel(SheetArea.colHeader);
                        if(csm)
                            csm.copy(-1,fromColumn,-1,toColumn,-1,columnCount)
                    }
                if(srcColumn < 0)
                    if(crossSheet)
                        staticMembers.crossSheetCopySpans(src._rowHeaderSpanModel,fromRow,-1,dest._rowHeaderSpanModel,toRow,-1,rowCount,-1);
                    else
                    {
                        var rsm = src._getSpanModel(SheetArea.rowHeader);
                        if(rsm)
                            rsm.copy(fromRow,-1,toRow,-1,rowCount,-1)
                    }
                if(crossSheet)
                    staticMembers.crossSheetCopySpans(src._spanModel,fromRow,fromColumn,dest._spanModel,toRow,toColumn,rowCount,columnCount);
                else
                {
                    var sm = src._getSpanModel(SheetArea.viewport);
                    if(sm)
                        sm.copy(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount)
                }
            },
            crossSheetCopySpans: function(srcSpanModel, fromRow, fromColumn, destSpanModel, toRow, toColumn, rowCount, columnCount)
            {
                var srcSpans = staticMembers.getSpans(srcSpanModel);
                var destSpans = staticMembers.getSpans(destSpanModel);
                var addList = [],
                    i,
                    j,
                    cs,
                    span;
                if(fromRow === -1)
                {
                    if(srcSpans && srcSpans.length > 0)
                        for(i = 0; i < srcSpans.length; i++)
                        {
                            cs = srcSpans[i];
                            if(cs.col >= fromColumn && cs.col + cs.colCount <= fromColumn + columnCount)
                                addList.push(new UI.Range(cs.row,toColumn + cs.col - fromColumn,cs.rowCount,cs.colCount))
                        }
                    if(destSpans && destSpans.length > 0)
                        for(i = 0; i < destSpans.length; i++)
                        {
                            cs = destSpans[i];
                            if(cs.col >= toColumn && cs.col < toColumn + columnCount)
                                for(j = 0; j < destSpanModel.length; j++)
                                {
                                    span = destSpanModel[j];
                                    if(span.row === cs.row && span.col === cs.col)
                                    {
                                        destSpanModel.splice(j,1);
                                        break
                                    }
                                }
                        }
                }
                else if(fromColumn === -1)
                {
                    if(srcSpans && srcSpans.length > 0)
                        for(i = 0; i < srcSpans.length; i++)
                        {
                            cs = srcSpans[i];
                            if(fromRow <= cs.row && cs.row + cs.rowCount <= fromRow + rowCount)
                                addList.push(new UI.Range(toRow + cs.row - fromRow,cs.col,cs.rowCount,cs.colCount))
                        }
                    if(destSpans && destSpans.length > 0)
                        for(i = 0; i < destSpans.length; i++)
                        {
                            cs = destSpans[i];
                            if(cs.row >= toRow && cs.row < toRow + rowCount)
                                for(j = 0; j < destSpanModel.length; j++)
                                {
                                    span = destSpanModel[j];
                                    if(span.row === cs.row && span.col === cs.col)
                                    {
                                        destSpanModel.splice(j,1);
                                        break
                                    }
                                }
                        }
                }
                else
                {
                    if(srcSpans && srcSpans.length > 0)
                        for(i = 0; i < srcSpans.length; i++)
                        {
                            cs = srcSpans[i];
                            if(fromRow <= cs.row && cs.row < fromRow + rowCount && fromColumn <= cs.col && cs.col < fromColumn + columnCount)
                                addList.push(new UI.Range(toRow + cs.row - fromRow,toColumn + cs.col - fromColumn,cs.rowCount,cs.colCount))
                        }
                    if(destSpans && destSpans.length > 0)
                        for(i = 0; i < destSpans.length; i++)
                        {
                            cs = destSpans[i];
                            if(cs.row >= toRow && cs.row < toRow + rowCount && cs.col >= toColumn && cs.col < toColumn + columnCount)
                                for(j = 0; j < destSpanModel.length; j++)
                                {
                                    span = destSpanModel[j];
                                    if(span.row === cs.row && span.col === cs.col)
                                    {
                                        destSpanModel.splice(j,1);
                                        break
                                    }
                                }
                        }
                }
                if(addList.length > 0 && destSpanModel)
                    for(i = 0; i < addList.length; i++)
                    {
                        var cr = addList[i];
                        destSpanModel.push(cr)
                    }
            },
            getSpans: function(spanModel)
            {
                if(spanModel && spanModel.length > 0)
                    return spanModel.slice(0);
                return null
            },
            copyColumnAxis: function(src, srcColumn, dest, destColumn, copyColumnCount, option)
            {
                var fromColumn = srcColumn;
                var toColumn = destColumn;
                var columnCount = copyColumnCount;
                if(srcColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destColumn < 0)
                    toColumn = 0;
                var colHeader = UI.SheetArea.colHeader;
                for(var c = 0; c < columnCount; c++)
                {
                    var width = src._getActualColumnWidth(c + fromColumn);
                    if(width !== undefined && width !== null)
                        dest.setColumnWidth(c + toColumn,width);
                    var visible = src.getColumnVisible(c + fromColumn);
                    if(visible !== undefined && visible !== null)
                        dest.setColumnVisible(c + toColumn,visible);
                    if((option & UI.CopyToOption.Style) > 0)
                    {
                        var style = src.getActualStyle(-1,c + fromColumn);
                        if(!style)
                            dest.setStyle(-1,c + toColumn,null);
                        else
                            dest.setStyle(-1,c + toColumn,staticMembers.cloneObject(style));
                        style = src.getActualStyle(-1,c + fromColumn,colHeader);
                        if(!style)
                            dest.setStyle(-1,c + toColumn,null,colHeader);
                        else
                            dest.setStyle(-1,c + toColumn,staticMembers.cloneObject(style),colHeader)
                    }
                }
                var colHeaderRowCount = Math.min(src.getRowCount(colHeader),dest.getRowCount(colHeader));
                for(var r = 0; r < colHeaderRowCount; r++)
                {
                    var height = src._getActualRowHeight(r,colHeader);
                    if(height !== undefined && height !== null)
                        dest.setRowHeight(r,height,colHeader)
                }
            },
            cloneAxisInfo: function(srcInfo, fromIndex, count)
            {
                var ret = [];
                for(var index = 0; index < count; index++)
                {
                    var axisInfo = srcInfo[fromIndex + index];
                    if(!axisInfo)
                        ret.push(null);
                    else
                    {
                        var vColumn = {};
                        for(var key in axisInfo)
                            if(axisInfo.hasOwnProperty(key))
                                vColumn[key] = axisInfo[key];
                        ret.push(vColumn)
                    }
                }
                return ret
            },
            getAxisInfo: function(srcInfo, fromIndex, count)
            {
                var ret = [];
                for(var index = 0; index < count; index++)
                    ret.push(srcInfo[fromIndex + index]);
                return ret
            },
            setAxisInfo: function(infos, destAxis, toIndex)
            {
                if(destAxis)
                    for(var index = 0; index < infos.length; index++)
                        destAxis[toIndex + index] = infos[index]
            },
            copyRowAxis: function(src, srcRow, dest, destRow, copyRowCount, option)
            {
                var fromRow = srcRow;
                var toRow = destRow;
                var rowCount = copyRowCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(destRow < 0)
                    toRow = 0;
                var rowHeader = UI.SheetArea.rowHeader;
                for(var r = 0; r < rowCount; r++)
                {
                    var height = src._getActualRowHeight(r + fromRow);
                    if(height !== undefined && height !== null)
                        dest.setRowHeight(r + toRow,height);
                    var visible = src.getRowVisible(r + fromRow);
                    if(visible !== undefined && visible !== null)
                        dest.setRowVisible(r + toRow,visible);
                    if((option & UI.CopyToOption.Style) > 0)
                    {
                        var style = src.getActualStyle(r + fromRow,-1);
                        if(!style)
                            dest.setStyle(r + toRow,-1,null);
                        else
                            dest.setStyle(r + toRow,-1,staticMembers.cloneObject(style));
                        style = src.getActualStyle(r + fromRow,-1,rowHeader);
                        if(!style)
                            dest.setStyle(r + toRow,-1,null,rowHeader);
                        else
                            dest.setStyle(r + toRow,-1,staticMembers.cloneObject(style),rowHeader)
                    }
                }
                var rowHeaderColCount = Math.min(src.getColumnCount(rowHeader),dest.getColumnCount(rowHeader));
                for(var c = 0; c < rowHeaderColCount; c++)
                {
                    var width = src._getActualColumnWidth(c,rowHeader);
                    if(width !== undefined && width !== null)
                        dest.setColumnWidth(c,width,rowHeader)
                }
            },
            copySheetInfo: function(src, dest, option)
            {
                if(!(src === dest && src._name === dest._name))
                {
                    if((option & UI.CopyToOption.Style) > 0)
                    {
                        var SheetArea = UI.SheetArea;
                        var colHeader = SheetArea.colHeader;
                        var rowHeader = SheetArea.rowHeader;
                        dest.setDefaultStyle(staticMembers.cloneObject(src.getDefaultStyle()));
                        dest.setDefaultStyle(staticMembers.cloneObject(src.getDefaultStyle(colHeader)),colHeader);
                        dest.setDefaultStyle(staticMembers.cloneObject(src.getDefaultStyle(rowHeader)),rowHeader)
                    }
                    dest.defaults.colWidth = src.defaults.colWidth;
                    dest.defaults.rowHeight = src.defaults.rowHeight;
                    dest.defaults.rowHeaderColWidth = src.defaults.rowHeaderColWidth
                }
            },
            moveTo: function(src, srcRow, srcColumn, dest, destRow, destColumn, moveRowCount, moveColumnCount, option)
            {
                staticMembers.checkArguments(src,srcRow,srcColumn,dest,destRow,destColumn,moveRowCount,moveColumnCount);
                var CopyToOption = UI.CopyToOption;
                src.suspendCalcService();
                dest.suspendCalcService();
                var savedValues = null;
                if((option & CopyToOption.BindingPath) > 0)
                    savedValues = staticMembers.moveBindingPath(src,srcRow,srcColumn,dest,destRow,destColumn,moveRowCount,moveColumnCount);
                try
                {
                    if((option & CopyToOption.Value) > 0)
                    {
                        staticMembers.moveValue(src,srcRow,srcColumn,dest,destRow,destColumn,moveRowCount,moveColumnCount);
                        if((option & CopyToOption.Formula) === 0)
                            staticMembers.clearFormula(dest,destRow,destColumn,moveRowCount,moveColumnCount);
                        if((option & CopyToOption.BindingPath) > 0)
                        {
                            var count = savedValues.length;
                            var baseRow = destRow < 0 ? 0 : destRow;
                            var baseCol = destColumn < 0 ? 0 : destColumn;
                            for(var i = 0; i < count; i++)
                            {
                                var cv = savedValues[i];
                                dest.setValue(baseRow + cv.row,baseCol + cv.col,cv.value)
                            }
                        }
                    }
                    if((option & CopyToOption.Formula) > 0)
                        staticMembers.moveFormula(src,srcRow,srcColumn,dest,destRow,destColumn,moveRowCount,moveColumnCount)
                }
                finally
                {
                    src.resumeCalcService();
                    dest.resumeCalcService()
                }
                if((option & CopyToOption.Style) > 0)
                    staticMembers.moveStyle(src,srcRow,srcColumn,dest,destRow,destColumn,moveRowCount,moveColumnCount);
                if((option & CopyToOption.Sparkline) > 0)
                    staticMembers.moveSparkline(src,srcRow,srcColumn,dest,destRow,destColumn,moveRowCount,moveColumnCount);
                if((option & CopyToOption.RangeGroup) > 0)
                {
                    if(srcRow < 0)
                        staticMembers.moveColumnRangeGroup(src,srcColumn,dest,destColumn,moveColumnCount);
                    if(srcColumn < 0)
                        staticMembers.moveRowRangeGroup(src,srcRow,dest,destRow,moveRowCount)
                }
                if((option & CopyToOption.Span) > 0)
                    staticMembers.moveSpan(src,srcRow,srcColumn,dest,destRow,destColumn,moveRowCount,moveColumnCount);
                if(srcRow < 0)
                    staticMembers.moveColumnAxis(src,srcColumn,dest,destColumn,moveColumnCount,option);
                if(srcColumn < 0)
                    staticMembers.moveRowAxis(src,srcRow,dest,destRow,moveRowCount,option);
                if(srcRow < 0 && destRow < 0 && srcColumn < 0 && destColumn < 0)
                    staticMembers.moveSheetInfo(src,dest,option);
                if(srcRow < 0)
                {
                    var fColumn = srcColumn;
                    var hColumnCount = moveColumnCount;
                    if(srcColumn < 0)
                    {
                        fColumn = 0;
                        hColumnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                    }
                    for(var c = 0; c < hColumnCount; c++)
                        if(src.isColumnBound(fColumn + c))
                            src._colInfos[fColumn + c] = null
                }
                var prop = "[MoveTo]";
                src._raisePropertyChanged(prop);
                dest._raisePropertyChanged(prop)
            },
            moveValue: function(src, srcRow, srcColumn, dest, destRow, destColumn, moveRowCount, moveColumnCount)
            {
                var crossSheet = !(src === dest && src._name === dest._name);
                var colHeader = UI.SheetArea.colHeader,
                    hRowCount,
                    hColumnCount,
                    r,
                    c,
                    value;
                if(srcRow < 0)
                {
                    var fColumn = srcColumn;
                    var tColumn = destColumn;
                    hRowCount = Math.min(src.getRowCount(colHeader),dest.getRowCount(colHeader));
                    hColumnCount = moveColumnCount;
                    if(srcColumn < 0)
                    {
                        fColumn = 0;
                        hColumnCount = src.getColumnCount()
                    }
                    if(destColumn < 0)
                        tColumn = 0;
                    if(crossSheet)
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                value = src._getModel(colHeader).getValue(r,fColumn + c);
                                if(value === undefined || value === null)
                                    if(src.isColumnBound(fColumn + c) && (r === src.colHeaderAutoTextIndex || r === hRowCount - 1 && src.colHeaderAutoTextIndex === -1))
                                        value = src.getDataColumnName(fColumn + c);
                                if(value === undefined || value === null)
                                    dest._setValueInternal(r,tColumn + c,colHeader,null,true);
                                else
                                {
                                    dest._setValueInternal(r,tColumn + c,colHeader,value,true);
                                    src._setValueInternal(r,fColumn + c,colHeader,null,true)
                                }
                            }
                    else
                    {
                        var savedColumnHeaderValues = new UI._GcSheetModel(hRowCount,hColumnCount,null);
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                value = src._getModel(colHeader).getValue(r,fColumn + c);
                                if(value === undefined || value === null)
                                    if(src.isColumnBound(fColumn + c) && (r === src.colHeaderAutoTextIndex || r === hRowCount - 1 && src.colHeaderAutoTextIndex === -1))
                                        value = src.getDataColumnName(fColumn + c);
                                if(value !== undefined && value !== null)
                                    savedColumnHeaderValues.setValue(r,c,value);
                                src._setValueInternal(r,fColumn + c,colHeader,null,true)
                            }
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                                dest._setValueInternal(r,tColumn + c,colHeader,savedColumnHeaderValues.getValue(r,c),true)
                    }
                }
                var rowHeader = UI.SheetArea.rowHeader;
                if(srcColumn < 0)
                {
                    var fRow = srcRow;
                    var tRow = destRow;
                    hRowCount = moveRowCount;
                    hColumnCount = Math.min(src.getColumnCount(rowHeader),dest.getColumnCount(rowHeader));
                    if(srcRow < 0)
                    {
                        fRow = 0;
                        hRowCount = src.getRowCount()
                    }
                    if(destRow < 0)
                        tRow = 0;
                    if(crossSheet)
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                value = src.getValue(fRow + r,c,rowHeader);
                                if(value === undefined || value === null)
                                    dest._setValueInternal(tRow + r,c,rowHeader,null,true);
                                else
                                {
                                    dest._setValueInternal(tRow + r,c,rowHeader,value,true);
                                    src._setValueInternal(fRow + r,c,rowHeader,null,true)
                                }
                            }
                    else
                    {
                        var savedRowHeaderValues = new UI._GcSheetModel(hRowCount,hColumnCount,null);
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                value = src._getModel(rowHeader).getValue(fRow + r,c);
                                if(value !== undefined && value !== null)
                                    savedRowHeaderValues.setValue(r,c,value);
                                src._setValueInternal(fRow + r,c,rowHeader,null,true)
                            }
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                                dest._setValueInternal(tRow + r,c,rowHeader,savedRowHeaderValues.getValue(r,c),true)
                    }
                }
                var fromRow = srcRow;
                var fromColumn = srcColumn;
                var toRow = destRow;
                var toColumn = destColumn;
                var rowCount = moveRowCount;
                var columnCount = moveColumnCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(srcColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destRow < 0)
                    toRow = 0;
                if(destColumn < 0)
                    toColumn = 0;
                if(crossSheet)
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                        {
                            value = src.getValue(fromRow + r,fromColumn + c);
                            if(value === undefined || value === null)
                                dest._setValueInternal(toRow + r,toColumn + c,null,null,true);
                            else
                            {
                                dest._setValueInternal(toRow + r,toColumn + c,null,value,true);
                                src._setValueInternal(fromRow + r,fromColumn + c,null,null,true)
                            }
                        }
                else
                {
                    var savedValues = new UI._GcSheetModel(rowCount,columnCount,null);
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                        {
                            value = src.getValue(fromRow + r,fromColumn + c);
                            if(value !== undefined && value !== null)
                                savedValues.setValue(r,c,value)
                        }
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                            src._setValueInternal(fromRow + r,fromColumn + c,null,null,true);
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                            dest._setValueInternal(toRow + r,toColumn + c,null,savedValues.getValue(r,c),true)
                }
            },
            moveFormula: function(src, srcRow, srcColumn, dest, destRow, destColumn, moveRowCount, moveColumnCount)
            {
                var fromRow = srcRow;
                var fromColumn = srcColumn;
                var toRow = destRow;
                var toColumn = destColumn;
                var rowCount = moveRowCount;
                var columnCount = moveColumnCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(srcColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destRow < 0)
                    toRow = 0;
                if(destColumn < 0)
                    toColumn = 0;
                var crossSheet = !(src === dest && src._name === dest._name);
                var srcSource = src._getSheetSource();
                var destSource = dest._getSheetSource();
                if(srcSource)
                {
                    srcSource.unlinkCellExpression(fromRow,fromColumn,rowCount,columnCount);
                    srcSource._addDependentsToAdjust(fromRow,fromColumn,rowCount,columnCount)
                }
                if(destSource)
                {
                    destSource.unlinkCellExpression(toRow,toColumn,rowCount,columnCount);
                    destSource._addDependentsToAdjust(toRow,toColumn,rowCount,columnCount)
                }
                var srcModel = src._getModel(),
                    srcCalcModel = src._getCalcModel();
                var destModel = dest._getModel(),
                    destCalcModel = dest._getCalcModel();
                var expressions = [],
                    r,
                    c;
                for(r = 0; r < rowCount; r++)
                    for(c = 0; c < columnCount; c++)
                    {
                        expressions.push(srcCalcModel.getFormula(r + fromRow,c + fromColumn));
                        srcModel.setFormula(r + fromRow,c + fromColumn,null);
                        srcCalcModel.setFormula(r + fromRow,c + fromColumn,null)
                    }
                var calcSvc = dest.getCalcService();
                for(r = 0; r < rowCount; r++)
                    for(c = 0; c < columnCount; c++)
                    {
                        var expr = expressions.shift();
                        destModel.setFormula(r + toRow,c + toColumn,calcSvc.unparse(expr,r + toRow,c + toColumn));
                        destCalcModel.setFormula(r + toRow,c + toColumn,expr)
                    }
                if(srcSource)
                    srcSource._addCellsToAdjust(fromRow,fromColumn,rowCount,columnCount);
                if(destSource)
                    destSource._addCellsToAdjust(toRow,toColumn,rowCount,columnCount);
                if(srcSource && srcSource._controller)
                    srcSource._controller.adjustFormulasOnMove(srcSource,fromRow,fromColumn,destSource,toRow,toColumn,rowCount,columnCount);
                if(crossSheet === true && destSource && destSource._controller)
                    destSource._controller.adjustFormulasOnMove(srcSource,fromRow,fromColumn,destSource,toRow,toColumn,rowCount,columnCount)
            },
            moveStyle: function(src, srcRow, srcColumn, dest, destRow, destColumn, moveRowCount, moveColumnCount)
            {
                var crossSheet = !(src === dest && src._name === dest._name);
                var colHeader = UI.SheetArea.colHeader,
                    hRowCount,
                    hColumnCount,
                    r,
                    c,
                    style;
                if(srcRow < 0)
                {
                    var fColumn = srcColumn;
                    var tColumn = destColumn;
                    hRowCount = Math.min(src.getRowCount(colHeader),dest.getRowCount(colHeader));
                    hColumnCount = moveColumnCount;
                    if(srcColumn < 0)
                    {
                        fColumn = 0;
                        hColumnCount = src.getColumnCount()
                    }
                    if(destColumn < 0)
                        tColumn = 0;
                    if(crossSheet)
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                style = src.getActualStyle(r,fColumn + c,colHeader);
                                if(!style)
                                    dest.setStyle(r,tColumn + c,null,colHeader);
                                else
                                {
                                    dest.setStyle(r,tColumn + c,staticMembers.cloneObject(style),colHeader);
                                    src.setStyle(r,fColumn + c,null,colHeader)
                                }
                            }
                    else
                    {
                        var savedColumnHeaderStyles = new UI._GcSheetModel(hRowCount,hColumnCount,null);
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                style = src.getActualStyle(r,fColumn + c,colHeader);
                                if(style)
                                    savedColumnHeaderStyles.setValue(r,c,staticMembers.cloneObject(style));
                                src.setStyle(r,fColumn + c,null,colHeader)
                            }
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                                dest.setStyle(r,tColumn + c,savedColumnHeaderStyles.getValue(r,c),colHeader)
                    }
                }
                var rowHeader = UI.SheetArea.rowHeader;
                if(srcColumn < 0)
                {
                    var fRow = srcRow;
                    var tRow = destRow;
                    hRowCount = moveRowCount;
                    hColumnCount = Math.min(src.getColumnCount(rowHeader),dest.getColumnCount(rowHeader));
                    if(srcRow < 0)
                    {
                        fRow = 0;
                        hRowCount = src.getRowCount()
                    }
                    if(destRow < 0)
                        tRow = 0;
                    if(crossSheet)
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                style = src.getActualStyle(fRow + r,c,rowHeader);
                                if(!style)
                                    dest.setStyle(tRow + r,c,null,rowHeader);
                                else
                                {
                                    dest.setStyle(tRow + r,c,staticMembers.cloneObject(style),rowHeader);
                                    src.setStyle(fRow + r,c,null,rowHeader)
                                }
                            }
                    else
                    {
                        var savedRowHeaderStyles = new UI._GcSheetModel(hRowCount,hColumnCount,null);
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                            {
                                style = src.getActualStyle(fRow + r,c,rowHeader);
                                if(style)
                                    savedRowHeaderStyles.setValue(r,c,staticMembers.cloneObject(style));
                                src.setStyle(fRow + r,c,null,rowHeader)
                            }
                        for(r = 0; r < hRowCount; r++)
                            for(c = 0; c < hColumnCount; c++)
                                dest.setStyle(tRow + r,c,savedRowHeaderStyles.getValue(r,c),rowHeader)
                    }
                }
                var fromRow = srcRow;
                var fromColumn = srcColumn;
                var toRow = destRow;
                var toColumn = destColumn;
                var rowCount = moveRowCount;
                var columnCount = moveColumnCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(srcColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destRow < 0)
                    toRow = 0;
                if(destColumn < 0)
                    toColumn = 0;
                if(crossSheet)
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                        {
                            style = src.getActualStyle(fromRow + r,fromColumn + c);
                            if(!style)
                                dest.setStyle(toRow + r,toColumn + c,null);
                            else
                            {
                                dest.setStyle(toRow + r,toColumn + c,staticMembers.cloneObject(style));
                                src.setStyle(fromRow + r,fromColumn + c,null)
                            }
                        }
                else
                {
                    var savedStyles = new UI._GcSheetModel(rowCount,columnCount,null);
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                        {
                            style = src.getActualStyle(fromRow + r,fromColumn + c);
                            if(style)
                                savedStyles.setValue(r,c,staticMembers.cloneObject(style));
                            src.setStyle(fromRow + r,fromColumn + c,null)
                        }
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                            dest.setStyle(toRow + r,toColumn + c,savedStyles.getValue(r,c))
                }
            },
            moveSparkline: function(src, srcRow, srcColumn, dest, destRow, destColumn, moveRowCount, moveColumnCount)
            {
                var fromRow = srcRow;
                var fromColumn = srcColumn;
                var toRow = destRow;
                var toColumn = destColumn;
                var rowCount = moveRowCount;
                var columnCount = moveColumnCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(srcColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destRow < 0)
                    toRow = 0;
                if(destColumn < 0)
                    toColumn = 0;
                var crossSheet = !(src === dest && src._name === dest._name);
                var sparkline;
                if(crossSheet)
                {
                    sparkline = dest._sparklineGroupManager;
                    if(sparkline)
                        sparkline._exMove(src,fromRow,fromColumn,toRow,toColumn,rowCount,columnCount)
                }
                else
                {
                    sparkline = src._sparklineGroupManager;
                    if(sparkline)
                        sparkline._move(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount)
                }
            },
            moveColumnRangeGroup: function(src, srcColumn, dest, destColumn, moveColumnCount)
            {
                var fromColumn = srcColumn;
                var toColumn = destColumn;
                var columnCount = moveColumnCount;
                if(fromColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destColumn < 0)
                    toColumn = 0;
                var crossSheet = !(src === dest && src._name === dest._name);
                if(crossSheet)
                {
                    staticMembers.crossSheetCopyRangeGroup(src.colRangeGroup,fromColumn,dest.colRangeGroup,toColumn,columnCount);
                    if(src.colRangeGroup)
                        src.colRangeGroup.items.clear(fromColumn,columnCount)
                }
                else
                {
                    var columnGroup = src.colRangeGroup;
                    if(columnGroup)
                        columnGroup._move(fromColumn,toColumn,columnCount)
                }
            },
            moveRowRangeGroup: function(src, srcRow, dest, destRow, moveRowCount)
            {
                var fromRow = srcRow;
                var toRow = destRow;
                var rowCount = moveRowCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(destRow < 0)
                    toRow = 0;
                var crossSheet = !(src === dest && src._name === dest._name);
                if(crossSheet)
                {
                    staticMembers.crossSheetCopyRangeGroup(src.rowRangeGroup,fromRow,dest.rowRangeGroup,toRow,rowCount);
                    if(src.rowRangeGroup)
                        src.rowRangeGroup.items.clear(fromRow,rowCount)
                }
                else
                {
                    var rowGroup = src.rowRangeGroup;
                    if(rowGroup)
                        rowGroup._move(fromRow,toRow,rowCount)
                }
            },
            moveSpan: function(src, srcRow, srcColumn, dest, destRow, destColumn, moveRowCount, moveColumnCount)
            {
                var fromRow = srcRow;
                var fromColumn = srcColumn;
                var toRow = destRow;
                var toColumn = destColumn;
                var rowCount = moveRowCount;
                var columnCount = moveColumnCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(fromColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destRow < 0)
                    toRow = 0;
                if(destColumn < 0)
                    toColumn = 0;
                var crossSheet = !(src === dest && src._name === dest._name);
                var SheetArea = UI.SheetArea;
                if(srcRow < 0)
                    if(crossSheet)
                    {
                        staticMembers.crossSheetCopySpans(src._colHeaderSpanModel,-1,fromColumn,dest._colHeaderSpanModel,-1,toColumn,-1,columnCount);
                        if(src._colHeaderSpanModel)
                            src._colHeaderSpanModel.clear(-1,fromColumn,-1,columnCount)
                    }
                    else
                    {
                        var csm = src._getSpanModel(SheetArea.colHeader);
                        if(csm)
                            csm.move(-1,fromColumn,-1,toColumn,-1,columnCount)
                    }
                if(srcColumn < 0)
                    if(crossSheet)
                    {
                        staticMembers.crossSheetCopySpans(src._rowHeaderSpanModel,fromRow,-1,dest._rowHeaderSpanModel,toRow,-1,rowCount,-1);
                        if(src._rowHeaderSpanModel)
                            src._rowHeaderSpanModel.clear(fromRow,-1,rowCount,-1)
                    }
                    else
                    {
                        var rsm = src._getSpanModel(SheetArea.rowHeader);
                        if(rsm)
                            rsm.move(fromRow,-1,toRow,-1,rowCount,-1)
                    }
                if(crossSheet)
                {
                    staticMembers.crossSheetCopySpans(src._spanModel,fromRow,fromColumn,dest._spanModel,toRow,toColumn,rowCount,columnCount);
                    if(src._spanModel)
                        src._spanModel.clear(fromRow,fromColumn,rowCount,columnCount)
                }
                else
                {
                    var sm = src._getSpanModel();
                    if(sm)
                        sm.move(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount)
                }
            },
            moveColumnAxis: function(src, srcColumn, dest, destColumn, moveColumnCount, option)
            {
                var fromColumn = srcColumn;
                var toColumn = destColumn;
                var columnCount = moveColumnCount;
                if(srcColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destColumn < 0)
                    toColumn = 0;
                var colHeader = UI.SheetArea.colHeader;
                for(var c = 0; c < columnCount; c++)
                {
                    var width = src._getActualColumnWidth(c + fromColumn);
                    if(width !== undefined && width !== null)
                    {
                        src.setColumnWidth(c + fromColumn,src.defaults.colWidth);
                        dest.setColumnWidth(c + toColumn,width)
                    }
                    var visible = src.getColumnVisible(c + fromColumn);
                    if(visible !== undefined && visible !== null)
                    {
                        src.setColumnVisible(c + fromColumn,true);
                        dest.setColumnVisible(c + toColumn,visible)
                    }
                    if((option & UI.CopyToOption.Style) > 0)
                    {
                        var style = src.getActualStyle(-1,c + fromColumn);
                        if(!style)
                            dest.setStyle(-1,c + toColumn,null);
                        else
                        {
                            dest.setStyle(-1,c + toColumn,staticMembers.cloneObject(style));
                            src.setStyle(-1,c + fromColumn,null)
                        }
                        style = src.getActualStyle(-1,c + fromColumn,colHeader);
                        if(!style)
                            dest.setStyle(-1,c + toColumn,null,colHeader);
                        else
                        {
                            dest.setStyle(-1,c + toColumn,staticMembers.cloneObject(style),colHeader);
                            src.setStyle(-1,c + fromColumn,null,colHeader)
                        }
                    }
                }
                var colHeaderRowCount = Math.min(src.getRowCount(colHeader),dest.getRowCount(colHeader));
                for(var r = 0; r < colHeaderRowCount; r++)
                {
                    var height = src._getActualRowHeight(r,colHeader);
                    if(height !== undefined && height !== null)
                    {
                        src.setRowHeight(r,src.defaults.colHeaderRowHeight,colHeader);
                        dest.setRowHeight(r,height,colHeader)
                    }
                }
            },
            moveRowAxis: function(src, srcRow, dest, destRow, moveRowCount, option)
            {
                var fromRow = srcRow;
                var toRow = destRow;
                var rowCount = moveRowCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(destRow < 0)
                    toRow = 0;
                var rowHeader = UI.SheetArea.rowHeader;
                for(var r = 0; r < rowCount; r++)
                {
                    var height = src._getActualRowHeight(r + fromRow);
                    if(height !== undefined && height !== null)
                    {
                        src.setRowHeight(r + fromRow,src.defaults.rowHeight);
                        dest.setRowHeight(r + toRow,height)
                    }
                    var visible = src.getRowVisible(r + fromRow);
                    if(visible !== undefined && visible !== null)
                    {
                        src.setRowVisible(r + fromRow,true);
                        dest.setRowVisible(r + toRow,visible)
                    }
                    if((option & UI.CopyToOption.Style) > 0)
                    {
                        var style = src.getActualStyle(r + fromRow,-1);
                        if(!style)
                            dest.setStyle(r + toRow,-1,null);
                        else
                        {
                            dest.setStyle(r + toRow,-1,staticMembers.cloneObject(style));
                            src.setStyle(r + fromRow,-1,null)
                        }
                        style = src.getActualStyle(r + fromRow,-1,rowHeader);
                        if(!style)
                            dest.setStyle(r + toRow,-1,null,rowHeader);
                        else
                        {
                            dest.setStyle(r + toRow,-1,staticMembers.cloneObject(style),rowHeader);
                            src.setStyle(r + fromRow,-1,null,rowHeader)
                        }
                    }
                }
                var rowHeaderColCount = Math.min(src.getColumnCount(rowHeader),dest.getColumnCount(rowHeader));
                for(var c = 0; c < rowHeaderColCount; c++)
                {
                    var width = src._getActualColumnWidth(c,rowHeader);
                    if(width !== undefined && width !== null)
                    {
                        src.setColumnWidth(c,src.defaults.rowHeaderColWidth,rowHeader);
                        dest.setColumnWidth(c,width,rowHeader)
                    }
                }
            },
            moveSheetInfo: function(src, dest, option)
            {
                if(!(src === dest && src._name === dest._name))
                {
                    if((option & UI.CopyToOption.Style) > 0)
                    {
                        var SheetArea = UI.SheetArea;
                        var colHeader = SheetArea.colHeader;
                        var rowHeader = SheetArea.rowHeader;
                        dest.setDefaultStyle(src.getDefaultStyle());
                        dest.setDefaultStyle(src.getDefaultStyle(colHeader),colHeader);
                        dest.setDefaultStyle(src.getDefaultStyle(rowHeader),rowHeader);
                        src.setDefaultStyle(null);
                        src.setDefaultStyle(null,colHeader);
                        src.setDefaultStyle(null,rowHeader)
                    }
                    dest.defaults.colWidth = src.defaults.colWidth;
                    dest.defaults.rowHeight = src.defaults.rowHeight;
                    dest.defaults.rowHeaderColWidth = src.defaults.rowHeaderColWidth;
                    src.defaults.colWidth = 62;
                    src.defaults.rowHeight = 20;
                    src.defaults.rowHeaderColWidth = 40
                }
            },
            moveBindingPath: function(src, srcRow, srcColumn, dest, destRow, destColumn, copyRowCount, copyColumnCount)
            {
                var fromRow = srcRow;
                var fromColumn = srcColumn;
                var toRow = destRow;
                var toColumn = destColumn;
                var rowCount = copyRowCount;
                var columnCount = copyColumnCount;
                if(srcRow < 0)
                {
                    fromRow = 0;
                    rowCount = Math.min(src.getRowCount(),dest.getRowCount())
                }
                if(srcColumn < 0)
                {
                    fromColumn = 0;
                    columnCount = Math.min(src.getColumnCount(),dest.getColumnCount())
                }
                if(destRow < 0)
                    toRow = 0;
                if(destColumn < 0)
                    toColumn = 0;
                var savedValues = [];
                var crossSheet = !(src === dest && src._name === dest._name);
                if(crossSheet)
                {
                    var path;
                    for(var r = 0; r < rowCount; r++)
                        for(var c = 0; c < columnCount; c++)
                        {
                            path = src.getBindingPath(fromRow + r,fromColumn + c);
                            dest.setBindingPath(toRow + r,toColumn + c,path);
                            if(path)
                                savedValues.push({
                                    row: r,
                                    col: c,
                                    value: src.getValue(fromRow + r,fromColumn + c)
                                });
                            src.setBindingPath(fromRow + r,fromColumn + c,null)
                        }
                }
                else
                {
                    var savedPaths = new UI._GcSheetModel(rowCount,columnCount,null);
                    var path;
                    for(var r = 0; r < rowCount; r++)
                        for(var c = 0; c < columnCount; c++)
                        {
                            path = src.getBindingPath(fromRow + r,fromColumn + c);
                            savedPaths.setBindingPath(r,c,path);
                            if(path)
                                savedValues.push({
                                    row: r,
                                    col: c,
                                    value: src.getValue(fromRow + r,fromColumn + c)
                                })
                        }
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                            src.setBindingPath(fromRow + r,fromColumn + c,null);
                    for(var r = 0; r < rowCount; r++)
                        for(var c = 0; c < columnCount; c++)
                            dest.setBindingPath(toRow + r,toColumn + c,savedPaths.getBindingPath(r,c))
                }
                return savedValues
            },
            setRangeText: function(sheet, row, column, data, rowDelimiter, columnDelimiter, cellDelimiter, flags)
            {
                if(!sheet)
                    throw new Error("sheet");
                if(row < -1 || row >= sheet.getRowCount())
                    raiseInvalidRangeException("row",row,"-1",sheet.getRowCount() - 1);
                if(column < -1 || column >= sheet.getColumnCount())
                    raiseInvalidRangeException("column",column,"-1",sheet.getColumnCount() - 1);
                if(data === undefined || data === null || data === "")
                    return;
                if(row === -1)
                    row = 0;
                if(column === -1)
                    column = 0;
                var sheetData = staticMembers.parseText(data,rowDelimiter,columnDelimiter,cellDelimiter);
                if(sheetData && sheetData.length > 0)
                    staticMembers.setSheetData(sheet,row,column,sheetData,flags)
            },
            getRangeText: function(sheet, row, rowCount, column, columnCount, rowDelimiter, columnDelimiter, cellDelimiter, forceCellDelimiter, flags)
            {
                if(!sheet)
                    throw new Error("sheet");
                if(row < -1 || row >= sheet.getRowCount())
                    raiseInvalidRangeException("row",row,"-1",sheet.getRowCount() - 1);
                if(rowCount < -1 || row + rowCount > sheet.getRowCount())
                    raiseInvalidRangeException("rowCount",row,"-1",sheet.getRowCount() - row);
                if(column < -1 || column >= sheet.getColumnCount())
                    raiseInvalidRangeException("column",column,"-1",sheet.getColumnCount() - 1);
                if(columnCount < -1 || column + columnCount > sheet.getColumnCount())
                    raiseInvalidRangeException("columnCount",column,"-1",sheet.getColumnCount() - column);
                var endRow = -1;
                var endColumn = -1;
                if(row === -1 && column === -1 && rowCount === -1 && columnCount === -1)
                {
                    row = 0;
                    column = 0;
                    endRow = sheet.getRowCount() - 1;
                    endColumn = sheet.getColumnCount() - 1
                }
                else
                {
                    if(row === -1)
                        row = 0;
                    if(column === -1)
                        column = 0;
                    if(rowCount === -1)
                        rowCount = sheet.getRowCount() - row;
                    if(columnCount === -1)
                        columnCount = sheet.getColumnCount() - column;
                    endRow = row + rowCount - 1;
                    endColumn = column + columnCount - 1
                }
                if(rowDelimiter === undefined || rowDelimiter === null || rowDelimiter === "")
                    rowDelimiter = "\r\n";
                if(columnDelimiter === undefined || columnDelimiter === null || columnDelimiter === "")
                    columnDelimiter = "\t";
                if(cellDelimiter === undefined || cellDelimiter === null || cellDelimiter === "")
                    cellDelimiter = "\"";
                var sbr = "";
                for(var r = row; r <= endRow; r++)
                {
                    var newLine = true;
                    for(var c = column; c <= endColumn; c++)
                    {
                        if(!newLine)
                            sbr += columnDelimiter;
                        newLine = false;
                        var cellStr = "";
                        var cellData = sheet.getValue(r,c);
                        if(cellData !== undefined && cellData !== null)
                            cellStr = cellData.toString();
                        sbr += cellStr
                    }
                    sbr += rowDelimiter
                }
                return sbr.toString()
            },
            parseText: function(data, rowDelimiter, columnDelimiter, cellDelimiter)
            {
                if(data === undefined || data === null || data === "")
                    return null;
                if(rowDelimiter === undefined || rowDelimiter === null || rowDelimiter === "")
                    rowDelimiter = "\r\n";
                if(columnDelimiter === undefined || columnDelimiter === null || columnDelimiter === "")
                    columnDelimiter = "\t";
                if(cellDelimiter === undefined || cellDelimiter === null || cellDelimiter === "")
                    cellDelimiter = "\"";
                if(!UI.StringHelper.EndsWith(data,rowDelimiter))
                    data += rowDelimiter;
                var sheetData = [],
                    rowData = [];
                var sbr = new UI.StringBuilder;
                var inCell = false;
                var dl = cellDelimiter.length;
                var rdl = rowDelimiter.length;
                var cdl = columnDelimiter.length;
                for(var index = 0; index < data.length; index++)
                {
                    sbr.Append(data[index]);
                    if(sbr.Length() >= dl && cellDelimiter === sbr.ToString(sbr.Length() - dl,dl))
                    {
                        if(inCell && data.length >= index + 1 + dl && cellDelimiter === data.substr(index + 1,dl))
                            index += dl
                    }
                    else if(sbr.Length() >= cdl && columnDelimiter === sbr.ToString(sbr.Length() - cdl,cdl))
                    {
                        sbr.Remove(sbr.Length() - cdl,cdl);
                        rowData.push(sbr.toString());
                        sbr.Remove(0,sbr.Length())
                    }
                    else if(sbr.Length() >= rdl && rowDelimiter === sbr.ToString(sbr.Length() - rdl,rdl))
                    {
                        sbr.Remove(sbr.Length() - rdl,rdl);
                        rowData.push(sbr.toString());
                        sheetData.push(rowData);
                        rowData = [];
                        sbr.Remove(0,sbr.Length())
                    }
                }
                return sheetData
            },
            setSheetData: function(sheet, rowIndex, columnIndex, data, flags)
            {
                var dataRowCount = data.length;
                var dataColumnCount = staticMembers.getMaxLength(data);
                if(dataRowCount === 0 || dataColumnCount === 0)
                    return;
                var opt = new ImportExportOptions(flags);
                opt.fixOptions(sheet);
                var SheetArea = UI.SheetArea;
                var rowHeaderColumnCount = opt.rowHeader ? sheet.getColumnCount(SheetArea.rowHeader) : 0;
                var columnHeaderRowCount = opt.columnHeader ? sheet.getRowCount(SheetArea.colHeader) : 0;
                var columnFooterRowCount = 0;
                dataColumnCount -= rowHeaderColumnCount;
                if(dataColumnCount <= 0)
                    dataColumnCount = 0;
                dataRowCount -= columnHeaderRowCount;
                if(dataRowCount <= 0)
                    columnFooterRowCount = 0;
                dataRowCount -= columnFooterRowCount;
                if(dataRowCount <= 0)
                    dataRowCount = 0;
                if(opt.expandRows && rowIndex + dataRowCount > sheet.getRowCount())
                    sheet.setRowCount(rowIndex + dataRowCount);
                if(opt.expandColumns && columnIndex + dataColumnCount > sheet.getColumnCount())
                    sheet.setColumnCount(columnIndex + dataColumnCount);
                for(var r = 0, sheetRowIndex = 0; r < data.length; r++, sheetRowIndex++)
                {
                    var rowData = data[r];
                    if(rowData.length <= 0)
                        continue;
                    if(columnHeaderRowCount > 0 && r < columnHeaderRowCount)
                        staticMembers.setRowData(sheet,rowData,sheetRowIndex,columnIndex,dataColumnCount,SheetArea.colHeader,opt);
                    else if(dataRowCount > 0 && sheetRowIndex < sheet.getRowCount())
                    {
                        if(r === columnHeaderRowCount)
                            sheetRowIndex = rowIndex;
                        staticMembers.setRowData(sheet,rowData,sheetRowIndex,0,rowHeaderColumnCount,SheetArea.rowHeader,opt);
                        rowData.splice(0,rowHeaderColumnCount);
                        staticMembers.setRowData(sheet,rowData,sheetRowIndex,columnIndex,dataColumnCount,SheetArea.viewport,opt)
                    }
                }
            },
            setRowData: function(sheet, rowData, sheetRowIndex, columnIndex, columnCount, area, opt)
            {
                for(var c = 0, sheetColumnIndex = columnIndex; c < rowData.length; c++, sheetColumnIndex++)
                    if(columnCount > 0 && sheetColumnIndex < sheet.getColumnCount(area))
                        staticMembers.setCellData(sheet,area,sheetRowIndex,sheetColumnIndex,rowData[c],opt)
            },
            setCellData: function(sheet, area, rowIndex, columnIndex, value, opt)
            {
                var setvalue = value;
                var autodisplayformatter = null;
                if(opt.unFormatted === false)
                {
                    var outPara = {value: value};
                    autodisplayformatter = (new UI.GeneralFormatter).GetPreferredDisplayFormatter(value,outPara);
                    setvalue = outPara.value
                }
                if(setvalue === undefined || setvalue === null)
                    sheet.setValue(rowIndex,columnIndex,setvalue,area);
                else if(value !== "")
                    if(opt.formula && value[0] === "=")
                        try
                        {
                            sheet.setFormula(rowIndex,columnIndex,value.substr(1),area)
                        }
                        catch(ex)
                        {
                            sheet.setText(rowIndex,columnIndex,value,area)
                        }
                    else
                    {
                        var style = sheet.getActualStyle(rowIndex,columnIndex,area);
                        if(style)
                            if(opt.unFormatted === false)
                            {
                                if(!style.formatter)
                                {
                                    var autoFormatter = new UI.AutoFormatter(autodisplayformatter);
                                    sheet.getCell(rowIndex,columnIndex,area)._setStyleProperty("_autoFormatter",autoFormatter)
                                }
                                else if(style.formatter.toString() === "@")
                                    setvalue = value.toString()
                            }
                            else if(style.formatter)
                                sheet.getCell(rowIndex,columnIndex,area).formatter(null);
                        sheet.setValue(rowIndex,columnIndex,setvalue,area)
                    }
                else
                    sheet.setValue(rowIndex,columnIndex,null,area)
            },
            getMaxLength: function(data)
            {
                if(data === undefined || data === null)
                    return 0;
                var len = 0;
                for(var i = 0; i < data.length; i++)
                {
                    var list = data[i];
                    len = Math.max(list.length,len)
                }
                return len
            },
            parseCsv: function(text, rowDelimiter, columnDelimiter, cellDelimiter)
            {
                var ret = null;
                var parsedText = staticMembers.parseText(text,rowDelimiter,columnDelimiter,cellDelimiter);
                if(parsedText)
                {
                    var rowCount = parsedText.length;
                    var columnCount = staticMembers.getMaxLength(parsedText);
                    var textArray = [];
                    for(var r = 0; r < rowCount; r++)
                    {
                        textArray[r] = [];
                        for(var c = 0; c < columnCount; c++)
                            if(c < parsedText[r].length)
                                textArray[r][c] = parsedText[r][c];
                            else
                                textArray[r][c] = null
                    }
                    ret = textArray
                }
                return ret
            }
        };
    var regExpKeyPattern = [/\\/g,/\(/g,/\[/g,/\{/g,/\^/g,/\$/g,/\|/g,/\)/g,/\+/g,/\./g];
    var questionMarkHolder = new RegExp("{113E2532-EAF5-444c-A5CB-3D7446971C4D}","g"),
        asteriskHolder = new RegExp("{E21523B3-0F1F-458f-B547-23D25713D0EC}","g");
    function fixFormulaKeyword(searchString, useWildCards, exactMatch)
    {
        for(var i in regExpKeyPattern)
            if(regExpKeyPattern[i])
                searchString = searchString.replace(regExpKeyPattern[i],regExpKeyPattern[i].source);
        if(useWildCards === true)
        {
            searchString = searchString.replace(/~\?/g,questionMarkHolder.source);
            searchString = searchString.replace(/~\*/g,asteriskHolder.source);
            searchString = searchString.replace(/\?/g,".");
            if(exactMatch)
                searchString = searchString.replace(/\*/g,"((.|\\n)+)");
            else
                searchString = searchString.replace(/\*/g,"((.|\\n)*)");
            searchString = searchString.replace(questionMarkHolder,"\\?");
            searchString = searchString.replace(asteriskHolder,"\\*")
        }
        else
        {
            searchString = searchString.replace(/\?/g,"\\?");
            searchString = searchString.replace(/\*/g,"\\*")
        }
        return searchString
    }
    UI.DataContext = function(read, create, update, remove)
    {
        this.read = read;
        this.create = create;
        this.update = update;
        this.remove = remove
    };
    var _gcSheet = ".gcSheet";
    var _gcSheetInternal = ".gcSheetInternal";
    var _mouseDown_gcSheet = "mousedown" + _gcSheet;
    var _mouseMove_gcSheet = "mousemove" + _gcSheet;
    var _mouseUp_gcSheet = "mouseup" + _gcSheet;
    var _mouseOut_gcSheet = "mouseout" + _gcSheet;
    var _dblclick_gcSheet = "dblclick" + _gcSheet;
    var _mouseWheel_gcSheet = "gcmousewheel" + _gcSheet;
    var _resize_gcSheet = "resize" + _gcSheet;
    var _value = "value";
    var _cssLeft = "left",
        _cssTop = "top",
        _cssWidth = "width",
        _cssHeight = "height",
        _cssHidden = "hidden",
        cssVisibility = "visibility";
    var ajax_Type = "POST",
        ajax_DataType = "json",
        ajax_MIME = "application/json;charset=UTF-8";
    var _jsonDateRegExp = new RegExp("^/Date\\(-?\\d+\\)/\\s*$");
    UI.EditorStatus = {
        Ready: 0,
        Enter: 1,
        Edit: 2
    };
    UI.GcSheet = function(name)
    {
        this._init(name)
    };
    UI.GcSheet.prototype = {
        getDataSource: function()
        {
            return this._dataSource
        },
        _isDataView: function(data)
        {
            var wijmo = window.wijmo;
            return wijmo && wijmo.data && wijmo.data.isDataView(data)
        },
        _dataChangedhandler: function(context)
        {
            if(this._updatingDataView)
                return;
            var self = this;
            this._bindToAutoRefresh(function(context)
            {
                if(context)
                {
                    self._dataViewUpdating = true;
                    if(!isNaN(context.length))
                        self.setRowCount(context.length);
                    self._dataViewUpdating = false
                }
            })(context)
        },
        _currentPositionChangedhandler: function(context)
        {
            var self = this;
            this._bindToAutoRefresh(function(context)
            {
                if(self._dataSource && self._dataSource.currentPosition)
                {
                    var pos = self._dataSource.currentPosition();
                    var pos = self._dataSource.currentPosition();
                    if(self._activeRowIndex !== pos)
                        self.setActiveCell(pos,self._activeColIndex)
                }
            })(context)
        },
        _isCellBinding: function(source)
        {
            return UI.CellBindingSource && source instanceof UI.CellBindingSource || source && source.hasOwnProperty("__cellBindingSource__")
        },
        getBindingPath: function(row, col, sheetArea)
        {
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = SheetArea.viewport;
            if(sheetArea === SheetArea.viewport)
            {
                var m = this._getModel(sheetArea);
                if(m)
                    return m.getBindingPath(row,col)
            }
            return null
        },
        setBindingPath: function(row, col, path, sheetArea)
        {
            return this._bindToAutoRefresh(function(row, col, path, sheetArea)
                {
                    var SheetArea = UI.SheetArea;
                    if(sheetArea === undefined || sheetArea === null)
                        sheetArea = SheetArea.viewport;
                    if(sheetArea === SheetArea.viewport)
                    {
                        var m = this._getModel(sheetArea);
                        if(m)
                            m.setBindingPath(row,col,path)
                    }
                    return this
                })(row,col,path,sheetArea)
        },
        _autoGenerateColumnCellType: function(col)
        {
            if(this._dataSource && this._colInfos && this._colInfos[col])
            {
                var item = this._getDataItem(this._dataSource,0);
                if(item)
                {
                    var value = item[this._colInfos[col].name];
                    if(typeof value === const_boolean)
                        this.setCellType(-1,col,new UI.CheckBoxCellType)
                }
            }
        },
        setDataSource: function(data, reset)
        {
            this._bindToAutoRefresh(function(data, reset)
            {
                if(!data || reset)
                    this.reset();
                if(this._dataSource && this._dataSource.dispose)
                    this._dataSource.dispose();
                this._dataSource = data;
                if(data && data.subscribe)
                    data.subscribe(this._dataChangedhandler,this);
                if(data && data.currentPosition && data.currentPosition.subscribe)
                    data.currentPosition.subscribe(this._currentPositionChangedhandler,this);
                if(this._isDataView(data))
                {
                    this.setRowCount(data.count());
                    if(this.autoGenerateColumns)
                    {
                        var ps = data.getProperties();
                        if(ps && ps.length > 0)
                        {
                            this.setColumnCount(ps.length);
                            this._colInfos = {};
                            for(var i = 0; i < ps.length; i++)
                                this._colInfos[i] = {name: ps[i].name}
                        }
                    }
                    else if(!this._colInfos)
                        this._colInfos = {}
                }
                else if(window.ko && window.ko.isObservable(data))
                {
                    this.setRowCount(data().length);
                    if(this.autoGenerateColumns)
                    {
                        var ps = this._getProperties(data()[0]);
                        if(ps && ps.length > 0)
                        {
                            this.setColumnCount(ps.length);
                            this._colInfos = {};
                            for(var i = 0; i < ps.length; i++)
                                this._colInfos[i] = {name: ps[i]}
                        }
                    }
                    else if(!this._colInfos)
                        this._colInfos = {}
                }
                else if(this._isCellBinding(data));
                else if(data && !isNaN(data.length))
                {
                    this.setRowCount(data.length);
                    if(this.autoGenerateColumns)
                    {
                        var ps = this._getProperties(data[0]);
                        if(ps && ps.length > 0)
                        {
                            this.setColumnCount(ps.length);
                            this._colInfos = {};
                            for(var i = 0; i < ps.length; i++)
                                this._colInfos[i] = {name: ps[i]}
                        }
                    }
                    else if(!this._colInfos)
                        this._colInfos = {}
                }
                this.doDataItemChanged()
            })(data,reset)
        },
        bindColumn: function(index, column)
        {
            var oldState = this.isPaintSuspended();
            this.isPaintSuspended(true);
            try
            {
                if(typeof column === const_string)
                    column = {name: column};
                if(!this._colInfos)
                    this._colInfos = {};
                this._colInfos[index] = column;
                if(column && column.formatter)
                    this.setFormatter(-1,index,column.formatter);
                if(column && column.cellType)
                    this.setCellType(-1,index,column.cellType)
            }
            finally
            {
                this.isPaintSuspended(oldState)
            }
        },
        bindColumns: function(columns)
        {
            var oldState = this.isPaintSuspended();
            this.isPaintSuspended(true);
            try
            {
                this._colInfos = {};
                if(columns)
                {
                    if(!isNaN(columns.length))
                        this.setColumnCount(columns.length);
                    for(var i = 0; i < columns.length; i++)
                    {
                        var colInfo = columns[i];
                        if(typeof colInfo === const_string)
                            colInfo = {name: colInfo};
                        this._colInfos[i] = colInfo;
                        if(colInfo && colInfo.formatter)
                            this.setFormatter(-1,i,colInfo.formatter);
                        if(colInfo && colInfo.cellType)
                            this.setCellType(-1,i,colInfo.cellType)
                    }
                }
            }
            finally
            {
                this.isPaintSuspended(oldState)
            }
        },
        getDataContext: function()
        {
            return this.dataContext
        },
        setDataContext: function(dataContext)
        {
            this.dataContext = dataContext;
            this.load()
        },
        _getDataLength: function(dataSource)
        {
            var ds = dataSource;
            if(ds)
            {
                var ko = window.ko;
                if(this._isDataView(ds))
                    return ds.count();
                else if(ko && ko.isObservable(ds))
                    return ds().length;
                else if(this._isCellBinding(ds))
                    return 0;
                else if(ds && !isNaN(ds.length))
                    return ds.length
            }
            return 0
        },
        _getDataItemType: function(dataSource)
        {
            var ds = dataSource;
            if(ds)
            {
                var dataLength = this._getDataLength(ds);
                for(var i = 0; i < dataLength; i++)
                {
                    var dataItem = this._getDataItem(ds,i);
                    if(dataItem)
                        return dataItem.constructor
                }
            }
            return null
        },
        _getDataItem: function(dataSource, row)
        {
            var ds = dataSource;
            var ko = window.ko;
            if(this._isDataView(ds))
                return ds.item(row);
            else if(ko && ko.isObservable(ds))
                return ds()[row];
            else if(this._isCellBinding(ds))
                return null;
            else
                return ds[row];
            return null
        },
        getDataItem: function(row)
        {
            var ds = this.getDataSource();
            if(!ds && this._getDataLength(ds) === 0)
                return null;
            var cc = this.getColumnCount();
            var t = {};
            var rowItem = this._getDataItem(ds,row);
            if(rowItem)
                for(var x in rowItem)
                    if(rowItem.hasOwnProperty(x) && typeof x !== const_function)
                        t[x] = rowItem[x];
            for(var i = 0; i < cc; i++)
            {
                var ci = this._colInfos[i];
                if(ci && ci.name && ci.name.length > 0)
                    t[ci.name] = this.getValue(row,i)
            }
            return t
        },
        getDirtyRows: function()
        {
            var rows = [];
            var dirtyNodes = this._dataModel.dirtyNodes;
            if(dirtyNodes)
                for(var t in dirtyNodes)
                    if(dirtyNodes.hasOwnProperty(t) && dirtyNodes[t] && dirtyNodes[t].rs === "e")
                        rows.push({
                            row: t,
                            item: this.getDataItem(t),
                            originalItem: !this._dataSource ? null : this._getDataItem(this._dataSource,t)
                        });
            return rows
        },
        getInsertRows: function()
        {
            var rows = [];
            var dirtyNodes = this._dataModel.dirtyNodes;
            if(dirtyNodes)
                for(var t in dirtyNodes)
                    if(dirtyNodes.hasOwnProperty(t) && dirtyNodes[t] && dirtyNodes[t].rs === "n")
                        rows.push({
                            row: t,
                            item: this.getDataItem(t)
                        });
            return rows
        },
        _getDeleteRows: function()
        {
            var rows = [];
            if(this.deletedRows)
                $.each(this.deletedRows,function(i, v)
                {
                    if(typeof v !== const_function)
                        if(v && v.data !== undefined && v.data !== null)
                            rows.push({
                                row: i,
                                originalItem: v.data
                            })
                });
            return rows
        },
        hasPendingChanges: function()
        {
            var dirtyNodes = this._dataModel.dirtyNodes,
                t;
            if(dirtyNodes)
                for(t in dirtyNodes)
                    if(dirtyNodes[t] && (dirtyNodes[t].rs === "e" || dirtyNodes[t].rs === "n"))
                        return true;
            if(this.deletedRows)
                for(t in this.deletedRows)
                    if(this.deletedRows[t])
                    {
                        var item = this.deletedRows[t];
                        if(typeof item !== const_function)
                            return true
                    }
            return false
        },
        clearPendingChanges: function()
        {
            this._dataModel.dirtyNodes = {};
            this.deletedRows = [];
            function clearDirty(items)
            {
                $.each(items,function(i, v)
                {
                    if(typeof i !== const_function && v && v.dirty)
                        v.dirty = null
                })
            }
            clearDirty(this._rowInfos);
            clearDirty(this._colInfos)
        },
        load: function()
        {
            if(!this.dataContext || !this.dataContext.read)
                return;
            var self = this;
            this._dataReceivedDelegate = function(event)
            {
                return self._onDataReceived(event)
            };
            $.getJSON(this.dataContext.read,this._dataReceivedDelegate)
        },
        updateRecord: function()
        {
            if(!this.autoUpdate || !this.dataContext || !this.dataContext.update || this.dataContext.update === "")
                return;
            var ds = this.getDataSource();
            if(!ds || this._getDataLength(ds) === 0)
                return;
            var self = this;
            this._dataUpdatedDelegate = function(event)
            {
                return self._onDataUpdated(event)
            };
            var n = this._activeRowIndex;
            if(n >= 0)
            {
                var item = this.getDataItem(n);
                var url = this.dataContext.update;
                if(this._dataModel.dataTable && this._dataModel.dataTable[n] && this._dataModel.dataTable[n].rs === "n")
                    url = this.dataContext.create;
                $.ajax({
                    url: url,
                    type: ajax_Type,
                    data: JSON.stringify(item),
                    dataType: ajax_DataType,
                    contentType: ajax_MIME,
                    success: this._dataUpdatedDelegate
                })
            }
        },
        _onDataUpdated: function(event){},
        _onDataReceived: function(event)
        {
            if(!event)
                return;
            var data = event.data;
            if(!data)
                return;
            this.setDataSource(data);
            if(!isNaN(event.total))
                this.setRowCount(event.total);
            if(window.localStorage)
            {
                var cc = this.getColumnCount();
                for(var i = 0; i < cc; i++)
                    if(window.localStorage["col," + i])
                        this.setColumnWidth(i,parseInt(window.localStorage["col," + i],10))
            }
            if(this.parent)
                this.parent._doResize();
            this.invalidateLayout();
            this.repaint()
        },
        _getProperties: function(t)
        {
            var r = [];
            if(t)
                for(var n in t)
                    if(typeof t[n] !== const_function)
                        r.push(n);
                    else if(typeof ko !== const_undefined && ko.isObservable(t[n]))
                        r.push(n);
            return r
        },
        referenceStyle: function(value)
        {
            var rs = UI.ReferenceStyle;
            if(arguments.length === 0)
                return this.getCalcService().useR1C1 ? rs.R1C1 : rs.A1;
            this.getCalcService().useR1C1 = value === rs.R1C1;
            return this
        },
        recalcAll: function()
        {
            var calcService = this.getCalcService();
            calcService.recalculateAll()
        },
        _recalcCell: function(model, row, col)
        {
            var calcService = this.getCalcService();
            calcService.recalculate({
                target: this,
                model: model
            },row,col)
        },
        getCalcService: function()
        {
            if(!this.calcService)
                this.calcService = new GrapeCity.Calc.Service(this);
            return this.calcService
        },
        _getCalcContexts: function()
        {
            var t = [];
            var vpModel = this._getModel();
            t.push({
                name: this._name,
                target: this,
                refModel: vpModel
            });
            return t
        },
        updateCalcContexts: function()
        {
            var calcService = this.getCalcService();
            calcService.contextChanged()
        },
        suspendCalcService: function()
        {
            if(this.calcService)
                this.calcService.suspend()
        },
        resumeCalcService: function()
        {
            if(this.calcService)
                this.calcService.resume()
        },
        _getSheetSource: function(sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null || sheetArea === UI.SheetArea.viewport)
                return this._vpSheetSource;
            return null
        },
        getCustomFunction: function(name)
        {
            if(this._functions && name && name !== '')
                return this._functions[name.toUpperCase()];
            return null
        },
        addCustomFunction: function(fn)
        {
            if(!fn || !(fn instanceof GrapeCity.Calc.Functions.Function))
                throw new Error("invalid custom function");
            var fnName = fn.name.toUpperCase();
            if(!this._functions)
                this._functions = {};
            this._functions[fnName] = fn;
            this.recalcAll()
        },
        removeCustomFunction: function(name)
        {
            if(this._functions && name && name !== '')
            {
                name = name.toUpperCase();
                if(this._functions.hasOwnProperty(name))
                {
                    delete this._functions[name];
                    this.recalcAll()
                }
            }
        },
        clearCustomFunctions: function()
        {
            if(this._functions)
            {
                delete this._functions;
                this.recalcAll()
            }
        },
        _findCustomFunction: function(name)
        {
            if(!name || name === '')
                return null;
            var fn = this.getCustomFunction(name);
            if(!fn && this.parent && this.parent.getCustomFunction)
                fn = this.parent.getCustomFunction(name);
            return fn
        },
        getCustomName: function(name)
        {
            return this._names ? this._names[name] : null
        },
        addCustomName: function(name, formula, baseRow, baseCol)
        {
            if(!name || name === '' || !formula || formula === '')
                throw new Error("invalid custom name");
            if(!this._names)
                this._names = {};
            var calcService = this.getCalcService();
            var expr = calcService.parse(formula,baseRow,baseCol);
            this._names[name] = new UI.NameInfo(name,expr,baseRow,baseCol);
            var nameCalc = this._getSheetSource()._getNameCalc(name,true);
            if(nameCalc)
            {
                nameCalc.updateListening(true,true);
                nameCalc.addToDirty()
            }
            this.recalcAll()
        },
        removeCustomName: function(name)
        {
            if(this._names && name && name !== '' && this._names.hasOwnProperty(name))
            {
                delete this._names[name];
                var nameCalc = this._getSheetSource()._getNameCalc(name);
                if(nameCalc)
                {
                    nameCalc.updateListening(true,false);
                    nameCalc.addToDirty()
                }
                this.recalcAll()
            }
        },
        clearCustomNames: function()
        {
            if(this._names)
            {
                var names = this._names;
                delete this._names;
                var sheetSource = this._getSheetSource();
                for(var i = 0; i < names.length; i++)
                {
                    var nameCalc = sheetSource._getNameCalc(names[i]);
                    if(nameCalc)
                    {
                        nameCalc.updateListening(true,false);
                        nameCalc.addToDirty()
                    }
                }
                this.recalcAll()
            }
        },
        _findCustomName: function(name)
        {
            if(!name || name === '')
                return null;
            var cn = this.getCustomName(name);
            if(!cn && this.parent && this.parent.getCustomName)
                cn = this.parent.getCustomName(name);
            return cn
        },
        setFormula: function(row, col, value, sheetArea)
        {
            var SheetArea = UI.SheetArea;
            if(sheetArea === SheetArea.colHeader || sheetArea === SheetArea.rowHeader)
                return;
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = SheetArea.viewport;
            var E = UI.Events;
            if(row < 0 || row >= this.getRowCount() || col < 0 || col >= this.getColumnCount())
            {
                this._trigger(E.InvalidOperation,{
                    sheet: this,
                    sheetName: this._name,
                    message: "Index out of range!"
                });
                return
            }
            var sheetSource = this._getSheetSource();
            if(sheetSource)
            {
                sheetSource.setFormula(row,col,value);
                if(this._conditionalFormats)
                    this._conditionalFormats._clearCache();
                if(this._eventHandler._eventSuspended < 1)
                    this._trigger(E.CellChanged,{
                        sheet: this,
                        sheetName: this._name,
                        row: row,
                        col: col,
                        sheetArea: sheetArea,
                        propertyName: "formula"
                    })
            }
        },
        getFormula: function(row, col, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null || sheetArea === UI.SheetArea.viewport)
            {
                var sheetSource = this._getSheetSource();
                if(sheetSource)
                    return sheetSource.getFormula(row,col)
            }
            return null
        },
        hasFormula: function(row, col, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null || sheetArea === UI.SheetArea.viewport)
            {
                var sheetSource = this._getSheetSource();
                if(sheetSource)
                    return sheetSource.hasFormula(row,col)
            }
            return false
        },
        _findFormulas: function(row, column, rowCount, columnCount){},
        _rebuildCalcNodes: function()
        {
            var calcModel = this._getCalcModel();
            if(!calcModel)
                return;
            var sheetSource = this._getSheetSource();
            if(!sheetSource)
                return;
            var row = calcModel.nextNonNullRow(-1);
            var rowCount = calcModel.getRowCount();
            var colCount = calcModel.getColumnCount();
            while(0 <= row && row < rowCount)
            {
                var col = calcModel.nextNonNullColumn(row,-1);
                while(0 <= col && col < colCount)
                {
                    var cellCalc = sheetSource._getCellCalc(row,col);
                    if(cellCalc)
                    {
                        cellCalc.stopListening();
                        cellCalc.startListening()
                    }
                    col = calcModel.nextNonNullColumn(row,col)
                }
                row = calcModel.nextNonNullRow(row)
            }
        },
        _name: "",
        defaults: null,
        gridline: null,
        borderColor: "black",
        borderWidth: 1,
        _zoomFactor: 1.0,
        frozenRowCount: 0,
        frozenColCount: 0,
        rowHeaderVisible: true,
        colHeaderVisible: true,
        rowHeaderAutoText: UI.HeaderAutoText.numbers,
        colHeaderAutoText: UI.HeaderAutoText.letters,
        rowHeaderAutoTextIndex: -1,
        colHeaderAutoTextIndex: -1,
        rowRangeGroup: null,
        colRangeGroup: null,
        _activeRowIndex: 0,
        _activeColIndex: 0,
        activeRowViewportIndex: 0,
        activeColViewportIndex: 0,
        _allowCellOverflow: false,
        maxCellOverflowDistance: 100,
        showEditingLocator: true,
        keyMap: null,
        isProtected: false,
        _allowUndo: true,
        _paintSuspended: false,
        _layoutSuspended: 0,
        checkingChanges: false,
        autoGenerateColumns: true,
        _allowEditorReservedLocations: true,
        _sheetTabColor: null,
        _frozenlineColor: "black",
        toJSON: function()
        {
            var sheet = this;
            return{
                    name: sheet._name,
                    defaults: sheet.defaults,
                    columns: sheet._colInfos,
                    rows: sheet._rowInfos,
                    autoGenerateColumns: sheet.autoGenerateColumns,
                    dataSource: sheet._dataSource,
                    frozenRowCount: sheet.frozenRowCount,
                    frozenColCount: sheet.frozenColCount,
                    rowCount: sheet.getRowCount(),
                    columnCount: sheet.getColumnCount(),
                    data: sheet._dataModel.toJSON(),
                    sparklineGroupManager: sheet._sparklineGroupManager.toJSON(),
                    spans: sheet._spanModel.toJSON(),
                    selections: sheet._selectionModel.toJSON(),
                    activeRow: sheet._activeRowIndex,
                    activeCol: sheet._activeColIndex,
                    gridline: sheet.gridline,
                    allowCellOverflow: sheet._allowCellOverflow,
                    referenceStyle: sheet.referenceStyle(),
                    _zoomFactor: sheet._zoomFactor,
                    theme: sheet.currentTheme(),
                    showRowRangeGroup: sheet._showRowRangeGroup,
                    showColumnRangeGroup: sheet._showColumnRangeGroup,
                    rowRangeGroup: sheet.rowRangeGroup.toJSON(),
                    colRangeGroup: sheet.colRangeGroup.toJSON(),
                    rowFilter: sheet._rowFilter ? sheet._rowFilter.toJSON() : undefined,
                    conditionalFormats: sheet.getConditionalFormats().toJSON(),
                    sheetTabColor: sheet._sheetTabColor,
                    frozenlineColor: sheet._frozenlineColor,
                    rowHeaderAutoText: sheet.rowHeaderAutoText,
                    colHeaderAutoText: sheet.colHeaderAutoText,
                    rowHeaderAutoTextIndex: sheet.rowHeaderAutoTextIndex,
                    colHeaderAutoTextIndex: sheet.colHeaderAutoTextIndex,
                    rowHeaderVisible: sheet.rowHeaderVisible,
                    colHeaderVisible: sheet.colHeaderVisible,
                    rowHeaderColCount: sheet.getColumnCount(UI.SheetArea.rowHeader),
                    colHeaderRowCount: sheet.getRowCount(UI.SheetArea.colHeader),
                    rowHeaderData: sheet._rowHeaderModel.toJSON(),
                    colHeaderData: sheet._colHeaderModel.toJSON(),
                    rowHeaderSpan: sheet._rowHeaderSpanModel.toJSON(),
                    colHeaderSpan: sheet._colHeaderSpanModel.toJSON(),
                    rowHeaderColInfos: sheet._rowHeaderColInfos,
                    colHeaderRowInfos: sheet._colHeaderRowInfos,
                    clipBoardOptions: sheet._clipBoardOptions,
                    isProtected: sheet.isProtected,
                    borderColor: sheet.borderColor,
                    borderWidth: sheet.borderWidth,
                    allowDragDrop: sheet._allowDragDrop,
                    allowDragFill: sheet._allowDragFill,
                    allowUndo: sheet._allowUndo,
                    allowEditorReservedLocations: sheet._allowEditorReservedLocations
                }
        },
        fromJSON: function(sheetSettings)
        {
            if(!sheetSettings)
                return;
            var oldState = this.isPaintSuspended();
            this.isPaintSuspended(true);
            try
            {
                var sheet = this;
                var rowCount = sheetSettings.rowCount;
                var columnCount = sheetSettings.columnCount;
                sheet._name = sheetSettings.name;
                if(sheetSettings.defaults)
                    sheet.defaults = sheetSettings.defaults;
                sheet.setRowCount(rowCount);
                sheet.setColumnCount(columnCount);
                sheet.setActiveCell(sheetSettings.activeRow,sheetSettings.activeCol);
                sheet.frozenRowCount = sheetSettings.frozenRowCount;
                sheet.frozenColCount = sheetSettings.frozenColCount;
                sheet.gridline = sheetSettings.gridline;
                sheet._allowCellOverflow = sheetSettings.allowCellOverflow;
                if(typeof sheetSettings.autoGenerateColumns !== const_undefined)
                    sheet.autoGenerateColumns = sheetSettings.autoGenerateColumns;
                if(sheetSettings.dataSource)
                    sheet._dataSource = sheetSettings.dataSource;
                if(sheetSettings.rows)
                {
                    sheet._rowInfos = sheetSettings.rows;
                    sheet._rowInfos.length = rowCount
                }
                if(sheetSettings.columns)
                {
                    sheet._colInfos = sheetSettings.columns;
                    sheet._colInfos.length = columnCount
                }
                if(sheetSettings.colStyles)
                    for(var t in sheetSettings.colStyles)
                        if(!isNaN(t))
                        {
                            var c = parseInt(t,10);
                            sheet.setStyle(-1,c,sheetSettings.colStyles[t],GrapeCity.UI.SheetArea.viewport)
                        }
                if(sheetSettings.rowStyles)
                    for(var t in sheetSettings.rowStyles)
                        if(!isNaN(t))
                        {
                            var r = parseInt(t,10);
                            sheet.setStyle(r,-1,sheetSettings.rowStyles[t],GrapeCity.UI.SheetArea.viewport)
                        }
                if(typeof sheetSettings.rowHeaderAutoText !== const_undefined)
                    sheet.rowHeaderAutoText = sheetSettings.rowHeaderAutoText;
                if(typeof sheetSettings.colHeaderAutoText !== const_undefined)
                    sheet.colHeaderAutoText = sheetSettings.colHeaderAutoText;
                if(typeof sheetSettings.rowHeaderAutoTextIndex !== const_undefined)
                    sheet.rowHeaderAutoTextIndex = sheetSettings.rowHeaderAutoTextIndex;
                if(typeof sheetSettings.colHeaderAutoTextIndex !== const_undefined)
                    sheet.colHeaderAutoTextIndex = sheetSettings.colHeaderAutoTextIndex;
                if(typeof sheetSettings.rowHeaderVisible !== const_undefined)
                    sheet.rowHeaderVisible = sheetSettings.rowHeaderVisible;
                if(typeof sheetSettings.colHeaderVisible !== const_undefined)
                    sheet.colHeaderVisible = sheetSettings.colHeaderVisible;
                if(typeof sheetSettings.rowHeaderColCount !== const_undefined)
                    sheet.setColumnCount(sheetSettings.rowHeaderColCount,UI.SheetArea.rowHeader);
                if(typeof sheetSettings.colHeaderRowCount !== const_undefined)
                    sheet.setRowCount(sheetSettings.colHeaderRowCount,UI.SheetArea.colHeader);
                if(sheetSettings.rowHeaderData)
                    sheet._rowHeaderModel.fromJSON(sheetSettings.rowHeaderData);
                if(sheetSettings.colHeaderData)
                    sheet._colHeaderModel.fromJSON(sheetSettings.colHeaderData);
                if(sheetSettings.rowHeaderSpan)
                    sheet._rowHeaderSpanModel.fromJSON(sheetSettings.rowHeaderSpan);
                if(sheetSettings.colHeaderSpan)
                    sheet._colHeaderSpanModel.fromJSON(sheetSettings.colHeaderSpan);
                if(sheetSettings.rowHeaderColInfos)
                    sheet._rowHeaderColInfos = sheetSettings.rowHeaderColInfos;
                if(sheetSettings.colHeaderRowInfos)
                    sheet._colHeaderRowInfos = sheetSettings.colHeaderRowInfos;
                if(typeof sheetSettings.referenceStyle !== const_undefined)
                    sheet.referenceStyle(sheetSettings.referenceStyle);
                if(typeof sheetSettings._zoomFactor !== const_undefined)
                    sheet._zoomFactor = sheetSettings._zoomFactor;
                if(sheetSettings.spans)
                    sheet._spanModel.fromJSON(sheetSettings.spans);
                if(sheetSettings.selections)
                {
                    sheet._clearSelectionImp();
                    sheet._selectionModel.fromJSON(sheetSettings.selections)
                }
                if(sheetSettings.data)
                {
                    sheet._dataModel.fromJSON(sheetSettings.data);
                    var data = sheetSettings.data.dataTable;
                    if(data)
                        for(var j in data)
                            if(typeof j !== const_function)
                                for(var k in data[j])
                                    if(typeof k !== const_function)
                                    {
                                        var node = data[j][k];
                                        var formula = node.formula;
                                        if(formula)
                                            sheet.setFormula(parseInt(j,10),parseInt(k,10),formula)
                                    }
                }
                if(sheetSettings.sparklineGroupManager)
                {
                    sheet._sparklineGroupManager.fromJSON(sheetSettings.sparklineGroupManager);
                    var groups = sheet._sparklineGroupManager.groups();
                    var model = sheet._getModel(),
                        sparklines,
                        sparkline;
                    for(var i = 0; i < groups.length; i++)
                    {
                        sparklines = groups[i]._innerList;
                        for(var j = 0; j < sparklines.length; j++)
                        {
                            sparkline = sparklines[j];
                            model.setSparkline(sparkline.row,sparkline.column,sparkline)
                        }
                    }
                }
                if(sheetSettings.theme)
                {
                    var st = sheetSettings.theme;
                    if(typeof st === "string" || typeof st === const_undefined)
                        sheet.currentTheme(st);
                    else
                    {
                        var tc = st._themeColor;
                        var BACKCOLOR1 = 0,
                            BACKCOLOR2 = 1,
                            TEXTCOLOR1 = 2,
                            TEXTCOLOR2 = 3,
                            ACCENT1 = 4,
                            ACCENT2 = 5,
                            ACCENT3 = 6,
                            ACCENT4 = 7,
                            ACCENT5 = 8,
                            ACCENT6 = 9,
                            HYPERLINK = 10,
                            FHYPERLINK = 11;
                        var text1 = tc._colorList[TEXTCOLOR1],
                            text2 = tc._colorList[TEXTCOLOR2],
                            background1 = tc._colorList[BACKCOLOR1],
                            background2 = tc._colorList[BACKCOLOR2],
                            accent1 = tc._colorList[ACCENT1],
                            accent2 = tc._colorList[ACCENT2],
                            accent3 = tc._colorList[ACCENT3],
                            accent4 = tc._colorList[ACCENT4],
                            accent5 = tc._colorList[ACCENT5],
                            accent6 = tc._colorList[ACCENT6],
                            hyperlink = tc._colorList[HYPERLINK],
                            followedHyperlink = tc._colorList[FHYPERLINK];
                        var themeColor = new UI.ThemeColor(st._name,new UI._Color(text1.a,text1.r,text1.g,text1.b),new UI._Color(text2.a,text2.r,text2.g,text2.b),new UI._Color(background1.a,background1.r,background1.g,background1.b),new UI._Color(background2.a,background2.r,background2.g,background2.b),new UI._Color(accent1.a,accent1.r,accent1.g,accent1.b),new UI._Color(accent2.a,accent2.r,accent2.g,accent2.b),new UI._Color(accent3.a,accent3.r,accent3.g,accent3.b),new UI._Color(accent4.a,accent4.r,accent4.g,accent4.b),new UI._Color(accent5.a,accent5.r,accent5.g,accent5.b),new UI._Color(accent6.a,accent6.r,accent6.g,accent6.b),new UI._Color(hyperlink.a,hyperlink.r,hyperlink.g,hyperlink.b),new UI._Color(followedHyperlink.a,followedHyperlink.r,followedHyperlink.g,followedHyperlink.b));
                        var theme = new UI.SpreadTheme(st._name,themeColor,st._headingFont,st._bodyFont);
                        sheet.currentTheme(theme)
                    }
                }
                if(typeof sheetSettings.showRowRangeGroup !== const_undefined)
                    sheet._showRowRangeGroup = sheetSettings.showRowRangeGroup;
                if(typeof sheetSettings.showColumnRangeGroup !== const_undefined)
                    sheet._showColumnRangeGroup = sheetSettings.showColumnRangeGroup;
                if(sheetSettings.rowRangeGroup)
                    sheet.rowRangeGroup.fromJSON(sheetSettings.rowRangeGroup);
                if(sheetSettings.colRangeGroup)
                    sheet.colRangeGroup.fromJSON(sheetSettings.colRangeGroup);
                if(sheetSettings.rowFilter)
                {
                    var range = sheetSettings.rowFilter.range;
                    var drf = new UI.HideRowFilter(new UI.Range(range.row,range.col,range.rowCount,range.colCount));
                    sheet.rowFilter(drf);
                    drf.fromJSON(sheetSettings.rowFilter)
                }
                else
                    sheet.rowFilter(null);
                if(sheetSettings.conditionalFormats)
                    sheet.getConditionalFormats().fromJSON(sheetSettings.conditionalFormats);
                if(sheetSettings.sheetTabColor)
                    sheet.sheetTabColor(sheetSettings.sheetTabColor);
                if(sheetSettings.frozenlineColor)
                    sheet.frozenlineColor(sheetSettings.frozenlineColor);
                if(typeof sheetSettings.clipBoardOptions !== const_undefined)
                    sheet._clipBoardOptions = sheetSettings.clipBoardOptions;
                if(typeof sheetSettings.isProtected !== const_undefined)
                    sheet.isProtected = sheetSettings.isProtected;
                if(sheetSettings.borderColor)
                    sheet.borderColor = sheetSettings.borderColor;
                if(typeof sheetSettings.borderWidth !== const_undefined)
                    sheet.borderWidth = sheetSettings.borderWidth;
                if(typeof sheetSettings.allowDragDrop !== const_undefined)
                    sheet._allowDragDrop = sheetSettings.allowDragDrop;
                if(typeof sheetSettings.allowDragFill !== const_undefined)
                    sheet._allowDragFill = sheetSettings.allowDragFill;
                if(typeof sheetSettings.allowUndo !== const_undefined)
                    sheet.allowUndo(sheetSettings.allowUndo);
                if(typeof sheetSettings.allowEditorReservedLocations !== const_undefined)
                    sheet.allowEditorReservedLocations(sheetSettings.allowEditorReservedLocations)
            }
            finally
            {
                this.isPaintSuspended(oldState)
            }
        },
        isPaintSuspended: function(value)
        {
            if(arguments.length === 0)
                return this._paintSuspended;
            else
            {
                if(this._paintSuspended !== value)
                {
                    this._paintSuspended = value;
                    if(!value && this._layoutSuspended <= 0)
                    {
                        this.invalidateLayout();
                        this.repaint()
                    }
                }
                return this
            }
        },
        getName: function()
        {
            return this._name
        },
        setName: function(name)
        {
            if(!this._isValidSheetName(name))
                throw'NotSupportedException';
            this._name = name;
            if(this.parent && this.parent._doTabHSResize)
                this.parent._doTabHSResize()
        },
        allowCellOverflow: function(value)
        {
            if(arguments.length === 0)
                return this._allowCellOverflow;
            var self = this;
            return this._bindToAutoRefresh(function(value)
                {
                    self._allowCellOverflow = value;
                    return self
                })(value)
        },
        allowUndo: function(value)
        {
            if(arguments.length === 0)
                return this._allowUndo;
            this._allowUndo = value;
            var undoManager = this.undoManager();
            if(undoManager)
                undoManager._allowUndo = value;
            return this
        },
        allowEditorReservedLocations: function(value)
        {
            if(arguments.length === 0)
                return this._allowEditorReservedLocations;
            this._allowEditorReservedLocations = value;
            return this
        },
        sheetTabColor: function(value)
        {
            if(arguments.length === 0)
                return this._sheetTabColor;
            this._sheetTabColor = value;
            if(this.parent && this.parent._doTabHSResize)
                this.parent._doTabHSResize();
            return this
        },
        frozenlineColor: function(value)
        {
            if(arguments.length === 0)
                return this._frozenlineColor;
            var self = this;
            return this._bindToAutoRefresh(function(value)
                {
                    self._frozenlineColor = value;
                    return self
                })(value)
        },
        addRows: function(row, count)
        {
            if(count <= 0)
                return;
            var ds = this.getDataSource();
            if(ds && this._isDataView(ds))
            {
                if(!ds.canAdd || !ds.canAdd())
                    return;
                row = this.getRowCount()
            }
            var oldState = this.isPaintSuspended();
            this.isPaintSuspended(true);
            try
            {
                if(row < 0 || row > this.getRowCount())
                    row = this.getRowCount();
                var sheetSource = this._getSheetSource();
                if(sheetSource)
                    sheetSource.onBeforeAddRemoveRows(row);
                var m = this._getModel();
                m.addRows(row,count);
                if(ds)
                {
                    var itemType = this._getDataItemType(ds);
                    if(this._isDataView(ds))
                    {
                        if(!this._dataViewUpdating)
                        {
                            this._updatingDataView = true;
                            if(itemType)
                            {
                                for(var i = 0; i < count; i++)
                                    ds.add(new itemType);
                                ds.commitEdit()
                            }
                            this._updatingDataView = false
                        }
                    }
                    else if(this._isCellBinding(ds));
                    else
                        for(var i = 0; i < count; i++)
                            if(itemType)
                                ds.splice(row,0,new itemType);
                            else
                                ds.splice(row,0,null)
                }
                var calc = this._getCalcModel();
                calc.addRows(row,count);
                var SheetArea = UI.SheetArea;
                var rm = this._getModel(SheetArea.rowHeader);
                rm.addRows(row,count);
                m.addElements(this._rowInfos,this.getRowCount(),row,count);
                var sm = this._getSpanModel(SheetArea.rowHeader);
                sm.addRows(row,count);
                sm = this._getSpanModel();
                sm.addRows(row,count);
                if(this._rowFilter)
                    this._rowFilter._addRows(row,count);
                if(this.rowRangeGroup)
                    this.rowRangeGroup._add(row,count);
                if(this._sparklineGroupManager)
                    this._sparklineGroupManager._addRows(row,count);
                if(this._conditionalFormats)
                    this._conditionalFormats._addRows(row,count);
                if(sheetSource)
                    sheetSource.onAfterAddRows(row,count);
                this._syncScrollbarSize();
                this.invalidateLayout()
            }
            finally
            {
                this.isPaintSuspended(oldState)
            }
        },
        _onDataDeleted: function(event){},
        deleteRows: function(row, count)
        {
            if(0 > row || row >= this.getRowCount() || count <= 0)
                return;
            var ds = this.getDataSource();
            if(ds && this._isDataView(ds))
                if(!ds.canRemove || !ds.canRemove())
                    return;
            var oldState = this.isPaintSuspended();
            this.isPaintSuspended(true);
            try
            {
                var sheetSource = this._getSheetSource();
                if(sheetSource)
                    sheetSource.onBeforeAddRemoveRows(row);
                var m = this._getModel();
                if(this.autoUpdate && this.dataContext && this.dataContext.remove)
                    if(ds && ds.length > 0)
                    {
                        var self = this;
                        this._dataDeleteDelegate = function(event)
                        {
                            return self._onDataDeleted(event)
                        };
                        var n = row;
                        if(n >= 0)
                        {
                            var item = this.getDataItem(n);
                            $.ajax({
                                url: this.dataContext.remove,
                                type: ajax_Type,
                                data: JSON.stringify(item),
                                dataType: ajax_DataType,
                                contentType: ajax_MIME,
                                success: this._dataDeleteDelegate
                            })
                        }
                    }
                if(!this.deletedRows)
                    this.deletedRows = [];
                var dataLength = this._getDataLength(ds);
                for(var i = 0; i < count && row + i < m.rowCount; i++)
                {
                    var d = null;
                    if(ds && row + i < dataLength)
                        d = this._getDataItem(ds,row + i);
                    var k = null;
                    if(m.dataTable && m.dataTable[row + i])
                        k = m.dataTable[row + i].key;
                    this.deletedRows.push({
                        row: row + i,
                        data: d,
                        key: k
                    })
                }
                if(ds)
                    if(this._isDataView(ds))
                    {
                        if(!this._dataViewUpdating)
                        {
                            this._updatingDataView = true;
                            var removeCount = Math.min(count,dataLength - row);
                            for(var r = 0; r < removeCount; r++)
                                ds.remove(ds.item(row));
                            this._updatingDataView = false
                        }
                    }
                    else if(this._isCellBinding(ds));
                    else if(row < dataLength)
                        ds.splice(row,count);
                m.deleteRows(row,count);
                var calc = this._getCalcModel();
                calc.deleteRows(row,count);
                var SheetArea = UI.SheetArea;
                var rm = this._getModel(SheetArea.rowHeader);
                rm.deleteRows(row,count);
                m.deleteElements(this._rowInfos,this.getRowCount(),row,count);
                var sm = this._getSpanModel(SheetArea.rowHeader);
                sm.removeRows(row,count);
                sm = this._getSpanModel();
                sm.removeRows(row,count);
                if(this._rowFilter)
                    this._rowFilter._removeRows(row,count);
                if(this.rowRangeGroup)
                    this.rowRangeGroup._remove(row,count);
                if(this._sparklineGroupManager)
                    this._sparklineGroupManager._removeRows(row,count);
                if(this._conditionalFormats)
                    this._conditionalFormats._removeRows(row,count);
                var top = this._scrollTopRow;
                if(top >= 0)
                {
                    var newTop = -1;
                    for(var t = top; t >= this.frozenRowCount; t--)
                        if(this.getRowVisible(t) && this._getZoomRowHeight(t) > 0)
                        {
                            newTop = t;
                            break
                        }
                    if(newTop === -1)
                        newTop = 0;
                    if(top !== newTop)
                    {
                        this._trigger(UI.Events.TopRowChanged,{
                            sheet: this,
                            sheetName: this._name,
                            oldTopRow: top,
                            newTopRow: newTop
                        });
                        this._scrollTopRow = newTop;
                        this._syncVScrollbarPosition()
                    }
                }
                if(sheetSource)
                    sheetSource.onAfterRemoveRows(row,count);
                this._syncScrollbarSize();
                this.invalidateLayout()
            }
            finally
            {
                this.isPaintSuspended(oldState)
            }
        },
        addColumns: function(col, count)
        {
            if(count <= 0)
                return;
            this._bindToAutoRefresh(function(col, count)
            {
                if(col < 0 || col > this.getColumnCount())
                    col = this.getColumnCount();
                if(this.parent && this.parent.gcSpreadsheet)
                    return;
                var sheetSource = this._getSheetSource();
                if(sheetSource)
                    sheetSource.onBeforeAddRemoveColumns(col);
                var m = this._getModel();
                m.addColumns(col,count);
                var calc = this._getCalcModel();
                calc.addColumns(col,count);
                var SheetArea = UI.SheetArea;
                var cm = this._getModel(SheetArea.colHeader);
                cm.addColumns(col,count);
                m.addElements(this._colInfos,this.getColumnCount(),col,count);
                var sm = this._getSpanModel(SheetArea.colHeader);
                sm.addColumns(col,count);
                sm = this._getSpanModel();
                sm.addColumns(col,count);
                if(this._rowFilter)
                    this._rowFilter._addColumns(col,count);
                if(this._allowCellOverflow === true)
                    this._clearCellOverflowModelCache();
                if(this.colRangeGroup)
                    this.colRangeGroup._add(col,count);
                if(this._sparklineGroupManager)
                    this._sparklineGroupManager._addColumns(col,count);
                if(this._conditionalFormats)
                    this._conditionalFormats._addColumns(col,count);
                if(sheetSource)
                    sheetSource.onAfterAddColumns(col,count);
                this._syncScrollbarSize();
                this.invalidateLayout()
            })(col,count)
        },
        deleteColumns: function(col, count)
        {
            if(0 > col || col >= this.getColumnCount() || count <= 0)
                return;
            if(this.parent && this.parent.gcSpreadsheet)
                return;
            this._bindToAutoRefresh(function(col, count)
            {
                var sheetSource = this._getSheetSource();
                if(sheetSource)
                    sheetSource.onBeforeAddRemoveColumns(col);
                var m = this._getModel();
                m.deleteColumns(col,count);
                var calc = this._getCalcModel();
                calc.deleteColumns(col,count);
                var SheetArea = UI.SheetArea;
                var cm = this._getModel(SheetArea.colHeader);
                cm.deleteColumns(col,count);
                m.deleteElements(this._colInfos,this.getColumnCount(),col,count);
                var sm = this._getSpanModel(SheetArea.colHeader);
                sm.removeColumns(col,count);
                sm = this._getSpanModel(SheetArea.viewport);
                sm.removeColumns(col,count);
                if(this._rowFilter)
                    this._rowFilter._removeColumns(col,count);
                if(this._allowCellOverflow === true)
                    this._clearCellOverflowModelCache();
                if(this.colRangeGroup)
                    this.colRangeGroup._remove(col,count);
                if(this._sparklineGroupManager)
                    this._sparklineGroupManager._removeColumns(col,count);
                if(this._conditionalFormats)
                    this._conditionalFormats._removeColumns(col,count);
                var left = this._scrollLeftCol;
                if(left >= 0)
                {
                    var newLeft = -1;
                    for(var t = left; t >= this.frozenColCount; t--)
                        if(this.getColumnVisible(t) && this._getZoomColumnWidth(t) > 0)
                        {
                            newLeft = t;
                            break
                        }
                    if(newLeft === -1)
                        newLeft = 0;
                    if(left !== newLeft)
                    {
                        this._trigger(UI.Events.LeftColumnChanged,{
                            sheet: this,
                            sheetName: this._name,
                            oldLeftCol: left,
                            newLeftCol: newLeft
                        });
                        this._scrollLeftCol = newLeft;
                        this._syncHScollbarPosition()
                    }
                }
                if(sheetSource)
                    sheetSource.onAfterRemoveColumns(col,count);
                this._syncScrollbarSize();
                this.invalidateLayout()
            })(col,count)
        },
        setFrozenCount: function(rowCount, colCount)
        {
            if(rowCount === undefined || rowCount === null || rowCount < 0 || isNaN(rowCount))
                rowCount = 0;
            if(colCount === undefined || colCount === null || colCount < 0 || isNaN(colCount))
                colCount = 0;
            this._bindToAutoRefresh(function(rowCount, colCount)
            {
                var invalid = false;
                if(this.frozenRowCount !== rowCount)
                {
                    this._scrollTopRow += rowCount - this.frozenRowCount;
                    this.frozenRowCount = rowCount;
                    invalid = true
                }
                if(this.frozenColCount !== colCount)
                {
                    this._scrollLeftCol += colCount - this.frozenColCount;
                    this.frozenColCount = colCount;
                    invalid = true
                }
                if(invalid)
                {
                    this.invalidateLayout();
                    this._syncScrollbarSize()
                }
            })(rowCount,colCount)
        },
        getRowCount: function(sheetArea)
        {
            var m = this._getModel(sheetArea);
            return m ? m.rowCount : 0
        },
        getColumnCount: function(sheetArea)
        {
            var m = this._getModel(sheetArea);
            return m ? m.colCount : 0
        },
        setRowCount: function(rowCount, sheetArea)
        {
            rowCount = parseInt(rowCount,10);
            if(isNaN(rowCount))
                return;
            if(rowCount < 0)
                return;
            this._bindToAutoRefresh(function(rowCount, sheetArea)
            {
                var SheetArea = UI.SheetArea;
                if(sheetArea === undefined || sheetArea === null)
                    sheetArea = SheetArea.viewport;
                if(sheetArea === SheetArea.viewport || sheetArea === SheetArea.rowHeader)
                {
                    if(this._dataModel.rowCount > rowCount)
                        this.deleteRows(rowCount,this._dataModel.rowCount - rowCount);
                    this._dataModel.rowCount = this._calcDataModel.rowCount = this._rowHeaderModel.rowCount = rowCount;
                    if(this.frozenRowCount > rowCount)
                        this.setFrozenCount(rowCount,this.frozenColCount);
                    if(!this._rowInfos)
                        this._rowInfos = {};
                    if(this.rowRangeGroup)
                        this.rowRangeGroup._setCount(rowCount)
                }
                else if(sheetArea === SheetArea.colHeader)
                {
                    if(!this._colHeaderRowInfos)
                        this._colHeaderRowInfos = {};
                    this._colHeaderModel.rowCount = rowCount
                }
                if(this.rowRangeGroup)
                    this.rowRangeGroup.refresh();
                this._syncScrollbarSize();
                if(this._activeRowIndex >= rowCount && (sheetArea === SheetArea.viewport || sheetArea === SheetArea.rowHeader))
                {
                    this._clearSelectionImp();
                    this._setActiveCellImp(-1,-1,this.activeRowViewportIndex,this.activeColViewportIndex)
                }
            })(rowCount,sheetArea)
        },
        setColumnCount: function(colCount, sheetArea)
        {
            colCount = parseInt(colCount,10);
            if(isNaN(colCount))
                return;
            if(colCount < 0)
                return;
            this._bindToAutoRefresh(function(colCount, sheetArea)
            {
                var SheetArea = UI.SheetArea;
                if(sheetArea === undefined || sheetArea === null)
                    sheetArea = SheetArea.viewport;
                if(sheetArea === SheetArea.viewport || sheetArea === SheetArea.colHeader)
                {
                    if(this._dataModel.colCount > colCount)
                        this.deleteColumns(colCount,this._dataModel.colCount - colCount);
                    this._dataModel.colCount = this._calcDataModel.colCount = this._colHeaderModel.colCount = this._colFooterModel.colCount = colCount;
                    if(this.frozenColCount > colCount)
                        this.setFrozenCount(this.frozenRowCount,colCount);
                    if(!this._colInfos)
                        this._colInfos = {};
                    if(this.colRangeGroup)
                        this.colRangeGroup._setCount(colCount)
                }
                else if(sheetArea === SheetArea.rowHeader)
                {
                    if(!this._rowHeaderColInfos)
                        this._rowHeaderColInfos = {};
                    this._rowHeaderModel.colCount = colCount
                }
                if(this.colRangeGroup)
                    this.colRangeGroup.refresh();
                this._syncScrollbarSize();
                if(this._activeColIndex >= colCount && (sheetArea === SheetArea.viewport || sheetArea === SheetArea.colHeader))
                {
                    this._clearSelectionImp();
                    this._setActiveCellImp(-1,-1,this.activeRowViewportIndex,this.activeColViewportIndex)
                }
            })(colCount,sheetArea)
        },
        getText: function(row, col, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var v = this.getValue(row,col,sheetArea);
            if(v === undefined || v === null)
                return"";
            else
            {
                var ct = this.getCellType(row,col,sheetArea);
                var style = this.getActualStyle(row,col,sheetArea);
                var fmt = style.formatter ? style.formatter : style._autoFormatter;
                v = ct.format(v,fmt);
                return v
            }
        },
        setText: function(row, col, value, sheetArea)
        {
            this._bindToAutoRefresh(function(row, col, value, sheetArea)
            {
                if(sheetArea === undefined || sheetArea === null)
                    sheetArea = UI.SheetArea.viewport;
                var t = value;
                var ct = this.getCellType(row,col,sheetArea);
                var formatStr = this.getCell(row,col,sheetArea).formatter();
                if(ct && formatStr)
                    t = ct.parse(value,formatStr);
                this.setValue(row,col,t,sheetArea)
            })(row,col,value,sheetArea)
        },
        getValue: function(row, col, sheetArea)
        {
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = SheetArea.viewport;
            var m = this._getModel(sheetArea);
            if(!m)
                return null;
            var t = this._getValueImp(m,row,col,sheetArea);
            var HeaderAutoText = UI.HeaderAutoText,
                index;
            var ko = window.ko;
            if(sheetArea === SheetArea.colHeader)
            {
                if(t === undefined || t === null)
                {
                    var ds = this.getDataSource();
                    if(this._isDataView(ds) || ds && ds.length > 0 || ko && ko.isObservable(ds))
                    {
                        var ci = this._colInfos[col];
                        if(ci && (this.colHeaderAutoTextIndex >= 0 && row === this.colHeaderAutoTextIndex || this.colHeaderAutoTextIndex === -1 && row === m.rowCount - 1))
                            t = ci.displayName || ci.name
                    }
                    if(t === undefined || t === null)
                    {
                        index = this.colHeaderAutoTextIndex;
                        if(index < 0 || index >= m.rowCount)
                            index = m.rowCount - 1;
                        if(row === index)
                            if(this.colHeaderAutoText === HeaderAutoText.letters)
                                t = this._indexToLetters(col + 1);
                            else if(this.colHeaderAutoText === HeaderAutoText.numbers)
                                t = col + 1
                    }
                }
            }
            else if(sheetArea === SheetArea.rowHeader)
                if(t === undefined || t === null)
                {
                    index = this.rowHeaderAutoTextIndex;
                    if(index < 0 || index >= m.colCount)
                        index = m.colCount - 1;
                    if(col === index)
                        if(this.rowHeaderAutoText === HeaderAutoText.letters)
                            t = this._indexToLetters(row + 1);
                        else if(this.rowHeaderAutoText === HeaderAutoText.numbers)
                            t = row + 1
                }
            return t
        },
        setValue: function(row, col, value, sheetArea, ignoreRecalc)
        {
            this._bindToAutoRefresh(function(row, col, value, sheetArea, ignoreRecalc)
            {
                if(sheetArea === undefined || sheetArea === null)
                    sheetArea = UI.SheetArea.viewport;
                var m = this._getModel(sheetArea);
                if(!m)
                    return;
                var rowCount = m.rowCount;
                var colCount = m.colCount;
                if(row < 0 || row >= rowCount || col < 0 || col >= colCount)
                    return;
                if(value !== undefined && value !== null && value.constructor === Date)
                    value = "/Date(" + value.getTime() + ")/";
                var oldValue = this._getValueImp(m,row,col,sheetArea);
                var ko = window.ko;
                if(sheetArea === undefined || sheetArea === null || sheetArea === UI.SheetArea.viewport)
                {
                    var valueSet = false;
                    if(this._dataSource)
                        if(this._isDataView(this._dataSource))
                        {
                            var dataLength = this._dataSource.count();
                            if(row < dataLength)
                            {
                                var drow = this._dataSource.item(row);
                                if(drow)
                                {
                                    var ci = this._colInfos[col];
                                    if(ci && ci.name && ci.name.length > 0)
                                    {
                                        this._dataSource.setProperty(drow,ci.name,value);
                                        valueSet = true
                                    }
                                }
                            }
                        }
                        else if(ko && ko.isObservable(this._dataSource))
                        {
                            var dataLength = this._dataSource().length;
                            if(row < dataLength)
                            {
                                var drow = this._dataSource()[row];
                                if(drow)
                                {
                                    var ci = this._colInfos[col];
                                    if(ci && ci.name && ci.name.length > 0)
                                    {
                                        if(typeof ci.value === const_function)
                                            ci.value(drow,value);
                                        else if(typeof ko !== const_undefined && ko.isWriteableObservable(drow[ci.name]))
                                            drow[ci.name](value);
                                        else
                                            drow[ci.name] = value;
                                        valueSet = true
                                    }
                                }
                            }
                        }
                        else if(this._isCellBinding(this._dataSource))
                        {
                            var path = this.getBindingPath(row,col,sheetArea);
                            if(path)
                            {
                                this._dataSource.setValue(path,value);
                                valueSet = true
                            }
                        }
                        else
                        {
                            var dataLength = this._dataSource.length;
                            if(row < dataLength)
                            {
                                var drow = this._dataSource[row];
                                if(drow)
                                {
                                    var ci = this._colInfos[col];
                                    if(ci && ci.name && ci.name.length > 0)
                                    {
                                        if(typeof ci.value === const_function)
                                            ci.value(drow,value);
                                        else if(typeof ko !== const_undefined && ko.isWriteableObservable(drow[ci.name]))
                                            drow[ci.name](value);
                                        else
                                            drow[ci.name] = value;
                                        valueSet = true
                                    }
                                }
                            }
                        }
                    if(!valueSet || this.checkingChanges)
                    {
                        m.setValue(row,col,value);
                        this._activeRowDirty = true
                    }
                }
                else
                    m.setValue(row,col,value);
                if((sheetArea === undefined || sheetArea === null || sheetArea === UI.SheetArea.viewport) && !ignoreRecalc)
                    this._recalcCell(m,row,col);
                if(this._allowCellOverflow)
                    this._clearCellOverflowModelCachebyRow(row,sheetArea);
                if(this._conditionalFormats)
                    this._conditionalFormats._clearCache();
                if(oldValue !== value)
                    this._trigger(UI.Events.CellChanged,{
                        sheet: this,
                        sheetName: this._name,
                        row: row,
                        col: col,
                        sheetArea: sheetArea,
                        propertyName: _value,
                        _oldValue: oldValue
                    })
            })(row,col,value,sheetArea,ignoreRecalc)
        },
        sortRange: function(row, column, rowCount, columnCount, byRows, sortInfo)
        {
            return this._bindToAutoRefresh(function(row, column, rowCount, columnCount, byRows, sortInfo)
                {
                    var oldState = this.isPaintSuspended();
                    this.isPaintSuspended(true);
                    try
                    {
                        var spans = this.getSpans(new UI.Range(row,column,rowCount,columnCount));
                        if(spans && spans.length > 0)
                            return false;
                        var rowcnt = this.getRowCount();
                        var colcnt = this.getColumnCount();
                        var ncol,
                            nrow,
                            modelrow,
                            modelcol,
                            modelrow2,
                            modelcol2;
                        var array,
                            data1,
                            data2;
                        if(row === -1)
                            row = 0;
                        if(rowCount === -1)
                            rowCount = rowcnt;
                        if(column === -1)
                            column = 0;
                        if(columnCount === -1)
                            columnCount = colcnt;
                        if(row < 0 || row >= rowcnt || column < 0 || column >= colcnt || rowCount < 0 || row + rowCount > rowcnt || columnCount < 0 || column + columnCount > colcnt || !sortInfo)
                            return false;
                        array = this._quickSort(row,column,rowCount,columnCount,byRows,sortInfo);
                        if(array)
                        {
                            var model = this._getModel(),
                                calcModel = this._getCalcModel(),
                                source = this._getSheetSource(),
                                viewIndex,
                                expr,
                                cellCalc;
                            if(byRows)
                                for(nrow = row; nrow < row + rowCount; nrow++)
                                {
                                    viewIndex = this._getSwapIndex(array,row,nrow,rowCount,true);
                                    if(nrow === viewIndex)
                                        continue;
                                    modelrow = nrow;
                                    modelrow2 = viewIndex;
                                    for(ncol = column; ncol < column + columnCount; ncol++)
                                    {
                                        modelcol = ncol;
                                        data1 = this.getValue(modelrow,modelcol);
                                        data2 = this.getValue(modelrow2,modelcol);
                                        var cell1Calc = source._getCellCalc(modelrow,modelcol,false);
                                        if(cell1Calc && cell1Calc.hasListeners())
                                            cell1Calc.addListenersToAdjust();
                                        var cell2Calc = source._getCellCalc(modelrow2,modelcol,false);
                                        if(cell2Calc && cell2Calc.hasListeners())
                                            cell2Calc.addListenersToAdjust();
                                        calcModel.swapNode(modelrow,modelcol,modelrow2,modelcol);
                                        expr = staticMembers._copyExpression(calcModel.getFormula(modelrow,modelcol),modelrow,modelcol);
                                        calcModel.setFormula(modelrow,modelcol,expr);
                                        cellCalc = source._getCellCalc(modelrow,modelcol,!!expr);
                                        if(cellCalc)
                                            cellCalc.startListening();
                                        expr = staticMembers._copyExpression(calcModel.getFormula(modelrow2,modelcol),modelrow2,modelcol);
                                        calcModel.setFormula(modelrow2,modelcol,expr);
                                        cellCalc = source._getCellCalc(modelrow2,modelcol,!!expr);
                                        if(cellCalc)
                                            cellCalc.startListening();
                                        source._controller.adjustFormulasOnSwap(calcModel,modelrow,modelcol,calcModel,modelrow2,modelcol,1,1);
                                        model.swapNode(modelrow,modelcol,modelrow2,modelcol);
                                        this.setValue(modelrow,modelcol,data2);
                                        this.setValue(modelrow2,modelcol,data1)
                                    }
                                }
                            else
                                for(ncol = column; ncol < column + columnCount; ncol++)
                                {
                                    viewIndex = this._getSwapIndex(array,column,ncol,columnCount,false);
                                    if(ncol === viewIndex)
                                        continue;
                                    modelcol = ncol;
                                    modelcol2 = viewIndex;
                                    for(nrow = row; nrow < row + rowCount; nrow++)
                                    {
                                        modelrow = nrow;
                                        data1 = this.getValue(modelrow,modelcol);
                                        data2 = this.getValue(modelrow,modelcol2);
                                        var cell1Calc = source._getCellCalc(modelrow,modelcol,false);
                                        if(cell1Calc && cell1Calc.hasListeners())
                                            cell1Calc.addListenersToAdjust();
                                        var cell2Calc = source._getCellCalc(modelrow,modelcol2,false);
                                        if(cell2Calc && cell2Calc.hasListeners())
                                            cell2Calc.addListenersToAdjust();
                                        calcModel.swapNode(modelrow,modelcol,modelrow,modelcol2);
                                        expr = staticMembers._copyExpression(calcModel.getFormula(modelrow,modelcol),modelrow,modelcol);
                                        calcModel.setFormula(modelrow,modelcol,expr);
                                        cellCalc = source._getCellCalc(modelrow,modelcol,!!expr);
                                        if(cellCalc)
                                            cellCalc.startListening();
                                        expr = staticMembers._copyExpression(calcModel.getFormula(modelrow,modelcol2),modelrow,modelcol2);
                                        calcModel.setFormula(modelrow,modelcol2,expr);
                                        cellCalc = source._getCellCalc(modelrow,modelcol2,!!expr);
                                        if(cellCalc)
                                            cellCalc.startListening();
                                        source._controller.adjustFormulasOnSwap(calcModel,modelrow,modelcol,calcModel,modelrow,modelcol2,1,1);
                                        model.swapNode(modelrow,modelcol,modelrow,modelcol2);
                                        this.setValue(modelrow,modelcol,data2);
                                        this.setValue(modelrow,modelcol2,data1)
                                    }
                                }
                            return true
                        }
                        return false
                    }
                    finally
                    {
                        this.resumeCalcService();
                        this.resumeEvent();
                        for(var r = 0; r < rowCount; r++)
                            for(var c = 0; c < columnCount; c++)
                                this._raiseCellChanged(_value,row + r,column + c,UI.SheetArea.viewport);
                        this._raisePropertyChanged("[sort]");
                        this.isPaintSuspended(oldState)
                    }
                })(row,column,rowCount,columnCount,byRows,sortInfo)
        },
        getActualStyle: function(row, column, sheetArea, sheetOnly)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var info = new UI.Style,
                composedStyle = this.getCompositeStyle(row,column,sheetArea);
            if(sheetArea === UI.SheetArea.viewport)
                if(!sheetOnly)
                {
                    var f = composedStyle.formatter;
                    if(f && f.HasFormatedColor && f.HasFormatedColor())
                    {
                        var clr = {};
                        f.Format(this.getValue(row,column),clr);
                        if(clr.value)
                            info.foreColor = clr.value
                    }
                    var conditionalFormats = this._conditionalFormats;
                    if(conditionalFormats && conditionalFormats.count() > 0)
                    {
                        var rules = conditionalFormats.getRules(row,column);
                        for(var n = 0; n < rules.length; n++)
                        {
                            var rule = rules[rules.length - 1 - n];
                            if(rule)
                                if(rule.isScaleRule())
                                {
                                    if(!(rule instanceof UI.DataBarRule || rule instanceof UI.IconSetRule))
                                    {
                                        var color = rule.evaluate(this,row,column,this.getValue(row,column,sheetArea));
                                        if(color)
                                            info.backColor = color
                                    }
                                }
                                else
                                {
                                    var expected = rule.evaluate(this,row,column,this.getValue(row,column,sheetArea));
                                    if(expected !== null && expected !== undefined)
                                        info.compose(expected)
                                }
                        }
                    }
                }
            info.compose(composedStyle);
            if(info.locked === undefined || info.locked === null)
                info.locked = true;
            return info._normalize(new UI._ThemeContext(this))
        },
        getStyle: function(row, col, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var m = this._getModel(sheetArea);
            if(m)
                return m.getStyle(row,col);
            return null
        },
        setStyle: function(row, col, value, sheetArea)
        {
            this._bindToAutoRefresh(function(row, col, value, sheetArea)
            {
                var SheetArea = UI.SheetArea;
                if(sheetArea === undefined || sheetArea === null)
                    sheetArea = SheetArea.viewport;
                var m = this._getModel(sheetArea);
                if(m)
                {
                    m.setStyle(row,col,value);
                    if(value && value.formatter)
                    {
                        var formatter = value.formatter;
                        if(typeof formatter === const_string)
                            formatter = new UI.GeneralFormatter(formatter);
                        if(formatter && typeof formatter.Init === const_function)
                            formatter.Init()
                    }
                    var E = UI.Events;
                    var pn = "[styleinfo]";
                    if(row !== -1 && col !== -1)
                        this._trigger(E.CellChanged,{
                            sheet: this,
                            sheetName: this._name,
                            row: row,
                            col: col,
                            sheetArea: sheetArea,
                            propertyName: pn
                        });
                    else if(row !== -1 && col === -1)
                        this._trigger(E.RowChanged,{
                            sheet: this,
                            sheetName: this._name,
                            row: row,
                            sheetArea: sheetArea,
                            propertyName: pn
                        });
                    else if(row === -1 && col !== -1)
                        this._trigger(E.ColumnChanged,{
                            sheet: this,
                            sheetName: this._name,
                            col: col,
                            sheetArea: sheetArea,
                            propertyName: pn
                        })
                }
                if(this._allowCellOverflow)
                    if(sheetArea === SheetArea.colHeader || row === -1)
                        this._clearCellOverflowModelCache();
                    else if(sheetArea === SheetArea.viewport || col === -1)
                        this._clearCellOverflowModelCachebyRow(row,sheetArea)
            })(row,col,value,sheetArea)
        },
        getDefaultStyle: function(sheetArea)
        {
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = SheetArea.viewport;
            var m = this._getModel(sheetArea);
            if(m)
            {
                var sheetDefStyle = m.getStyle(-1,-1);
                var defStyle = new UI.Style;
                if(sheetArea === SheetArea.colHeader || sheetArea === SheetArea.rowHeader)
                {
                    defStyle.foreColor = "black";
                    defStyle.hAlign = UI.HorizontalAlign.center;
                    defStyle.vAlign = UI.VerticalAlign.center
                }
                else
                {
                    defStyle.foreColor = "black";
                    defStyle.hAlign = UI.HorizontalAlign.general;
                    defStyle.vAlign = UI.VerticalAlign.top
                }
                if(sheetDefStyle)
                {
                    sheetDefStyle.compose(defStyle);
                    return sheetDefStyle
                }
                else
                {
                    m.setStyle(-1,-1,defStyle);
                    return defStyle
                }
            }
            return null
        },
        setDefaultStyle: function(style, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var m = this._getModel(sheetArea);
            if(m)
                m.setStyle(-1,-1,style)
        },
        getCompositeStyle: function(row, column, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var destInfo = new UI.Style;
            var m = this._getModel(sheetArea);
            if(m)
            {
                var rowCount = m.rowCount,
                    colCount = m.colCount,
                    tmp;
                if(0 <= row && row < rowCount && 0 <= column && column < colCount)
                {
                    var s = m.getStyle(row,column);
                    if(s)
                        destInfo.compose(s)
                }
                if(0 <= row && row < rowCount)
                {
                    tmp = m.getStyle(row,-1);
                    if(tmp)
                        destInfo.compose(tmp)
                }
                if(0 <= column && column < colCount)
                {
                    tmp = m.getStyle(-1,column);
                    if(tmp)
                        destInfo.compose(tmp)
                }
            }
            var sheetInfo = this.getDefaultStyle(sheetArea);
            if(sheetInfo)
                destInfo.compose(sheetInfo);
            return destInfo
        },
        getCellType: function(row, col, sheetArea)
        {
            var style = this.getActualStyle(row,col,sheetArea);
            if(style && style.cellType)
                return style.cellType;
            else
            {
                if(!this._defaultCellType)
                    this._defaultCellType = new UI.TextCellType;
                if(sheetArea === UI.SheetArea.corner)
                    return new UI.CornerCellType;
                else if(sheetArea === UI.SheetArea.colHeader)
                    return new UI.ColumnHeaderCellType;
                else if(sheetArea === UI.SheetArea.rowHeader)
                    return new UI.RowHeaderCellType;
                else
                    return this._defaultCellType
            }
        },
        setCellType: function(row, col, value, sheetArea)
        {
            var style = this.getStyle(row,col,sheetArea);
            if(!style)
                style = new UI.Style;
            style.cellType = value;
            this.setStyle(row,col,style,sheetArea)
        },
        getFormatter: function(row, col, sheetArea)
        {
            var style = this.getActualStyle(row,col,sheetArea);
            return style ? style.formatter : undefined
        },
        setFormatter: function(row, col, value, sheetArea)
        {
            var style = this.getStyle(row,col,sheetArea);
            if(!style)
                style = new UI.Style;
            style.formatter = value;
            this.setStyle(row,col,style,sheetArea)
        },
        getDataValidator: function(row, col, sheetArea)
        {
            var style = this.getActualStyle(row,col,sheetArea);
            return style ? style.validator : undefined
        },
        setDataValidator: function(row, col, value, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var style = this.getStyle(row,col,sheetArea);
            if(!style)
                style = new UI.Style;
            style.validator = value;
            this.setStyle(row,col,style,sheetArea);
            if(value)
            {
                if(value.condition instanceof UI.FormulaCondition && (value.condition._baseRow === undefined || value.condition._baseRow === null))
                    value.condition._baseRow = row !== -1 ? row : 0;
                if(value.condition instanceof UI.FormulaCondition && (value.condition._baseCol === undefined || value.condition._baseCol === null))
                    value.condition._baseCol = col !== -1 ? col : 0
            }
            var E = UI.Events,
                pn = "validator";
            this._trigger(E.CellChanged,{
                sheet: this,
                sheetName: this._name,
                row: row,
                col: col,
                sheetArea: sheetArea,
                propertyName: pn
            });
            if(row !== -1 && col === -1)
                this._trigger(E.RowChanged,{
                    sheet: this,
                    sheetName: this._name,
                    row: row,
                    sheetArea: sheetArea,
                    propertyName: pn
                });
            else if(row === -1 && col !== -1)
                this._trigger(E.ColumnChanged,{
                    sheet: this,
                    sheetName: this._name,
                    col: col,
                    sheetArea: sheetArea,
                    propertyName: pn
                })
        },
        isValid: function(row, column, value)
        {
            try
            {
                this._validatingRow = row;
                this._validatingColumn = column;
                this._validatingValue = value;
                this._isValidatingCell = true;
                var dv = this.getDataValidator(row,column);
                if(dv)
                    return dv.isValid(this,row,column,value)
            }
            finally
            {
                this._validatingRow = -1;
                this._validatingColumn = -1;
                this._validatingValue = null;
                this._isValidatingCell = false
            }
            return true
        },
        addSelection: function(row, column, rowCount, columnCount)
        {
            this._bindToAutoRefresh(function(row, column, rowCount, columnCount)
            {
                var r = row;
                var c = column;
                var rc = rowCount;
                var cc = columnCount;
                if(r !== -1 && c !== -1)
                {
                    var spans = this._getSpanModel().slice(0);
                    if(spans && spans.length > 0)
                    {
                        var newSelection = this._cellRangeInflate(spans,new UI.Range(row,column,rowCount,columnCount));
                        r = newSelection.row;
                        c = newSelection.col;
                        rc = newSelection.rowCount;
                        cc = newSelection.colCount
                    }
                }
                this._selectionModel.add(r,c,rc,cc)
            })(row,column,rowCount,columnCount)
        },
        getSelections: function()
        {
            return this._selectionModel
        },
        _addSpanImp: function(row, col, rowCount, colCount, sheetArea)
        {
            this._bindToAutoRefresh(function(row, col, rowCount, colCount, sheetArea)
            {
                if(sheetArea === undefined || sheetArea === null)
                    sheetArea = UI.SheetArea.viewport;
                var sm = this._getSpanModel(sheetArea);
                sm.clear(row,col,rowCount,colCount);
                var r = new UI.Range(row,col,rowCount,colCount);
                var newRange = this._getActualRange(r,sheetArea);
                sm.push(newRange);
                if(this._allowCellOverflow)
                    for(r = row; r < row + rowCount; r++)
                        this._clearCellOverflowModelCachebyRow(r,sheetArea);
                var selections = this.getSelections();
                for(var i = 0; i < selections.length; i++)
                    if(selections[i].intersect(row,col,rowCount,colCount))
                        selections[i] = selections[i].union(r)
            })(row,col,rowCount,colCount,sheetArea)
        },
        addSpan: function(row, col, rowCount, colCount, sheetArea)
        {
            if(rowCount === 1 && colCount === 1)
                return;
            if(sheetArea === UI.SheetArea.corner)
                return;
            var sm = this._getSpanModel(sheetArea);
            var enumrator = sm.getEnumerator(row,col,rowCount,colCount);
            var canSpan = true;
            while(enumrator.moveNext())
            {
                var range = enumrator.current();
                var selectionRange = new UI.Range(row,col,rowCount,colCount);
                if(!selectionRange.containsRange(range))
                {
                    canSpan = false;
                    break
                }
            }
            if(!canSpan)
                throw new Error("Invalid range");
            this._addSpanImp(row,col,rowCount,colCount,sheetArea)
        },
        removeSpan: function(row, col, sheetArea)
        {
            this._bindToAutoRefresh(function(row, col, sheetArea)
            {
                if(sheetArea === undefined || sheetArea === null)
                    sheetArea = UI.SheetArea.viewport;
                var sm = this._getSpanModel(sheetArea);
                for(var i = 0; i < sm.length; i++)
                {
                    var rg = sm[i];
                    if(rg.row === row && rg.col === col)
                    {
                        sm.splice(i,1);
                        if(this._allowCellOverflow)
                            for(var r = rg.row; r < rg.row + rg.rowCount; r++)
                                this._clearCellOverflowModelCachebyRow(r,sheetArea);
                        return
                    }
                }
            })(row,col,sheetArea)
        },
        repaint: function(clipRect)
        {
            if(!this._paintSuspended)
                this._render.repaint(clipRect)
        },
        startEdit: function(selectAll, defaultText)
        {
            this._startEditImp(this._getCanvas(),this._activeRowIndex,this._activeColIndex,null,null,selectAll,defaultText)
        },
        editorStatus: function()
        {
            return this._editorStatus ? this._editorStatus : UI.EditorStatus.Ready
        },
        isEditing: function()
        {
            return this._editorStatus === UI.EditorStatus.Enter || this._editorStatus === UI.EditorStatus.Edit
        },
        doKeyDown: function(event)
        {
            this._eventHandler.doKeyDown(event)
        },
        doKeyUp: function(event)
        {
            this._eventHandler.doKeyUp(event)
        },
        removeKeyMap: function(keyCode, ctrl, shift, alt, action)
        {
            if(!this.keyMap)
                return;
            for(var i = 0; i < this.keyMap.length; i++)
            {
                var ka = this.keyMap[i];
                if(ka && ka.key === keyCode && ka.ctrl === ctrl && ka.shift === shift && ka.alt === alt)
                {
                    this.keyMap.splice(i,1);
                    break
                }
            }
        },
        addKeyMap: function(keyCode, ctrl, shift, alt, action)
        {
            if(!this.keyMap)
                this.keyMap = [];
            var ka = this._getKeyAction(keyCode,ctrl,shift,alt);
            if(ka)
                ka.action = action;
            else
                this.keyMap.push(new UI.KeyMap(keyCode,ctrl,shift,alt,action))
        },
        moveActiveCell: function(dir, wrap, beginRow, beginCol, repaint, scrolled)
        {
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    row: this._activeRowIndex,
                    col: this._activeColIndex,
                    cancel: false
                };
            this._trigger(UI.Events.LeaveCell,args);
            if(args && args.cancel === true)
                return false;
            if(!this.endEdit())
                return false;
            if(this._activeRowDirty)
            {
                this.updateRecord();
                this._activeRowDirty = false
            }
            var prevRowIndex = this._activeRowIndex;
            var prevcolIndex = this._activeColIndex;
            if(beginRow === undefined || beginRow === null)
                beginRow = this._activeRowIndex;
            if(beginCol === undefined || beginCol === null)
                beginCol = this._activeColIndex;
            var Dir = UI.Direction;
            if(dir === Dir.left)
                this._moveActiveCellLeft(beginRow,beginCol,wrap,false,false);
            else if(dir === Dir.right)
                this._moveActiveCellRight(beginRow,beginCol,wrap,false,false);
            else if(dir === Dir.up)
                this._moveActiveCellUp(beginRow,beginCol,wrap,false,false);
            else if(dir === Dir.down)
                this._moveActiveCellDown(beginRow,beginCol,wrap,false,false);
            repaint = this.moveActiveCellEnd(dir,prevRowIndex,prevcolIndex,repaint,scrolled);
            return repaint
        },
        _moveActiveCellInSelection: function(dir)
        {
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    row: this._activeRowIndex,
                    col: this._activeColIndex,
                    cancel: false
                };
            this._trigger(UI.Events.LeaveCell,args);
            if(args && args.cancel === true)
                return;
            this.endEdit();
            if(this._activeRowDirty)
            {
                this.updateRecord();
                this._activeRowDirty = false
            }
            var prevRowIndex = this._activeRowIndex;
            var prevcolIndex = this._activeColIndex;
            if(dir === UI.Direction.left)
                this._moveActiveCellLeftInSelection(this._activeRowIndex,this._activeColIndex);
            else if(dir === UI.Direction.right)
                this._moveActiveCellRightInSelection(this._activeRowIndex,this._activeColIndex);
            this.moveActiveCellEnd(dir,prevRowIndex,prevcolIndex)
        },
        subscriptionHandlers: [],
        doDataItemChanged: function()
        {
            var self = this;
            this.activeDataItemChangedhandler = function(context)
            {
                self.repaint()
            };
            var dataItems = this.getDataSource();
            if(dataItems && typeof window.ko !== const_undefined)
            {
                while(this.subscriptionHandlers.length > 0)
                {
                    var s = this.subscriptionHandlers.pop();
                    if(typeof s.dispose === const_function)
                        s.dispose()
                }
                var item = null;
                if(this._isDataView(dataItems))
                {
                    if(dataItems.currentPosition)
                        dataItems.currentPosition(this._activeRowIndex);
                    item = dataItems.currentItem()
                }
                else if(this._isCellBinding(dataItems));
                else
                    item = dataItems[this._activeRowIndex];
                if(item)
                    for(var x in item)
                        if(item[x] && typeof item[x].subscribe === const_function)
                            this.subscriptionHandlers.push(item[x].subscribe(this.activeDataItemChangedhandler,null,null))
            }
        },
        moveActiveCellEnd: function(dir, prevRowIndex, prevcolIndex, repaint, scrolled)
        {
            var oldSelections = this._selectionModel.toArray();
            if(!this.shift && !this._isNavigateInSelection)
            {
                var activeSelectedRange = this._fixRange(this._getActiveSelectedRange());
                if(this._selectionModel.length > 1 || activeSelectedRange.rowCount > 1 || activeSelectedRange.colCount > 1)
                    repaint = true;
                this._clearSelectionImp()
            }
            var span = this._spanModel.find(this._activeRowIndex,this._activeColIndex);
            if(span)
            {
                this._activeRowCount = span.rowCount;
                this._activeColCount = span.colCount
            }
            else
            {
                this._activeRowCount = 1;
                this._activeColCount = 1
            }
            if(!this._isNavigateInSelection)
            {
                var activeRange = this._getExtendedRange(this._activeRowIndex,this._activeColIndex);
                var selectionPolicy = this.selectionPolicy(),
                    selectionUnit = this.selectionUnit();
                if(selectionPolicy === UI.SelectionPolicy.Single)
                    this._selectionModel.clear();
                else if(selectionPolicy === UI.SelectionPolicy.Range)
                    this._selectionModel.clear();
                if(selectionUnit === UI.SelectionUnit.Row)
                {
                    activeRange.col = -1;
                    activeRange.colCount = -1
                }
                else if(selectionUnit === UI.SelectionUnit.Column)
                {
                    activeRange.row = -1;
                    activeRange.rowCount = -1
                }
                this._replaceActiveSelectedRange(activeRange.row,activeRange.col,activeRange.rowCount,activeRange.colCount,false);
                var newSelections = this._selectionModel.toArray();
                if(this._eventHandler._notEqualSelecions(oldSelections,newSelections))
                {
                    var E = UI.Events;
                    this._trigger(E.SelectionChanging,{
                        sheet: this,
                        sheetName: this._name,
                        oldSelections: oldSelections,
                        newSelections: newSelections
                    });
                    this._trigger(E.SelectionChanged,{
                        sheet: this,
                        sheetName: this._name
                    })
                }
            }
            var firstCol = this.frozenColCount ? this._getNextVisualColumn(this.frozenColCount - 1) : this._getFirstVisualColumn();
            var firstRow1 = this.frozenRowCount ? this._getNextVisualRow(this.frozenRowCount - 1) : this._getFirstVisualRow();
            if(this._activeColIndex < this._scrollLeftCol && this._activeColIndex >= firstCol)
                scrolled = this._setLeftColumn(this._getPrevVisualColumn(this._activeColIndex + 1)) || scrolled;
            if(this._activeColIndex > this._getLastFullyVisibleColumn() && this._activeColIndex <= this._getLastVisualColumn())
            {
                var cls = this._getColumnLayout(1);
                if(!cls || cls.length <= 0)
                    return false;
                var w = 0;
                var c = this._activeColIndex;
                var sheetLayout1 = this._getSheetLayout();
                while(c > this._scrollLeftCol)
                {
                    w += this._getZoomColumnWidth(c);
                    if(w > sheetLayout1.viewportWidth)
                        break;
                    c--
                }
                var newLeftCol = this._getNextVisualColumn(c);
                scrolled = this._setLeftColumn(newLeftCol) || scrolled
            }
            if(this._activeRowIndex < this._scrollTopRow && this._activeRowIndex >= firstRow1)
                scrolled = this._setTopRow(this._getPrevVisualRow(this._activeRowIndex + 1)) || scrolled;
            if(this._activeRowIndex > this._getLastFullyVisibleRow() && this._activeRowIndex <= this._getLastVisualRow())
            {
                var rls = this._getRowLayout(1);
                if(!rls || rls.length <= 0)
                    return false;
                var firstRow2 = this.frozenRowCount ? this._getNextVisualRow(this.frozenRowCount - 1) : this._getFirstVisualRow();
                var h = 0;
                var r = this._activeRowIndex;
                var sheetLayout = this._getSheetLayout();
                while(r > this._scrollTopRow)
                {
                    h += this._getZoomRowHeight(r);
                    if(h > sheetLayout.viewportHeight)
                        break;
                    r--
                }
                var newTopRow = this._getNextVisualRow(r);
                scrolled = this._setTopRow(newTopRow) || scrolled
            }
            if(this._activeRowIndex === this._getLastVisualRow())
                scrolled = this._setTopRow(this._getLastPageTopRow()) || scrolled;
            if(this._activeColIndex === this._getLastVisualColumn())
                scrolled = this._setLeftColumn(this._getLastPageLeftColumn()) || scrolled;
            if(prevRowIndex != this._activeRowIndex)
                this.doDataItemChanged();
            if(repaint && !scrolled)
            {
                this.invalidateLayout();
                this.repaint();
                repaint = true
            }
            else if(!repaint && !scrolled)
            {
                var rs,
                    cs;
                var prevSpan = this._spanModel.find(prevRowIndex,prevcolIndex);
                if(prevSpan)
                {
                    rs = prevSpan.rowCount;
                    cs = prevSpan.colCount
                }
                else
                {
                    rs = 1;
                    cs = 1
                }
                this._render.repaintSelection(new UI.Range(prevRowIndex,prevcolIndex,rs,cs));
                this._render.repaintSelection(new UI.Range(this._activeRowIndex,this._activeColIndex,this._activeRowCount,this._activeColCount))
            }
            var handler = this._eventHandler;
            var bLoadData = handler.oldTop !== this._scrollTopRow;
            handler.oldTop = this._scrollTopRow;
            handler.oldLeft = this._scrollLeftCol;
            if(bLoadData)
            {
                var topRow = this.getViewportTopRow(1);
                var bottomRow = this.getViewportBottomRow(1);
                var start = Math.max(0,topRow - 60);
                if(start < this.getRowCount())
                    handler._loadData(start,2 * bottomRow - topRow)
            }
            this._trigger(UI.Events.EnterCell,{
                sheet: this,
                sheetName: this._name,
                row: this._activeRowIndex,
                col: this._activeColIndex
            });
            handler._updateValidationUI(this._activeRowIndex,this._activeColIndex);
            return repaint || scrolled
        },
        endEdit: function(ignoreValueChange)
        {
            var editor = this._editor;
            var action;
            if(editor && editor.parentNode)
            {
                var ct = this.getCellType(this._activeRowIndex,this._activeColIndex);
                var v = ct.getEditorValue(editor);
                var args = {
                        sheet: this,
                        sheetName: this._name,
                        row: this._activeRowIndex,
                        col: this._activeColIndex,
                        editingText: v,
                        cancel: false
                    };
                this._trigger(UI.Events.EditEnd,args);
                if(args && args.cancel === true)
                    return;
                if(this._activeRowIndex >= 0 && this._activeColIndex >= 0 && !ignoreValueChange)
                {
                    var oldValue = editor._oldValue;
                    if(ct.isEditingValueChanged(oldValue,v))
                    {
                        var cellEditInfo = {
                                row: this._activeRowIndex,
                                col: this._activeColIndex,
                                newValue: v,
                                autoFormat: true
                            };
                        action = new UI.UndoRedo.CellEditUndoAction(this,cellEditInfo);
                        this._doCommand(action);
                        var needRetry = action.applyResult === UI.DataValidationResult.Retry;
                        if(needRetry === true)
                        {
                            ct.focus(editor);
                            return false
                        }
                    }
                }
                this._dirty = true;
                ct.deactivateEditor(editor)
            }
            this._eventHandler._setFocus();
            this._editingTimeValue = false;
            if(this._editorStatus !== UI.EditorStatus.Ready)
            {
                var oldStatus = this._editorStatus;
                this._editorStatus = UI.EditorStatus.Ready;
                this._trigger(UI.Events.EditorStatusChanged,{
                    sheet: this,
                    sheetName: this._name,
                    oldStatus: oldStatus,
                    newStatus: UI.EditorStatus.Ready
                })
            }
            this._editor = null;
            if(action && action.applyResult === UI.DataValidationResult.Discard)
                return false;
            return true
        },
        hitTest: function(x, y, forMove)
        {
            this._getSheetLayout();
            var target = {
                    x: x,
                    y: y,
                    rowViewportIndex: null,
                    colViewportIndex: null,
                    row: -1,
                    col: -1,
                    resizeInfo: null,
                    hitTestType: null,
                    groupHitInfo: null,
                    filterButtonHitInfo: null,
                    dragInfo: null,
                    cellTypeHitInfo: null
                };
            var hitInfo = this._render.groupHitTest(x,y);
            if(hitInfo)
                target.groupHitInfo = hitInfo;
            else
            {
                target.rowViewportIndex = this._getRowPane(y);
                target.colViewportIndex = this._getColumnPane(x);
                target.row = this._getRowIndex(y,target.rowViewportIndex);
                target.col = this._getColumnIndex(x,target.colViewportIndex);
                if(target.rowViewportIndex >= 0 && target.rowViewportIndex < 2 && target.colViewportIndex >= 0)
                {
                    var cellLayout = this._getCellLayout(target.rowViewportIndex,target.colViewportIndex).findCell(target.row,target.col);
                    if(cellLayout)
                        if(forMove !== true)
                        {
                            target.row = cellLayout.row;
                            target.col = cellLayout.col
                        }
                }
                target.hitTestType = this._getSheetAreaFromHitTest(target);
                target.resizeInfo = this._eventHandler.getResizingRowCol(target,x,y);
                if(!target.resizeInfo)
                {
                    target.dragInfo = this._eventHandler.getDragInfo(target,x,y);
                    if(!target.dragInfo)
                    {
                        target.filterButtonHitInfo = this._getFilterButtonHitInfo(target,x,y);
                        if(!target.filterButtonHitInfo)
                            target.cellTypeHitInfo = this._getCellTypeHitInfo(target,x,y)
                    }
                }
            }
            return target
        },
        getCellRect: function(row, col, rowViewportIndex, colViewportIndex)
        {
            var SheetArea = UI.SheetArea;
            var sheetArea = SheetArea.viewport;
            if(rowViewportIndex === -1)
                sheetArea = SheetArea.colHeader;
            else if(colViewportIndex === -1)
                sheetArea = SheetArea.rowHeader;
            else if(rowViewportIndex === 2)
                sheetArea = SheetArea.colFooter;
            var Rect = UI.Rect;
            var sheetLayout = this._getSheetLayout();
            if(rowViewportIndex === -1 && colViewportIndex === -1)
                return new Rect(sheetLayout.headerX,sheetLayout.headerY,sheetLayout.rowHeaderWidth,sheetLayout.colHeaderHeight);
            var bounds = this._bounds;
            if(rowViewportIndex === undefined || rowViewportIndex === null)
                rowViewportIndex = row < this.frozenRowCount ? 0 : 1;
            if(colViewportIndex === undefined || colViewportIndex === null)
                colViewportIndex = col < this.frozenColCount ? 0 : 1;
            var rowLayout = this._getRowLayout(rowViewportIndex,sheetArea).findRow(row);
            var colLayout = this._getColumnLayout(colViewportIndex,sheetArea).findCol(col);
            var cellLayout = this._getCellLayout(rowViewportIndex,colViewportIndex,sheetArea).findCell(row,col);
            if(cellLayout)
                return new Rect(cellLayout.x - bounds.x,cellLayout.y - bounds.y,cellLayout.width,cellLayout.height);
            if(rowLayout && colLayout)
                return new Rect(colLayout.x - bounds.x,rowLayout.y - bounds.y,colLayout.width,rowLayout.height);
            else
                return new Rect
        },
        _setActiveCellImp: function(row, col, rowViewportIndex, colViewportIndex)
        {
            if(this._activeRowIndex !== row)
                if(this._activeRowDirty)
                {
                    this.updateRecord();
                    this._activeRowDirty = false
                }
            var span = this._spanModel.find(row,col);
            if(span)
            {
                this._activeRowCount = span.rowCount;
                this._activeColCount = span.colCount
            }
            else
            {
                this._activeRowCount = 1;
                this._activeColCount = 1
            }
            this._render.repaintSelection(new UI.Range(this._activeRowIndex,this._activeColIndex,1,1));
            this._activeRowIndex = row;
            this._activeColIndex = col;
            this._leadingCellRow = row;
            this._leadingCellCol = col;
            this.activeRowViewportIndex = rowViewportIndex;
            this.activeColViewportIndex = colViewportIndex;
            this._render.repaintSelection(new UI.Range(row,col,1,1))
        },
        setActiveCell: function(row, col, rowViewportIndex, colViewportIndex)
        {
            this._bindToAutoRefresh(function(row, col, rowViewportIndex, colViewportIndex)
            {
                var rowCount = this.getRowCount();
                var colCount = this.getColumnCount();
                if(row < 0)
                    row = 0;
                else if(row >= rowCount)
                    row = rowCount - 1;
                if(col < 0)
                    col = 0;
                else if(col >= colCount)
                    col = colCount - 1;
                this._clearSelectionImp();
                this._setActiveCellImp(row,col,rowViewportIndex,colViewportIndex);
                var span = this._spanModel.find(row,col);
                if(span)
                    this._setSelectedRange(span.row,span.col,span.rowCount,span.colCount);
                else
                    this._setSelectedRange(row,col,1,1)
            })(row,col,rowViewportIndex,colViewportIndex)
        },
        getActiveRowIndex: function()
        {
            return this._activeRowIndex
        },
        getActiveColumnIndex: function()
        {
            return this._activeColIndex
        },
        selectionPolicy: function(value)
        {
            if(arguments.length === 0)
                return this._selectionModel.selectionPolicy;
            this._selectionModel.selectionPolicy = value;
            return this
        },
        selectionUnit: function(value)
        {
            if(arguments.length === 0)
                return this._selectionModel.selectionUnit;
            this._selectionModel.selectionUnit = value;
            return this
        },
        getRowResizable: function(row, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var v = true;
            var infos = this._getRowInfos(sheetArea);
            if(infos && infos[row] && infos[row].resizable !== undefined && infos[row].resizable !== null)
                v = infos[row].resizable;
            return v
        },
        setRowResizable: function(row, value, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var infos = this._getRowInfos(sheetArea);
            if(!infos[row])
                infos[row] = {};
            infos[row].resizable = value;
            infos[row].dirty = true;
            this._trigger(UI.Events.RowChanged,{
                sheet: this,
                sheetName: this._name,
                row: row,
                sheetArea: sheetArea,
                propertyName: "resizable"
            })
        },
        getColumnResizable: function(col, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var v = true;
            var infos = this._getColumnInfos(sheetArea);
            if(infos && infos[col] && infos[col].resizable !== undefined && infos[col].resizable !== null)
                v = infos[col].resizable;
            return v
        },
        setColumnResizable: function(col, value, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var infos = this._getColumnInfos(sheetArea);
            if(!infos[col])
                infos[col] = {};
            infos[col].resizable = value;
            infos[col].dirty = true;
            this._trigger(UI.Events.ColumnChanged,{
                sheet: this,
                sheetName: this._name,
                col: col,
                sheetArea: sheetArea,
                propertyName: "resizable"
            })
        },
        getRowHeight: function(row, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null || sheetArea === UI.SheetArea.rowHeader || sheetArea === UI.SheetArea.viewport)
            {
                if(row < 0 || row >= this.getRowCount(sheetArea))
                    return 0;
                if(this._rowFilter && this._rowFilter.isHideRowFilter())
                    if(this._rowFilter.isFiltered() && this._rowFilter.isRowFilteredOut(row))
                        return 0;
                if(this.rowRangeGroup && !this.rowRangeGroup._isEmpty())
                    if(this.rowRangeGroup.isCollapsed(row))
                        return 0
            }
            var h = this.defaults.rowHeight;
            if(sheetArea === UI.SheetArea.colHeader)
                h = this.defaults.colHeaderRowHeight;
            var infos = this._getRowInfos(sheetArea);
            if(infos && infos[row])
                if(infos[row].visible === false)
                    return 0;
                else if(!isNaN(infos[row].size))
                    h = parseInt(infos[row].size,10);
            return h
        },
        _getActualRowHeight: function(row, sheetArea)
        {
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.rowHeader || sheetArea === SheetArea.viewport)
                if(row < 0 || row >= this.getRowCount(sheetArea))
                    return 0;
            var h = this.defaults.rowHeight;
            if(sheetArea === UI.SheetArea.colHeader)
                h = this.defaults.colHeaderRowHeight;
            var infos = this._getRowInfos(sheetArea);
            if(infos && infos[row])
                if(!isNaN(infos[row].size))
                    h = parseInt(infos[row].size,10);
            return h
        },
        setRowHeight: function(row, value, sheetArea)
        {
            this._bindToAutoRefresh(function(row, value, sheetArea)
            {
                if(sheetArea === undefined || sheetArea === null)
                    sheetArea = UI.SheetArea.viewport;
                var infos = this._getRowInfos(sheetArea);
                if(!infos[row])
                    infos[row] = {};
                infos[row].size = value;
                infos[row].dirty = true;
                this._trigger(UI.Events.RowChanged,{
                    sheet: this,
                    sheetName: this._name,
                    row: row,
                    sheetArea: sheetArea,
                    propertyName: _cssHeight
                })
            })(row,value,sheetArea)
        },
        getRowVisible: function(row, sheetArea)
        {
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.viewport || sheetArea === SheetArea.rowHeader)
            {
                if(this._rowFilter && this._rowFilter.isHideRowFilter())
                    if(this._rowFilter.isFiltered() && this._rowFilter.isRowFilteredOut(row))
                        return false;
                if(this.rowRangeGroup && !this.rowRangeGroup._isEmpty())
                    if(0 <= row && row < this.getRowCount(sheetArea) && this.rowRangeGroup.isCollapsed(row))
                        return false
            }
            var v = true;
            var infos = this._getRowInfos(sheetArea);
            if(infos && infos[row] && infos[row].visible !== undefined && infos[row].visible !== null)
                v = infos[row].visible;
            return v
        },
        setRowVisible: function(row, value, sheetArea)
        {
            this._bindToAutoRefresh(function(row, value, sheetArea)
            {
                if(sheetArea === undefined || sheetArea === null)
                    sheetArea = UI.SheetArea.viewport;
                var infos = this._getRowInfos(sheetArea);
                if(!infos[row])
                    infos[row] = {};
                if(infos[row].visible === value)
                    return;
                infos[row].visible = value;
                infos[row].dirty = true;
                this._trigger(UI.Events.RowChanged,{
                    sheet: this,
                    sheetName: this._name,
                    row: row,
                    sheetArea: sheetArea,
                    propertyName: "isVisible"
                })
            })(row,value,sheetArea)
        },
        getColumnWidth: function(col, sheetArea)
        {
            var w = this.defaults.colWidth;
            var SheetArea = UI.SheetArea;
            if(sheetArea === SheetArea.rowHeader)
                w = this.defaults.rowHeaderColWidth;
            if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.colHeader || sheetArea === SheetArea.viewport)
            {
                if(col < 0 || col >= this.getColumnCount(sheetArea))
                    return 0;
                if(this.colRangeGroup && !this.colRangeGroup._isEmpty())
                    if(this.colRangeGroup.isCollapsed(col))
                        return 0
            }
            var infos = this._getColumnInfos(sheetArea);
            if(infos && infos[col])
            {
                if(infos[col].visible === false)
                    return 0;
                if(!isNaN(infos[col].size))
                    w = parseInt(infos[col].size,10)
            }
            return w
        },
        _getActualColumnWidth: function(col, sheetArea)
        {
            var w = this.defaults.colWidth;
            var SheetArea = UI.SheetArea;
            if(sheetArea === SheetArea.rowHeader)
                w = this.defaults.rowHeaderColWidth;
            if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.colHeader || sheetArea === SheetArea.viewport || sheetArea === SheetArea.colFooter)
                if(col < 0 || col >= this.getColumnCount(sheetArea))
                    return 0;
            var infos = this._getColumnInfos(sheetArea);
            if(infos && infos[col])
                if(!isNaN(infos[col].size))
                    w = parseInt(infos[col].size,10);
            return w
        },
        setColumnWidth: function(col, value, sheetArea)
        {
            this._bindToAutoRefresh(function(col, value, sheetArea)
            {
                if(sheetArea === undefined || sheetArea === null)
                    sheetArea = UI.SheetArea.viewport;
                var infos = this._getColumnInfos(sheetArea);
                if(!infos[col])
                    infos[col] = {};
                infos[col].size = value;
                infos[col].dirty = true;
                this._trigger(UI.Events.ColumnChanged,{
                    sheet: this,
                    sheetName: this._name,
                    col: col,
                    sheetArea: sheetArea,
                    propertyName: _cssWidth
                });
                if(this._allowCellOverflow)
                    this._clearCellOverflowModelCache()
            })(col,value,sheetArea)
        },
        getColumnVisible: function(col, sheetArea)
        {
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.colHeader || sheetArea === SheetArea.viewport)
                if(this.colRangeGroup && !this.colRangeGroup._isEmpty())
                    if(0 <= col && col < this.getColumnCount(sheetArea) && this.colRangeGroup.isCollapsed(col))
                        return false;
            var v = true;
            var infos = this._getColumnInfos(sheetArea);
            if(infos && infos[col] && infos[col].visible !== undefined && infos[col].visible !== null)
                v = infos[col].visible;
            return v
        },
        setColumnVisible: function(col, value, sheetArea)
        {
            this._bindToAutoRefresh(function(col, value, sheetArea)
            {
                if(sheetArea === undefined || sheetArea === null)
                    sheetArea = UI.SheetArea.viewport;
                var infos = this._getColumnInfos(sheetArea);
                if(!infos[col])
                    infos[col] = {};
                if(infos[col].visible === value)
                    return;
                infos[col].visible = value;
                infos[col].dirty = true;
                if(this._allowCellOverflow === true)
                    this._clearCellOverflowModelCache();
                this._trigger(UI.Events.ColumnChanged,{
                    sheet: this,
                    sheetName: this._name,
                    col: col,
                    sheetArea: sheetArea,
                    propertyName: "isVisible"
                })
            })(col,value,sheetArea)
        },
        zoom: function(factor)
        {
            factor = parseFloat(factor);
            if(isNaN(factor) || !isFinite(factor))
                return;
            if(factor > 4)
                factor = 4;
            else if(factor < 0.25)
                factor = 0.25;
            this._bindToAutoRefresh(function(factor)
            {
                if(this.showEditingLocator)
                    this._showEditingLocator();
                if(this._allowCellOverflow && this._zoomFactor !== factor)
                    this._clearCellOverflowModelCache();
                this._zoomFactor = factor
            })(factor)
        },
        invalidateLayout: function()
        {
            this._layoutModel = null;
            this._cachedGroupLayout = null;
            this._rowLayoutCache = {
                colHeader: [],
                viewport: [],
                colFooter: []
            };
            this._colLayoutCache = {
                rowHeader: [],
                viewport: []
            };
            this._clearCellOverflowModelCache();
            this._filterButtonsModel = null;
            if(this._eventHandler && window.gcGlobal.activeElement === this)
                this._eventHandler._updateValidationUI(this._activeRowIndex,this._activeColIndex);
            this._dirty = true
        },
        getViewportHeight: function(rowViewportIndex)
        {
            var layout = this._getSheetLayout();
            return rowViewportIndex > 0 ? layout.viewportHeight : layout.frozenHeight
        },
        getViewportWidth: function(columnViewportIndex)
        {
            var layout = this._getSheetLayout();
            return columnViewportIndex > 0 ? layout.viewportWidth : layout.frozenWidth
        },
        getViewportTopRow: function(rowViewportIndex)
        {
            if(rowViewportIndex === 0)
                return 0;
            else
                return Math.max(this.frozenRowCount,this._scrollTopRow)
        },
        getViewportBottomRow: function(rowViewportIndex)
        {
            var topRow = this.getViewportTopRow(rowViewportIndex);
            var viewportHeight = this.getViewportHeight(rowViewportIndex);
            var rowHeightTotal = 0;
            var rowCount = 0;
            var rc = this.getRowCount();
            if(rowViewportIndex === 0)
                rc = Math.min(this.frozenRowCount,rc);
            for(var index = topRow; index < rc && rowHeightTotal < viewportHeight; index++, rowCount++)
                rowHeightTotal += this._getZoomRowHeight(index);
            return topRow + rowCount - 1
        },
        getViewportLeftColumn: function(columnViewportIndex)
        {
            if(columnViewportIndex === 0)
                return 0;
            else
                return Math.max(this.frozenColCount,this._scrollLeftCol)
        },
        getViewportRightColumn: function(columnViewportIndex)
        {
            var leftColumn = this.getViewportLeftColumn(columnViewportIndex);
            var viewportWidth = this.getViewportWidth(columnViewportIndex);
            var columnCount = 0;
            var columnWidthTotal = 0;
            var cc = this.getColumnCount();
            if(columnViewportIndex === 0)
                cc = Math.min(this.frozenColCount,cc);
            for(var index = leftColumn; index < cc && columnWidthTotal < viewportWidth; index++, columnCount++)
                columnWidthTotal += this._getZoomColumnWidth(index);
            return leftColumn + columnCount - 1
        },
        clearSelection: function()
        {
            this._bindToAutoRefresh(function()
            {
                this._clearSelectionImp();
                this._setActiveCellImp(0,0)
            })()
        },
        _clearSelectionImp: function()
        {
            this._selectionModel.clear()
        },
        _validationError: function(row, column, value)
        {
            var dv = this.getDataValidator(row,column);
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    row: row,
                    col: column,
                    validator: dv,
                    validationResult: UI.DataValidationResult.ForceApply
                };
            this._trigger(UI.Events.ValidationError,args);
            return args.validationResult
        },
        getSpans: function(range, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var sm = this._getSpanModel(sheetArea);
            if(!range)
                return sm.slice(0);
            var t = [];
            for(var i = 0; i < sm.length; i++)
            {
                var rg = sm[i];
                if(!range || rg.intersect(range.row,range.col,range.rowCount,range.colCount))
                    t.push(rg)
            }
            return t
        },
        getCell: function(row, col, sheetArea)
        {
            return new UI.Cell(this,row,col,sheetArea)
        },
        getCells: function(row, col, row2, col2, sheetArea)
        {
            var cell = this.getCell(row,col,sheetArea);
            cell.row2 = row2;
            cell.col2 = col2;
            return cell
        },
        getRow: function(index, sheetArea)
        {
            return new UI.Row(this,index,sheetArea)
        },
        getRows: function(index, index2, sheetArea)
        {
            var row = this.getRow(index,sheetArea);
            row.index2 = index2;
            return row
        },
        getColumn: function(index, sheetArea)
        {
            return new UI.Col(this,index,sheetArea)
        },
        getColumns: function(index, index2, sheetArea)
        {
            var col = this.getColumn(index,sheetArea);
            col.index2 = index2;
            return col
        },
        setBorder: function(cellRange, border, option, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var oldState = this.isPaintSuspended();
            this.isPaintSuspended(true);
            try
            {
                var actualRange = this._getActualRange(cellRange,sheetArea);
                var row = actualRange.row;
                var col = actualRange.col;
                var rowCount = actualRange.rowCount;
                var colCount = actualRange.colCount;
                var r,
                    c;
                if(option.left || option.all || option.outline)
                    for(r = 0; r < rowCount; r++)
                        this.getCell(row + r,col,sheetArea).borderLeft(border);
                if(option.top || option.all || option.outline)
                    for(c = 0; c < colCount; c++)
                        this.getCell(row,col + c,sheetArea).borderTop(border);
                if(option.right || option.all || option.outline)
                    for(r = 0; r < rowCount; r++)
                        this.getCell(row + r,col + colCount - 1,sheetArea).borderRight(border);
                if(option.bottom || option.all || option.outline)
                    for(c = 0; c < colCount; c++)
                        this.getCell(row + rowCount - 1,col + c,sheetArea).borderBottom(border);
                if(option.innerHorizontal || option.all || option.inside)
                    for(r = 0; r < rowCount - 1; r++)
                        for(c = 0; c < colCount; c++)
                        {
                            this.getCell(row + r,col + c,sheetArea).borderBottom(border);
                            this.getCell(row + r + 1,col + c,sheetArea).borderTop(border)
                        }
                if(option.innerVertical || option.all || option.inside)
                    for(c = 0; c < colCount - 1; c++)
                        for(r = 0; r < rowCount; r++)
                        {
                            this.getCell(row + r,col + c,sheetArea).borderRight(border);
                            this.getCell(row + r,col + c + 1,sheetArea).borderLeft(border)
                        }
            }
            finally
            {
                this.isPaintSuspended(oldState)
            }
        },
        search: function(searchCondition)
        {
            if(!searchCondition)
                return null;
            var SearchFoundFlags = UI.SearchFoundFlags;
            var SearchResult = UI.SearchResult;
            if(!searchCondition.searchString || searchCondition.searchTarget === SearchFoundFlags.None || this.getRowCount(searchCondition.sheetArea) <= 0 && this.getColumnCount(searchCondition.sheetArea) <= 0)
                return new SearchResult;
            if(searchCondition.rowStart === -1)
                searchCondition.rowStart = 0;
            if(searchCondition.columnStart === -1)
                searchCondition.columnStart = 0;
            if(searchCondition.findBeginRow === -1)
                searchCondition.findBeginRow = searchCondition.rowStart;
            if(searchCondition.findBeginColumn === -1)
                searchCondition.findBeginColumn = searchCondition.columnStart;
            if(searchCondition.rowEnd === -1)
                searchCondition.rowEnd = this.getRowCount(searchCondition.sheetArea) - 1;
            if(searchCondition.columnEnd === -1)
                searchCondition.columnEnd = this.getColumnCount(searchCondition.sheetArea) - 1;
            var searchResult = new SearchResult;
            var enumerator = new UI.CellsEnumerator(this,searchCondition);
            if((searchCondition.searchFlags & UI.SearchFlags.BlockRange) > 0)
                enumerator.isBlockRange = true;
            while(enumerator.moveNext())
            {
                var rowIndex = enumerator.currentRow;
                var columnIndex = enumerator.currentColumn;
                var cell = enumerator.current();
                if((searchCondition.searchTarget & SearchFoundFlags.CellText) > 0)
                {
                    var cellText = cell.text();
                    if(this._trySearch(cellText,searchCondition.searchString,searchCondition.searchFlags) && cellText !== "")
                    {
                        searchResult.searchFoundFlag |= SearchFoundFlags.CellText;
                        searchResult.foundString = cellText
                    }
                }
                if((searchCondition.searchTarget & SearchFoundFlags.CellFormula) > 0)
                {
                    var cellFormula = cell.formula();
                    if(typeof cellFormula === const_string)
                        cellFormula = cellFormula.toString();
                    else
                        cellFormula = null;
                    if(this._trySearch(cellFormula,searchCondition.searchString,searchCondition.searchFlags) && cellFormula !== "")
                    {
                        searchResult.searchFoundFlag |= SearchFoundFlags.CellFormula;
                        searchResult.foundString = cellFormula
                    }
                }
                if(searchResult.searchFoundFlag !== SearchFoundFlags.None)
                {
                    searchResult.foundRowIndex = rowIndex;
                    searchResult.foundColumnIndex = columnIndex;
                    return searchResult
                }
            }
            return new SearchResult
        },
        showCell: function(row, col, verticalPosition, horizontalPosition)
        {
            if(row < 0 || row >= this.getRowCount())
                return;
            if(col < 0 || col >= this.getColumnCount())
                return;
            var frozenRowCount = isNaN(this.frozenRowCount) ? 0 : this.frozenRowCount;
            var frozenColCount = isNaN(this.frozenColCount) ? 0 : this.frozenColCount;
            var topRow = this._scrollTopRow;
            var leftColumn = this._scrollLeftCol;
            var columnViewportIndex = 1;
            var rowViewportIndex = 1;
            var HorizontalPosition = UI.HorizontalPosition,
                dWidth;
            if(horizontalPosition !== HorizontalPosition.left)
                if(horizontalPosition === HorizontalPosition.center)
                {
                    dWidth = Math.floor((this.getViewportWidth(columnViewportIndex) - Math.floor(this.getColumnWidth(col) * this._zoomFactor)) / 2);
                    for(; 0 < col; col--)
                    {
                        dWidth -= Math.floor(this.getColumnWidth(col - 1) * this._zoomFactor);
                        if(dWidth < 0)
                            break
                    }
                }
                else if(horizontalPosition === HorizontalPosition.right)
                {
                    dWidth = this.getViewportWidth(columnViewportIndex) - Math.floor(this.getColumnWidth(col) * this._zoomFactor);
                    for(; 0 < col; col--)
                    {
                        dWidth -= Math.floor(this.getColumnWidth(col - 1) * this._zoomFactor);
                        if(dWidth < 0)
                            break
                    }
                }
                else if(horizontalPosition === HorizontalPosition.nearest)
                    if(col >= leftColumn)
                    {
                        dWidth = this.getViewportWidth(columnViewportIndex) - Math.floor(this.getColumnWidth(col) * this._zoomFactor);
                        for(; leftColumn < col; col--)
                        {
                            dWidth -= Math.floor(this.getColumnWidth(col - 1) * this._zoomFactor);
                            if(dWidth < 0)
                                break
                        }
                    }
            var VerticalPosition = UI.VerticalPosition,
                height;
            if(verticalPosition !== VerticalPosition.top)
                if(verticalPosition === VerticalPosition.center)
                {
                    height = Math.floor((this.getViewportHeight(rowViewportIndex) - Math.floor(this.getRowHeight(row) * this._zoomFactor)) / 2);
                    for(; 0 < row; row--)
                    {
                        height -= Math.floor(this.getRowHeight(row - 1) * this._zoomFactor);
                        if(height < 0)
                            break
                    }
                }
                else if(verticalPosition === VerticalPosition.bottom)
                {
                    height = this.getViewportHeight(rowViewportIndex) - Math.floor(this.getRowHeight(row) * this._zoomFactor);
                    for(; 0 < row; row--)
                    {
                        height -= Math.floor(this.getRowHeight(row - 1) * this._zoomFactor);
                        if(height < 0)
                            break
                    }
                }
                else if(verticalPosition === VerticalPosition.nearest)
                    if(!(row < topRow || topRow === -1))
                    {
                        height = this.getViewportHeight(rowViewportIndex) - Math.floor(this.getRowHeight(row) * this._zoomFactor);
                        for(; topRow < row; row--)
                        {
                            height -= Math.floor(this.getRowHeight(row - 1) * this._zoomFactor);
                            if(height < 0)
                                break
                        }
                    }
            if(row >= frozenRowCount && row !== topRow)
            {
                this._scrollTopRow = row;
                this._syncVScrollbarPosition()
            }
            if(col >= frozenColCount && col !== leftColumn)
            {
                this._scrollLeftCol = col;
                this._syncHScollbarPosition()
            }
            if(row !== topRow || col !== leftColumn)
            {
                this.invalidateLayout();
                this.repaint()
            }
        },
        showColumn: function(col, horizontalPosition)
        {
            this.showCell(this._scrollTopRow,col,UI.VerticalPosition.top,horizontalPosition)
        },
        showRow: function(row, verticalPosition)
        {
            this.showCell(row,this._scrollLeftCol,verticalPosition,UI.HorizontalPosition.left)
        },
        bind: function(type, data, fn)
        {
            this._eventHandler.bind(type + _gcSheet,data,fn)
        },
        unbind: function(type, fn)
        {
            this._eventHandler.unbind(type + _gcSheet,fn)
        },
        unbindAll: function()
        {
            this._eventHandler.unbind(_gcSheet)
        },
        _bind: function(type, data, fn)
        {
            this._eventHandler.bind(type + _gcSheetInternal,data,fn)
        },
        _unbind: function(type, fn)
        {
            this._eventHandler.unbind(type + _gcSheetInternal,fn)
        },
        _unbindAll: function()
        {
            this._eventHandler.unbind(_gcSheetInternal)
        },
        suspendEvent: function()
        {
            this._eventHandler._eventSuspended++
        },
        resumeEvent: function()
        {
            this._eventHandler._eventSuspended--;
            if(this._eventHandler._eventSuspended < 0)
                this._eventHandler._eventSuspended = 0
        },
        currentTheme: function(value)
        {
            if(arguments.length === 0)
            {
                if(!this._currentTheme)
                    this._currentTheme = UI.SpreadThemes.Office;
                return this._currentTheme
            }
            var spreadThemes = UI.SpreadThemes;
            if(typeof value === const_string)
                if(spreadThemes.hasOwnProperty(value))
                    value = spreadThemes[value];
                else
                    value = new UI.SpreadTheme(value);
            this._bindToAutoRefresh(function(value)
            {
                this._currentTheme = value
            })(value);
            return this
        },
        reset: function()
        {
            this.defaults = {
                rowHeight: 20,
                colWidth: 62,
                rowHeaderColWidth: 40,
                colHeaderRowHeight: 20
            };
            this.gridline = {
                color: "#D0D7E5",
                showVerticalGridline: true,
                showHorizontalGridline: true
            };
            this._rowLayoutCache = {
                colHeader: [],
                viewport: [],
                colFooter: []
            };
            this._colLayoutCache = {
                rowHeader: [],
                viewport: []
            };
            this._dragRect = {};
            this._render = new UI._SheetRender(this);
            this._eventHandler = new UI._SheetEventHandler(this);
            var GcSheetModel = UI._GcSheetModel;
            this._dataModel = new GcSheetModel;
            this._dataModel.name = this._name;
            this._calcDataModel = new GcSheetModel(this._dataModel.rowCount,this._dataModel.colCount,this._name + "_calc");
            this._rowHeaderModel = new GcSheetModel(this._dataModel.rowCount,1);
            this._colHeaderModel = new GcSheetModel(1,this._dataModel.colCount);
            this._colFooterModel = new GcSheetModel(1,this._dataModel.colCount,this._name + "cf");
            var SpanModel = UI._SpanModel;
            this._spanModel = new SpanModel;
            this._colHeaderSpanModel = new SpanModel;
            this._rowHeaderSpanModel = new SpanModel;
            this._selectionModel = new UI._SelectionModel;
            this._vpSheetSource = new GrapeCity.Calc._SheetSource(this,UI.SheetArea.viewport);
            this._cellOverflowModelCache = null;
            if(typeof UI.RangeGroup !== const_undefined)
            {
                this.rowRangeGroup = new UI.RangeGroup(this.getRowCount());
                this.colRangeGroup = new UI.RangeGroup(this.getColumnCount())
            }
            this._showRowRangeGroup = true;
            this._showColumnRangeGroup = true;
            this._rowInfos = null;
            this._colInfos = null;
            this._dataSource = null;
            this.dataContext = null;
            this.autoGenerateColumns = true;
            this.autoUpdate = true;
            this._rowFilter = null;
            if(typeof UI.HideRowFilter !== const_undefined)
                this._filterButtonsModel = null;
            this._initDefaultKeyMap();
            this._scrollTopRow = 0;
            this._scrollLeftCol = 0;
            this.frozenRowCount = 0;
            this.frozenColCount = 0;
            if(UI.WorksheetSparklineGroupManager)
                this._sparklineGroupManager = new UI.WorksheetSparklineGroupManager(this,this);
            this._syncScrollbarSize();
            this.invalidateLayout();
            this._dirty = true
        },
        undoManager: function()
        {
            if(!this._undoManager)
                this._undoManager = this.parent && this.parent._undoManager ? this.parent._undoManager : new UI._UndoManager(this,-1,this.allowUndo());
            return this._undoManager
        },
        clipBoardOptions: function(value)
        {
            if(arguments.length === 0)
            {
                if(this._clipBoardOptions === undefined || this._clipBoardOptions === null)
                    this._clipBoardOptions = UI.ClipboardPasteOptions.All;
                return this._clipBoardOptions
            }
            else
            {
                this._clipBoardOptions = value;
                return this
            }
        },
        doCommand: function(action)
        {
            this._doCommand(action)
        },
        copyTo: function(fromRow, fromColumn, toRow, toColumn, rowCount, columnCount, option)
        {
            var oldState = this.isPaintSuspended();
            this.isPaintSuspended(true);
            this.suspendEvent();
            staticMembers.copyTo(this,fromRow,fromColumn,this,toRow,toColumn,rowCount,columnCount,option);
            this.resumeEvent();
            this.isPaintSuspended(oldState)
        },
        moveTo: function(fromRow, fromColumn, toRow, toColumn, rowCount, columnCount, option)
        {
            var oldState = this.isPaintSuspended();
            this.isPaintSuspended(true);
            this.suspendEvent();
            staticMembers.moveTo(this,fromRow,fromColumn,this,toRow,toColumn,rowCount,columnCount,option);
            this.resumeEvent();
            this.isPaintSuspended(oldState)
        },
        setCsv: function(row, column, text, rowDelimiter, columnDelimiter, flags)
        {
            var oldState = this.isPaintSuspended();
            this.isPaintSuspended(true);
            this.suspendEvent();
            staticMembers.setRangeText(this,row,column,text,rowDelimiter,columnDelimiter,"\"",flags);
            this.resumeEvent();
            this.isPaintSuspended(oldState)
        },
        getCsv: function(row, column, rowCount, columnCount, rowDelimiter, columnDelimiter)
        {
            return staticMembers.getRangeText(this,row,rowCount,column,columnCount,rowDelimiter,columnDelimiter,"\"",false,UI.TextFileOpenFlags.None)
        },
        canUserDragDrop: function(value)
        {
            if(arguments.length === 0)
            {
                if(this.parent && this.parent.canUserDragDrop)
                    this._allowDragDrop = this.parent.canUserDragDrop();
                else if(this._allowDragDrop === undefined || this._allowDragDrop === null)
                    this._allowDragDrop = true;
                return this._allowDragDrop
            }
            else
            {
                if(this.parent && this.parent.canUserDragDrop)
                    this.parent.canUserDragDrop(value);
                else
                    this._allowDragDrop = value;
                return this
            }
        },
        canUserDragFill: function(value)
        {
            if(arguments.length === 0)
            {
                if(this.parent && this.parent.canUserDragFill)
                    this._allowDragFill = this.parent.canUserDragFill();
                else if(this._allowDragFill === undefined || this._allowDragFill === null)
                    this._allowDragFill = true;
                return this._allowDragFill
            }
            else
            {
                var oldValue = this.canUserDragFill();
                if(oldValue !== value)
                {
                    if(this.parent && this.parent.canUserDragFill)
                        this.parent.canUserDragFill(value);
                    else
                        this._allowDragFill = value;
                    if(this.getSelections().length === 1)
                        this._render.repaintSelection(this.getSelections().toArray()[0])
                }
                return this
            }
        },
        isColumnBound: function(column)
        {
            if(column < 0 || column >= this.getColumnCount())
                return false;
            var ds = this.getDataSource();
            if(ds)
                if(this._colInfos)
                {
                    var ci = this._colInfos[column];
                    return!!(ci && ci.name)
                }
            return false
        },
        getDataColumnName: function(column)
        {
            if(column < 0 || column >= this.getColumnCount())
                return null;
            var ds = this.getDataSource();
            if(ds)
                if(this._colInfos)
                {
                    var ci = this._colInfos[column];
                    return ci.displayName || ci.name
                }
            return null
        },
        getSparkline: function(row, col)
        {
            var sheetArea = UI.SheetArea.viewport;
            var m = this._getModel(sheetArea);
            return m.getSparkline(row,col)
        },
        setSparkline: function(row, col, dataRange, dataOrientation, sparklineType, sparklineSetting, dateAxisRange, dateAxisOrientation)
        {
            return this._bindToAutoRefresh(function(row, col, dataRange, dataOrientation, sparklineType, sparklineSetting, dateAxisRange, dateAxisOrientation)
                {
                    var data = dataRange;
                    var sparkline = new UI.Sparkline(row,col,data,dataOrientation,sparklineType,sparklineSetting);
                    if(dateAxisRange && dateAxisOrientation !== undefined && dateAxisOrientation !== null)
                    {
                        sparkline.dateAxisData(dateAxisRange);
                        sparkline.dateAxisOrientation(dateAxisOrientation);
                        sparkline.group().displayDateAxis = true
                    }
                    this.removeSparkline(row,col);
                    var m = this._getModel();
                    m.setSparkline(row,col,sparkline);
                    this._sparklineGroupManager.add(sparkline.group());
                    this._raiseCellChanged("sparkline",row,col,UI.SheetArea.viewport);
                    return sparkline
                })(row,col,dataRange,dataOrientation,sparklineType,sparklineSetting,dateAxisRange,dateAxisOrientation)
        },
        removeSparkline: function(row, col)
        {
            this._bindToAutoRefresh(function(row, col)
            {
                var sheetArea = UI.SheetArea.viewport;
                var m = this._getModel(sheetArea);
                var old = m.getSparkline(row,col);
                if(old)
                {
                    var g = old.group();
                    g.remove(old);
                    if(g.count() <= 0)
                        this._sparklineGroupManager.remove(g)
                }
                m.setSparkline(row,col,null)
            })(row,col)
        },
        groupSparkline: function(sparklines)
        {
            return this._bindToAutoRefresh(function(sparklines)
                {
                    var first = null;
                    for(var i = 0; i < sparklines.length; i++)
                    {
                        var si = sparklines[i];
                        if(!si)
                            continue;
                        if(!first)
                        {
                            first = si.group();
                            continue
                        }
                        var sg = si.group();
                        sg.remove(si);
                        first.add(si);
                        if(sg.count() <= 0)
                            this._sparklineGroupManager.remove(sg)
                    }
                    return first
                })(sparklines)
        },
        ungroupSparkline: function(group)
        {
            this._bindToAutoRefresh(function(group)
            {
                if(!group)
                    return;
                var sparklines = [];
                sparklines = sparklines.concat(group._innerList);
                for(var i = 0; i < sparklines.length; i++)
                {
                    var sparkline = sparklines[i];
                    if(sparkline)
                    {
                        group.remove(sparkline);
                        var newGroup = group.clone();
                        newGroup.add(sparkline);
                        this._sparklineGroupManager.add(newGroup)
                    }
                }
                this._sparklineGroupManager.remove(group)
            })(group)
        },
        fillAuto: function(startRange, wholeRange, series)
        {
            if(!wholeRange)
                throw new Error("range");
            if(!this._checkFillRange(startRange,wholeRange,series))
                return;
            this._bindToAutoRefresh(function(startRange, wholeRange, series)
            {
                if(startRange)
                    this._eventHandler._dragFillStartRange = startRange;
                var fill = new UI.FillImp(this);
                fill.fillAuto(wholeRange,series)
            })(startRange,wholeRange,series)
        },
        fillAutobyDirection: function(startRange, wholeRange, direction)
        {
            if(!wholeRange)
                throw new Error("range");
            if(!this._checkFillRange(startRange,wholeRange,null,direction))
                return;
            this._bindToAutoRefresh(function(startRange, wholeRange, direction)
            {
                if(startRange)
                    this._eventHandler._dragFillStartRange = startRange;
                var fill = new UI.FillImp(this);
                fill.fillAutobyDirection(wholeRange,direction)
            })(startRange,wholeRange,direction)
        },
        fillLinear: function(startRange, wholeRange, series, step, stop)
        {
            if(!wholeRange)
                throw new Error("range");
            if(!this._checkFillRange(startRange,wholeRange,series))
                return;
            this._bindToAutoRefresh(function(startRange, wholeRange, series, step, stop)
            {
                if(startRange)
                    this._eventHandler._dragFillStartRange = startRange;
                var fill = new UI.FillImp(this);
                fill.fillLinear(wholeRange,series,step,stop)
            })(startRange,wholeRange,series,step,stop)
        },
        fillGrowth: function(startRange, wholeRange, series, step, stop)
        {
            if(!wholeRange)
                throw new Error("range");
            if(!this._checkFillRange(startRange,wholeRange,series))
                return;
            this._bindToAutoRefresh(function(startRange, wholeRange, series, step, stop)
            {
                if(startRange)
                    this._eventHandler._dragFillStartRange = startRange;
                var fill = new UI.FillImp(this);
                fill.fillGrowth(wholeRange,series,step,stop)
            })(startRange,wholeRange,series,step,stop)
        },
        fillDate: function(startRange, wholeRange, series, unit, step, stop)
        {
            if(!wholeRange)
                throw new Error("range");
            if(!this._checkFillRange(startRange,wholeRange,series))
                return;
            this._bindToAutoRefresh(function(startRange, wholeRange, series, unit, step, stop)
            {
                if(startRange)
                    this._eventHandler._dragFillStartRange = startRange;
                var fill = new UI.FillImp(this);
                var oaStop = null;
                if(stop !== undefined && stop !== null)
                    oaStop = new UI._DateTimeHelper(stop).toOADate();
                fill.fillDate(wholeRange,series,unit,step,oaStop)
            })(startRange,wholeRange,series,unit,step,stop)
        },
        _checkFillRange: function(startRange, wholeRange, series, direction)
        {
            if(!startRange || !wholeRange)
                return false;
            if(!wholeRange.containsRange(startRange))
                return false;
            if(series !== undefined && series !== null)
            {
                var FillSeries = UI.FillSeries;
                if(series === FillSeries.Row)
                {
                    if(startRange.row === wholeRange.row && startRange.rowCount === wholeRange.rowCount)
                        return true
                }
                else if(series === FillSeries.Column)
                    if(startRange.col === wholeRange.col && startRange.colCount === wholeRange.colCount)
                        return true
            }
            if(direction !== undefined && direction !== null)
            {
                var FillDirection = UI.FillDirection;
                if(direction === FillDirection.Up || direction === FillDirection.Down)
                {
                    if(startRange.col === wholeRange.col && startRange.colCount === wholeRange.colCount)
                        return true
                }
                else if(direction === FillDirection.Left || direction === FillDirection.Right)
                    if(startRange.row === wholeRange.row && startRange.rowCount === wholeRange.rowCount)
                        return true
            }
            return false
        },
        clear: function(row, column, rowCount, columnCount, area, type)
        {
            this._bindToAutoRefresh(function(row, column, rowCount, columnCount, area, type)
            {
                this.suspendCalcService();
                try
                {
                    var r,
                        c,
                        i,
                        j;
                    var viewport = UI.SheetArea.viewport,
                        StorageType = UI.StorageType;
                    if(area === undefined || area === null || area === viewport)
                    {
                        if((type & StorageType.Data) === StorageType.Data)
                        {
                            if(this._dataSource || this.dataContext)
                            {
                                this.suspendEvent();
                                for(r = row; r < row + rowCount; r++)
                                    for(c = column; c < column + columnCount; c++)
                                        this.setValue(r,c,null,area);
                                this.resumeEvent()
                            }
                            if(this._conditionalFormats)
                                this._conditionalFormats._clearCache();
                            if(this._rowFilter && this._rowFilter.range)
                                this._rowFilter._clear(row,column,rowCount,columnCount)
                        }
                        if((type & StorageType.Sparkline) === StorageType.Sparkline)
                            if(this._sparklineGroupManager)
                                this._sparklineGroupManager.clear(row,column,rowCount,columnCount)
                    }
                    var block = this._getModel(area);
                    var calcBlock = this._getCalcModel(area);
                    if(block)
                    {
                        r = row === -1 ? 0 : row;
                        var rc = row === -1 ? block.rowCount : rowCount;
                        c = column === -1 ? 0 : column;
                        var cc = column === -1 ? block.colCount : columnCount;
                        block.clear(r,c,rc,cc,type);
                        if(calcBlock)
                            calcBlock.clear(r,c,rc,cc,type);
                        if((type & StorageType.Style) === StorageType.Style)
                            if(!(row >= 0 && column >= 0))
                                if(column >= 0)
                                    for(j = 0; j < cc; j++)
                                        this.setStyle(-1,c + j,null,area);
                                else if(row >= 0)
                                    for(i = 0; i < rc; i++)
                                        this.setStyle(r + i,-1,null,area);
                                else
                                    this.setStyle(-1,-1,null,area);
                        if((type & StorageType.Axis) === StorageType.Axis)
                            if(!(row >= 0 && column >= 0))
                                if(column >= 0)
                                    for(j = 0; j < cc; j++)
                                    {
                                        this.setColumnVisible(c + j,true,area);
                                        this.setColumnResizable(c + j,true,area);
                                        this.setColumnWidth(c + j,area === UI.SheetArea.rowHeader ? this.defaults.rowHeaderColWidth : this.defaults.colWidth,area)
                                    }
                                else if(row >= 0)
                                    for(i = 0; i < rc; i++)
                                    {
                                        this.setRowVisible(r + i,true,area);
                                        this.setRowResizable(r + i,true,area);
                                        this.setRowHeight(r + i,area === UI.SheetArea.colHeader ? this.defaults.colHeaderRowHeight : this.defaults.rowHeight,area)
                                    }
                                else
                                {
                                    for(j = 0; j < cc; j++)
                                    {
                                        this.setColumnVisible(c + j,true,area);
                                        this.setColumnResizable(c + j,true,area);
                                        this.setColumnWidth(c + j,area === UI.SheetArea.rowHeader ? this.defaults.rowHeaderColWidth : this.defaults.colWidth,area)
                                    }
                                    for(i = 0; i < rc; i++)
                                    {
                                        this.setRowVisible(r + i,true,area);
                                        this.setRowResizable(r + i,true,area);
                                        this.setRowHeight(r + i,area === UI.SheetArea.colHeader ? this.defaults.colHeaderRowHeight : this.defaults.rowHeight,area)
                                    }
                                }
                        for(i = 0; i < rc; i++)
                            for(j = 0; j < cc; j++)
                            {
                                r = row + i;
                                c = column + j;
                                if((type & StorageType.Data) === StorageType.Data)
                                    this._raiseCellChanged(_value,r,c,area);
                                if((type & StorageType.Style) === StorageType.Style)
                                    this._raiseCellChanged("style",r,c,area);
                                if((type & StorageType.Sparkline) === StorageType.Sparkline)
                                    this._raiseCellChanged("sparkline",r,c,area);
                                if((type & StorageType.Axis) === StorageType.Axis)
                                    this._raiseCellChanged("axis",r,c,area);
                                if((type & StorageType.BindingPath) === StorageType.BindingPath)
                                    this._raiseCellChanged("bindingPath",r,c,area)
                            }
                    }
                    if(area === undefined || area === null || area === viewport)
                        if((type & StorageType.Data) === StorageType.Data)
                            this._getSheetSource()._addCellsToDirty(row,column,rowCount,columnCount)
                }
                finally
                {
                    this.resumeCalcService()
                }
            })(row,column,rowCount,columnCount,area,type)
        },
        getConditionalFormats: function()
        {
            if(!this._conditionalFormats)
                this._conditionalFormats = new UI.ConditionalFormats(this);
            return this._conditionalFormats
        },
        rowFilter: function(value)
        {
            if(arguments.length === 0)
                return this._rowFilter;
            else
                return this._bindToAutoRefresh(function(value)
                    {
                        if(this._rowFilter)
                            this._rowFilter.reset();
                        this._rowFilter = value;
                        if(this._rowFilter)
                            this._rowFilter.sheet = this;
                        return this
                    })(value)
        },
        autoFitColumn: function(column)
        {
            if(column < 0 || column >= this.getColumnCount())
                return;
            var action = new UI.UndoRedo.ColumnAutoFitUndoAction(this,[{col: column}],false);
            action.execute(this)
        },
        autoFitRow: function(row)
        {
            if(row < 0 || row >= this.getRowCount())
                return;
            var action = new UI.UndoRedo.RowAutoFitUndoAction(this,[{row: row}],false);
            action.execute(this)
        },
        setGridlineOptions: function(options)
        {
            this._bindToAutoRefresh(function(options)
            {
                if(options)
                {
                    if(!this.gridline)
                        this.gridline = {};
                    if(options.color)
                        this.gridline.color = options.color;
                    if(options.showVerticalGridline !== null && options.showVerticalGridline !== undefined)
                        this.gridline.showVerticalGridline = options.showVerticalGridline;
                    if(options.showHorizontalGridline !== null && options.showHorizontalGridline !== undefined)
                        this.gridline.showHorizontalGridline = options.showHorizontalGridline
                }
            })(options)
        },
        getGridlineOptions: function()
        {
            if(!this.gridline)
                this.gridline = {};
            var options = {};
            options.color = this.gridline.color;
            options.showVerticalGridline = this.gridline.showVerticalGridline;
            options.showHorizontalGridline = this.gridline.showHorizontalGridline;
            return options
        },
        getFrozenRowCount: function()
        {
            return this.frozenRowCount
        },
        getFrozenColumnCount: function()
        {
            return this.frozenColCount
        },
        setRowHeaderVisible: function(visible)
        {
            this._bindToAutoRefresh(function(options)
            {
                this.rowHeaderVisible = visible
            })(visible)
        },
        getRowHeaderVisible: function()
        {
            return this.rowHeaderVisible
        },
        setColumnHeaderVisible: function(visible)
        {
            this._bindToAutoRefresh(function(options)
            {
                this.colHeaderVisible = visible
            })(visible)
        },
        getColumnHeaderVisible: function()
        {
            return this.colHeaderVisible
        },
        setRowHeaderAutoText: function(autoText)
        {
            this._bindToAutoRefresh(function(autoText)
            {
                this.rowHeaderAutoText = autoText
            })(autoText)
        },
        getRowHeaderAutoText: function()
        {
            return this.rowHeaderAutoText
        },
        setColumnHeaderAutoText: function(autoText)
        {
            this._bindToAutoRefresh(function(autoText)
            {
                this.colHeaderAutoText = autoText
            })(autoText)
        },
        getColumnHeaderAutoText: function()
        {
            return this.colHeaderAutoText
        },
        setRowHeaderAutoTextIndex: function(autoTextIndex)
        {
            this._bindToAutoRefresh(function(autoTextIndex)
            {
                this.rowHeaderAutoTextIndex = autoTextIndex
            })(autoTextIndex)
        },
        getRowHeaderAutoTextIndex: function()
        {
            return this.rowHeaderAutoTextIndex
        },
        setColumnHeaderAutoTextIndex: function(autoTextIndex)
        {
            this._bindToAutoRefresh(function(autoTextIndex)
            {
                this.colHeaderAutoTextIndex = autoTextIndex
            })(autoTextIndex)
        },
        getColumnHeaderAutoTextIndex: function()
        {
            return this.colHeaderAutoTextIndex
        },
        setIsProtected: function(isProtected)
        {
            this._bindToAutoRefresh(function(autoTextIndex)
            {
                this.isProtected = isProtected
            })(isProtected)
        },
        getIsProtected: function()
        {
            return this.isProtected
        },
        setArray: function(row, column, array, setFormula)
        {
            var rowCount = this.getRowCount(),
                columnCount = this.getColumnCount();
            if(array && 0 <= row && row < rowCount && 0 <= column && column < columnCount)
            {
                this.suspendCalcService();
                var oldState = this.isPaintSuspended();
                this.isPaintSuspended(true);
                try
                {
                    var self = this;
                    $.each(array,function(i, v)
                    {
                        if(!(v instanceof Array))
                        {
                            var r = row + i,
                                c = column;
                            if(r < rowCount && c < columnCount)
                                if(setFormula)
                                    self.setFormula(r,c,v,UI.SheetArea.viewport);
                                else
                                    self.setValue(r,c,v,UI.SheetArea.viewport,true)
                        }
                        else
                            $.each(v,function(j, vv)
                            {
                                var r = row + i,
                                    c = column + j;
                                if(r < rowCount && c < columnCount)
                                    if(setFormula)
                                        self.setFormula(r,c,vv,UI.SheetArea.viewport);
                                    else
                                        self.setValue(r,c,vv,UI.SheetArea.viewport,true)
                            })
                    })
                }
                finally
                {
                    this.resumeCalcService();
                    this.isPaintSuspended(oldState)
                }
            }
        },
        getArray: function(row, column, rowCount, columnCount, getFormula)
        {
            var array = [];
            var rc = this.getRowCount(),
                cc = this.getColumnCount();
            if(0 <= row && row < rc && 0 <= column && column < cc)
            {
                if(row + rowCount > rc)
                    rowCount = rc - row;
                if(column + columnCount > cc)
                    columnCount = cc - column;
                for(var i = 0; i < rowCount; i++)
                {
                    array[i] = [];
                    for(var j = 0; j < columnCount; j++)
                        if(getFormula)
                            array[i][j] = this.getFormula(row + i,column + j);
                        else
                            array[i][j] = this.getValue(row + i,column + j)
                }
            }
            return array
        },
        showRowRangeGroup: function(value)
        {
            if(arguments.length === 0)
                return this._showRowRangeGroup;
            if(this._showRowRangeGroup !== value)
                this._bindToAutoRefresh(function(value)
                {
                    this._showRowRangeGroup = value
                })(value);
            return this
        },
        showColumnRangeGroup: function(value)
        {
            if(arguments.length === 0)
                return this._showColumnRangeGroup;
            if(this._showColumnRangeGroup !== value)
                this._bindToAutoRefresh(function(value)
                {
                    this._showColumnRangeGroup = value
                })(value);
            return this
        },
        _GROUPBUTTON_WIDTH: 15.0,
        _GROUPBUTTON_HEIGHT: 15.0,
        _GROUPPADDING: 2.0,
        _rowLayoutCache: null,
        _colLayoutCache: null,
        _scrollCallback: null,
        _dragRect: null,
        _scrollTopRow: 0,
        _scrollLeftCol: 0,
        _activeRowDirty: false,
        _initPaint: function()
        {
            var isStandardCanvas = GrapeCity.UI._isStandardCanvas();
            if(isStandardCanvas)
                return;
            var control = this._getCanvas();
            if(control && control.firstChild && control.firstChild.loaded)
            {
                this.invalidateLayout();
                this.repaint()
            }
            else
            {
                var self = this;
                window.setTimeout(function()
                {
                    self.initPaint.call(self)
                },10)
            }
        },
        _bindToAutoRefresh: function(fn, context)
        {
            if(!context)
                context = this;
            return function()
                {
                    var t = fn.apply(context,arguments);
                    if(!context._paintSuspended)
                    {
                        if(typeof context.invalidateLayout === const_function)
                            context.invalidateLayout();
                        if(typeof context.repaint === const_function)
                            context.repaint()
                    }
                    return t
                }
        },
        _isValidSheetName: function(sheetName)
        {
            if(!sheetName || sheetName === "")
                return false;
            if(!this.parent)
                return true;
            var length = this.parent.sheets.length;
            for(var i = 0; i < length; i++)
            {
                var sheet = this.parent.sheets[i];
                if(sheet !== this)
                    if(sheetName === sheet._name)
                        return false
            }
            return true
        },
        _init: function(name)
        {
            this._name = name;
            this._bounds = new UI.Rect(0,0,0,0);
            this.reset()
        },
        applyOptions: function(options)
        {
            if(!options)
                return;
            if(typeof options.name === const_string && options.name.length > 0)
                this._name = options.name;
            if(options.data)
                this.setDataSource(options.data);
            if(typeof options.defaultRowHeight === const_number)
                this.defaults.rowHeight = options.defaultRowHeight;
            if(typeof options.defaultColWidth === const_number)
                this.defaults.colWidth = options.defaultColWidth;
            if(typeof options.defaultRowHeaderColWidth === const_number)
                this.defaults.rowHeaderColWidth = options.defaultRowHeaderColWidth;
            if(typeof options.defaultColHeaderRowHeight === const_number)
                this.defaults.colHeaderRowHeight = options.defaultColHeaderRowHeight;
            if(typeof options.rowCount === const_number)
                this.setRowCount(options.rowCount);
            if(typeof options.colCount === const_number)
                this.setColumnCount(options.colCount);
            if(typeof options.frozenRowCount === const_number)
                this.frozenRowCount = options.frozenRowCount;
            if(typeof options.frozenColCount === const_number)
                this.frozenColCount = options.frozenColCount;
            if(options.gridlineColor)
                this.gridline.color = options.gridlineColor;
            if(typeof options.showVerticalGridline === const_boolean)
                this.gridline.showVerticalGridline = options.showVerticalGridline;
            if(typeof options.showHorizontalGridline === const_boolean)
                this.gridline.showHorizontalGridline = options.showHorizontalGridline;
            if(options.borderColor)
                this.borderColor = options.borderColor;
            if(typeof options.borderWidth === const_number)
                this.borderWidth = options.borderWidth;
            if(typeof options._zoomFactor === const_number)
                this._zoomFactor = options._zoomFactor;
            if(typeof options.rowHeaderVisible === const_boolean)
                this.rowHeaderVisible = options.rowHeaderVisible;
            if(typeof options.colHeaderVisible === const_boolean)
                this.colHeaderVisible = options.colHeaderVisible;
            if(typeof options.autoUpdate === const_boolean)
                this.autoUpdate = options.autoUpdate;
            if(typeof options.autoGenerateColumns === const_boolean)
                this.autoGenerateColumns = options.autoGenerateColumns;
            if(options.rowHeaderAutoText)
                this.rowHeaderAutoText = options.rowHeaderAutoText;
            if(options.colHeaderAutoText)
                this.colHeaderAutoText = options.colHeaderAutoText;
            if(typeof options._activeRowIndex === const_number)
                this._activeRowIndex = options._activeRowIndex;
            if(typeof options._activeColIndex === const_number)
                this._activeColIndex = options._activeColIndex;
            if(typeof options._allowCellOverflow === const_boolean)
                this._allowCellOverflow = options._allowCellOverflow;
            if(typeof options.isProtected === const_boolean)
                this.isProtected = options.isProtected;
            if(typeof options.allowUndo === const_boolean)
                this.allowUndo(options.allowUndo);
            if(options.columns && options.columns.length > 0)
            {
                this.autoGenerateColumns = false;
                this.bindColumns(options.columns)
            }
            if(options.dataContext)
            {
                var dc = new UI.DataContext(options.dataContext.read,options.dataContext.create,options.dataContext.update,options.dataContext.remove);
                this.setDataContext(dc)
            }
        },
        _initDefaultKeyMap: function()
        {
            var spreadActions = UI.SpreadActions;
            var key = UI.Key;
            this.addKeyMap(key.left,false,false,false,spreadActions.navigationLeft);
            this.addKeyMap(key.left,true,false,false,spreadActions.navigationHome2);
            this.addKeyMap(key.right,false,false,false,spreadActions.navigationRight);
            this.addKeyMap(key.right,true,false,false,spreadActions.navigationEnd);
            this.addKeyMap(key.up,false,false,false,spreadActions.navigationUp);
            this.addKeyMap(key.up,true,false,false,spreadActions.navigationTop);
            this.addKeyMap(key.down,false,false,false,spreadActions.navigationDown);
            this.addKeyMap(key.down,true,false,false,spreadActions.navigationBottom);
            this.addKeyMap(key.home,false,false,false,spreadActions.navigationHome);
            this.addKeyMap(key.home,true,false,false,spreadActions.navigationFirst);
            this.addKeyMap(key.end,false,false,false,spreadActions.navigationEnd);
            this.addKeyMap(key.end,true,false,false,spreadActions.navigationLast);
            this.addKeyMap(key.tab,false,false,false,spreadActions.commitInputNavigationTabNext);
            this.addKeyMap(key.tab,false,true,false,spreadActions.commitInputNavigationTabPrevious);
            this.addKeyMap(key.pup,false,false,false,spreadActions.navigationPageUp);
            this.addKeyMap(key.pup,true,false,false,spreadActions.navigationPreviousSheet);
            this.addKeyMap(key.pdn,false,false,false,spreadActions.navigationPageDown);
            this.addKeyMap(key.pdn,true,false,false,spreadActions.navigationNextSheet);
            this.addKeyMap(key.del,false,false,false,spreadActions.clear);
            this.addKeyMap(key.backspace,false,false,false,spreadActions.clearAndEditing);
            this.addKeyMap(key.enter,false,false,false,spreadActions.commitInputNavigationDown);
            this.addKeyMap(key.enter,false,true,false,spreadActions.commitInputNavigationUp);
            this.addKeyMap(key.esc,false,false,false,spreadActions.cancelInput);
            this.addKeyMap(key.left,false,true,false,spreadActions.selectionLeft);
            this.addKeyMap(key.right,false,true,false,spreadActions.selectionRight);
            this.addKeyMap(key.up,false,true,false,spreadActions.selectionUp);
            this.addKeyMap(key.down,false,true,false,spreadActions.selectionDown);
            this.addKeyMap(key.home,false,true,false,spreadActions.selectionHome);
            this.addKeyMap(key.end,false,true,false,spreadActions.selectionEnd);
            this.addKeyMap(key.pup,false,true,false,spreadActions.selectionPageUp);
            this.addKeyMap(key.pdn,false,true,false,spreadActions.selectionPageDown);
            this.addKeyMap(key.left,true,true,false,spreadActions.selectionHome);
            this.addKeyMap(key.right,true,true,false,spreadActions.selectionEnd);
            this.addKeyMap(key.up,true,true,false,spreadActions.selectionTop);
            this.addKeyMap(key.down,true,true,false,spreadActions.selectionBottom);
            this.addKeyMap(key.home,true,true,false,spreadActions.selectionFirst);
            this.addKeyMap(key.end,true,true,false,spreadActions.selectionLast);
            this.addKeyMap(key.c,true,false,false,spreadActions.copy);
            this.addKeyMap(key.x,true,false,false,spreadActions.cut);
            this.addKeyMap(key.v,true,false,false,spreadActions.paste);
            this.addKeyMap(key.z,true,false,false,spreadActions.undo);
            this.addKeyMap(key.y,true,false,false,spreadActions.redo)
        },
        _setHost: function(host)
        {
            var canvas = document.createElement("canvas");
            canvas.setAttribute("id",host.getAttribute("id") + "_vp");
            $(canvas).html("You need a browser which full supports HTML5 Canvas to run SpreadJS");
            host.appendChild(canvas);
            canvas.gcObject = true;
            if(this._canvas)
            {
                $(this._canvas).unbind(_mouseDown_gcSheet);
                $(this._canvas).unbind(_mouseMove_gcSheet);
                $(this._canvas).unbind(_mouseUp_gcSheet);
                $(this._canvas).unbind(_mouseOut_gcSheet);
                $(this._canvas).unbind(_dblclick_gcSheet);
                $(this._canvas).unbind(_mouseWheel_gcSheet);
                if(this._canvas.parentNode)
                    this._canvas.parentNode.removeChild(this._canvas)
            }
            this._canvas = canvas;
            canvas.setAttribute('renderMethod','auto');
            var self = this;
            this._mouseDownDelegate = function(event)
            {
                return self._eventHandler.doMouseDown(event)
            };
            this._mouseMoveDelegate = function(event)
            {
                if(self._eventHandler._isMouseCapture)
                    return;
                self._eventHandler.doMouseMove(event)
            };
            this._mouseUpDelegate = function(event)
            {
                if(!self._continueMouseUpBubble)
                {
                    if(self._eventHandler._isMouseCapture)
                        return;
                    return self._eventHandler.doMouseUp(event)
                }
            };
            this._mouseOutDelegate = function(event)
            {
                return self._eventHandler.doMouseOut(event)
            };
            this._dblClickDelegate = function(event)
            {
                var sheet = self,
                    i,
                    selectedRange,
                    action;
                var currentTarget = sheet._currentTarget;
                if(sheet._currentTarget)
                {
                    sheet._trigger(UI.Events.CellDoubleClick,{
                        sheet: sheet,
                        sheetName: sheet._name,
                        sheetArea: sheet._currentTarget.hitTestType,
                        row: sheet._currentTarget.row,
                        col: sheet._currentTarget.col
                    });
                    var resizeInfo = currentTarget.resizeInfo;
                    if(resizeInfo)
                    {
                        if(resizeInfo.action === "sizeRow")
                        {
                            var rowList = [];
                            if(sheet._isRowSelected(resizeInfo.index))
                                for(i = 0; i < sheet._selectionModel.length; i++)
                                {
                                    selectedRange = sheet._selectionModel[i];
                                    if(selectedRange.col === -1)
                                    {
                                        selectedRange = sheet._getActualRange(selectedRange);
                                        for(var r = 0; r < selectedRange.rowCount; r++)
                                            rowList.push({row: selectedRange.row + r})
                                    }
                                }
                            else
                                rowList.push({row: resizeInfo.index});
                            action = new UI.UndoRedo.RowAutoFitUndoAction(sheet,rowList,false);
                            sheet._doCommand(action)
                        }
                        else
                        {
                            var columnList = [];
                            if(sheet._isColumnSelected(resizeInfo.index))
                                for(i = 0; i < sheet._selectionModel.length; i++)
                                {
                                    selectedRange = sheet._selectionModel[i];
                                    if(selectedRange.row === -1)
                                    {
                                        selectedRange = sheet._getActualRange(selectedRange);
                                        for(var c = 0; c < selectedRange.colCount; c++)
                                            columnList.push({col: selectedRange.col + c})
                                    }
                                }
                            else
                                columnList.push({col: resizeInfo.index});
                            action = new UI.UndoRedo.ColumnAutoFitUndoAction(sheet,columnList,false);
                            sheet._doCommand(action)
                        }
                        return
                    }
                }
                return self._eventHandler.startEdit(event)
            };
            $(canvas).bind(_mouseDown_gcSheet,this._mouseDownDelegate);
            $(canvas).bind(_mouseMove_gcSheet,this._mouseMoveDelegate);
            $(canvas).bind(_mouseUp_gcSheet,this._mouseUpDelegate);
            $(canvas).bind(_mouseOut_gcSheet,this._mouseOutDelegate);
            $(canvas).bind(_dblclick_gcSheet,this._dblClickDelegate);
            this._mouseWheelDelegate = function(e)
            {
                e = e ? e : window.event;
                var wheelData = e.detail ? e.detail : e.wheelDelta / -40;
                self._eventHandler.doMouseWheel(e,parseInt(wheelData,10));
                UI.cancelDefault(e);
                return false
            };
            $(canvas).bind(_mouseWheel_gcSheet,this._mouseWheelDelegate);
            $(window).unbind(_resize_gcSheet);
            $(window).bind(_resize_gcSheet,UI.createEventHandler(this._eventHandler,this._eventHandler.doResize));
            this._initializeActiveCell();
            this._eventHandler.doResize()
        },
        _disposeValidationUI: function()
        {
            if(this._validationInputMessage)
            {
                $(this._validationInputMessage).remove();
                this._validationInputMessage = null
            }
            if(this._validationButton)
            {
                $(this._validationButton).remove();
                this._validationButton = null
            }
            if(this._validationSelect)
            {
                $(this._validationSelect).remove();
                this._validationSelect = null
            }
        },
        _dispose: function()
        {
            if(this.isEditing())
                this.endEdit();
            var canvas = this._canvas;
            if(canvas)
            {
                $(canvas).unbind(_mouseDown_gcSheet);
                $(canvas).unbind(_mouseMove_gcSheet);
                $(canvas).unbind(_mouseUp_gcSheet);
                $(canvas).unbind(_mouseOut_gcSheet);
                $(canvas).unbind(_dblclick_gcSheet);
                $(canvas).unbind(_mouseWheel_gcSheet);
                canvas.parentNode.removeChild(canvas)
            }
            this._eventHandler._dispose();
            this._canvas = null;
            $(window).unbind(_resize_gcSheet);
            if(this._filterDialiog)
                this._filterDialiog.close();
            if(this._smartTag)
                this._smartTag.close();
            if(this._editor)
                $(this._editor).remove();
            this._disposeValidationUI()
        },
        _getModel: function(sheetArea)
        {
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.viewport)
                return this._dataModel;
            else if(sheetArea === SheetArea.rowHeader)
                return this._rowHeaderModel;
            else if(sheetArea === SheetArea.colHeader)
                return this._colHeaderModel;
            return null
        },
        _getCalcModel: function(sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null || sheetArea === UI.SheetArea.viewport)
                return this._calcDataModel;
            return null
        },
        _getValueImp: function(m, row, col, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null || sheetArea === UI.SheetArea.viewport)
                if(this.xSheet)
                    return this.xSheet.getValue(row,col);
            var t = m.getValue(row,col);
            var ko = window.ko;
            if((t === undefined || t === null) && this._dataSource && (sheetArea === undefined || sheetArea === null || sheetArea === UI.SheetArea.viewport))
                if(this._isDataView(this._dataSource))
                {
                    var dataLength = this._dataSource.count();
                    if(row < dataLength)
                    {
                        var drow = this._dataSource.item(row);
                        if(!drow)
                            return null;
                        var ci = this._colInfos[col];
                        if(ci && ci.name && ci.name.length > 0)
                            return this._dataSource.getProperty(drow,ci.name)
                    }
                }
                else if(ko && ko.isObservable(this._dataSource))
                {
                    var dataLength = this._dataSource().length;
                    if(row < dataLength)
                    {
                        var drow = this._dataSource()[row];
                        if(!drow)
                            return null;
                        var ci = this._colInfos[col];
                        if(ci && ci.name && ci.name.length > 0)
                            if(typeof ci.value === const_function)
                                return ci.value(drow);
                            else if(typeof ko !== const_undefined && ko.isObservable(drow[ci.name]))
                                t = drow[ci.name]();
                            else
                                t = drow[ci.name];
                        else if(typeof drow === "string" || !isNaN(drow))
                            if(col === 0)
                                return drow
                    }
                }
                else if(this._isCellBinding(this._dataSource))
                {
                    var path = this.getBindingPath(row,col,sheetArea);
                    if(path)
                        return this._dataSource.getValue(path)
                }
                else
                {
                    var dataLength = this._dataSource.length;
                    if(row < dataLength)
                    {
                        var drow = this._dataSource[row];
                        if(drow === undefined || drow === null)
                            return null;
                        var ci = this._colInfos[col];
                        if(ci && ci.name && ci.name.length > 0)
                            if(typeof ci.value === const_function)
                                return ci.value(drow);
                            else
                            {
                                var ko = window.ko;
                                if(typeof ko !== const_undefined && ko.isObservable(drow[ci.name]))
                                    t = drow[ci.name]();
                                else
                                    t = drow[ci.name]
                            }
                        else if(typeof drow === "string" || !isNaN(drow))
                            if(col === 0)
                                return drow
                    }
                }
            if(typeof t === const_string)
            {
                var x = t.match(_jsonDateRegExp);
                if(x && x.length > 0)
                {
                    x = x[0].substring(6);
                    t = new Date(parseFloat(x))
                }
            }
            return t
        },
        _getSwapIndex: function(array, start, index, count, row)
        {
            var t = array[index - start];
            while(t.index < index)
                t = array[t.index - start];
            return t.index
        },
        _quickSortImp: function(arr)
        {
            if(arr.length <= 1)
                return arr;
            var pivotIndex = Math.floor(arr.length / 2);
            var pivot = arr[pivotIndex];
            var left = [];
            var right = [];
            var equal = [];
            for(var i = 0; i < arr.length; i++)
            {
                var compareResult = this._sortCompare(arr[i],pivot);
                if(compareResult < 0)
                    left.push(arr[i]);
                else if(compareResult > 0)
                    right.push(arr[i]);
                else
                    equal.push(arr[i])
            }
            return this._quickSortImp(left).concat(equal,this._quickSortImp(right))
        },
        _quickSort: function(row, column, rowCount, columnCount, byRows, sortInfo)
        {
            var count = byRows ? rowCount : columnCount;
            var array = [];
            var temp;
            if(byRows)
                for(temp = 0; temp < count; temp++)
                    array[temp] = {
                        sheet: this,
                        index: row + temp,
                        byRows: byRows,
                        sortInfo: sortInfo
                    };
            else
                for(temp = 0; temp < count; temp++)
                    array[temp] = {
                        sheet: this,
                        index: column + temp,
                        byRows: byRows,
                        sortInfo: sortInfo
                    };
            array = this._quickSortImp(array);
            return array
        },
        _isEquals: function(v1, v2)
        {
            if(v1 instanceof Date && v2 instanceof Date)
                return new UI._DateTimeHelper(v1).toOADate() === new UI._DateTimeHelper(v2).toOADate();
            else
                return v1 === v2
        },
        _isGreaterThan: function(v1, v2)
        {
            if(typeof v1 === "boolean")
                v1 = v1 ? 1 : 0;
            else if(v1 instanceof Date)
                v1 = new UI._DateTimeHelper(v1).toOADate();
            if(typeof v2 === "boolean")
                v2 = v2 ? 1 : 0;
            else if(v2 instanceof Date)
                v2 = new UI._DateTimeHelper(v2).toOADate();
            if(typeof v1 !== typeof v2 && (typeof v1 === "number" || typeof v2 === "number"))
                return typeof v2 === "number";
            return v1 > v2
        },
        _sortCompare: function(x, y)
        {
            if(x.sortInfo && x.sortInfo.length > 0)
            {
                var ret = 0,
                    value1,
                    value2,
                    value1IsNullOrEmpty,
                    value2IsNullOrEmpty;
                for(var i = 0; i < x.sortInfo.length; i++)
                {
                    if(x.sortInfo[i])
                    {
                        var ascending = x.sortInfo[i].ascending;
                        var index = x.sortInfo[i].index;
                        if(0 <= index)
                            if(x.byRows)
                            {
                                value1 = x.sheet.getValue(x.index,index);
                                value2 = y.sheet.getValue(y.index,index);
                                value1IsNullOrEmpty = value1 === undefined || value1 === null || value1 === "";
                                value2IsNullOrEmpty = value2 === undefined || value2 === null || value2 === "";
                                if(value1IsNullOrEmpty || value2IsNullOrEmpty)
                                {
                                    if(value1IsNullOrEmpty && value2IsNullOrEmpty)
                                        ret = 0;
                                    else if(value1IsNullOrEmpty && !value2IsNullOrEmpty)
                                        ret = 1;
                                    else if(!value1IsNullOrEmpty && value2IsNullOrEmpty)
                                        ret = -1
                                }
                                else if(this._isEquals(value1,value2))
                                    ret = 0;
                                else if(this._isGreaterThan(value1,value2))
                                    ret = ascending ? 1 : -1;
                                else
                                    ret = ascending ? -1 : 1
                            }
                            else
                            {
                                value1 = x.sheet.getValue(index,x.index);
                                value2 = y.sheet.getValue(index,y.index);
                                value1IsNullOrEmpty = value1 === undefined || value1 === null || value1 === "";
                                value2IsNullOrEmpty = value2 === undefined || value2 === null || value2 === "";
                                if(value1IsNullOrEmpty || value2IsNullOrEmpty)
                                {
                                    if(value1IsNullOrEmpty && value2IsNullOrEmpty)
                                        ret = 0;
                                    else if(value1IsNullOrEmpty && !value2IsNullOrEmpty)
                                        ret = 1;
                                    else if(!value1IsNullOrEmpty && value2IsNullOrEmpty)
                                        ret = -1
                                }
                                else if(this._isEquals(value1,value2))
                                    ret = 0;
                                else if(this._isGreaterThan(value1,value2))
                                    ret = ascending ? 1 : -1;
                                else
                                    ret = ascending ? -1 : 1
                            }
                    }
                    if(ret !== 0)
                        break
                }
                return ret
            }
            return 0
        },
        _getActualRange: function(range, sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            var actualRange = new UI.Range;
            if(range === null || range === undefined)
                return actualRange;
            actualRange.col = range.col;
            actualRange.row = range.row;
            actualRange.colCount = range.colCount;
            actualRange.rowCount = range.rowCount;
            if(actualRange.col === -1)
            {
                actualRange.col = 0;
                actualRange.colCount = this.getColumnCount(sheetArea)
            }
            if(actualRange.row === -1)
            {
                actualRange.row = 0;
                actualRange.rowCount = this.getRowCount(sheetArea)
            }
            return actualRange
        },
        _getRangeRect: function(rowViewportIndex, colViewportIndex, range)
        {
            var layout = this._getSheetLayout();
            var leftCol = 0;
            var topRow = 0;
            var rect = new UI.Rect(layout.frozenX,layout.frozenY,0,0);
            var actualRange = this._getActualRange(range);
            var frozenColCount = isNaN(this.frozenColCount) ? 0 : this.frozenColCount;
            var frozenRowCount = isNaN(this.frozenRowCount) ? 0 : this.frozenRowCount;
            if(colViewportIndex > 0)
            {
                leftCol = Math.max(frozenColCount,this._scrollLeftCol);
                rect.x = layout.viewportX
            }
            if(rowViewportIndex > 0)
            {
                topRow = Math.max(frozenRowCount,this._scrollTopRow);
                rect.y = layout.viewportY
            }
            var row,
                col;
            if(actualRange.col >= leftCol)
            {
                for(col = leftCol; col < actualRange.col; col++)
                    if(this.getColumnVisible(col))
                    {
                        rect.x += this._getZoomColumnWidth(col);
                        if(rect.x > layout.x + layout.width)
                            return rect
                    }
                for(col = actualRange.col; col < actualRange.col + actualRange.colCount; col++)
                    if(this.getColumnVisible(col))
                    {
                        rect.width += this._getZoomColumnWidth(col);
                        if(rect.width + rect.x > layout.x + layout.width)
                            break
                    }
            }
            else if(frozenColCount > 0 && actualRange.col < frozenColCount)
            {
                for(col = frozenColCount - 1; col >= actualRange.col; col--)
                    if(this.getColumnVisible(col))
                    {
                        rect.x -= this._getZoomColumnWidth(col);
                        if(rect.x < layout.x)
                        {
                            col--;
                            break
                        }
                    }
                for(col = actualRange.col; col < Math.min(frozenColCount,actualRange.col + actualRange.colCount); col++)
                    if(this.getColumnVisible(col))
                    {
                        rect.width += this._getZoomColumnWidth(col);
                        if(rect.width + rect.x > layout.x + layout.width)
                            break
                    }
                for(col = leftCol; col < actualRange.col + actualRange.colCount; col++)
                    if(this.getColumnVisible(col))
                    {
                        rect.width += this._getZoomColumnWidth(col);
                        if(rect.width + rect.x > layout.x + layout.width)
                            break
                    }
            }
            else
            {
                for(col = leftCol - 1; col >= actualRange.col; col--)
                    if(this.getColumnVisible(col))
                    {
                        rect.x -= this._getZoomColumnWidth(col);
                        if(rect.x < layout.x)
                        {
                            col--;
                            break
                        }
                    }
                for(col = col + 1; col < actualRange.col + actualRange.colCount; col++)
                    if(this.getColumnVisible(col))
                    {
                        rect.width += this._getZoomColumnWidth(col);
                        if(rect.width + rect.x > layout.x + layout.width)
                            break
                    }
            }
            if(actualRange.row >= topRow)
            {
                for(row = topRow; row < actualRange.row; row++)
                    if(this.getRowVisible(row))
                    {
                        rect.y += this._getZoomRowHeight(row);
                        if(rect.y > layout.y + layout.height)
                            return rect
                    }
                for(row = actualRange.row; row < actualRange.row + actualRange.rowCount; row++)
                    if(this.getRowVisible(row))
                    {
                        rect.height += this._getZoomRowHeight(row);
                        if(rect.height + rect.y > layout.y + layout.height)
                            break
                    }
            }
            else if(frozenRowCount > 0 && actualRange.row < frozenRowCount)
            {
                for(row = frozenRowCount - 1; row >= actualRange.row; row--)
                    if(this.getRowVisible(row))
                    {
                        rect.y -= this._getZoomRowHeight(row);
                        if(rect.y < layout.y)
                        {
                            row--;
                            break
                        }
                    }
                for(row = actualRange.row; row < Math.min(frozenRowCount,actualRange.row + actualRange.rowCount); row++)
                    if(this.getRowVisible(row))
                    {
                        rect.height += this._getZoomRowHeight(row);
                        if(rect.height + rect.y > layout.y + layout.height)
                            break
                    }
                for(row = topRow; row < actualRange.row + actualRange.rowCount; row++)
                    if(this.getRowVisible(row))
                    {
                        rect.height += this._getZoomRowHeight(row);
                        if(rect.height + rect.y > layout.y + layout.height)
                            break
                    }
            }
            else
            {
                for(row = topRow - 1; row >= actualRange.row; row--)
                    if(this.getRowVisible(row))
                    {
                        rect.y -= this._getZoomRowHeight(row);
                        if(rect.y < layout.y)
                        {
                            row--;
                            break
                        }
                    }
                for(row = row + 1; row < actualRange.row + actualRange.rowCount; row++)
                    if(this.getRowVisible(row))
                    {
                        rect.height += this._getZoomRowHeight(row);
                        if(rect.height + rect.y > layout.y + layout.height)
                            break
                    }
            }
            return rect
        },
        _getActiveSelectionRect: function(rowViewportIndex, colViewportIndex)
        {
            return this._getRangeRect(rowViewportIndex,colViewportIndex,this._getActiveSelectedRange())
        },
        _getCanvas: function()
        {
            var c = this._canvas;
            if(!c && this.parent)
                c = this.parent.canvas;
            if(c && !c.getContext && c.firstChild)
                c.getContext = c.firstChild.getContext;
            return c
        },
        _draw: function(ctx, clipRect)
        {
            var ctx2 = this._render._getCtx();
            if(ctx2)
                this._render.paint(ctx2,clipRect)
        },
        _getStringWidth: function(value, font)
        {
            var htmlSpan = this._getEditingSpan();
            htmlSpan.style.font = "";
            if(font)
                htmlSpan.style.font = font;
            else
                htmlSpan.style.font = this._render._getZoomFont(this._render._getDefaultFont());
            value = value + "";
            $(htmlSpan).text(value);
            var spanWidth = htmlSpan.offsetWidth;
            var htmlPre = this._getEditingPre();
            if(font)
                htmlPre.style.font = font;
            else
                htmlPre.style.font = this._render._getZoomFont(this._render._getDefaultFont());
            $(htmlPre).text(value);
            var preWidth = htmlPre.offsetWidth;
            if($.browser.mozilla)
            {
                spanWidth += 3;
                preWidth += 3
            }
            else if($.browser.msie)
            {
                spanWidth += 1;
                preWidth += 1
            }
            return Math.max(spanWidth,preWidth)
        },
        _getFontHeight: function(font)
        {
            var htmlSpan = this._getEditingSpan();
            if(font)
                htmlSpan.style.font = font;
            else
                htmlSpan.style.font = this._render._getDefaultFont();
            htmlSpan.innerHTML = "H";
            return htmlSpan.offsetHeight
        },
        _getEditingSpan: function()
        {
            if(!this._editingSpan)
            {
                var span = document.createElement("span");
                span.style.visibility = _cssHidden;
                span.style.top = "-10000px";
                span.style.left = "-10000px";
                span.style.position = "absolute";
                span.className = "gcStringWidthSpanStyle";
                span.setAttribute("gcUIElement","gcStringWidthSpan");
                document.body.insertBefore(span,null);
                this._editingSpan = span
            }
            return this._editingSpan
        },
        _getEditingPre: function()
        {
            if(!this._editingPre)
            {
                var pre = document.createElement("pre");
                pre.style.visibility = _cssHidden;
                pre.style.top = "-10000px";
                pre.style.left = "-10000px";
                pre.style.position = "absolute";
                pre.className = "gcStringWidthPreStyle";
                pre.setAttribute("gcUIElement","gcStringWidthPre");
                document.body.insertBefore(pre,null);
                this._editingPre = pre
            }
            return this._editingPre
        },
        _getColumnPane: function(x)
        {
            var layout = this._getSheetLayout();
            var colViewportIndex = null;
            if(layout.headerX < x && x < layout.headerX + layout.rowHeaderWidth)
                colViewportIndex = -1;
            else if(layout.frozenX < x && x < layout.frozenX + layout.frozenWidth)
                colViewportIndex = 0;
            else if(layout.viewportX < x && x < layout.viewportX + layout.viewportWidth)
                colViewportIndex = 1;
            return colViewportIndex
        },
        _getRowPane: function(y)
        {
            var layout = this._getSheetLayout();
            var rowViewportIndex = null;
            if(layout.headerY < y && y < layout.headerY + layout.colHeaderHeight)
                rowViewportIndex = -1;
            else if(layout.frozenY < y && y < layout.frozenY + layout.frozenHeight)
                rowViewportIndex = 0;
            else if(layout.viewportY < y && y < layout.viewportY + layout.viewportHeight)
                rowViewportIndex = 1;
            return rowViewportIndex
        },
        _getRowIndex: function(y, rowViewportIndex)
        {
            var rowLayouts = null;
            if(rowViewportIndex === -1)
                rowLayouts = this._getRowLayout(0,UI.SheetArea.colHeader);
            else if(rowViewportIndex === 0)
                rowLayouts = this._getRowLayout(0);
            else
                rowLayouts = this._getRowLayout(1);
            for(var i = 0; i < rowLayouts.length; i++)
            {
                var layout = rowLayouts[i];
                if(layout.y <= y && y < layout.y + layout.height)
                    return layout.row
            }
        },
        _getColumnIndex: function(x, colViewportIndex)
        {
            var colLayouts = null;
            if(colViewportIndex === -1)
                colLayouts = this._getColumnLayout(0,UI.SheetArea.rowHeader);
            else if(colViewportIndex === 0)
                colLayouts = this._getColumnLayout(0);
            else
                colLayouts = this._getColumnLayout(1);
            for(var i = 0; i < colLayouts.length; i++)
            {
                var layout = colLayouts[i];
                if(layout.x <= x && x < layout.x + layout.width)
                    return layout.col
            }
        },
        _isActiveCell: function(r, c)
        {
            return this._activeRowIndex <= r && r < this._activeRowIndex + this._activeRowCount && this._activeColIndex <= c && c < this._activeColIndex + this._activeColCount
        },
        _isColumnSelected: function(c)
        {
            for(var i = 0; i < this._selectionModel.length; i++)
            {
                var selectedRange = this._selectionModel[i];
                var col = selectedRange.col === -1 ? 0 : selectedRange.col;
                if(selectedRange.row === -1 && c >= col && c < col + selectedRange.colCount)
                    return true
            }
            return false
        },
        _isRowSelected: function(r)
        {
            for(var i = 0; i < this._selectionModel.length; i++)
            {
                var selectedRange = this._selectionModel[i];
                var row = selectedRange.row === -1 ? 0 : selectedRange.row;
                if(selectedRange.col === -1 && r >= row && r < row + selectedRange.rowCount)
                    return true
            }
            return false
        },
        _isSelected: function(r, c, sheetArea)
        {
            var selected = false;
            for(var i = 0; i < this._selectionModel.length; i++)
            {
                var cr = this._selectionModel[i];
                var span = this._spanModel.find(cr.row,cr.col);
                if(span && span.containsRange(cr))
                    cr = span;
                var selectedRange = this._getActualRange(cr);
                var SheetArea = UI.SheetArea;
                if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.viewport)
                    selected = selectedRange.row <= r && r < selectedRange.row + selectedRange.rowCount && selectedRange.col <= c && c < selectedRange.col + selectedRange.colCount;
                else if(sheetArea === SheetArea.rowHeader)
                    selected = r === this._activeRowIndex || selectedRange.row <= r && r < selectedRange.row + selectedRange.rowCount;
                else if(sheetArea === SheetArea.colHeader || sheetArea === SheetArea.colFooter)
                    selected = c === this._activeColIndex || selectedRange.col <= c && c < selectedRange.col + selectedRange.colCount || c < 0 && selectedRange.row === 0 && selectedRange.col === 0 && selectedRange.rowCount === this.getRowCount() && selectedRange.colCount === this.getColumnCount();
                else if(sheetArea === SheetArea.corner)
                    selected = selectedRange.row === -1 && selectedRange.col === -1 && selectedRange.rowCount === this.getRowCount() && selectedRange.colCount === this.getColumnCount();
                if(selected)
                    return selected
            }
            return selected
        },
        _isAllSelected: function(r, c, sheetArea)
        {
            var selected = false;
            var sheetRowCount = this.getRowCount();
            var sheetColCount = this.getColumnCount();
            var SheetArea = UI.SheetArea;
            for(var i = 0; i < this._selectionModel.length; i++)
            {
                var selectedRange = this._selectionModel[i];
                if(sheetArea !== undefined && sheetArea !== null && sheetArea !== SheetArea.viewport)
                    if(sheetArea === SheetArea.rowHeader)
                    {
                        var row = selectedRange.row === -1 ? 0 : selectedRange.row;
                        selected = selectedRange.col === -1 && r >= row && r < row + selectedRange.rowCount
                    }
                    else if(sheetArea === SheetArea.colHeader || sheetArea === SheetArea.colFooter)
                    {
                        var col = selectedRange.col === -1 ? 0 : selectedRange.col;
                        selected = selectedRange.row === -1 && c >= col && c < col + selectedRange.colCount
                    }
                    else if(sheetArea === SheetArea.corner)
                        selected = selectedRange.row === -1 && selectedRange.col === -1 && selectedRange.rowCount === sheetRowCount && selectedRange.colCount === sheetColCount;
                if(selected)
                    return selected
            }
            return selected
        },
        _isHover: function(r, c, sheetArea)
        {
            var ishover = false;
            var target = this._currentTarget;
            var SheetArea = UI.SheetArea;
            if(this._currentTarget && sheetArea !== undefined && sheetArea !== null)
                if(sheetArea === SheetArea.colHeader)
                {
                    var inSpans = false,
                        span = this._colHeaderSpanModel.find(target.row,target.col);
                    if(span)
                        inSpans = span.contains(r,c,1,1);
                    var hovered = r === target.row && c === target.col || inSpans;
                    return target.hitTestType === sheetArea && hovered
                }
                else if(sheetArea === SheetArea.rowHeader)
                {
                    var inSpans = false,
                        span = this._rowHeaderSpanModel.find(target.row,target.col);
                    if(span)
                        inSpans = span.contains(r,c,1,1);
                    var hovered = r === target.row && c === target.col || inSpans;
                    return target.hitTestType === sheetArea && hovered
                }
                else if(sheetArea === SheetArea.colFooter)
                    return target.hitTestType === sheetArea && r === target.row && c === target.col;
                else if(sheetArea === SheetArea.viewport)
                    return target.hitTestType === sheetArea && r === target.row && c === target.col;
                else if(sheetArea === SheetArea.corner)
                    return target.hitTestType === sheetArea;
            return ishover
        },
        _indexToLetters: function(index)
        {
            var t = "A";
            var aCode = t.charCodeAt(0);
            var sb = "";
            for(; index > 0; index = (index - 1) / 26)
            {
                var n = parseInt((index - 1) % 26,10);
                if(index === 1 || n >= 0 && index > 1)
                    sb = String.fromCharCode(aCode + n) + sb
            }
            return sb
        },
        _doStartEdit: function(canvas, x, y)
        {
            var target = this.hitTest(x,y);
            if(!target)
                return;
            if(target.row >= 0 && target.col >= 0 && target.rowViewportIndex >= 0 && target.colViewportIndex >= 0 && !target.resizeInfo && !target.dragInfo && target.row === this._activeRowIndex && target.col === this._activeColIndex)
            {
                var isEditing = this.isEditing();
                this._startEditImp(canvas,target.row,target.col);
                if(!isEditing && this.isEditing())
                {
                    var r = target.row,
                        c = target.col,
                        sheetArea = target.hitTestType;
                    var ct = this.getCellType(target.row,target.col);
                    if(ct._triggerButtonClicked)
                    {
                        var cellRect = this.getCellRect(r,c);
                        var cellStyle = this.getActualStyle(r,c,sheetArea);
                        var info = ct.getHitInfo(x,y,r,c,cellStyle,cellRect,sheetArea);
                        if(info && info.isReservedLocation)
                            ct._triggerButtonClicked(this,r,c)
                    }
                }
            }
        },
        _getSheetAreaFromHitTest: function(target)
        {
            var sheetArea = null;
            var SheetArea = UI.SheetArea;
            if(!(target.rowViewportIndex === 2 && target.colViewportIndex < 0))
                if(target.rowViewportIndex < 0 && target.colViewportIndex < 0)
                    sheetArea = SheetArea.corner;
                else if(target.rowViewportIndex < 0 && target.colViewportIndex >= 0)
                    sheetArea = SheetArea.colHeader;
                else if(target.rowViewportIndex >= 0 && target.rowViewportIndex < 2 && target.colViewportIndex < 0)
                    sheetArea = SheetArea.rowHeader;
                else if(target.rowViewportIndex >= 0 && target.rowViewportIndex < 2 && target.colViewportIndex >= 0)
                    sheetArea = SheetArea.viewport;
            return sheetArea
        },
        _startEditImp: function(canvas, row, col, rowViewportIndex, colViewportIndex, selectAll, defaultText)
        {
            if(this.isEditing())
                return;
            if(this.getRowHeight(row) <= 0 || this.getColumnWidth(col) <= 0)
                return;
            this.showCell(this._activeRowIndex,this._activeColIndex,UI.VerticalPosition.nearest,UI.HorizontalPosition.nearest);
            var cellStyle = this.getActualStyle(row,col);
            if(this.isProtected && cellStyle.locked !== false)
                return;
            var r = row;
            var c = col;
            if(r >= 0 && c >= 0)
            {
                this._activeRowIndex = r;
                this._activeColIndex = c;
                var ct = this.getCellType(row,col);
                if(!ct)
                    return;
                var editor = ct.createEditorElement();
                if(!editor)
                    return;
                this._editor = editor;
                Object.defineProperty(editor,"sheet",{
                    value: this,
                    writable: false
                });
                var args = {
                        sheet: this,
                        sheetName: this._name,
                        row: r,
                        col: c,
                        cancel: false
                    };
                this._trigger(UI.Events.EditStarting,args);
                if(args && args.cancel === true)
                    return;
                var v = this.getValue(r,c),
                    f = this.getFormula(r,c);
                var oldStatus = this._editorStatus;
                if(this._startEditByKeydown)
                    this._editorStatus = UI.EditorStatus.Enter;
                else if((v === null || v === undefined) && (f === null || f === undefined))
                    this._editorStatus = UI.EditorStatus.Enter;
                else
                    this._editorStatus = UI.EditorStatus.Edit;
                this._trigger(UI.Events.EditorStatusChanged,{
                    sheet: this,
                    sheetName: this._name,
                    oldStatus: oldStatus,
                    newStatus: this._editorStatus
                });
                var cellRect = this.getCellRect(r,c,rowViewportIndex,colViewportIndex);
                var sheetLayout = this._getSheetLayout();
                var curstyle = canvas.currentStyle;
                if(document.defaultView && document.defaultView.getComputedStyle)
                    curstyle = document.defaultView.getComputedStyle(canvas,'');
                if(!curstyle)
                    curstyle = canvas.style;
                if(curstyle.borderTopWidth !== undefined && curstyle.borderTopWidth !== null && !isNaN(curstyle.borderTopWidth))
                    cellRect.y += parseFloat(curstyle.borderTopWidth);
                if(curstyle.borderLeftWidth !== undefined && curstyle.borderLeftWidth !== null && !isNaN(curstyle.borderLeftWidth))
                    cellRect.x += parseFloat(curstyle.borderLeftWidth);
                ct.activateEditor(this._editor,cellStyle,cellRect);
                var t = this.getFormula(r,c);
                if(t)
                    t = "=" + t;
                var actualValue = t;
                if(!t || t.length === 0)
                {
                    t = this.getValue(r,c);
                    if(t && t.length > 0 && /^=/ig.test(t))
                        t = "'" + t;
                    actualValue = t;
                    t = ct._formatEditorValue(this._editor,cellStyle,t)
                }
                if(defaultText !== null && defaultText !== undefined)
                    t = defaultText;
                ct.setEditorValue(this._editor, t);
                this._editor._oldValue = t;
                this._editor._originalValue = actualValue;
                ct.updateEditor(this._editor,cellStyle,cellRect);
                var colHeader = UI.SheetArea.colHeader,
                    rowHeader = UI.SheetArea.rowHeader;
                if(this.showEditingLocator)
                {
                    var locator = ct._getLocator();
                    editor._editingLocator = locator;
                    var colHeaderText = this.getValue(this._getModel(colHeader).rowCount - 1,col,colHeader);
                    var rowHeaderText = this.getValue(row,this._getModel(rowHeader).colCount - 1,rowHeader);
                    locator.innerHTML = colHeaderText + "" + rowHeaderText;
                    ct._updateEditorLocator(this._editor);
                    document.body.insertBefore(locator,null)
                }
                ct.focus(this._editor);
                if(selectAll)
                    ct.selectAll(this._editor);
                var crect = this.getCellRect(row,col,rowViewportIndex,colViewportIndex);
                crect.x -= 2;
                crect.y -= 2;
                crect.width += 4;
                crect.height += 4;
                if(this._allowCellOverflow)
                {
                    crect.x = sheetLayout.frozenX;
                    crect.width = sheetLayout.viewportWidth
                }
                this.repaint(crect);
                window.gcGlobal.activeElement = this
            }
        },
        _showEditingLocator: function()
        {
            if(this.isEditing() === false)
                return;
            if(this._editor && this._editor._editingLocator)
            {
                var editor = $(this._editor);
                var x = parseInt(editor.css(_cssLeft),10) + this.getColumnWidth(this._activeColIndex) / 2,
                    y = parseInt(editor.css(_cssTop),10) + this.getRowHeight(this._activeRowIndex) / 2;
                var t = $(this._getCanvas()).offset();
                x -= t.left;
                y -= t.top;
                var row = this._getRowIndex(y,this._getRowPane(y));
                var col = this._getColumnIndex(x,this._getColumnPane(x));
                if(row === this._activeRowIndex && col === this._activeColIndex)
                    $(this._editor._editingLocator).css(cssVisibility,_cssHidden);
                else
                    $(this._editor._editingLocator).css(cssVisibility,"visible")
            }
        },
        _getKeyAction: function(keyCode, ctrl, shift, alt)
        {
            if(!this.keyMap)
                return null;
            var n = this.keyMap.length;
            for(var i = 0; i < n; i++)
            {
                var ka = this.keyMap[i];
                if(ka && ka.key === keyCode && ka.ctrl === ctrl && ka.shift === shift && ka.alt === alt)
                    return ka
            }
            return null
        },
        _moveActiveCellUp: function(r, c, wrap, checkTab, checkFocus)
        {
            if(r === 0 && !wrap)
                return;
            var rc = this.getRowCount();
            var cc = this.getColumnCount();
            if(this._leadingCellCol === undefined || this._leadingCellCol === null)
                this._leadingCellCol = 0;
            var cell = this._getPrevRow(r,this._leadingCellCol,checkTab,checkFocus);
            if(!wrap)
                this._adjustCell(cell);
            r = cell.r;
            c = cell.c;
            if(r < 0 && wrap)
            {
                c = this._getPrevVisualColumn(c);
                if(c < 0 || c === undefined || c === null)
                    c = this._getPrevVisualColumn(cc);
                this._leadingCellCol = c;
                cell = this._getPrevRow(rc,c,checkTab,checkFocus);
                r = cell.r;
                c = cell.c
            }
            if(this._canMoveCurrentTo(r,c))
            {
                this._leadingCellRow = r;
                this._activeRowIndex = r;
                this._activeColIndex = c
            }
        },
        _moveActiveCellDown: function(r, c, wrap, checkTab, checkFocus)
        {
            var rc = this.getRowCount();
            var cc = this.getColumnCount();
            if(r === rc - 1 && !wrap)
                return;
            if(this._leadingCellCol === undefined || this._leadingCellCol === null)
                this._leadingCellCol = 0;
            var cell = this._getNextRow(r,this._leadingCellCol,checkTab,checkFocus);
            if(!wrap)
                this._adjustCell(cell);
            r = cell.r;
            c = cell.c;
            if(r === rc && wrap)
            {
                c = this._getNextVisualColumn(c);
                if(c >= cc || c === undefined || c === null)
                    c = this._getNextVisualColumn(-1);
                this._leadingCellCol = c;
                cell = this._getNextRow(-1,c,checkTab,checkFocus);
                r = cell.r;
                c = cell.c
            }
            if(this._canMoveCurrentTo(r,c))
            {
                this._leadingCellRow = r;
                this._activeRowIndex = r;
                this._activeColIndex = c
            }
        },
        _moveActiveCellLeft: function(r, c, wrap, checkTab, checkFocus)
        {
            if(c === 0 && !wrap)
                return;
            var rc = this.getRowCount();
            var cc = this.getColumnCount();
            if(this._leadingCellRow === undefined || this._leadingCellRow === null)
                this._leadingCellRow = 0;
            var cell = this._getPrevColumn(this._leadingCellRow,c,checkTab,checkFocus);
            if(!wrap)
                this._adjustCell(cell);
            r = cell.r;
            c = cell.c;
            if(c < 0 && wrap)
            {
                r = this._getPrevVisualRow(r);
                if(r < 0 || r === undefined || r === null)
                    r = this._getPrevVisualRow(rc);
                this._leadingCellRow = r;
                cell = this._getPrevColumn(r,cc,checkTab,checkFocus);
                r = cell.r;
                c = cell.c
            }
            if(this._canMoveCurrentTo(r,c))
            {
                this._leadingCellCol = c;
                this._activeRowIndex = r;
                this._activeColIndex = c
            }
        },
        _adjustCell: function(cell)
        {
            if(cell.r < 0)
                cell.r = this._getFirstVisualRow();
            else if(cell.r >= this.getRowCount())
                cell.r = this._getLastVisualRow();
            if(cell.c < 0)
                cell.c = this._getFirstVisualColumn();
            else if(cell.c >= this.getColumnCount())
                cell.c = this._getLastVisualColumn()
        },
        _fixRange: function(range)
        {
            var r = range.row < 0 ? 0 : range.row,
                c = range.col < 0 ? 0 : range.col,
                rc = range.row < 0 ? this.getRowCount() : range.rowCount,
                cc = range.col < 0 ? this.getColumnCount() : range.colCount;
            return new UI.Range(r,c,rc,cc)
        },
        _moveActiveCellLeftInSelection: function(r, c)
        {
            var activeRange = this._fixRange(this._getActiveSelectedRange());
            var beginRowIndex = activeRange.row;
            var beginColIndex = activeRange.col;
            var endColIndex = activeRange.col + activeRange.colCount - 1;
            var cell = this._getPrevColumnInSelection(r,c,false,false);
            r = cell.r;
            c = cell.c;
            if(c < beginColIndex)
            {
                r -= 1;
                if(r < beginRowIndex)
                {
                    activeRange = this._fixRange(this._getActiveSelectedRange(UI.Direction.left));
                    var row = activeRange.row + activeRange.rowCount - 1;
                    var col = activeRange.col + activeRange.colCount;
                    if(col < this._scrollLeftCol)
                        this._scrollLeftCol = col;
                    else if(col > this._getPageRightColumn())
                    {
                        this._scrollLeftCol = col;
                        this._setLeftColumn(this._getPrevPageLeftColumn())
                    }
                    if(row < this._scrollTopRow)
                        this._setTopRow(row);
                    else if(row > this._getPageBottomRow())
                    {
                        this._scrollTopRow = row;
                        this._setTopRow(this._getPrevPageTopRow())
                    }
                    this._moveActiveCellLeftInSelection(row,col);
                    return
                }
                this._moveActiveCellLeftInSelection(r,endColIndex + 1);
                return
            }
            if(r >= 0)
            {
                this._activeRowIndex = r;
                this._activeColIndex = c;
                this._leadingCellRow = r;
                this._leadingCellCol = c
            }
        },
        _moveActiveCellRightInSelection: function(r, c)
        {
            var activeRange = this._fixRange(this._getActiveSelectedRange());
            var beginColIndex = activeRange.col;
            var endRowIndex = activeRange.row + activeRange.rowCount - 1;
            var endColIndex = activeRange.col + activeRange.colCount - 1;
            var cell = this._getNextColumnInSelection(r,c,false,false);
            r = cell.r;
            c = cell.c;
            if(c > endColIndex)
            {
                r += 1;
                if(r > endRowIndex)
                {
                    activeRange = this._fixRange(this._getActiveSelectedRange(UI.Direction.right));
                    var row = activeRange.row;
                    var col = activeRange.col;
                    if(col < this._scrollLeftCol)
                        this._setLeftColumn(this._getNextVisualColumn(col - 1));
                    else if(col > this._getPageRightColumn())
                    {
                        this._scrollLeftCol = col;
                        this._setLeftColumn(this._getPrevPageLeftColumn())
                    }
                    if(row < this._scrollTopRow)
                        this._setTopRow(this._getNextVisualRow(row - 1));
                    else if(row > this._getPageBottomRow())
                    {
                        this._scrollTopRow = row;
                        this._setTopRow(this._getPrevPageTopRow())
                    }
                    this._moveActiveCellRightInSelection(row,col - 1);
                    return
                }
                this._moveActiveCellRightInSelection(r,beginColIndex - 1);
                return
            }
            if(r >= 0)
            {
                this._activeRowIndex = r;
                this._activeColIndex = c;
                this._leadingCellRow = r;
                this._leadingCellCol = c
            }
        },
        _moveActiveCellRight: function(r, c, wrap, checkTab, checkFocus)
        {
            var rc = this.getRowCount();
            var cc = this.getColumnCount();
            if(c === cc - 1 && !wrap)
                return;
            if(this._leadingCellRow === undefined || this._leadingCellRow === null)
                this._leadingCellRow = 0;
            var cell = this._getNextColumn(this._leadingCellRow,c,checkTab,checkFocus);
            if(!wrap)
                this._adjustCell(cell);
            r = cell.r;
            c = cell.c;
            if(c === cc && wrap)
            {
                r = this._getNextVisualRow(r);
                if(r >= rc || r === undefined || r === null)
                    r = this._getNextVisualRow(-1);
                this._leadingCellRow = r;
                cell = this._getNextColumn(r,-1,checkTab,checkFocus);
                r = cell.r;
                c = cell.c
            }
            if(this._canMoveCurrentTo(r,c))
            {
                this._leadingCellCol = c;
                this._activeRowIndex = r;
                this._activeColIndex = c
            }
        },
        _getPrevColumn: function(r, c, checkTab, checkFocus)
        {
            var startPosition = {
                    r: r,
                    c: c
                };
            while(c >= 0)
            {
                c--;
                if(c < 0)
                    break;
                var spans = this.getSpans(new UI.Range(r,c,1,1));
                if(spans && spans.length > 0)
                {
                    var span = spans[0];
                    if(c >= span.col)
                    {
                        c = span.col;
                        r = span.row
                    }
                }
                if(this._canMoveCurrentTo(r,c,startPosition))
                    return{
                            r: r,
                            c: c
                        }
            }
            return{
                    r: r,
                    c: c
                }
        },
        _getPrevColumnInSelection: function(r, c, checkTab, checkFocus)
        {
            while(c >= 0)
            {
                c--;
                if(c < 0)
                    break;
                var span = this._spanModel.find(r,c);
                if(span)
                {
                    var activeSelectedRange = this._getActiveSelectedRange();
                    if(activeSelectedRange.row <= span.row && span.row + span.rowCount <= activeSelectedRange.row + activeSelectedRange.rowCount && activeSelectedRange.col <= span.col && span.col + span.colCount <= activeSelectedRange.col + activeSelectedRange.colCount)
                    {
                        if(!(span.row === r && span.col === c))
                            continue
                    }
                    else
                        continue;
                    if(c >= span.col)
                    {
                        c = span.col;
                        r = span.row
                    }
                }
                if(this._canMoveCurrentTo(r,c))
                    return{
                            r: r,
                            c: c
                        }
            }
            return{
                    r: r,
                    c: c
                }
        },
        _getNextColumn: function(r, c, checkTab, checkFocus)
        {
            var colCount = this.getColumnCount();
            var startPosition = {
                    r: r,
                    c: c
                };
            while(c < colCount)
            {
                var currentSpan = this._spanModel.find(r,c);
                if(!currentSpan)
                    c++;
                else
                    c += currentSpan.colCount;
                if(c >= colCount)
                    break;
                var spans = this.getSpans(new UI.Range(r,c,1,1));
                if(spans && spans.length > 0)
                {
                    var span = spans[0];
                    if(c > span.col)
                        c = Math.max(c,span.col + span.colCount);
                    else
                        r = span.row
                }
                if(this._canMoveCurrentTo(r,c,startPosition))
                    return{
                            r: r,
                            c: c
                        }
            }
            return{
                    r: r,
                    c: c
                }
        },
        _getNextColumnInSelection: function(r, c, checkTab, checkFocus)
        {
            var colCount = this.getColumnCount();
            while(c < colCount)
            {
                var currentSpan = this._spanModel.find(r,c);
                if(!currentSpan)
                    c++;
                else
                    c += currentSpan.colCount;
                if(c >= colCount)
                    break;
                var span = this._spanModel.find(r,c);
                if(span)
                {
                    var activeSelectedRange = this._getActiveSelectedRange();
                    if(activeSelectedRange.row <= span.row && span.row + span.rowCount <= activeSelectedRange.row + activeSelectedRange.rowCount && activeSelectedRange.col <= span.col && span.col + span.colCount <= activeSelectedRange.col + activeSelectedRange.colCount)
                    {
                        if(!(span.row === r && span.col === c))
                            continue
                    }
                    else
                        continue;
                    if(c > span.col)
                        c = Math.max(c,span.col + span.colCount);
                    else
                        r = span.row
                }
                if(this._canMoveCurrentTo(r,c))
                    return{
                            r: r,
                            c: c
                        }
            }
            return{
                    r: r,
                    c: c
                }
        },
        _canMoveCurrentTo: function(r, c, startPosition)
        {
            var canMove = false;
            var rowVisible = this.getRowVisible(r);
            var colVisible = this.getColumnVisible(c);
            var rowHeight = this._getZoomRowHeight(r);
            var colWidth = this._getZoomColumnWidth(c);
            canMove = r >= 0 && r < this.getRowCount() && c >= 0 && c < this.getColumnCount() && rowVisible && colVisible && rowHeight > 0 && colWidth > 0;
            return canMove
        },
        _getPrevRow: function(r, c, checkTab, checkFocus)
        {
            var startPosition = {
                    r: r,
                    c: c
                };
            while(r >= 0)
            {
                r--;
                if(r < 0)
                    break;
                var spans = this.getSpans(new UI.Range(r,c,1,1));
                if(spans && spans.length > 0)
                {
                    var span = spans[0];
                    if(r >= span.row)
                    {
                        r = span.row;
                        c = span.col
                    }
                }
                if(this._canMoveCurrentTo(r,c,startPosition))
                    return{
                            r: r,
                            c: c
                        }
            }
            return{
                    r: r,
                    c: c
                }
        },
        _getNextRow: function(r, c, checkTab, checkFocus)
        {
            var rowCount = this.getRowCount();
            var startPosition = {
                    r: r,
                    c: c
                };
            while(r < rowCount)
            {
                var currentSpan = this._spanModel.find(r,c);
                if(!currentSpan)
                    r++;
                else
                    r += currentSpan.rowCount;
                if(r >= rowCount)
                    break;
                var spans = this.getSpans(new UI.Range(r,c,1,1));
                if(spans && spans.length > 0)
                {
                    var span = spans[0];
                    if(r > span.row)
                        r = Math.max(r,span.row + span.rowCount);
                    else
                        c = span.col
                }
                if(this._canMoveCurrentTo(r,c,startPosition))
                    return{
                            r: r,
                            c: c
                        }
            }
            return{
                    r: r,
                    c: c
                }
        },
        _setSelectedRange: function(r, c, rc, cc, repaint)
        {
            this._selectionModel.add(r,c,rc,cc);
            if(repaint)
                this._render.repaintSelection()
        },
        _extendSelectedRange: function(r, c, repaint)
        {
            var extendedRange = this._getExtendedRange(r,c);
            var newRow = extendedRange.row,
                newCol = extendedRange.col,
                newRowCount = extendedRange.rowCount,
                newColCount = extendedRange.colCount;
            var selectionPolicy = this.selectionPolicy(),
                selectionUnit = this.selectionUnit();
            if(selectionPolicy === UI.SelectionPolicy.Single)
                return;
            else if(selectionPolicy === UI.SelectionPolicy.Range)
                this._selectionModel.clear();
            if(selectionUnit === UI.SelectionUnit.Row)
            {
                newCol = -1;
                newColCount = -1
            }
            else if(selectionUnit === UI.SelectionUnit.Column)
            {
                newRow = -1;
                newRowCount = -1
            }
            this._replaceActiveSelectedRange(newRow,newCol,newRowCount,newColCount,repaint)
        },
        _getExtendedRange: function(r, c)
        {
            var anchorRange = new UI.Range(this._activeRowIndex,this._activeColIndex,1,1);
            var spanAnchorRange = this._spanModel.find(this._activeRowIndex,this._activeColIndex);
            if(spanAnchorRange)
                anchorRange = spanAnchorRange;
            var endRange = new UI.Range(r,c,1,1);
            var spanEndRange = this._spanModel.find(r,c);
            if(spanEndRange)
                endRange = spanEndRange;
            var extendedRange = anchorRange.union(endRange);
            var spans = this.getSpans();
            if(spans && spans.length > 0)
                extendedRange = this._inflateRangeToCoverSpans(spans,extendedRange);
            return extendedRange
        },
        _replaceActiveSelectedRange: function(r, c, rc, cc, repaint)
        {
            var oldSelectedRange = this._getActiveSelectedRange();
            if(this._selectionModel.length > 0)
                this._selectionModel.splice(this._selectionModel.activeSelectedRangeIndex,1,new UI.Range(r,c,rc,cc));
            else
                this._selectionModel.add(r,c,rc,cc);
            if(repaint)
            {
                var newSelectedRange = this._getActiveSelectedRange();
                if(newSelectedRange.row === oldSelectedRange.row && newSelectedRange.col === oldSelectedRange.col && newSelectedRange.rowCount === oldSelectedRange.rowCount && newSelectedRange.colCount === oldSelectedRange.colCount)
                    return;
                var render = this._render;
                if(oldSelectedRange.containsRange(newSelectedRange))
                    render.repaintSelection(oldSelectedRange);
                else if(newSelectedRange.containsRange(oldSelectedRange))
                    render.repaintSelection(newSelectedRange);
                else
                {
                    render.repaintSelection(oldSelectedRange);
                    render.repaintSelection(newSelectedRange)
                }
            }
        },
        _changeActiveSelectedRange: function(key, isCtrl)
        {
            if(this._selectionModel.length <= 0)
                return;
            var oldActiveRange = this._getActiveSelectedRange();
            var activeRange = new UI.Range(oldActiveRange.row,oldActiveRange.col,oldActiveRange.rowCount,oldActiveRange.colCount);
            if(activeRange.row < 0)
            {
                activeRange.row = 0;
                activeRange.rowCount = this.getRowCount()
            }
            if(activeRange.col < 0)
            {
                activeRange.col = 0;
                activeRange.colCount = this.getColumnCount()
            }
            var newRange = null;
            var Key = UI.Key;
            if(key === Key.left)
                newRange = isCtrl ? this._searchSelectedRangebyLeftCtrl(activeRange) : this._searchSelectedRangebyLeft(activeRange);
            else if(key === Key.right)
                newRange = isCtrl ? this._searchSelectedRangebyRightCtrl(activeRange) : this._searchSelectedRangebyRight(activeRange);
            else if(key === Key.up)
                newRange = isCtrl ? this._searchSelectedRangebyUpCtrl(activeRange) : this._searchSelectedRangebyUp(activeRange);
            else if(key === Key.down)
                newRange = isCtrl ? this._searchSelectedRangebyDownCtrl(activeRange) : this._searchSelectedRangebyDown(activeRange);
            else if(key === Key.home)
                newRange = isCtrl ? this._searchSelectedRangebyHomeCtrl(activeRange) : this._searchSelectedRangebyHome(activeRange);
            else if(key === Key.end)
                newRange = isCtrl ? this._searchSelectedRangebyEndCtrl(activeRange) : this._searchSelectedRangebyEnd(activeRange);
            else if(key === Key.pup)
                newRange = this._searchSelectedRangebyPageUp(activeRange);
            else if(key === Key.pdn)
                newRange = this._searchSelectedRangebyPageDown(activeRange);
            if(newRange)
            {
                if(oldActiveRange.row < 0)
                {
                    newRange.row = -1;
                    newRange.rowCount = -1
                }
                if(oldActiveRange.col < 0)
                {
                    newRange.col = -1;
                    newRange.colCount = -1
                }
                var oldSelections = this._selectionModel.toArray();
                var newRow = newRange.row,
                    newCol = newRange.col,
                    newRowCount = newRange.rowCount,
                    newColCount = newRange.colCount;
                var selectionPolicy = this.selectionPolicy(),
                    selectionUnit = this.selectionUnit();
                if(selectionPolicy === UI.SelectionPolicy.Single)
                    return;
                else if(selectionPolicy === UI.SelectionPolicy.Range)
                    this._selectionModel.clear();
                if(selectionUnit === UI.SelectionUnit.Row)
                {
                    newCol = -1;
                    newColCount = -1
                }
                else if(selectionUnit === UI.SelectionUnit.Column)
                {
                    newRow = -1;
                    newRowCount = -1
                }
                this._replaceActiveSelectedRange(newRow,newCol,newRowCount,newColCount,true);
                var newSelections = this._selectionModel.toArray();
                if(this._eventHandler._notEqualSelecions(oldSelections,newSelections))
                {
                    var E = UI.Events;
                    this._trigger(E.SelectionChanging,{
                        sheet: this,
                        sheetName: this._name,
                        oldSelections: oldSelections,
                        newSelections: newSelections
                    });
                    this._trigger(E.SelectionChanged,{
                        sheet: this,
                        sheetName: this._name
                    })
                }
            }
        },
        _searchSelectedRangebyLeft: function(activeRange)
        {
            var beginCol = activeRange.col + activeRange.colCount - 1;
            var endCol = 0;
            var startPosition = {
                    r: activeRange.row + activeRange.rowCount - 1,
                    c: beginCol
                };
            var extendedRange,
                row,
                col,
                row2,
                col2,
                rowCount,
                colCount;
            while(beginCol > endCol)
            {
                beginCol--;
                if(!this._canMoveCurrentTo(startPosition.r,beginCol,startPosition))
                    continue;
                extendedRange = this._getExtendedRange(startPosition.r,beginCol);
                row = Math.min(activeRange.row,extendedRange.row);
                col = Math.min(activeRange.col,extendedRange.col);
                row2 = Math.max(activeRange.row + activeRange.rowCount - 1,extendedRange.row + extendedRange.rowCount - 1);
                col2 = Math.min(activeRange.col + activeRange.colCount - 1,extendedRange.col + extendedRange.colCount - 1);
                rowCount = row2 - row + 1;
                colCount = col2 - col + 1;
                if(!(row === activeRange.row && col === activeRange.col && rowCount === activeRange.rowCount && colCount === activeRange.colCount))
                {
                    var newLeftCol = this._getPrevVisualColumn(this._scrollLeftCol);
                    if(col === newLeftCol || col2 === newLeftCol)
                        this._setLeftColumn(newLeftCol);
                    return new UI.Range(row,col,rowCount,colCount)
                }
            }
            return null
        },
        _searchSelectedRangebyLeftCtrl: function(activeRange, isHomeKey)
        {
            var firstCol = this.frozenColCount ? this._getNextVisualColumn(this.frozenColCount - 1) : this._getFirstVisualColumn();
            var beginCol = isHomeKey ? firstCol : this._getFirstVisualColumn();
            if(beginCol === undefined || beginCol === null)
                return;
            else if(this.frozenColCount <= 0 || isHomeKey)
                this._setLeftColumn(beginCol);
            var extendedRange,
                row,
                col,
                row2,
                col2,
                rowCount,
                colCount;
            extendedRange = this._getExtendedRange(activeRange.row,beginCol);
            row = Math.min(activeRange.row,extendedRange.row);
            col = Math.min(activeRange.col,extendedRange.col);
            row2 = Math.max(activeRange.row + activeRange.rowCount - 1,extendedRange.row + extendedRange.rowCount - 1);
            col2 = Math.min(activeRange.col + activeRange.colCount - 1,extendedRange.col + extendedRange.colCount - 1);
            rowCount = row2 - row + 1;
            colCount = col2 - col + 1;
            return new UI.Range(row,col,rowCount,colCount)
        },
        _searchSelectedRangebyRight: function(activeRange)
        {
            var beginCol = activeRange.col;
            var endCol = this.getColumnCount() - 1;
            var startPosition = {
                    r: activeRange.row + activeRange.rowCount - 1,
                    c: beginCol
                };
            var extendedRange,
                row,
                col,
                row2,
                col2,
                rowCount,
                colCount;
            while(beginCol < endCol)
            {
                beginCol++;
                if(!this._canMoveCurrentTo(startPosition.r,beginCol,startPosition))
                    continue;
                extendedRange = this._getExtendedRange(startPosition.r,beginCol);
                row = Math.min(activeRange.row,extendedRange.row);
                col = Math.max(activeRange.col,extendedRange.col);
                row2 = Math.max(activeRange.row + activeRange.rowCount - 1,extendedRange.row + extendedRange.rowCount - 1);
                col2 = Math.max(activeRange.col + activeRange.colCount - 1,extendedRange.col + extendedRange.colCount - 1);
                rowCount = row2 - row + 1;
                colCount = col2 - col + 1;
                if(!(row === activeRange.row && col === activeRange.col && rowCount === activeRange.rowCount && colCount === activeRange.colCount))
                {
                    var firstCol = this.frozenColCount ? this._getNextVisualColumn(this.frozenColCount - 1) : null;
                    var pageRightCol = this._getPageRightColumn();
                    if(firstCol !== undefined && firstCol !== null && (col === firstCol || col2 === firstCol))
                        this._setLeftColumn(firstCol);
                    else if(col === pageRightCol || col2 === pageRightCol)
                        this._setLeftColumn(this._getNextVisualColumn(this._scrollLeftCol));
                    return new UI.Range(row,col,rowCount,colCount)
                }
            }
            return null
        },
        _searchSelectedRangebyRightCtrl: function(activeRange)
        {
            var newPageLeftCol = this._getLastPageLeftColumn();
            if(newPageLeftCol === undefined || newPageLeftCol === null)
                return;
            else
                this._setLeftColumn(newPageLeftCol);
            var endCol = this._getLastVisualColumn();
            var extendedRange,
                row,
                col,
                row2,
                col2,
                rowCount,
                colCount;
            extendedRange = this._getExtendedRange(activeRange.row,endCol);
            row = Math.min(activeRange.row,extendedRange.row);
            col = Math.max(activeRange.col,extendedRange.col);
            row2 = Math.max(activeRange.row + activeRange.rowCount - 1,extendedRange.row + extendedRange.rowCount - 1);
            col2 = Math.max(activeRange.col + activeRange.colCount - 1,extendedRange.col + extendedRange.colCount - 1);
            rowCount = row2 - row + 1;
            colCount = col2 - col + 1;
            return new UI.Range(row,col,rowCount,colCount)
        },
        _searchSelectedRangebyUp: function(activeRange)
        {
            var beginRow = activeRange.row + activeRange.rowCount - 1;
            var endRow = 0;
            var startPosition = {
                    r: beginRow,
                    c: activeRange.col + activeRange.colCount - 1
                };
            var extendedRange,
                row,
                col,
                row2,
                col2,
                rowCount,
                colCount;
            while(beginRow > endRow)
            {
                beginRow--;
                if(!this._canMoveCurrentTo(beginRow,startPosition.c,startPosition))
                    continue;
                extendedRange = this._getExtendedRange(beginRow,startPosition.c);
                row = Math.min(activeRange.row,extendedRange.row);
                col = Math.min(activeRange.col,extendedRange.col);
                row2 = Math.min(activeRange.row + activeRange.rowCount - 1,extendedRange.row + extendedRange.rowCount - 1);
                col2 = Math.max(activeRange.col + activeRange.colCount - 1,extendedRange.col + extendedRange.colCount - 1);
                rowCount = row2 - row + 1;
                colCount = col2 - col + 1;
                if(!(row === activeRange.row && col === activeRange.col && rowCount === activeRange.rowCount && colCount === activeRange.colCount))
                {
                    var newTopRow = this._getPrevVisualRow(this._scrollTopRow);
                    if(row === newTopRow || row2 === newTopRow)
                        this._setTopRow(newTopRow);
                    return new UI.Range(row,col,rowCount,colCount)
                }
            }
            return null
        },
        _searchSelectedRangebyUpCtrl: function(activeRange, isHomeKey)
        {
            var firstRow = this.frozenRowCount ? this._getNextVisualRow(this.frozenRowCount - 1) : this._getFirstVisualRow();
            var beginRow = isHomeKey ? firstRow : this._getFirstVisualRow();
            if(beginRow === undefined || beginRow === null)
                return;
            else if(this.frozenRowCount <= 0 || isHomeKey)
                this._setTopRow(beginRow);
            var extendedRange,
                row,
                col,
                row2,
                col2,
                rowCount,
                colCount;
            extendedRange = this._getExtendedRange(beginRow,activeRange.col);
            row = Math.min(activeRange.row,extendedRange.row);
            col = Math.min(activeRange.col,extendedRange.col);
            row2 = Math.min(activeRange.row + activeRange.rowCount - 1,extendedRange.row + extendedRange.rowCount - 1);
            col2 = Math.max(activeRange.col + activeRange.colCount - 1,extendedRange.col + extendedRange.colCount - 1);
            rowCount = row2 - row + 1;
            colCount = col2 - col + 1;
            return new UI.Range(row,col,rowCount,colCount)
        },
        _searchSelectedRangebyDown: function(activeRange)
        {
            var beginRow = activeRange.row;
            var endRow = this.getRowCount() - 1;
            var startPosition = {
                    r: beginRow,
                    c: activeRange.col + activeRange.colCount - 1
                };
            var extendedRange,
                row,
                col,
                row2,
                col2,
                rowCount,
                colCount;
            while(beginRow < endRow)
            {
                beginRow++;
                if(!this._canMoveCurrentTo(beginRow,startPosition.c,startPosition))
                    continue;
                extendedRange = this._getExtendedRange(beginRow,startPosition.c);
                row = Math.max(activeRange.row,extendedRange.row);
                col = Math.min(activeRange.col,extendedRange.col);
                row2 = Math.max(activeRange.row + activeRange.rowCount - 1,extendedRange.row + extendedRange.rowCount - 1);
                col2 = Math.max(activeRange.col + activeRange.colCount - 1,extendedRange.col + extendedRange.colCount - 1);
                rowCount = row2 - row + 1;
                colCount = col2 - col + 1;
                if(!(row === activeRange.row && col === activeRange.col && rowCount === activeRange.rowCount && colCount === activeRange.colCount))
                {
                    var firstRow = this.frozenRowCount ? this._getNextVisualRow(this.frozenRowCount - 1) : null;
                    var pageBottomRow = this._getPageBottomRow();
                    if(firstRow !== undefined && firstRow !== null && (row === firstRow || row2 === firstRow))
                        this._setTopRow(firstRow);
                    else if(row === pageBottomRow || row2 === pageBottomRow)
                        this._setTopRow(this._getNextVisualRow(this._scrollTopRow));
                    return new UI.Range(row,col,rowCount,colCount)
                }
                else
                    continue
            }
            return null
        },
        _searchSelectedRangebyDownCtrl: function(activeRange)
        {
            var newTopRow = this._getLastPageTopRow();
            if(newTopRow === undefined || newTopRow === null)
                return;
            else
                this._setTopRow(newTopRow);
            var endRow = this._getLastVisualRow();
            var extendedRange,
                row,
                col,
                row2,
                col2,
                rowCount,
                colCount;
            extendedRange = this._getExtendedRange(endRow,activeRange.col);
            row = Math.max(activeRange.row,extendedRange.row);
            col = Math.min(activeRange.col,extendedRange.col);
            row2 = Math.max(activeRange.row + activeRange.rowCount - 1,extendedRange.row + extendedRange.rowCount - 1);
            col2 = Math.max(activeRange.col + activeRange.colCount - 1,extendedRange.col + extendedRange.colCount - 1);
            rowCount = row2 - row + 1;
            colCount = col2 - col + 1;
            return new UI.Range(row,col,rowCount,colCount)
        },
        _searchSelectedRangebyHome: function(activeRange)
        {
            var beginCol = this.frozenColCount ? this.frozenColCount - 1 : -1;
            var endCol = this._activeColIndex;
            var startPosition = {
                    r: activeRange.row + activeRange.rowCount - 1,
                    c: activeRange.col
                };
            var extendedRange,
                row,
                col,
                row2,
                col2,
                rowCount,
                colCount;
            while(beginCol < endCol)
            {
                beginCol++;
                if(!this._canMoveCurrentTo(startPosition.r,beginCol,startPosition))
                    continue;
                if(activeRange.col <= beginCol && activeRange.col + activeRange.colCount - 1 === this._activeColIndex)
                    break;
                extendedRange = this._getExtendedRange(startPosition.r,beginCol);
                row = Math.min(activeRange.row,extendedRange.row);
                col = Math.min(activeRange.col,extendedRange.col);
                row2 = Math.max(activeRange.row + activeRange.rowCount - 1,extendedRange.row + extendedRange.rowCount - 1);
                col2 = Math.min(activeRange.col + activeRange.colCount - 1,extendedRange.col + extendedRange.colCount - 1);
                rowCount = row2 - row + 1;
                colCount = col2 - col + 1;
                this._setLeftColumn(this._getFirstVisualColumn());
                return new UI.Range(row,col,rowCount,colCount)
            }
            return null
        },
        _searchSelectedRangebyHomeCtrl: function(activeRange)
        {
            activeRange = this._searchSelectedRangebyLeftCtrl(activeRange,true);
            activeRange = this._searchSelectedRangebyUpCtrl(activeRange,true);
            return activeRange
        },
        _searchSelectedRangebyEnd: function(activeRange)
        {
            var beginCol = this.getColumnCount();
            var endCol = this._activeColIndex;
            var startPosition = {
                    r: activeRange.row + activeRange.rowCount - 1,
                    c: activeRange.col + activeRange.colCount - 1
                };
            var extendedRange,
                row,
                col,
                row2,
                col2,
                rowCount,
                colCount;
            while(beginCol > endCol)
            {
                beginCol--;
                if(!this._canMoveCurrentTo(startPosition.r,beginCol,startPosition))
                    continue;
                if(activeRange.col + activeRange.colCount - 1 >= beginCol && activeRange.col === this._activeColIndex)
                    break;
                extendedRange = this._getExtendedRange(startPosition.r,beginCol);
                row = Math.min(activeRange.row,extendedRange.row);
                col = Math.max(activeRange.col,extendedRange.col);
                row2 = Math.max(activeRange.row + activeRange.rowCount - 1,extendedRange.row + extendedRange.rowCount - 1);
                col2 = Math.max(activeRange.col + activeRange.colCount - 1,extendedRange.col + extendedRange.colCount - 1);
                rowCount = row2 - row + 1;
                colCount = col2 - col + 1;
                this._setLeftColumn(this._getLastPageLeftColumn());
                return new UI.Range(row,col,rowCount,colCount)
            }
            return null
        },
        _searchSelectedRangebyEndCtrl: function(activeRange)
        {
            activeRange = this._searchSelectedRangebyRightCtrl(activeRange);
            activeRange = this._searchSelectedRangebyDownCtrl(activeRange);
            return activeRange
        },
        _searchSelectedRangebyPageUp: function(activeRange)
        {
            var prevPageTopRow = this._getPrevPageTopRow();
            if(prevPageTopRow === undefined || prevPageTopRow === null)
                return null;
            var rls = this._getRowLayout(1);
            var scrolled = this._setTopRow(prevPageTopRow);
            var beginRow = -1;
            if(scrolled)
                beginRow = this._getNextVisualRow(activeRange.row + activeRange.rowCount - 1 - rls.length);
            else if(this.frozenRowCount <= 0)
                beginRow = this._getFirstVisualRow();
            if(beginRow < this._scrollTopRow)
                beginRow = this._scrollTopRow;
            else if(beginRow >= this._getPageBottomRow())
                beginRow = this._getPrevVisualRow(this._getPageBottomRow());
            var extendedRange,
                row,
                col,
                row2,
                col2,
                rowCount,
                colCount;
            extendedRange = this._getExtendedRange(beginRow,activeRange.col);
            row = Math.min(activeRange.row,extendedRange.row);
            col = Math.min(activeRange.col,extendedRange.col);
            row2 = Math.min(activeRange.row + activeRange.rowCount - 1,extendedRange.row + extendedRange.rowCount - 1);
            col2 = Math.max(activeRange.col + activeRange.colCount - 1,extendedRange.col + extendedRange.colCount - 1);
            rowCount = row2 - row + 1;
            colCount = col2 - col + 1;
            return new UI.Range(row,col,rowCount,colCount)
        },
        _searchSelectedRangebyPageDown: function(activeRange)
        {
            var nextPageTopRow = this._getNextPageTopRow();
            if(nextPageTopRow === undefined || nextPageTopRow === null)
                return null;
            var rls = this._getRowLayout(1);
            this._setTopRow(nextPageTopRow);
            var beginRow = this._getPrevVisualRow(activeRange.row + activeRange.rowCount - 1 + rls.length);
            if(beginRow < this._scrollTopRow)
                beginRow = this._scrollTopRow;
            else if(beginRow >= this._getPageBottomRow())
                if(this._scrollTopRow >= this._getLastPageTopRow())
                    beginRow = this._getPageBottomRow();
                else
                    beginRow = this._getPrevVisualRow(this._getPageBottomRow());
            var extendedRange,
                row,
                col,
                row2,
                col2,
                rowCount,
                colCount;
            extendedRange = this._getExtendedRange(beginRow,activeRange.col);
            row = Math.max(activeRange.row,extendedRange.row);
            col = Math.min(activeRange.col,extendedRange.col);
            row2 = Math.max(activeRange.row + activeRange.rowCount - 1,extendedRange.row + extendedRange.rowCount - 1);
            col2 = Math.max(activeRange.col + activeRange.colCount - 1,extendedRange.col + extendedRange.colCount - 1);
            rowCount = row2 - row + 1;
            colCount = col2 - col + 1;
            return new UI.Range(row,col,rowCount,colCount)
        },
        _getPrevPageTopRow: function()
        {
            var rls = this._getRowLayout(1);
            if(!rls || rls.length <= 0)
                return null;
            var firstRow = this.frozenRowCount ? this._getNextVisualRow(this.frozenRowCount - 1) : this._getFirstVisualRow();
            var h = 0;
            var r = this._scrollTopRow;
            var sheetLayout = this._getSheetLayout();
            while(r > firstRow)
            {
                h += this._getZoomRowHeight(r);
                if(h > sheetLayout.viewportHeight)
                    break;
                r--
            }
            return r
        },
        _getPrevPageLeftColumn: function()
        {
            var rls = this._getColumnLayout(1);
            if(!rls || rls.length <= 0)
                return null;
            if(rls[0].col === this._getFirstVisualColumn())
                return rls[0].col;
            var w = 0;
            var c = this._scrollLeftCol;
            var sheetLayout = this._getSheetLayout();
            while(c > 0)
            {
                w += this._getZoomColumnWidth(c);
                if(w > sheetLayout.viewportWidth)
                    break;
                c--
            }
            return c
        },
        _getNextPageTopRow: function()
        {
            var rls = this._getRowLayout(1);
            if(!rls || rls.length <= 0)
                return null;
            if(this._getLastVisualRow() === rls[rls.length - 1].row)
                return this._scrollTopRow;
            return rls[rls.length - 1].row
        },
        _getLastPageTopRow: function()
        {
            if(this._getLastVisualRow() === this._getPageBottomRow())
            {
                var sheetLayout = this._getSheetLayout();
                var rls = this._getRowLayout(1);
                if(rls && rls.length >= 1)
                {
                    var endRowLayout = rls[rls.length - 1];
                    if(endRowLayout.y + endRowLayout.height <= sheetLayout.viewportHeight)
                        return this._scrollTopRow
                }
            }
            var catchTopRow = this._scrollTopRow;
            try
            {
                this._scrollTopRow = this._getLastVisualRow();
                var lastPageTopRow = this._getPrevPageTopRow();
                lastPageTopRow = this._getNextVisualRow(lastPageTopRow);
                return lastPageTopRow
            }
            catch(e){}
            finally
            {
                this._scrollTopRow = catchTopRow
            }
        },
        _getLastPageLeftColumn: function()
        {
            var sheetLayout;
            if(this._getLastVisualColumn() === this._getPageRightColumn())
            {
                sheetLayout = this._getSheetLayout();
                var rls = this._getColumnLayout(1);
                if(rls && rls.length >= 1)
                {
                    var endColLayout = rls[rls.length - 1];
                    if(endColLayout.x + endColLayout.width <= sheetLayout.width)
                        return this._scrollLeftCol
                }
            }
            sheetLayout = this._getSheetLayout();
            var width = 0;
            var col = this._getLastVisualColumn();
            while(col > 0)
            {
                width += this._getZoomColumnWidth(col);
                if(width > sheetLayout.viewportWidth)
                    break;
                col--
            }
            if(col > 0)
                col = this._getNextVisualColumn(col);
            return col
        },
        _getPageBottomRow: function()
        {
            var rls = this._getRowLayout(1);
            if(!rls || rls.length <= 0)
                return null;
            return rls[rls.length - 1].row
        },
        _getPageRightColumn: function()
        {
            var rls = this._getColumnLayout(1);
            if(!rls || rls.length <= 0)
                return null;
            return rls[rls.length - 1].col
        },
        _getLastFullyVisibleRow: function()
        {
            var rls = this._getRowLayout(1);
            if(!rls || rls.length <= 0)
                return null;
            var layout = this._getSheetLayout();
            var i = rls.length - 1;
            if(rls[i].y + rls[i].height <= layout.viewportY + layout.viewportHeight)
                return rls[i].row;
            else
                return rls[Math.max(i - 1,0)].row
        },
        _getLastFullyVisibleColumn: function()
        {
            var cls = this._getColumnLayout(1);
            if(!cls || cls.length <= 0)
                return null;
            var layout = this._getSheetLayout();
            var i = cls.length - 1;
            if(cls[i].x + cls[i].width <= layout.viewportX + layout.viewportWidth)
                return cls[i].col;
            else
                return cls[Math.max(i - 1,0)].col
        },
        _getFirstVisualRow: function()
        {
            return this._getNextVisualRow(-1)
        },
        _getLastVisualRow: function(sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            return this._getPrevVisualRow(this.getRowCount(sheetArea),sheetArea)
        },
        _getFirstVisualColumn: function()
        {
            return this._getNextVisualColumn(-1)
        },
        _getLastVisualColumn: function(sheetArea)
        {
            if(sheetArea === undefined || sheetArea === null)
                sheetArea = UI.SheetArea.viewport;
            return this._getPrevVisualColumn(this.getColumnCount(sheetArea),sheetArea)
        },
        _getFirstPageLeftColumn: function()
        {
            var firstLeftCol = 0;
            var frozenColCount = isNaN(this.frozenColCount) ? 0 : this.frozenColCount;
            if(frozenColCount > 0)
                firstLeftCol = this._getNextVisualColumn(frozenColCount - 1);
            else
                firstLeftCol = this._getFirstVisualColumn();
            return firstLeftCol
        },
        _getFirstPageTopRow: function()
        {
            var firsTopRow = 0;
            var frozenRowCount = isNaN(this.frozenRowCount) ? 0 : this.frozenRowCount;
            if(frozenRowCount > 0)
                firsTopRow = this._getNextVisualRow(frozenRowCount - 1);
            else
                firsTopRow = this._getFirstVisualRow();
            return firsTopRow
        },
        _getNextVisualRow: function(row)
        {
            var endRow = this.getRowCount() - 1;
            while(row < endRow)
            {
                row++;
                if(this.getRowVisible(row) && this._getZoomRowHeight(row) > 0)
                    return row
            }
            return null
        },
        _getPrevVisualRow: function(row, sheetArea)
        {
            while(row > 0)
            {
                row--;
                if(this.getRowVisible(row,sheetArea) && this._getZoomRowHeight(row,sheetArea) > 0)
                    return row
            }
            return null
        },
        _getNextVisualColumn: function(col)
        {
            var endCol = this.getColumnCount() - 1;
            while(col < endCol)
            {
                col++;
                if(this.getColumnVisible(col) && this._getZoomColumnWidth(col) > 0)
                    return col
            }
            return null
        },
        _getPrevVisualColumn: function(col, sheetArea)
        {
            while(col > 0)
            {
                col--;
                if(this.getColumnVisible(col,sheetArea) && this._getZoomColumnWidth(col,sheetArea) > 0)
                    return col
            }
            return null
        },
        _inflateRangeToCoverSpans: function(spans, cellRange)
        {
            if(spans && spans.length > 0)
                for(var i = 0; i < spans.length; i++)
                {
                    var cr = spans[i];
                    if(cellRange.intersect(cr.row,cr.col,cr.rowCount,cr.colCount))
                    {
                        spans.splice(i--,1);
                        return this._inflateRangeToCoverSpans(spans,cellRange.union(cr))
                    }
                }
            return cellRange
        },
        _getActiveSelectedRange: function(dir)
        {
            var activeSelectedRange = new UI.Range(-1,-1,0,0);
            if(this._selectionModel.length <= 0)
                return activeSelectedRange;
            var Dir = UI.Direction;
            if(dir === Dir.left)
            {
                this._selectionModel.activeSelectedRangeIndex--;
                if(this._selectionModel.activeSelectedRangeIndex < 0)
                    this._selectionModel.activeSelectedRangeIndex = this._selectionModel.length - 1
            }
            else if(dir === Dir.right)
            {
                this._selectionModel.activeSelectedRangeIndex++;
                if(this._selectionModel.activeSelectedRangeIndex >= this._selectionModel.length)
                    this._selectionModel.activeSelectedRangeIndex = 0
            }
            if(this._selectionModel.activeSelectedRangeIndex >= 0)
                activeSelectedRange = this._selectionModel[this._selectionModel.activeSelectedRangeIndex];
            var span = this._spanModel.find(activeSelectedRange.row,activeSelectedRange.col);
            if(span && span.containsRange(activeSelectedRange))
                activeSelectedRange = span;
            return activeSelectedRange
        },
        _initializeActiveCell: function()
        {
            if(this._activeRowIndex === undefined || this._activeRowIndex === null || this._activeColIndex === undefined || this._activeColIndex === null || this._selectionModel.length <= 0)
            {
                var row = 0;
                var col = 0;
                var spanCell = this._spanModel.find(row,col);
                if(!spanCell)
                {
                    this._selectionModel.add(row,col,1,1);
                    this._activeRowIndex = row;
                    this._activeColIndex = col;
                    this._activeRowCount = 1;
                    this._activeColCount = 1
                }
                else
                {
                    this._selectionModel.add(row,col,spanCell.rowCount,spanCell.colCount);
                    this._activeRowIndex = row;
                    this._activeColIndex = col;
                    this._activeRowCount = spanCell.rowCount;
                    this._activeColCount = spanCell.colCount
                }
                if(this._selectionModel.length > 0)
                {
                    var currentSelection = this._selectionModel[0];
                    this._render.repaintSelection(currentSelection)
                }
            }
        },
        _setHoverCell: function(target)
        {
            var hoveredCellChanged = false;
            var current = this._currentTarget;
            if(!current)
                hoveredCellChanged = true;
            if(!hoveredCellChanged && !target)
                hoveredCellChanged = true;
            if(!hoveredCellChanged)
                hoveredCellChanged = target.col !== current.col || target.row !== current.row || target.colViewportIndex !== current.colViewportIndex || target.rowViewportIndex !== current.rowViewportIndex || target.hitTestType !== current.hitTestType;
            this._currentTarget = target;
            var adjX,
                adjY,
                gl;
            if(hoveredCellChanged)
            {
                this._hoverCell = true;
                var sheetLayout = this._getSheetLayout();
                var r = null;
                var SheetArea = UI.SheetArea;
                if(current)
                    if(current.rowViewportIndex < 0 || current.colViewportIndex < 0)
                    {
                        r = this.getCellRect(current.row,current.col,current.rowViewportIndex,current.colViewportIndex);
                        if(r && r.width > 0 && r.height > 0)
                        {
                            adjX = 0;
                            adjY = 0;
                            gl = this._getGroupLayout();
                            if(gl)
                            {
                                adjX = gl.width;
                                adjY = gl.height
                            }
                            if(r.x < sheetLayout.rowHeaderWidth + adjX || r.y < sheetLayout.colHeaderHeight + adjY || r.y >= sheetLayout.height - sheetLayout.footerHeight)
                                if(current.colViewportIndex < 0 && current.col === this.getColumnCount(SheetArea.rowHeader) - 1)
                                    this._render.update(r.x - 1,r.y - 1,r.width + 1,r.height + 2);
                                else if(current.row === this.getRowCount(SheetArea.colHeader) - 1)
                                    this._render.update(r.x - 1,r.y - 1,r.width + 2,r.height + 1);
                                else
                                    this._render.update(r.x - 1,r.y - 1,r.width + 2,r.height + 2)
                        }
                    }
                if(target)
                    if(target.rowViewportIndex < 0 || target.colViewportIndex < 0)
                    {
                        r = this.getCellRect(target.row,target.col,target.rowViewportIndex,target.colViewportIndex);
                        if(r && r.width > 0 && r.height > 0)
                        {
                            adjX = 0;
                            adjY = 0;
                            gl = this._getGroupLayout();
                            if(gl)
                            {
                                adjX = gl.width;
                                adjY = gl.height
                            }
                            if(r.x < sheetLayout.rowHeaderWidth + adjX || r.y < sheetLayout.colHeaderHeight + adjY || r.y >= sheetLayout.height - sheetLayout.footerHeight)
                                if(target.colViewportIndex < 0 && target.col === this.getColumnCount(SheetArea.rowHeader) - 1)
                                    this._render.update(r.x - 1,r.y - 1,r.width + 1,r.height + 2);
                                else if(target.row === this.getRowCount(SheetArea.colHeader) - 1)
                                    this._render.update(r.x - 1,r.y - 1,r.width + 2,r.height + 1);
                                else
                                    this._render.update(r.x - 1,r.y - 1,r.width + 2,r.height + 2)
                        }
                    }
                this._hoverCell = false
            }
        },
        _getAvailableActiveCell: function(row, col, isHorizontal)
        {
            var range = this._skipSpanCell(row,col,isHorizontal);
            return this._skipInvisibleCell(range)
        },
        _skipSpanCell: function(row, col, isHorizontal)
        {
            var spanCell = this._spanModel.find(row,col);
            if(!spanCell)
                return new UI.Range(row,col,1,1);
            else if(isHorizontal && spanCell.rowCount === 1 || !isHorizontal && spanCell.colCount === 1)
                return spanCell;
            if(isHorizontal)
                col++;
            else
                row++;
            return this._skipSpanCell(row,col,isHorizontal)
        },
        _skipInvisibleCell: function(range)
        {
            var row = range.row,
                i;
            var rowCount = this.getRowCount();
            for(i = row; i < rowCount; i++)
                if(this.getRowVisible(i) === true)
                    break;
            if(i < rowCount)
                row = i;
            var col = range.col;
            var columnCount = this.getColumnCount();
            for(i = col; i < columnCount; i++)
                if(this.getColumnVisible(i) === true)
                    break;
            if(i < columnCount)
                col = i;
            return new UI.Range(row,col,range.rowCount,range.colCount)
        },
        _processKeyMap: function(event)
        {
            var ret = true;
            var ka = this._getKeyAction(event.keyCode,event.ctrlKey,event.shiftKey,event.altKey);
            if(ka)
                if(typeof ka.action === const_function)
                {
                    ret = ka.action.call(this);
                    if(event.keyCode === UI.Key.enter && event.ctrlKey === false && event.altKey === false)
                        UI.cancelDefault(event);
                    if(typeof ret === "object")
                    {
                        if(ret.processed)
                            UI.cancelDefault(event);
                        ret = false
                    }
                }
            return ret
        },
        _getRowInfos: function(sheetArea)
        {
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.viewport || sheetArea === SheetArea.rowHeader)
            {
                if(!this._rowInfos)
                    this._rowInfos = {};
                return this._rowInfos
            }
            else if(sheetArea === SheetArea.colHeader)
            {
                if(!this._colHeaderRowInfos)
                    this._colHeaderRowInfos = {};
                return this._colHeaderRowInfos
            }
        },
        _getZoomRowHeight: function(row, sheetArea)
        {
            return parseInt(this.getRowHeight(row,sheetArea) * this._zoomFactor,10)
        },
        _getColumnInfos: function(sheetArea)
        {
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.viewport || sheetArea === SheetArea.colHeader)
            {
                if(!this._colInfos)
                    this._colInfos = {};
                return this._colInfos
            }
            else if(sheetArea === SheetArea.rowHeader)
            {
                if(!this._rowHeaderColInfos)
                    this._rowHeaderColInfos = {};
                return this._rowHeaderColInfos
            }
            return null
        },
        _getZoomColumnWidth: function(col, sheetArea)
        {
            return parseInt(this.getColumnWidth(col,sheetArea) * this._zoomFactor,10)
        },
        _getGroupLayout: function()
        {
            if(!this._cachedGroupLayout)
                this._cachedGroupLayout = this._createGroupLayout();
            return this._cachedGroupLayout
        },
        _createGroupLayout: function()
        {
            var groupLayout = {
                    x: 0,
                    y: 0,
                    width: 0,
                    height: 0
                };
            if(this._showRowRangeGroup && this.rowRangeGroup)
            {
                var rowMaxLevel = this.rowRangeGroup.getMaxLevel();
                if(rowMaxLevel >= 0)
                {
                    var width = Math.min(this._GROUPBUTTON_WIDTH,this._GROUPBUTTON_WIDTH * this._zoomFactor);
                    groupLayout.width = width * (rowMaxLevel + 2) + this._GROUPPADDING * 2
                }
            }
            if(this._showColumnRangeGroup && this.colRangeGroup)
            {
                var columnMaxLevel = this.colRangeGroup.getMaxLevel();
                if(columnMaxLevel >= 0)
                {
                    var height = Math.min(this._GROUPBUTTON_HEIGHT,this._GROUPBUTTON_HEIGHT * this._zoomFactor);
                    groupLayout.height = height * (columnMaxLevel + 2) + this._GROUPPADDING * 2
                }
            }
            return groupLayout
        },
        _createLayout: function()
        {
            var Rect = UI.Rect;
            var bounds = new Rect(this._bounds.x + this.borderWidth,this._bounds.y + this.borderWidth,Math.max(0,this._bounds.width - 2 * this.borderWidth),Math.max(0,this._bounds.height - 2 * this.borderWidth));
            var totalWidth = bounds.width;
            var totalHeight = bounds.height;
            var layout = {
                    x: bounds.x,
                    y: bounds.y,
                    width: bounds.width,
                    height: bounds.height,
                    rowHeaderWidth: 0,
                    colHeaderHeight: 0,
                    frozenWidth: 0,
                    frozenHeight: 0,
                    footerHeight: 0,
                    footerWidth: 0,
                    headerCornerRect: function()
                    {
                        return new Rect(this.x,this.y,this.rowHeaderWidth,this.colHeaderHeight)
                    },
                    colHeaderRect: function(colViewportIndex)
                    {
                        if(colViewportIndex === 0)
                            return new Rect(this.frozenX,this.y,this.frozenWidth,this.colHeaderHeight);
                        else if(colViewportIndex === 1)
                            return new Rect(this.viewportX,this.y,this.viewportWidth,this.colHeaderHeight);
                        else
                            return new Rect(this.frozenX,this.y,this.frozenWidth + this.viewportWidth,this.colHeaderHeight)
                    },
                    rowHeaderRect: function(rowViewportIndex)
                    {
                        if(rowViewportIndex === 0)
                            return new Rect(this.x,this.frozenY,this.rowHeaderWidth,this.frozenHeight);
                        else if(rowViewportIndex === 1)
                            return new Rect(this.x,this.viewportY,this.rowHeaderWidth,this.viewportHeight);
                        else
                            return new Rect(this.x,this.frozenY,this.rowHeaderWidth,this.frozenHeight + this.viewportHeight)
                    },
                    viewportRect: function(rowViewportIndex, colViewportIndex)
                    {
                        if(rowViewportIndex === 0)
                            if(colViewportIndex === 0)
                                return new Rect(this.frozenX,this.frozenY,this.frozenWidth,this.frozenHeight);
                            else if(colViewportIndex === 1)
                                return new Rect(this.viewportX,this.frozenY,this.viewportWidth,this.frozenHeight);
                            else
                                return new Rect(this.frozenX,this.frozenY,this.frozenWidth + this.viewportWidth,this.frozenHeight);
                        else if(rowViewportIndex === 1)
                            if(colViewportIndex === 0)
                                return new Rect(this.frozenX,this.viewportY,this.frozenWidth,this.viewportHeight);
                            else if(colViewportIndex === 1)
                                return new Rect(this.viewportX,this.viewportY,this.viewportWidth,this.viewportHeight);
                            else
                                return new Rect(this.frozenX,this.viewportY,this.frozenWidth + this.viewportWidth,this.viewportHeight);
                        else if(colViewportIndex === 0)
                            return new Rect(this.frozenX,this.frozenY,this.frozenWidth,this.frozenHeight + this.viewportHeight);
                        else if(colViewportIndex === 1)
                            return new Rect(this.viewportX,this.frozenY,this.viewportWidth,this.frozenHeight + this.viewportHeight);
                        else
                            return new Rect(this.frozenX,this.frozenY,this.frozenWidth + this.viewportWidth,this.frozenHeight + this.viewportHeight)
                    },
                    footerRect: function(colViewportIndex)
                    {
                        if(colViewportIndex === 0)
                            return new Rect(this.frozenX,this.footerY,this.frozenWidth,this.footerHeight);
                        else if(colViewportIndex === 1)
                            return new Rect(this.viewportX,this.footerY,this.viewportWidth,this.footerHeight);
                        else
                            return new Rect(this.frozenX,this.footerY,this.frozenWidth + this.viewportWidth,this.footerHeight)
                    },
                    footerCornerRect: function()
                    {
                        return new Rect(this.footerX,this.footerY,this.footerWidth,this.footerHeight)
                    }
                };
            var groupLayout = this._getGroupLayout();
            layout.x += groupLayout.width;
            layout.y += groupLayout.height;
            var SheetArea = UI.SheetArea,
                r,
                rc,
                c,
                cc;
            if(this.rowHeaderVisible === null || this.rowHeaderVisible === undefined || this.rowHeaderVisible)
            {
                cc = this.getColumnCount(SheetArea.rowHeader);
                for(c = 0; c < cc; c++)
                    layout.rowHeaderWidth += this._getZoomColumnWidth(c,SheetArea.rowHeader)
            }
            if(this.colHeaderVisible === null || this.colHeaderVisible === undefined || this.colHeaderVisible)
            {
                rc = this.getRowCount(SheetArea.colHeader);
                for(r = 0; r < rc; r++)
                    layout.colHeaderHeight += this._getZoomRowHeight(r,SheetArea.colHeader)
            }
            if(this._colFooterVisible)
            {
                rc = this.getRowCount(SheetArea.colFooter);
                for(r = 0; r < rc; r++)
                    layout.footerHeight += this._getZoomRowHeight(r,SheetArea.colFooter)
            }
            layout.footerWidth = layout.rowHeaderWidth;
            if(this.frozenColCount > 0)
            {
                cc = this.getColumnCount();
                for(c = 0; c < this.frozenColCount && c < cc; c++)
                    if(this.getColumnVisible(c))
                        layout.frozenWidth += this._getZoomColumnWidth(c)
            }
            if(layout.frozenWidth > layout.width)
                layout.frozenWidth = layout.width - layout.rowHeaderWidth;
            if(this.frozenRowCount > 0)
            {
                rc = this.getRowCount();
                for(r = 0; r < this.frozenRowCount && r < rc; r++)
                    if(this.getRowVisible(r))
                        layout.frozenHeight += this._getZoomRowHeight(r)
            }
            if(layout.frozenHeight > layout.height - layout.colHeaderHeight)
                layout.frozenHeight = layout.height - layout.colHeaderHeight;
            totalWidth -= layout.rowHeaderWidth;
            totalHeight -= layout.colHeaderHeight;
            totalWidth -= layout.frozenWidth;
            totalHeight -= layout.frozenHeight;
            totalHeight -= layout.footerHeight;
            layout.viewportWidth = Math.max(0,totalWidth);
            layout.viewportHeight = Math.max(0,totalHeight);
            layout.headerX = layout.x;
            layout.headerY = layout.y;
            layout.frozenX = layout.headerX + layout.rowHeaderWidth;
            layout.frozenY = layout.headerY + layout.colHeaderHeight;
            layout.viewportX = layout.frozenX + layout.frozenWidth;
            layout.viewportY = layout.frozenY + layout.frozenHeight;
            layout.footerX = layout.headerX;
            layout.footerY = layout.y + layout.height - layout.footerHeight;
            return layout
        },
        _getSheetLayout: function()
        {
            if(!this._layoutModel)
                this._layoutModel = this._createLayout();
            return this._layoutModel
        },
        _getColumnLayout: function(j, sheetArea)
        {
            var cl = null;
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.viewport || sheetArea === SheetArea.colHeader)
                cl = this._colLayoutCache.viewport[j];
            else if(sheetArea === SheetArea.rowHeader)
                cl = this._colLayoutCache.rowHeader[j];
            if(cl)
                return cl;
            cl = this._createColumnLayout(j,sheetArea);
            if(sheetArea === SheetArea.viewport || sheetArea === SheetArea.colHeader)
                this._colLayoutCache.viewport[j] = cl;
            else if(sheetArea === SheetArea.rowHeader)
                this._colLayoutCache.rowHeader[j] = cl;
            return cl
        },
        _createColumnLayout: function(j, sheetArea)
        {
            var colLayouts = new UI._LayoutModel;
            var layout = this._getSheetLayout();
            var viewportX = 0;
            var viewportWidth = 0;
            var col;
            var colCount = this.getColumnCount(sheetArea);
            var colX;
            var colWidth = 0;
            var frozenColCount = isNaN(this.frozenColCount) ? 0 : Math.min(this.frozenColCount,this.getColumnCount());
            var leftCol = 0;
            if(sheetArea === UI.SheetArea.rowHeader)
            {
                viewportX = layout.headerX;
                viewportWidth = layout.rowHeaderWidth
            }
            else
            {
                if(j === 0)
                    colCount = frozenColCount;
                else if(j > 0)
                    leftCol = Math.max(frozenColCount,this._scrollLeftCol);
                viewportX = j > 0 ? layout.viewportX : layout.frozenX;
                viewportWidth = j > 0 ? layout.viewportWidth : layout.frozenWidth
            }
            if(this.colVisibleCount === 0)
                colCount = 0;
            for(col = leftCol, colX = 0; col < colCount && (sheetArea === UI.SheetArea.rowHeader || j === 0 || colX <= viewportWidth); col++, colX += colWidth)
            {
                if(!this.getColumnVisible(col,sheetArea))
                {
                    colX -= colWidth;
                    continue
                }
                colWidth = this._getZoomColumnWidth(col,sheetArea);
                if(colWidth >= 0)
                    colLayouts.push(new UI._Layout(-1,col,viewportX + colX,-1,colWidth,-1))
            }
            return colLayouts
        },
        _getRowLayout: function(i, sheetArea)
        {
            var rl = null;
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.viewport || sheetArea === SheetArea.rowHeader)
                rl = this._rowLayoutCache.viewport[i];
            else if(sheetArea === SheetArea.colHeader)
                rl = this._rowLayoutCache.colHeader[i];
            else if(sheetArea === SheetArea.colFooter)
                rl = this._rowLayoutCache.colFooter[i];
            if(rl)
                return rl;
            rl = this._createRowLayout(i,sheetArea);
            if(sheetArea === SheetArea.viewport || sheetArea === SheetArea.rowHeader)
                this._rowLayoutCache.viewport[i] = rl;
            else if(sheetArea === SheetArea.colHeader)
                this._rowLayoutCache.colHeader[i] = rl;
            else if(sheetArea === SheetArea.colFooter)
                this._rowLayoutCache.colFooter[i] = rl;
            return rl
        },
        _createRowLayout: function(i, sheetArea)
        {
            var rowLayouts = new UI._LayoutModel;
            var layout = this._getSheetLayout();
            var viewportY = 0;
            var viewportHeight = 0;
            var row;
            var rowCount = this.getRowCount(sheetArea);
            var rowY;
            var rowHeight = 0;
            var frozenRowCount = isNaN(this.frozenRowCount) ? 0 : Math.min(this.frozenRowCount,this.getRowCount());
            var topRow = 0;
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.viewport || sheetArea === SheetArea.rowHeader)
            {
                if(i === 0)
                    rowCount = frozenRowCount;
                else if(i > 0)
                    topRow = Math.max(frozenRowCount,this._scrollTopRow);
                viewportY = i > 0 ? layout.viewportY : layout.frozenY;
                viewportHeight = i > 0 ? layout.viewportHeight : layout.frozenHeight
            }
            else if(sheetArea === SheetArea.colHeader)
            {
                viewportY = layout.headerY;
                viewportHeight = layout.colHeaderHeight
            }
            else if(sheetArea === SheetArea.colFooter)
            {
                viewportY = layout.footerY;
                viewportHeight = layout.footerHeight
            }
            for(row = topRow, rowY = viewportY; row < rowCount && (sheetArea === SheetArea.colHeader || sheetArea === SheetArea.colFooter || i === 0 || rowY < viewportY + viewportHeight); row++, rowY += rowHeight)
            {
                if(!this.getRowVisible(row,sheetArea))
                {
                    rowY -= rowHeight;
                    continue
                }
                rowHeight = this._getZoomRowHeight(row,sheetArea);
                if(rowHeight >= 0)
                    rowLayouts.push(new UI._Layout(row,-1,-1,rowY,-1,rowHeight))
            }
            return rowLayouts
        },
        _getCellLayout: function(i, j, sheetArea)
        {
            var layout = this._getSheetLayout();
            var viewportY = i > 0 ? layout.viewportY : layout.frozenY;
            var viewportHeight = i > 0 ? layout.viewportHeight : layout.frozenHeight;
            var viewportX = j > 0 ? layout.viewportX : layout.frozenX;
            var viewportWidth = j > 0 ? layout.viewportWidth : layout.frozenWidth;
            var SheetArea = UI.SheetArea;
            if(sheetArea === SheetArea.rowHeader)
            {
                viewportX = layout.headerX;
                viewportWidth = layout.rowHeaderWidth
            }
            else if(sheetArea === SheetArea.colHeader)
            {
                viewportY = layout.headerY;
                viewportHeight = layout.colHeaderHeight
            }
            else if(sheetArea === SheetArea.colFooter)
            {
                viewportY = layout.footerY;
                viewportHeight = layout.footerHeight
            }
            var rowCount = this.getRowCount(sheetArea);
            var colCount = this.getColumnCount(sheetArea);
            var rowLayoutModel = this._getRowLayout(i,sheetArea);
            var colLayoutModel = this._getColumnLayout(j,sheetArea);
            var cellLayoutModel = new UI._LayoutModel;
            if(rowLayoutModel.length > 0 && colLayoutModel.length > 0)
            {
                var topRow = rowLayoutModel[0].row;
                var leftCol = colLayoutModel[0].col;
                var bottomRow = rowLayoutModel[rowLayoutModel.length - 1].row;
                var rightCol = colLayoutModel[colLayoutModel.length - 1].col;
                var spans = this.getSpans(new UI.Range(topRow,leftCol,bottomRow - topRow + 1,rightCol - leftCol + 1),sheetArea);
                this._addCellLayout(spans,topRow,leftCol,bottomRow,rightCol,cellLayoutModel,rowCount,colCount,sheetArea,rowLayoutModel,colLayoutModel);
                if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.viewport)
                {
                    spans = new UI._SpanModel;
                    this._addCellLayout(spans,topRow,leftCol,bottomRow,rightCol,cellLayoutModel,rowCount,colCount,sheetArea,rowLayoutModel,colLayoutModel)
                }
            }
            return cellLayoutModel
        },
        _addCellLayout: function(spans, topRow, leftCol, bottomRow, rightCol, cellLayoutModel, rowCount, colCount, sheetArea, rowLayoutModel, colLayoutModel)
        {
            for(var k = 0; k < spans.length; k++)
            {
                var cellSpan = spans[k];
                if(cellSpan.intersect(topRow,leftCol,bottomRow - topRow + 1,rightCol - leftCol + 1))
                {
                    var x = 0,
                        y = 0,
                        width = 0,
                        height = 0,
                        row,
                        col;
                    for(col = cellSpan.col; col < leftCol; col++)
                        x -= this._getZoomColumnWidth(col,sheetArea);
                    for(col = leftCol; col < cellSpan.col; col++)
                        x += this._getZoomColumnWidth(col,sheetArea);
                    for(row = cellSpan.row; row < topRow; row++)
                        y -= this._getZoomRowHeight(row,sheetArea);
                    for(row = topRow; row < cellSpan.row; row++)
                        y += this._getZoomRowHeight(row,sheetArea);
                    for(col = cellSpan.col; col < cellSpan.col + cellSpan.colCount && col < colCount; col++)
                        width += this._getZoomColumnWidth(col,sheetArea);
                    for(row = cellSpan.row; row < cellSpan.row + cellSpan.rowCount && row < rowCount; row++)
                        height += this._getZoomRowHeight(row,sheetArea);
                    cellLayoutModel.push(new UI._Layout(cellSpan.row,cellSpan.col,colLayoutModel[0].x + x,rowLayoutModel[0].y + y,width,height,cellSpan.rowCount,cellSpan.colCount))
                }
            }
        },
        _syncHScollbarPosition: function()
        {
            if(this._scrollCallback)
                this._scrollCallback({
                    action: "scroll",
                    orientation: "horizontal"
                })
        },
        _syncVScrollbarPosition: function()
        {
            if(this._scrollCallback)
                this._scrollCallback({
                    action: "scroll",
                    orientation: "vertical"
                })
        },
        _syncScrollbarSize: function()
        {
            if(this._scrollCallback)
                this._scrollCallback({action: "resize"})
        },
        _setTopRow: function(row, forceRepaint)
        {
            if(forceRepaint || row !== undefined && row !== null && row >= this._getFirstPageTopRow() && row <= this._getLastVisualRow() && row !== this._scrollTopRow)
            {
                this._trigger(UI.Events.TopRowChanged,{
                    sheet: this,
                    sheetName: this._name,
                    oldTopRow: this._scrollTopRow,
                    newTopRow: row
                });
                var painted = false;
                if(GrapeCity.UI._useDoubleBuffer())
                {
                    var bufferCtx = this._render._getBufferCtx();
                    var oldTop = this._scrollTopRow;
                    var sl = null;
                    var rls = this._getRowLayout(1,GrapeCity.UI.SheetArea.viewport);
                    this._scrollTopRow = row;
                    if(this._scrollTopRow > oldTop && rls)
                    {
                        var rl = rls.findRow(this._scrollTopRow);
                        if(rl)
                        {
                            painted = true;
                            sl = this._getSheetLayout();
                            if(rls.length <= 0)
                                return false;
                            var i = rls.length - 1;
                            var lrl = rls[i];
                            var adj = rls[i].height + 2;
                            if(rls[i].y + rls[i].height > sl.viewportHeight)
                                adj += rls[i - 1].height;
                            if(sl.viewportHeight - rl.y - adj > 0)
                            {
                                this._render.translateScreen(sl.x,rl.y,sl.rowHeaderWidth + sl.frozenWidth + sl.viewportWidth,sl.viewportHeight - rl.y - adj,sl.x,sl.viewportY);
                                this._render._copyDoubleBufferRect(new UI.Rect(sl.x,rl.y,sl.rowHeaderWidth + sl.frozenWidth + sl.viewportWidth,sl.viewportHeight - rl.y - adj),sl.x,sl.viewportY,null,bufferCtx,bufferCtx);
                                for(var i = oldTop; i < this._scrollTopRow; i++)
                                    this._clearCellOverflowModelCachebyRow(i);
                                var tmp = this._cellOverflowModelCache;
                                this.invalidateLayout();
                                this._cellOverflowModelCache = tmp;
                                rls = this._getRowLayout(1,GrapeCity.UI.SheetArea.viewport);
                                if(!rls || rls.length <= 0)
                                    return false;
                                var lrl2 = rls[rls.length - 1];
                                for(var i = lrl.row + 1; i <= lrl2.row + 1; i++)
                                    this._render._buildCellOverflowLayoutModel(i,GrapeCity.UI.SheetArea.viewport);
                                this._render.update(sl.x,sl.viewportY + sl.viewportHeight - rl.y - adj,sl.rowHeaderWidth + sl.frozenWidth + sl.viewportWidth,rl.y + adj,false)
                            }
                            else
                            {
                                this.invalidateLayout();
                                this.repaint()
                            }
                        }
                    }
                    else
                    {
                        sl = this._getSheetLayout();
                        var h = 0;
                        for(var i = this._scrollTopRow; i < oldTop && h < sl.viewportHeight; i++)
                            h += this._getZoomRowHeight(i);
                        if(h < sl.viewportHeight)
                        {
                            painted = true;
                            var rls = this._getRowLayout(1,GrapeCity.UI.SheetArea.viewport);
                            if(!rls)
                                return false;
                            var frl = rls.findRow(oldTop);
                            if(frl)
                            {
                                var adj = frl.height + 2;
                                var activeCol = this.getActiveColumnIndex();
                                if(activeCol >= 0 && activeCol < this.getColumnCount())
                                {
                                    var colLayout = this._getColumnLayout(1,GrapeCity.UI.SheetArea.viewport).findCol(activeCol);
                                    if(colLayout)
                                    {
                                        var rect = this.getCellRect(frl.row,activeCol);
                                        if(rect)
                                            adj = rect.height + 2
                                    }
                                }
                                this._render.translateScreen(sl.x,sl.viewportY + adj,sl.rowHeaderWidth + sl.frozenWidth + sl.viewportWidth,sl.viewportHeight - h - adj,sl.x,sl.viewportY + h + adj);
                                this._render._copyDoubleBufferRect(new UI.Rect(sl.x,sl.viewportY + adj,sl.rowHeaderWidth + sl.frozenWidth + sl.viewportWidth,sl.viewportHeight - h - adj),sl.x,sl.viewportY + h + adj,null,bufferCtx,bufferCtx);
                                for(var i = this._scrollTopRow; i < oldTop; i++)
                                    this._render._buildCellOverflowLayoutModel(i,GrapeCity.UI.SheetArea.viewport);
                                if(rls.length <= 0)
                                    return false;
                                var lrl = rls[rls.length - 1];
                                var tmp = this._cellOverflowModelCache;
                                this.invalidateLayout();
                                this._cellOverflowModelCache = tmp;
                                rls = this._getRowLayout(1,GrapeCity.UI.SheetArea.viewport);
                                if(!rls || rls.length <= 0)
                                    return false;
                                var lrl2 = rls[rls.length - 1];
                                for(var i = lrl2.row + 1; i <= lrl.row; i++)
                                    this._clearCellOverflowModelCachebyRow(i);
                                this._render.update(sl.x,sl.viewportY,sl.rowHeaderWidth + sl.frozenWidth + sl.viewportWidth,h + adj,false)
                            }
                        }
                    }
                }
                else
                    this._scrollTopRow = row;
                if(!painted)
                {
                    this.invalidateLayout();
                    this.repaint()
                }
                this._syncVScrollbarPosition();
                return true
            }
        },
        _setLeftColumn: function(col, forceRepaint)
        {
            if(forceRepaint || col !== undefined && col !== null && col >= this._getFirstPageLeftColumn() && col <= this._getLastVisualColumn() && col !== this._scrollLeftCol)
            {
                this._trigger(UI.Events.LeftColumnChanged,{
                    sheet: this,
                    sheetName: this._name,
                    oldLeftCol: this._scrollLeftCol,
                    newLeftCol: col
                });
                this._scrollLeftCol = col;
                this.invalidateLayout();
                this.repaint();
                this._syncHScollbarPosition();
                return true
            }
        },
        _getSpanModel: function(sheetArea)
        {
            var SheetArea = UI.SheetArea;
            if(sheetArea === undefined || sheetArea === null || sheetArea === SheetArea.viewport)
                return this._spanModel;
            else if(sheetArea === SheetArea.colHeader)
                return this._colHeaderSpanModel;
            else if(sheetArea === SheetArea.rowHeader)
                return this._rowHeaderSpanModel
        },
        _clipboardCopy: function(range, isCutting)
        {
            if(!range)
                return;
            var columnDelimiter = "\t";
            var rowDelimiter = "\r\n";
            var cellDelimiter = "\"";
            var newRange = new UI.Range(range.row,range.col,range.rowCount,range.colCount);
            if(newRange.row === -1)
                newRange.row = 0;
            if(newRange.col === -1)
                newRange.col = 0;
            if(newRange.rowCount === -1)
                newRange.rowCount = this.getRowCount() - newRange.row;
            if(newRange.colCount === -1)
                newRange.colCount = this.getColumnCount() - newRange.col;
            var copyData = this.getCsv(newRange.row,newRange.col,newRange.rowCount,newRange.colCount,rowDelimiter,columnDelimiter);
            var ch = this._getClipboardHelper();
            ch.fromSheet = this;
            ch.range = range;
            ch.isCutting = isCutting;
            try
            {
                if(window.clipboardData && window.clipboardData.setData)
                {
                    var args = {
                            sheet: this,
                            sheetName: this._name,
                            copyData: copyData,
                            cancel: false
                        };
                    var E = UI.Events;
                    this._trigger(E.ClipboardChanging,args);
                    if(args && args.cancel === false)
                    {
                        window.clipboardData.setData("Text",copyData);
                        this._trigger(E.ClipboardChanged,{
                            sheet: this,
                            sheetName: this._name,
                            copyData: copyData
                        })
                    }
                    else
                    {
                        ch.fromSheet = null;
                        ch.range = null
                    }
                }
            }
            catch(e){}
        },
        _trySearch: function(source, searchString, searchFlags)
        {
            if(source)
            {
                var sf = UI.SearchFlags;
                source = source.toString();
                searchString = searchString.toString();
                var exactMatch = (searchFlags & sf.ExactMatch) > 0;
                if(exactMatch && (searchFlags & sf.UseWildCards) === 0)
                    if((searchFlags & sf.IgnoreCase) > 0)
                    {
                        var sourceLower = source.toLowerCase();
                        var searchStringLower = searchString.toLowerCase();
                        return sourceLower === searchStringLower
                    }
                    else
                        return source === searchString;
                else
                {
                    var ignoreCase = (searchFlags & sf.IgnoreCase) > 0;
                    var useWildCards = (searchFlags & sf.UseWildCards) > 0;
                    var rgExp;
                    if(ignoreCase)
                        rgExp = new RegExp(fixFormulaKeyword(searchString,useWildCards,exactMatch),"i");
                    else
                        rgExp = new RegExp(fixFormulaKeyword(searchString,useWildCards,exactMatch));
                    var result = source.search(rgExp) > -1;
                    if(exactMatch && result && searchString.search(/\*/) <= -1)
                        result = source.length === searchString.length;
                    return result
                }
            }
            return false
        },
        _getFilterButtonHitInfo: function(target, x, y)
        {
            if(!this._rowFilter)
                return null;
            if(!this._filterButtonsModel)
            {
                this._filterButtonsModel = {};
                for(var rv = -1; rv <= 1; rv++)
                {
                    this._filterButtonsModel[rv] = {};
                    for(var cv = -1; cv <= 1; cv++)
                        this._filterButtonsModel[rv][cv] = []
                }
                var range = this._getActualRange(this._rowFilter.range);
                var row = range.row - 1,
                    col = range.col;
                var rowviewport = 1,
                    colviewport = 1;
                var sheetArea = UI.SheetArea.viewport;
                if(row < 0)
                {
                    rowviewport = -1;
                    sheetArea = UI.SheetArea.colHeader;
                    row = this.getRowCount(sheetArea) - 1
                }
                else if(row < this.frozenRowCount)
                    rowviewport = 0;
                var model = this._filterButtonsModel[rowviewport];
                for(var j = 0; j < range.colCount; j++)
                {
                    var tempRow = row,
                        tempCol = col + j;
                    var spans = this.getSpans(new UI.Range(tempRow,tempCol,1,1),sheetArea);
                    if(spans && spans.length > 0)
                        tempRow = spans[0].row;
                    colviewport = tempCol < this.frozenColCount ? 0 : 1;
                    var cellRect = this.getCellRect(tempRow,tempCol,rowviewport,colviewport);
                    var filterButtonRect = this._render._getFilterButtonRect(cellRect);
                    model[colviewport].push({
                        row: tempRow,
                        col: tempCol,
                        x: filterButtonRect.x,
                        y: filterButtonRect.y,
                        width: filterButtonRect.width,
                        height: filterButtonRect.height,
                        sheetArea: sheetArea
                    })
                }
            }
            for(var r in this._filterButtonsModel)
                if(this._filterButtonsModel[r])
                {
                    var rowFilterButtonsModel = this._filterButtonsModel[r];
                    for(var c in rowFilterButtonsModel)
                        if(rowFilterButtonsModel[c])
                        {
                            var filterButtonsModel = rowFilterButtonsModel[c];
                            if(filterButtonsModel.length > 0)
                                for(var i = 0; i < filterButtonsModel.length; i++)
                                {
                                    var filterButtonInfo = filterButtonsModel[i];
                                    if(target.row === filterButtonInfo.row && target.col === filterButtonInfo.col && target.hitTestType === filterButtonInfo.sheetArea)
                                        if(x >= filterButtonInfo.x && x <= filterButtonInfo.x + filterButtonInfo.width && y >= filterButtonInfo.y && y <= filterButtonInfo.y + filterButtonInfo.height)
                                            return filterButtonInfo
                                }
                        }
                }
            return null
        },
        _getCellTypeHitInfo: function(target, x, y)
        {
            var r = target.row,
                c = target.col,
                sheetArea = target.hitTestType;
            var ct = this.getCellType(r,c,sheetArea);
            var cellRect = this.getCellRect(r,c);
            var cellStyle = this.getActualStyle(r,c,sheetArea);
            if(this.isProtected && cellStyle.locked)
                return null;
            return this._allowEditorReservedLocations ? ct.getHitInfo(x,y,r,c,cellStyle,cellRect,sheetArea) : null
        },
        _clearCellOverflowModelCachebyRow: function(row, sheetArea)
        {
            if(this._cellOverflowModelCache && (sheetArea === undefined || sheetArea === null || sheetArea === UI.SheetArea.viewport))
                this._cellOverflowModelCache[row] = null
        },
        _clearCellOverflowModelCache: function()
        {
            if(this._cellOverflowModelCache)
                this._cellOverflowModelCache = null
        },
        _trigger: function(type, data)
        {
            this._eventHandler.trigger(type,data)
        },
        _disposeUserEvents: function()
        {
            this.unbindAll();
            this._unbindAll()
        },
        _doCommand: function(action)
        {
            var undoManager = this.undoManager();
            undoManager.doAction(action)
        },
        _refreshTabStrip: function()
        {
            if(this.parent && this.parent._tab && this.parent._tab.repaint)
                this.parent._tab.repaint()
        },
        _isAnyCellInRangeLocked: function(range)
        {
            var ar = this._getActualRange(range);
            for(var row = ar.row; row < ar.row + ar.rowCount; row++)
                for(var col = ar.col; col < ar.col + ar.colCount; col++)
                {
                    var style = this.getActualStyle(row,col);
                    if(style.locked === true)
                        return true
                }
            return false
        },
        _isValidRange: function(row, column, rowCount, columnCount, maxRowCount, maxColumnCount)
        {
            if(-1 <= row && row < maxRowCount && -1 <= column && column < maxColumnCount)
                if(row === -1 && column === -1)
                    return true;
                else if(row === -1)
                {
                    if(columnCount !== 0 && column + columnCount <= maxColumnCount)
                        return true
                }
                else if(column === -1)
                {
                    if(rowCount !== 0 && row + rowCount <= maxRowCount)
                        return true
                }
                else if(columnCount !== 0 && column + columnCount <= maxColumnCount && rowCount !== 0 && row + rowCount <= maxRowCount)
                    return true;
            return false
        },
        _setValueInternal: function(row, col, sheetArea, value, raiseCellPropertyChanged)
        {
            this.setValue(row,col,value,sheetArea,false)
        },
        _hasPartSpans: function(row, column, rowCount, columnCount)
        {
            var enumerator,
                cs;
            if(row < 0 && column < 0)
                return false;
            else if(row < 0)
            {
                var columnHeaderSpanModel = this._colHeaderSpanModel;
                if(columnHeaderSpanModel && columnHeaderSpanModel.length !== 0)
                {
                    enumerator = columnHeaderSpanModel.getEnumerator(-1,column,-1,columnCount);
                    cs = null;
                    while(enumerator.moveNext())
                    {
                        cs = enumerator.current();
                        if(cs.col < column || cs.col + cs.colCount > column + columnCount)
                            return true
                    }
                }
            }
            else if(column < 0)
            {
                var rowHeaderSpanModel = this._rowHeaderSpanModel;
                if(rowHeaderSpanModel && rowHeaderSpanModel.length !== 0)
                {
                    enumerator = rowHeaderSpanModel.getEnumerator(row,-1,rowCount,-1);
                    cs = null;
                    while(enumerator.moveNext())
                    {
                        cs = enumerator.current();
                        if(cs.row < row || cs.row + cs.rowCount > row + rowCount)
                            return true
                    }
                }
            }
            var spanModel = this._spanModel;
            if(spanModel && spanModel.length !== 0)
            {
                var spanEnumerator = spanModel.getEnumerator(row,column,rowCount,columnCount);
                cs = null;
                while(spanEnumerator.moveNext())
                {
                    cs = spanEnumerator.current();
                    if(row !== -1)
                        if(cs.row < row || cs.row + cs.rowCount > row + rowCount)
                            return true;
                    if(column !== -1)
                        if(cs.col < column || cs.col + cs.colCount > column + columnCount)
                            return true
                }
            }
            return false
        },
        _hasSpans: function(row, column, rowCount, columnCount)
        {
            var spans = this._spanModel;
            if(spans)
                for(var i = 0; i < spans.length; i++)
                {
                    var span = spans[i];
                    if(span.intersect(row,column,rowCount,columnCount))
                        return true
                }
            return false
        },
        _hasPartArrayFormulas: function(row, column, rowCount, columnCount)
        {
            var arrayFormulas = this._getsArrayFormulas(row,column,rowCount,columnCount);
            if(arrayFormulas && arrayFormulas.length > 0)
            {
                var count = arrayFormulas.length;
                for(var i = 0; i < count; i++)
                {
                    var cs = arrayFormulas[i][0];
                    if(row !== -1)
                        if(cs.row < row || cs.row + cs.rowCount > row + rowCount)
                            return true;
                    if(column !== -1)
                        if(cs.col < column || cs.col + cs.colCount > column + columnCount)
                            return true
                }
            }
            return false
        },
        _getsArrayFormulas: function(row, column, rowCount, columnCount)
        {
            var formulas = this._findFormulas(row,column,rowCount,columnCount);
            if(formulas && formulas.length > 0)
            {
                var arrayFormulas = [];
                var ranges = [];
                var count = formulas.length,
                    i;
                for(i = 0; i < count; i++)
                {
                    var formula = formulas[i][1];
                    if(formula && formula !== "" && formula[0] === "{" && formula[formula.length - 1] === "}")
                    {
                        ranges.push(formulas[i][0]);
                        arrayFormulas.push(formula)
                    }
                }
                if(arrayFormulas.length > 0)
                {
                    var ret = {};
                    for(i = 0; i < arrayFormulas.length; i++)
                    {
                        ret[i][0] = ranges[i];
                        ret[i][1] = arrayFormulas[i]
                    }
                    return ret
                }
            }
            return null
        },
        _raiseDragDropBlock: function(fromRow, fromColumn, toRow, toColumn, rowCount, columnCount, copy, insert, copyOption)
        {
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    fromRow: fromRow,
                    fromCol: fromColumn,
                    toRow: toRow,
                    toCol: toColumn,
                    rowCount: rowCount,
                    colCount: columnCount,
                    copy: copy,
                    insert: insert,
                    copyOption: copyOption,
                    cancel: false
                };
            this._trigger(UI.Events.DragDropBlock,args);
            return args && args.cancel === true
        },
        _raiseDragDropBlockCompleted: function(fromRow, fromColumn, toRow, toColumn, rowCount, columnCount, copy, insert, copyOption)
        {
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    fromRow: fromRow,
                    fromCol: fromColumn,
                    toRow: toRow,
                    toCol: toColumn,
                    rowCount: rowCount,
                    colCount: columnCount,
                    copy: copy,
                    insert: insert,
                    copyOption: copyOption
                };
            this._trigger(UI.Events.DragDropBlockCompleted,args)
        },
        _raiseInvalidOperation: function(message)
        {
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    message: message
                };
            this._trigger(UI.Events.InvalidOperation,args)
        },
        _suspendInvalidate: function()
        {
            this._layoutSuspended++
        },
        _resumeInvalidate: function()
        {
            this._layoutSuspended--;
            if(this._layoutSuspended <= 0)
            {
                if(!this.isPaintSuspended())
                {
                    this.invalidateLayout();
                    this.repaint()
                }
                this._layoutSuspended = 0
            }
        },
        _raiseSelectionChanging: function(oldSelections, newSelections)
        {
            var notEqual = this._eventHandler._notEqualSelecions(oldSelections,newSelections);
            if(notEqual === true)
            {
                this._trigger(UI.Events.SelectionChanging,{
                    sheet: this,
                    sheetName: this._name,
                    oldSelections: oldSelections,
                    newSelections: newSelections
                });
                return true
            }
            return false
        },
        _raiseSelectionChanged: function()
        {
            this._trigger(UI.Events.SelectionChanged,{
                sheet: this,
                sheetName: this._name
            })
        },
        _raiseDragFillBlock: function(fillRange, fillDirection, fillType)
        {
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    fillRange: fillRange,
                    autoFillType: fillType,
                    fillDirection: fillDirection,
                    cancel: false
                };
            this._trigger(UI.Events.DragFillBlock,args);
            return args && args.cancel === true
        },
        _raiseDragFillBlockCompleted: function(fillRange, fillDirection, fillType)
        {
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    fillRange: fillRange,
                    autoFillType: fillType,
                    fillDirection: fillDirection
                };
            this._trigger(UI.Events.DragFillBlockCompleted,args)
        },
        _raiseCellChanged: function(propertyName, row, column, sheetArea)
        {
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    row: row,
                    col: column,
                    sheetArea: sheetArea,
                    propertyName: propertyName
                };
            this._trigger(UI.Events.CellChanged,args)
        },
        _raisePropertyChanged: function(propertyName){},
        _nextNonNullRow: function(row, rowHeader)
        {
            row++;
            var SheetArea = UI.SheetArea;
            var model = rowHeader ? this._getModel(SheetArea.rowHeader) : this._getModel(SheetArea.viewport);
            var count = model.rowCount;
            if(rowHeader === false && this._dataSource)
                count = Math.max(count,this._getDataLength(this._dataSource));
            while(row >= 0 && row < count)
            {
                if(model.dataTable.hasOwnProperty(row) && model.dataTable[row])
                    break;
                if(rowHeader === false && this._dataSource)
                    if(this._getDataItem(this._dataSource,row))
                        break;
                row++
            }
            if(row < count)
                return row;
            return-1
        },
        _nextNonNullColumn: function(row, col, columnHeader)
        {
            var dr = null;
            var SheetArea = UI.SheetArea;
            var model = columnHeader ? this._getModel(SheetArea.colHeader) : this._getModel(SheetArea.viewport);
            if(row >= 0 && row < model.rowCount)
                if(model.dataTable.hasOwnProperty(row))
                    dr = model.dataTable[row];
            if(columnHeader === false && !dr && this._dataSource)
                if(row >= 0 && row < this._getDataLength(this._dataSource))
                    dr = this._getDataItem(this._dataSource,row);
            if(!dr)
                return-1;
            col++;
            while(col >= 0 && (col < model.colCount || col < dr.length))
            {
                if(dr.hasOwnProperty(col) && dr[col])
                    break;
                if(columnHeader === false && this._dataSource)
                {
                    var value = this._getValueImp(model,row,col);
                    if(value !== undefined && value !== null)
                        break
                }
                col++
            }
            if(col < model.colCount || columnHeader === false && dr && col < dr.length)
                return col;
            return-1
        },
        _closeDragFillPopup: function()
        {
            if(this._smartTag)
                this._smartTag.close()
        },
        _cellRangeInflate: function(spans, cellRange)
        {
            if(spans && spans.length > 0)
                for(var i = 0; i < spans.length; i++)
                {
                    var cr = spans[i];
                    if(cellRange.intersect(cr.row,cr.col,cr.rowCount,cr.colCount))
                    {
                        spans.splice(i--,1);
                        return this._cellRangeInflate(spans,this._unionCellRange(cellRange,cr))
                    }
                }
            return cellRange
        },
        _unionCellRange: function(range1, range2)
        {
            var ltr = Math.min(range1.row,range2.row);
            var ltc = Math.min(range1.col,range2.col);
            var rbr = Math.max(range1.row + range1.rowCount - 1,range2.row + range2.rowCount - 1);
            var rbc = Math.max(range1.col + range1.colCount - 1,range2.col + range2.colCount - 1);
            var Range = UI.Range;
            if(ltr >= 0 && ltc >= 0)
                return new Range(ltr,ltc,rbr - ltr + 1,rbc - ltc + 1);
            else if(ltr >= 0)
                return new Range(ltr,-1,rbr - ltr + 1,-1);
            else if(ltc >= 0)
                return new Range(-1,ltc,-1,rbc - ltc + 1);
            else
                return new Range(-1,-1,-1,-1)
        },
        _getClipboardHelper: function()
        {
            if(!this._clipboardHelper && this.parent && this.parent._clipboardHelper)
                this._clipboardHelper = this.parent._clipboardHelper;
            else if(!this._clipboardHelper)
                this._clipboardHelper = {
                    fromSheet: null,
                    range: null,
                    isCutting: false
                };
            return this._clipboardHelper
        },
        _checkPastedRange: function(fromSheet, fromRange, toRange, isCutting, clipboardText, outPara)
        {
            var msg_sheetViewPasteSouceSheetCellsAreLocked = "Source sheet's cells are locked.";
            var msg_sheetViewTheCopyAreaAndPasteAreaAreNotTheSameSize = "The copy and paste areas are not the same size.";
            var msg_sheetViewPasteDestinationSheetCellsAreLocked = "The cell you are trying to change is protected and therefore read-only.";
            var msg_sheetViewPasteChangeMergeCell = "Cannot change part of a merged cell.";
            var msg_sheetViewPasteChangePartOfArrayFormula = "Cannot change part of an array.";
            outPara.pastedInternal = false;
            outPara.pastedRange = null;
            if(!fromSheet && (!clipboardText || clipboardText === ""))
                return false;
            var toSheet = this;
            if(this._isPastedInternal(fromSheet,fromRange,toSheet,clipboardText) || !clipboardText)
            {
                outPara.pastedInternal = true;
                if(isCutting && fromSheet.isProtected && fromSheet._isAnyCellInRangeLocked(fromRange))
                {
                    this._raiseInvalidOperation(msg_sheetViewPasteSouceSheetCellsAreLocked);
                    return false
                }
                outPara.pastedRange = this._getPastedRange(fromSheet,fromRange,toSheet,toRange,isCutting)
            }
            else
                outPara.pastedRange = this._getPastedRangefromText(toRange,clipboardText);
            if(!outPara.pastedRange)
            {
                this._raiseInvalidOperation(msg_sheetViewTheCopyAreaAndPasteAreaAreNotTheSameSize);
                return false
            }
            if(toSheet.isProtected && toSheet._isAnyCellInRangeLocked(outPara.pastedRange))
            {
                this._raiseInvalidOperation(msg_sheetViewPasteDestinationSheetCellsAreLocked);
                return false
            }
            if(outPara.pastedInternal)
            {
                if(fromSheet._hasPartSpans(fromRange.row,fromRange.col,fromRange.rowCount,fromRange.colCount))
                {
                    this._raiseInvalidOperation(msg_sheetViewPasteChangeMergeCell);
                    return false
                }
                if(fromSheet._hasPartArrayFormulas(fromRange.row,fromRange.col,fromRange.rowCount,fromRange.colCount))
                {
                    this._raiseInvalidOperation(msg_sheetViewPasteChangePartOfArrayFormula);
                    return false
                }
                var toRowCount = outPara.pastedRange.row < 0 ? toSheet.getRowCount() : outPara.pastedRange.rowCount;
                var toColumnCount = outPara.pastedRange.col < 0 ? toSheet.getColumnCount() : outPara.pastedRange.colCount;
                var rowCount = fromRange.row < 0 ? fromSheet.getRowCount() : fromRange.rowCount;
                var columnCount = fromRange.Column < 0 ? fromSheet.getColumnCount() : fromRange.colCount;
                if(toRowCount > rowCount || toColumnCount > columnCount)
                {
                    var toRow = toRange.row;
                    var toColumn = toRange.col;
                    if(toRange.row < 0 && rowCount < toSheet.getRowCount())
                        toRow = 0;
                    if(toRange.col < 0 && columnCount < toSheet.getColumnCount())
                        toColumn = 0;
                    if(toRowCount % rowCount !== 0 || toColumnCount % columnCount !== 0)
                    {
                        toRowCount = rowCount;
                        toColumnCount = columnCount;
                        outPara.pastedRange = new UI.Range(toRow,toColumn,toRowCount,toColumnCount)
                    }
                    var rn = Math.floor(toRowCount / rowCount);
                    var cn = Math.floor(toColumnCount / columnCount);
                    for(var r = 0; r < rn; r++)
                        for(var c = 0; c < cn; c++)
                        {
                            if(toSheet._hasPartSpans(toRow < 0 ? -1 : toRow + r * rowCount,toColumn < 0 ? -1 : toColumn + c * columnCount,toRow < 0 ? -1 : rowCount,toColumn < 0 ? -1 : columnCount))
                            {
                                this._raiseInvalidOperation(msg_sheetViewPasteChangeMergeCell);
                                return false
                            }
                            if(toSheet._hasPartArrayFormulas(toRow < 0 ? -1 : toRow + r * rowCount,toColumn < 0 ? -1 : toColumn + c * columnCount,toRow < 0 ? -1 : rowCount,toColumn < 0 ? -1 : columnCount))
                            {
                                this._raiseInvalidOperation(msg_sheetViewPasteChangePartOfArrayFormula);
                                return false
                            }
                        }
                }
                else
                {
                    if(toSheet._hasPartSpans(outPara.pastedRange.row,outPara.pastedRange.col,outPara.pastedRange.rowCount,outPara.pastedRange.colCount))
                    {
                        this._raiseInvalidOperation(msg_sheetViewPasteChangeMergeCell);
                        return false
                    }
                    if(toSheet._hasPartArrayFormulas(outPara.pastedRange.row,outPara.pastedRange.col,outPara.pastedRange.rowCount,outPara.pastedRange.colCount))
                    {
                        this._raiseInvalidOperation(msg_sheetViewPasteChangePartOfArrayFormula);
                        return false
                    }
                }
            }
            else
            {
                if(toSheet._hasPartSpans(outPara.pastedRange.row,outPara.pastedRange.col,outPara.pastedRange.rowCount,outPara.pastedRange.colCount))
                {
                    this._raiseInvalidOperation(msg_sheetViewPasteChangeMergeCell);
                    return false
                }
                if(toSheet._hasPartArrayFormulas(outPara.pastedRange.row,outPara.pastedRange.col,outPara.pastedRange.rowCount,outPara.pastedRange.colCount))
                {
                    this._raiseInvalidOperation(msg_sheetViewPasteChangePartOfArrayFormula);
                    return false
                }
                if(outPara.pastedRange.row + outPara.pastedRange.rowCount > toSheet.getRowCount() || outPara.pastedRange.col + outPara.pastedRange.colCount > toSheet.getColumnCount())
                {
                    this._raiseInvalidOperation(msg_sheetViewTheCopyAreaAndPasteAreaAreNotTheSameSize);
                    return false
                }
            }
            return true
        },
        _isPastedInternal: function(srcSheet, srcRange, destSheet, clipboadText)
        {
            return srcSheet && srcRange && destSheet && srcSheet.getCsv(srcRange.row,srcRange.col,srcRange.rowCount,srcRange.colCount,"\r\n","\t") === clipboadText
        },
        _getPastedRange: function(fromSheet, fromRange, toSheet, toRange, isCutting)
        {
            var fromRow = fromRange.row < 0 ? 0 : fromRange.row;
            var fromColumn = fromRange.col < 0 ? 0 : fromRange.col;
            var fromRowCount = fromRange.row < 0 ? fromSheet.getRowCount() : fromRange.rowCount;
            var fromColumnCount = fromRange.col < 0 ? fromSheet.getColumnCount() : fromRange.colCount;
            var toRow = toRange.row < 0 ? 0 : toRange.row;
            var toColumn = toRange.col < 0 ? 0 : toRange.col;
            var toRowCount = toRange.row < 0 ? toSheet.getRowCount() : toRange.rowCount;
            var toColumnCount = toRange.col < 0 ? toSheet.getColumnCount() : toRange.colCount;
            if(isCutting || toRowCount % fromRowCount !== 0 || toColumnCount % fromColumnCount !== 0)
            {
                toRowCount = fromRowCount;
                toColumnCount = fromColumnCount
            }
            if(!this._isValidRange(fromRow,fromColumn,fromRowCount,fromColumnCount,fromSheet.getRowCount(),fromSheet.getColumnCount()))
                return null;
            if(!this._isValidRange(toRow,toColumn,toRowCount,toColumnCount,toSheet.getRowCount(),toSheet.getColumnCount()))
                return null;
            var fixedToRange = new UI.Range(toRow,toColumn,toRowCount,toColumnCount);
            if(!isCutting && fromSheet._name === toSheet._name)
                if(fixedToRange.contains(fromRow,fromColumn,fromRowCount,fromColumnCount))
                {
                    if((fromRow - toRow) % fromRowCount !== 0 || (fromColumn - toColumn) % fromColumnCount !== 0)
                        return null
                }
                else if(fixedToRange.intersect(fromRow,fromColumn,fromRowCount,fromColumnCount))
                    if(toRowCount > fromRowCount || toColumnCount > fromColumnCount)
                        return null;
            if(toRange.row === -1)
            {
                toRow = -1;
                toRowCount = -1
            }
            if(toRange.col === -1)
            {
                toColumn = -1;
                toColumnCount = -1
            }
            return new UI.Range(toRow,toColumn,toRowCount,toColumnCount)
        },
        _getPastedRangefromText: function(toRange, clipboadText)
        {
            var ret = null;
            var textArray = staticMembers.parseCsv(clipboadText,"\r\n","\t","\"");
            if(textArray)
            {
                var row = toRange.row < 0 ? 0 : toRange.row;
                var column = toRange.col < 0 ? 0 : toRange.col;
                var rowCount = textArray.length;
                var columnCount = staticMembers.getMaxLength(textArray);
                ret = new UI.Range(row,column,rowCount,columnCount)
            }
            return ret
        },
        _getClipboardData: function()
        {
            try
            {
                if(window.clipboardData && window.clipboardData.getData)
                    return window.clipboardData.getData("Text")
            }
            catch(e)
            {
                return null
            }
        },
        _clearClipboard: function()
        {
            try
            {
                if(window.clipboardData && window.clipboardData.clearData)
                    window.clipboardData.clearData("Text")
            }
            catch(e){}
        },
        _clipboardPaste: function(fromSheet, fromRange, toSheet, toRange, isCutting, clipboardText, option)
        {
            if(fromSheet && toSheet._name === fromSheet._name && toSheet.parent && !toSheet.parent.sheets.contains(fromSheet))
            {
                this._clearClipboard();
                return
            }
            var toRow,
                toColumn,
                rowCount,
                columnCount,
                r,
                c;
            if(fromSheet && fromRange)
            {
                var convertPasteOption = UI.UndoRedo.ClipboardPasteRangeUndoAction.convertPasteOption;
                if(isCutting)
                {
                    staticMembers.moveTo(fromSheet,fromRange.row,fromRange.col,toSheet,toRange.row,toRange.col,fromRange.rowCount,fromRange.colCount,convertPasteOption(option));
                    this._clearClipboard()
                }
                else
                {
                    var toRowCount = toRange.row < 0 ? toSheet.getRowCount() : toRange.rowCount;
                    var toColumnCount = toRange.col < 0 ? toSheet.getColumnCount() : toRange.colCount;
                    rowCount = fromRange.row < 0 ? fromSheet.getRowCount() : fromRange.rowCount;
                    columnCount = fromRange.col < 0 ? fromSheet.getColumnCount() : fromRange.colCount;
                    if(toRowCount > rowCount || toColumnCount > columnCount)
                    {
                        toRow = toRange.row;
                        toColumn = toRange.col;
                        if(toRange.row < 0 && rowCount < toSheet.getRowCount())
                            toRow = 0;
                        if(toRange.col < 0 && columnCount < toSheet.getColumnCount())
                            toColumn = 0;
                        if(toRowCount % rowCount !== 0 || toColumnCount % columnCount !== 0)
                        {
                            toRowCount = rowCount;
                            toColumnCount = columnCount
                        }
                        var rn = Math.floor(toRowCount / rowCount);
                        var cn = Math.floor(toColumnCount / columnCount);
                        fromSheet.suspendCalcService();
                        toSheet.suspendCalcService();
                        try
                        {
                            for(r = 0; r < rn; r++)
                                for(c = 0; c < cn; c++)
                                    staticMembers.copyTo(fromSheet,fromRange.row,fromRange.col,toSheet,toRow < 0 ? -1 : toRow + r * rowCount,toColumn < 0 ? -1 : toColumn + c * columnCount,toRow < 0 ? -1 : rowCount,toColumn < 0 ? -1 : columnCount,convertPasteOption(option))
                        }
                        finally
                        {
                            fromSheet.resumeCalcService();
                            toSheet.resumeCalcService()
                        }
                    }
                    else
                        staticMembers.copyTo(fromSheet,fromRange.row,fromRange.col,toSheet,toRange.row,toRange.col,fromRange.rowCount,fromRange.colCount,convertPasteOption(option))
                }
            }
            else
            {
                toRow = toRange.row;
                toColumn = toRange.col;
                rowCount = toRange.rowCount;
                columnCount = toRange.colCount;
                var spanEnumerator = toSheet._spanModel.getEnumerator(toRow,toColumn,rowCount,columnCount);
                while(spanEnumerator.moveNext())
                {
                    var cs = spanEnumerator.current();
                    if(cs)
                        toSheet._spanModel.remove(cs)
                }
                if(!clipboardText || clipboardText === "")
                    for(r = 0; r < rowCount; r++)
                        for(c = 0; c < columnCount; c++)
                            toSheet.setValue(toRow + r,toColumn + c,null);
                else
                    toSheet.setCsv(toRow,toColumn,clipboardText,"\r\n","\t",UI.TextFileOpenFlags.ImportFormula)
            }
        },
        _raiseClipboardPasting: function(cellRange, pastOption)
        {
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    cellRange: cellRange,
                    pastOption: pastOption,
                    cancel: false
                };
            this._trigger(UI.Events.ClipboardPasting,args);
            return args.cancel === true
        },
        _raiseClipboardPasted: function(cellRange, pastOption)
        {
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    cellRange: cellRange,
                    pastOption: pastOption
                };
            this._trigger(UI.Events.ClipboardPasted,args)
        },
        _raiseValueChanged: function(row, column)
        {
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    row: row,
                    col: column
                };
            this._trigger(UI.Events.ValueChanged,args)
        },
        _raiseRangeDataChanged: function(row, column, rowCount, columnCount)
        {
            var args = {
                    sheet: this,
                    sheetName: this._name,
                    row: row,
                    column: column,
                    rowCount: rowCount,
                    columnCount: columnCount
                };
            this._trigger(UI.Events.RangeChanged,args)
        },
        _copyFormula: function(fromRow, fromColumn, toRow, toColumn, rowCount, columnCount)
        {
            staticMembers.copyFormula(this,fromRow,fromColumn,this,toRow,toColumn,rowCount,columnCount)
        }
    }
})(window,jQuery);
(function(window)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    UI.AutoFillType = {
        CopyCells: 0,
        FillSeries: 1,
        FillFormattingOnly: 2,
        FillWithoutFormatting: 3,
        ClearValues: 4
    };
    var AutoFillType = UI.AutoFillType;
    UI.FillDirection = {
        Left: 0,
        Right: 1,
        Up: 2,
        Down: 3
    };
    var FillDirection = UI.FillDirection;
    UI.FillSeries = {
        Column: 0,
        Row: 1
    };
    var FillSeries = UI.FillSeries;
    UI.StorageType = {
        Data: 0x01,
        Style: 0x02,
        Sparkline: 0x10,
        Axis: 0x20,
        BindingPath: 0x40
    };
    var StorageType = UI.StorageType;
    UI.DragFillDirection = {
        Left: 0,
        Right: 1,
        Up: 2,
        Down: 3,
        LeftClear: 4,
        UpClear: 5
    };
    var DragFillDirection = UI.DragFillDirection;
    function CopyMoveSheetInfo(){}
    function __navigateNoWrap(sheet, direction)
    {
        if(sheet && sheet._editorStatus !== UI.EditorStatus.Edit)
        {
            sheet.moveActiveCell(direction,false);
            return true
        }
        return false
    }
    function __navigateNoWrapFrom(sheet, direction, row, col)
    {
        if(sheet && sheet._editorStatus !== UI.EditorStatus.Edit)
            sheet.moveActiveCell(direction,false,row,col)
    }
    function __alterSelection(sheet, key, isCtrl)
    {
        if(sheet && sheet._editorStatus !== UI.EditorStatus.Edit)
        {
            sheet.endEdit();
            sheet._changeActiveSelectedRange(key,isCtrl)
        }
    }
    function CellData(row, column, value)
    {
        this.row = row;
        this.column = column;
        this.value = value
    }
    function CopyMoveCellsInfo(rowCount, columnCount)
    {
        this._rowCount = rowCount;
        this._columnCount = columnCount;
        this._init()
    }
    CopyMoveCellsInfo.prototype = {
        _init: function()
        {
            this._values = [];
            this._formulas = [];
            this._sparklines = [];
            this._styles = [];
            this._tags = [];
            this._bindingPaths = [];
            this._spans = null;
            this._arrayFormulas = null;
            this._valueSaved = false;
            this._formulaSaved = false;
            this._sparklineSaved = false;
            this._styleSaved = false;
            this._tagSaved = false;
            this._spanSaved = false;
            this._arrayFormulaSaved = false;
            this._bindingPathSaved = false
        },
        saveValue: function(row, column, value)
        {
            if(value !== undefined && value !== null)
                this._values.push(new CellData(row,column,value));
            this._valueSaved = true
        },
        getValues: function()
        {
            return this._values
        },
        saveFormula: function(row, column, formula)
        {
            if(formula && formula !== "")
                this._formulas.push(new CellData(row,column,formula));
            this._formulaSaved = true
        },
        getFormulas: function()
        {
            return this._formulas
        },
        saveArrayFormula: function(arrayFormulas)
        {
            this._arrayFormulas = arrayFormulas;
            this._arrayFormulaSaved = true
        },
        getArrayFormula: function()
        {
            return this._arrayFormulas
        },
        saveSparkline: function(row, column, sparkline)
        {
            if(sparkline)
                this._sparklines.push(new CellData(row,column,sparkline));
            this._sparklineSaved = true
        },
        getSparklines: function()
        {
            return this._sparklines
        },
        saveStyle: function(row, column, style)
        {
            if(style)
                this._styles.push(new CellData(row,column,style));
            this._styleSaved = true
        },
        getStyles: function()
        {
            return this._styles
        },
        saveTag: function(row, column, tag)
        {
            if(tag !== undefined && tag !== null)
                this._tags.push(new CellData(row,column,tag));
            this._tagSaved = true
        },
        getTags: function()
        {
            return this._tags
        },
        saveBindingPath: function(row, column, path)
        {
            if(path !== undefined && path !== null)
                this._bindingPaths.push(new CellData(row,column,path));
            this._bindingPathSaved = true
        },
        getBindingPaths: function()
        {
            return this._bindingPaths
        },
        saveSpan: function(span)
        {
            if(!this._spans)
                this._spans = [];
            this._spans.push(span);
            this._spanSaved = true
        },
        isValueSaved: function()
        {
            return this._valueSaved
        },
        isFormulaSaved: function()
        {
            return this._formulaSaved
        },
        isArrayFormulaSaved: function()
        {
            return this._arrayFormulaSaved
        },
        isSparklineSaved: function()
        {
            return this._sparklineSaved
        },
        isStyleSaved: function()
        {
            return this._styleSaved
        },
        isTagSaved: function()
        {
            return this._tagSaved
        },
        isSpanSaved: function()
        {
            return this._spanSaved
        },
        isBindingPathSaved: function()
        {
            return this._bindingPathSaved
        }
    };
    function CopyMoveColumnsInfo(columnCount)
    {
        this._columnCount = columnCount;
        this._init()
    }
    CopyMoveColumnsInfo.prototype = {
        _init: function()
        {
            this._widths = {};
            this._visibles = {};
            this._resizables = {};
            this._tags = {};
            this._viewportColumnStyles = {};
            this._headerColumnStyles = {};
            this._levels = {};
            this._collapsed = {};
            this._bindingFields = {};
            this._widthSaved = false;
            this._visibleSaved = false;
            this._resizableSaved = false;
            this._tagSaved = false;
            this._viewportColumnStyleSaved = false;
            this._headerColumnStyleSaved = false;
            this._rangeGroupSaved = false;
            this._bindingFieldSaved = false
        },
        saveWidth: function(index, width)
        {
            if(this._widths[index] !== null)
                if(width < 0)
                    this._widths[index] = null;
                else
                    this._widths[index] = width;
            else if(width >= 0)
                this._widths[index] = width;
            this._widthSaved = true
        },
        getWidth: function(index)
        {
            if(this._widths[index] !== null)
                return this._widths[index];
            return-1
        },
        saveVisible: function(index, visible)
        {
            if(this._visibles[index] !== null)
                if(visible)
                    this._visibles[index] = null;
                else
                    this._visibles[index] = visible;
            else if(!visible)
                this._visibles[index] = visible;
            this._visibleSaved = true
        },
        getVisible: function(index)
        {
            if(this._visibles[index] !== null)
                return this._visibles[index];
            return true
        },
        saveResizable: function(index, resizable)
        {
            if(this._resizables[index] !== null)
                if(resizable)
                    this._resizables[index] = null;
                else
                    this._resizables[index] = resizable;
            else if(!resizable)
                this._resizables[index] = resizable;
            this._resizableSaved = true
        },
        getResizable: function(index)
        {
            if(this._resizables[index] !== null)
                return this._resizables[index];
            return true
        },
        saveTag: function(index, tag)
        {
            if(this._tags[index] !== null)
                if(tag === undefined || tag === null)
                    this._tags[index] = null;
                else
                    this._tags[index] = tag;
            else if(tag !== null)
                this._tags[index] = tag;
            this._tagSaved = true
        },
        getTag: function(index)
        {
            if(this._tags[index] !== null)
                return this._tags[index];
            return null
        },
        saveViewportColumnStyle: function(index, style)
        {
            if(this._viewportColumnStyles[index])
                if(!style)
                    this._viewportColumnStyles[index] = null;
                else
                    this._viewportColumnStyles[index] = style;
            else if(style)
                this._viewportColumnStyles[index] = style;
            this._viewportColumnStyleSaved = true
        },
        getViewportColumnStyle: function(index)
        {
            if(this._viewportColumnStyles[index])
                return this._viewportColumnStyles[index];
            return null
        },
        saveHeaderColumnStyle: function(index, style)
        {
            if(this._headerColumnStyles[index])
                if(!style)
                    this._headerColumnStyles[index] = null;
                else
                    this._headerColumnStyles[index] = style;
            else if(style)
                this._headerColumnStyles[index] = style;
            this._headerColumnStyleSaved = true
        },
        getHeaderColumnStyle: function(index)
        {
            if(this._headerColumnStyles[index])
                return this._headerColumnStyles[index];
            return null
        },
        saveRangeGroup: function(index, level, collapsed)
        {
            if(this._levels[index] !== null)
                if(level < 0)
                    this._levels[index] = null;
                else
                    this._levels[index] = level;
            else if(level >= 0)
                this._levels[index] = level;
            if(this._collapsed[index] !== null)
                if(collapsed)
                    this._collapsed[index] = collapsed;
                else
                    this._collapsed[index] = null;
            else if(collapsed)
                this._collapsed[index] = collapsed;
            this._rangeGroupSaved = true
        },
        getRangeGroup: function(index, outData)
        {
            outData.level = -1;
            outData.collapsed = false;
            if(this._levels[index] !== null)
                outData.level = this._levels[index];
            if(this._collapsed[index] !== null)
                outData.collapsed = this._collapsed[index]
        },
        saveBindingField: function(index, fieldName)
        {
            this._bindingFields[index] = fieldName;
            this._bindingFieldSaved = true
        },
        getBindingField: function(index, outData)
        {
            outData.fieldName = null;
            if(this._bindingFields[index] !== null)
            {
                outData.fieldName = this._bindingFields[index];
                return true
            }
            return false
        },
        isWidthSaved: function()
        {
            return this._widthSaved
        },
        isVisibleSaved: function()
        {
            return this._visibleSaved
        },
        isResizableSaved: function()
        {
            return this._resizableSaved
        },
        isTagSaved: function()
        {
            return this._tagSaved
        },
        isViewportColumnStyleSaved: function()
        {
            return this._viewportColumnStyleSaved
        },
        isHeaderColumnStyleSaved: function()
        {
            return this._headerColumnStyleSaved
        },
        isRangeGroupSaved: function()
        {
            return this._rangeGroupSaved
        },
        isBindingFieldSaved: function()
        {
            return this._bindingFieldSaved
        }
    };
    var CopyMoveHelper = {
            getStyleObject: function(sheet, row, column, area)
            {
                return sheet.getStyle(row,column,area)
            },
            setStyleObject: function(sheet, row, column, area, style)
            {
                if(typeof style !== "string")
                    sheet.setStyle(row,column,style,area)
            },
            saveSheetInfo: function(sheet, sheetInfo, option)
            {
                if((option & UI.CopyToOption.Style) > 0)
                {
                    sheetInfo.saveDefaultStyle(sheet.getDefaultStyle());
                    sheetInfo.saveColumnHeaderDefaultStyle(sheet.getDefaultStyle(UI.SheetArea.colHeader));
                    sheetInfo.saveRowHeaderDefaultStyle(sheet.getDefaultStyle(UI.SheetArea.rowHeader))
                }
                sheetInfo.saveDefaultColumnWidth(sheet.defaults.colWidth);
                sheetInfo.saveDefaultRowHeight(sheet.defaults.rowHeight);
                sheetInfo.saveColumnHeaderDefaultRowHeight(sheet.defaults.rowHeight);
                sheetInfo.saveRowHeaderDefaultColumnWidth(sheet.defaults.rowHeaderColWidth)
            },
            saveColumnHeaderInfo: function(sheet, headerCellsInfo, columnsInfo, baseColumn, option)
            {
                var rowCount = headerCellsInfo._rowCount;
                var columnCount = headerCellsInfo._columnCount;
                var r,
                    c;
                for(r = 0; r < rowCount; r++)
                    for(c = 0; c < columnCount; c++)
                    {
                        if((option & UI.CopyToOption.Value) > 0)
                        {
                            var value = sheet._getModel(UI.SheetArea.colHeader).getValue(r,baseColumn + c);
                            headerCellsInfo.saveValue(r,c,value)
                        }
                        if((option & UI.CopyToOption.Style) > 0)
                            headerCellsInfo.saveStyle(r,c,CopyMoveHelper.getStyleObject(sheet,r,baseColumn + c,UI.SheetArea.colHeader))
                    }
                if((option & UI.CopyToOption.Value) > 0)
                    for(c = 0; c < columnCount; c++)
                        if(sheet.isColumnBound(baseColumn + c))
                            columnsInfo.saveBindingField(c,sheet.getDataColumnName(baseColumn + c));
                if((option & UI.CopyToOption.Span) > 0)
                {
                    var spanEnumerator = sheet._colHeaderSpanModel.getEnumerator(0,baseColumn,rowCount,columnCount);
                    while(spanEnumerator.moveNext())
                        headerCellsInfo.saveSpan(spanEnumerator.current())
                }
                columnCount = columnsInfo._columnCount;
                for(c = 0; c < columnCount; c++)
                {
                    columnsInfo.saveWidth(c,sheet._getActualColumnWidth(baseColumn + c));
                    columnsInfo.saveVisible(c,sheet.getColumnVisible(baseColumn + c));
                    columnsInfo.saveResizable(c,sheet.getColumnResizable(baseColumn + c))
                }
                if((option & UI.CopyToOption.Style) > 0)
                    for(c = 0; c < columnCount; c++)
                    {
                        columnsInfo.saveViewportColumnStyle(c,CopyMoveHelper.getStyleObject(sheet,-1,baseColumn + c,UI.SheetArea.viewport));
                        columnsInfo.saveHeaderColumnStyle(c,CopyMoveHelper.getStyleObject(sheet,-1,baseColumn + c,UI.SheetArea.colHeader))
                    }
                if((option & UI.CopyToOption.RangeGroup) > 0)
                {
                    var group = sheet.colRangeGroup;
                    if(group && !group._isEmpty())
                        for(c = 0; c < columnCount; c++)
                            columnsInfo.saveRangeGroup(c,group.getLevel(baseColumn + c),group.getCollapsed(baseColumn + c))
                }
            },
            saveRowHeaderInfo: function(sheet, headerCellsInfo, rowsInfo, baseRow, option)
            {
                if((option & UI.CopyToOption.All) <= 0)
                    return;
                var rowCount = headerCellsInfo._rowCount;
                var columnCount = headerCellsInfo._columnCount;
                var r,
                    c;
                for(r = 0; r < rowCount; r++)
                    for(c = 0; c < columnCount; c++)
                    {
                        if((option & UI.CopyToOption.Value) > 0)
                        {
                            var value = sheet._getModel(UI.SheetArea.rowHeader).getValue(baseRow + r,c);
                            headerCellsInfo.saveValue(r,c,value)
                        }
                        if((option & UI.CopyToOption.Style) > 0)
                            headerCellsInfo.saveStyle(r,c,CopyMoveHelper.getStyleObject(sheet,baseRow + r,c,UI.SheetArea.rowHeader))
                    }
                if((option & UI.CopyToOption.Span) > 0)
                {
                    var spanEnumerator = sheet._rowHeaderSpanModel.getEnumerator(baseRow,0,rowCount,columnCount);
                    while(spanEnumerator.moveNext())
                        headerCellsInfo.saveSpan(spanEnumerator.current())
                }
                rowCount = rowsInfo._rowCount;
                for(r = 0; r < rowCount; r++)
                {
                    rowsInfo.saveHeight(r,sheet._getActualRowHeight(baseRow + r));
                    rowsInfo.saveVisible(r,sheet.getRowVisible(baseRow + r));
                    rowsInfo.saveResizable(r,sheet.getRowResizable(baseRow + r))
                }
                if((option & UI.CopyToOption.Style) > 0)
                    for(r = 0; r < rowCount; r++)
                    {
                        rowsInfo.saveViewportRowStyle(r,CopyMoveHelper.getStyleObject(sheet,baseRow + r,-1,UI.SheetArea.viewport));
                        rowsInfo.saveHeaderRowStyle(r,CopyMoveHelper.getStyleObject(sheet,baseRow + r,-1,UI.SheetArea.rowHeader))
                    }
                if((option & UI.CopyToOption.RangeGroup) > 0)
                {
                    var group = sheet.rowRangeGroup;
                    if(group && !group._isEmpty())
                        for(r = 0; r < rowCount; r++)
                            rowsInfo.saveRangeGroup(r,group.getLevel(baseRow + r),group.getCollapsed(baseRow + r))
                }
            },
            saveViewportInfo: function(sheet, cellsInfo, baseRow, baseColumn, option)
            {
                if((option & UI.CopyToOption.All) <= 0)
                    return;
                var rowCount = cellsInfo._rowCount;
                var columnCount = cellsInfo._columnCount;
                for(var r = 0; r < rowCount; r++)
                    for(var c = 0; c < columnCount; c++)
                    {
                        if((option & UI.CopyToOption.Value) > 0)
                            cellsInfo.saveValue(r,c,sheet.getValue(baseRow + r,baseColumn + c,UI.SheetArea.viewport));
                        if((option & UI.CopyToOption.Value) > 0 || (option & UI.CopyToOption.Formula) > 0)
                        {
                            cellsInfo.saveFormula(r,c,sheet.getFormula(baseRow + r,baseColumn + c));
                            var arrayFormulas = sheet._getsArrayFormulas(baseRow,baseColumn,rowCount,columnCount);
                            cellsInfo.saveArrayFormula(arrayFormulas)
                        }
                        if((option & UI.CopyToOption.Sparkline) > 0)
                        {
                            var sparkline = sheet.getSparkline(baseRow + r,baseColumn + c);
                            var dataRange = null;
                            var dataAxisRange = null;
                            if(sparkline)
                            {
                                dataRange = sparkline.data();
                                dataAxisRange = sparkline.dateAxisData()
                            }
                            if(sparkline && dataRange)
                                cellsInfo.saveSparkline(r,c,sparkline.clone());
                            else
                                cellsInfo.saveSparkline(r,c,null)
                        }
                        if((option & UI.CopyToOption.Style) > 0)
                            cellsInfo.saveStyle(r,c,CopyMoveHelper.getStyleObject(sheet,baseRow + r,baseColumn + c,UI.SheetArea.viewport));
                        if((option & UI.CopyToOption.BindingPath) > 0)
                            cellsInfo.saveBindingPath(r,c,sheet.getBindingPath(baseRow + r,baseColumn + c,UI.SheetArea.viewport))
                    }
                if((option & UI.CopyToOption.Span) > 0)
                {
                    var spanEnumerator = sheet._spanModel.getEnumerator(baseRow,baseColumn,rowCount,columnCount);
                    while(spanEnumerator.moveNext())
                        cellsInfo.saveSpan(spanEnumerator.current())
                }
            },
            undoCellsInfo: function(sheet, cellsInfo, baseRow, baseColumn, area)
            {
                var rowCount = cellsInfo._rowCount;
                var columnCount = cellsInfo._columnCount;
                var key,
                    cs,
                    cr,
                    formula,
                    count,
                    i;
                sheet.suspendCalcService();
                try
                {
                    for(var r = 0; r < rowCount; r++)
                        for(var c = 0; c < columnCount; c++)
                        {
                            if(cellsInfo.isBindingPathSaved())
                                sheet.setBindingPath(baseRow + r,baseColumn + c,null,area);
                            if(cellsInfo.isFormulaSaved() && area === UI.SheetArea.viewport)
                                sheet.setFormula(baseRow + r,baseColumn + c,null);
                            if(cellsInfo.isSparklineSaved() && area === UI.SheetArea.viewport)
                                sheet.removeSparkline(baseRow + r,baseColumn + c);
                            if(cellsInfo.isValueSaved())
                                sheet.setValue(baseRow + r,baseColumn + c,null,area);
                            if(cellsInfo.isStyleSaved())
                                CopyMoveHelper.setStyleObject(sheet,baseRow + r,baseColumn + c,area,null)
                        }
                    if(cellsInfo.isBindingPathSaved())
                    {
                        var paths = cellsInfo.getBindingPaths();
                        for(key = 0; key < paths.length; key++)
                        {
                            var p = paths[key];
                            sheet.setBindingPath(baseRow + p.row,baseColumn + p.column,p.value,area)
                        }
                    }
                    if(cellsInfo.isFormulaSaved() && area === UI.SheetArea.viewport)
                    {
                        var formulas = cellsInfo.getFormulas();
                        for(key = 0; key < formulas.length; key++)
                        {
                            formula = formulas[key];
                            sheet.setFormula(baseRow + formula.row,baseColumn + formula.column,formula.value)
                        }
                    }
                    if(cellsInfo.isSparklineSaved() && area === UI.SheetArea.viewport)
                    {
                        var sparklines = cellsInfo.getSparklines();
                        for(var s = 0; s < sparklines.length; s++)
                        {
                            var item = sparklines[s];
                            var sparkline = item.value;
                            if(!sparkline)
                                sheet.removeSparkline(baseRow + item.row,baseColumn + item.column);
                            else if(!sparkline.dateAxisData())
                                sheet.setSparkline(baseRow + item.row,baseColumn + item.column,sparkline.data(),sparkline.dataOrientation(),sparkline.sparklineType(),sparkline.setting());
                            else
                                sheet.setSparkline(baseRow + item.row,baseColumn + item.column,sparkline.data(),sparkline.dataOrientation(),sparkline.sparklineType(),sparkline.setting(),sparkline.dateAxisData(),sparkline.dateAxisOrientation())
                        }
                    }
                    if(cellsInfo.isValueSaved())
                    {
                        var values = cellsInfo.getValues();
                        for(key = 0; key < values.length; key++)
                        {
                            var v = values[key];
                            sheet.setValue(baseRow + v.row,baseColumn + v.column,v.value,area)
                        }
                    }
                    if(cellsInfo.isStyleSaved())
                    {
                        var styles = cellsInfo.getStyles();
                        for(key = 0; key < styles.length; key++)
                        {
                            var style = styles[key];
                            CopyMoveHelper.setStyleObject(sheet,baseRow + style.row,baseColumn + style.column,area,style.value)
                        }
                    }
                    if(cellsInfo.isArrayFormulaSaved() && area === UI.SheetArea.viewport)
                    {
                        var arrayFormulas = sheet._getsArrayFormulas(baseRow,baseColumn,rowCount,columnCount);
                        if(arrayFormulas && arrayFormulas.length > 0)
                        {
                            count = arrayFormulas.getLength(0);
                            for(i = 0; i < count; i++)
                            {
                                cr = arrayFormulas[i][0];
                                sheet.setFormula(cr.row,cr.col,cr.rowCount,cr.colCount,null)
                            }
                        }
                        arrayFormulas = cellsInfo.getArrayFormula();
                        if(arrayFormulas && arrayFormulas.length > 0)
                        {
                            count = arrayFormulas.getLength(0);
                            for(i = 0; i < count; i++)
                            {
                                cr = arrayFormulas[i][0];
                                formula = arrayFormulas[i][1];
                                if(formula.StartsWith("{") && formula.EndsWith("}"))
                                    formula = formula.substring(1,formula.length - 2);
                                sheet.setFormula(cr.row,cr.col,cr.rowCount,cr.colCount,formula)
                            }
                        }
                    }
                }
                finally
                {
                    sheet.resumeCalcService()
                }
                var spanModel = null;
                if(area === UI.SheetArea.viewport)
                    spanModel = sheet._spanModel;
                else if(area === UI.SheetArea.colHeader)
                    spanModel = sheet._colHeaderSpanModel;
                else if(area === UI.SheetArea.rowHeader)
                    spanModel = sheet._rowHeaderSpanModel;
                if(spanModel && spanModel.length > 0)
                {
                    var removedSpans = [];
                    var spans = spanModel.getEnumerator(baseRow,baseColumn,rowCount,columnCount);
                    while(spans.moveNext())
                    {
                        cs = spans.current();
                        if(cs)
                            removedSpans.push(cs)
                    }
                    for(key = 0; key < removedSpans.length; key++)
                    {
                        cs = removedSpans[key];
                        spanModel.remove(cs)
                    }
                }
                if(cellsInfo.isSpanSaved())
                    if(spanModel)
                        for(key = 0; key < cellsInfo._spans.length; key++)
                        {
                            cs = cellsInfo._spans[key];
                            spanModel.push(cs)
                        }
            },
            undoColumnsInfo: function(sheet, columnsInfo, baseColumn)
            {
                var columnCount = columnsInfo._columnCount;
                var c;
                if(columnsInfo.isBindingFieldSaved())
                {
                    var outdata = {fieldName: null};
                    for(c = 0; c < columnCount; c++)
                        if(columnsInfo.getBindingField(c,outdata))
                            sheet.bindColumn(baseColumn + c,outdata.fieldName)
                }
                if(columnsInfo.isWidthSaved())
                    for(c = 0; c < columnCount; c++)
                        sheet.setColumnWidth(baseColumn + c,columnsInfo.getWidth(c),UI.SheetArea.viewport);
                if(columnsInfo.isVisibleSaved())
                    for(c = 0; c < columnCount; c++)
                        sheet.setColumnVisible(baseColumn + c,columnsInfo.getVisible(c),UI.SheetArea.viewport);
                if(columnsInfo.isResizableSaved())
                    for(c = 0; c < columnCount; c++)
                        sheet.setColumnResizable(baseColumn + c,columnsInfo.getResizable(c),UI.SheetArea.viewport);
                if(columnsInfo.isViewportColumnStyleSaved())
                    for(c = 0; c < columnCount; c++)
                        CopyMoveHelper.setStyleObject(sheet,-1,baseColumn + c,UI.SheetArea.viewport,columnsInfo.getViewportColumnStyle(c));
                if(columnsInfo.isHeaderColumnStyleSaved())
                    for(c = 0; c < columnCount; c++)
                        CopyMoveHelper.setStyleObject(sheet,-1,baseColumn + c,UI.SheetArea.colHeader,columnsInfo.getHeaderColumnStyle(c));
                if(columnsInfo.isRangeGroupSaved())
                {
                    var group = sheet.colRangeGroup;
                    if(group)
                    {
                        var outData = {
                                level: null,
                                collapsed: null
                            };
                        for(c = 0; c < columnCount; c++)
                        {
                            columnsInfo.getRangeGroup(c,outData);
                            group._setLevel(baseColumn + c,outData.level);
                            group.setCollapsed(baseColumn + c,outData.collapsed)
                        }
                    }
                }
            },
            undoRowsInfo: function(sheet, rowsInfo, baseRow)
            {
                var rowCount = rowsInfo._rowCount;
                var r;
                if(rowsInfo.isHeightSaved())
                    for(r = 0; r < rowCount; r++)
                        sheet.setRowHeight(baseRow + r,rowsInfo.getHeight(r),UI.SheetArea.viewport);
                if(rowsInfo.isVisibleSaved())
                    for(r = 0; r < rowCount; r++)
                        sheet.setRowVisible(baseRow + r,rowsInfo.getVisible(r),UI.SheetArea.viewport);
                if(rowsInfo.isResizableSaved())
                    for(r = 0; r < rowCount; r++)
                        sheet.setRowResizable(baseRow + r,rowsInfo.getResizable(r),UI.SheetArea.viewport);
                if(rowsInfo.isViewportRowStyleSaved())
                    for(r = 0; r < rowCount; r++)
                        CopyMoveHelper.setStyleObject(sheet,baseRow + r,-1,UI.SheetArea.viewport,rowsInfo.getViewportRowStyle(r));
                if(rowsInfo.isHeaderRowStyleSaved())
                    for(r = 0; r < rowCount; r++)
                        CopyMoveHelper.setStyleObject(sheet,baseRow + r,-1,UI.SheetArea.rowHeader,rowsInfo.getHeaderRowStyle(r));
                if(rowsInfo.isRangeGroupSaved())
                {
                    var group = sheet.rowRangeGroup;
                    if(group)
                    {
                        var outData = {
                                level: null,
                                collapsed: null
                            };
                        for(r = 0; r < rowCount; r++)
                        {
                            rowsInfo.getRangeGroup(r,outData);
                            group._setLevel(baseRow + r,outData.level);
                            group.setCollapsed(baseRow + r,outData.collapsed)
                        }
                    }
                }
            },
            undoSheetInfo: function(sheet, sheetInfo)
            {
                if(sheetInfo.isDefaultStyleSaved())
                    sheet.setDefaultStyle(sheetInfo.getDefaultStyle());
                if(sheetInfo.isDefaultColumnWidthSaved())
                    sheet.defaults.colWidth = sheetInfo.getDefaultColumnWidth();
                if(sheetInfo.isDefaultRowHeightSaved())
                    sheet.defaults.rowHeight = sheetInfo.getDefaultRowHeight();
                if(sheetInfo.isColumnHeaderDefaultStyleSaved())
                    sheet.setDefaultStyle(sheetInfo.getColumnHeaderDefaultStyle(),UI.SheetArea.colHeader);
                if(sheetInfo.isColumnHeaderDefaultRowHeightSaved())
                    sheet.defaults.rowHeight = sheetInfo.getColumnHeaderDefaultRowHeight();
                if(sheetInfo.isRowHeaderDefaultStyleSaved())
                    sheet.setDefaultStyle(sheetInfo.getRowHeaderDefaultStyle(),UI.SheetArea.rowHeader);
                if(sheetInfo.isRowHeaderDefaultColumnWidthSaved())
                    sheet.defaults.rowHeaderColWidth = sheetInfo.getRowHeaderDefaultColumnWidth()
            },
            getValues: function(sheet, row, column, rowCount, columnCount)
            {
                if(row < 0)
                {
                    row = 0;
                    rowCount = sheet.getRowCount()
                }
                if(column < 0)
                {
                    column = 0;
                    columnCount = sheet.getColumnCount()
                }
                var values = [];
                for(var r = 0; r < rowCount; r++)
                    for(var c = 0; c < columnCount; c++)
                    {
                        var value = sheet.getValue(row + r,column + c);
                        if(value !== undefined && value !== null)
                            values.push(new CellData(r,c,value))
                    }
                return values
            },
            raiseRangeDataChanged: function(sheetView, row, column, rowCount, columnCount, oldValues)
            {
                if(!sheetView)
                    return;
                if(row < 0)
                {
                    row = 0;
                    rowCount = sheetView.getRowCount()
                }
                if(column < 0)
                {
                    column = 0;
                    columnCount = sheetView.getColumnCount()
                }
                sheetView._raiseRangeDataChanged(row,column,rowCount,columnCount)
            },
            convertToKey: function(row, column)
            {
                var key = 0;
                key = row;
                key <<= 32;
                key |= column;
                return key
            },
            remove: function(cellDatas, row, column)
            {
                var ret = null;
                var index = -1;
                var count = cellDatas.length;
                if(count > 0)
                    for(var i = 0; i < count; i++)
                        if(cellDatas[i].row === row && cellDatas[i].column === column)
                        {
                            index = i;
                            break
                        }
                if(index !== -1)
                {
                    ret = cellDatas[index];
                    cellDatas.splice(index,1)
                }
                return ret
            }
        };
    function CopyMoveRowsInfo(rowCount)
    {
        this._rowCount = rowCount;
        this._init()
    }
    CopyMoveRowsInfo.prototype = {
        _init: function()
        {
            this._heights = {};
            this._visibles = {};
            this._resizables = {};
            this._tags = {};
            this._viewportRowStyles = {};
            this._headerRowStyles = {};
            this._levels = {};
            this._collapsed = {};
            this._heightSaved = false;
            this._visibleSaved = false;
            this._resizableSaved = false;
            this._tagSaved = false;
            this._viewportRowStyleSaved = false;
            this._headerRowStyleSaved = false;
            this._rangeGroupSaved = false
        },
        saveHeight: function(index, height)
        {
            if(this._heights[index] !== null)
                if(height < 0)
                    this._heights[index] = null;
                else
                    this._heights[index] = height;
            else if(height >= 0)
                this._heights[index] = height;
            this._heightSaved = true
        },
        getHeight: function(index)
        {
            if(this._heights[index] !== null)
                return this._heights[index];
            return-1
        },
        saveVisible: function(index, visible)
        {
            if(this._visibles[index] !== null)
                if(visible)
                    this._visibles[index] = null;
                else
                    this._visibles[index] = visible;
            else if(!visible)
                this._visibles[index] = visible;
            this._visibleSaved = true
        },
        getVisible: function(index)
        {
            if(this._visibles[index] !== null)
                return this._visibles[index];
            return true
        },
        saveResizable: function(index, resizable)
        {
            if(this._resizables[index] !== null)
                if(resizable)
                    this._resizables[index] = null;
                else
                    this._resizables[index] = resizable;
            else if(!resizable)
                this._resizables[index] = resizable;
            this._resizableSaved = true
        },
        getResizable: function(index)
        {
            if(this._resizables[index] !== null)
                return this._resizables[index];
            return true
        },
        saveTag: function(index, tag)
        {
            if(this._tags[index] !== null)
                if(tag === undefined || tag === null)
                    this._tags[index] = null;
                else
                    this._tags[index] = tag;
            else if(tag !== null)
                this._tags[index] = tag;
            this._tagSaved = true
        },
        getTag: function(index)
        {
            if(this._tags[index] !== null)
                return this._tags[index];
            return null
        },
        saveViewportRowStyle: function(index, style)
        {
            if(this._viewportRowStyles[index])
                if(!style)
                    this._viewportRowStyles[index] = null;
                else
                    this._viewportRowStyles[index] = style;
            else if(style)
                this._viewportRowStyles[index] = style;
            this._viewportRowStyleSaved = true
        },
        getViewportRowStyle: function(index)
        {
            if(this._viewportRowStyles[index])
                return this._viewportRowStyles[index];
            return null
        },
        saveHeaderRowStyle: function(index, style)
        {
            if(this._headerRowStyles[index])
                if(!style)
                    this._headerRowStyles[index] = null;
                else
                    this._headerRowStyles[index] = style;
            else if(style)
                this._headerRowStyles[index] = style;
            this._headerRowStyleSaved = true
        },
        getHeaderRowStyle: function(index)
        {
            if(this._headerRowStyles[index])
                return this._headerRowStyles[index];
            return null
        },
        saveRangeGroup: function(index, level, collapsed)
        {
            if(this._levels[index] !== null)
                if(level < 0)
                    this._levels[index] = null;
                else
                    this._levels[index] = level;
            else if(level >= 0)
                this._levels[index] = level;
            if(this._collapsed[index] !== null)
                if(collapsed)
                    this._collapsed[index] = collapsed;
                else
                    this._collapsed[index] = null;
            else if(collapsed)
                this._collapsed[index] = collapsed;
            this._rangeGroupSaved = true
        },
        getRangeGroup: function(index, outData)
        {
            outData.level = -1;
            outData.collapsed = false;
            if(this._levels[index] !== null)
                outData.level = this._levels[index];
            if(this._collapsed[index] !== null)
                outData.collapsed = this._collapsed[index]
        },
        isHeightSaved: function()
        {
            return this._heightSaved
        },
        isVisibleSaved: function()
        {
            return this._visibleSaved
        },
        isResizableSaved: function()
        {
            return this._resizableSaved
        },
        isTagSaved: function()
        {
            return this._tagSaved
        },
        isViewportRowStyleSaved: function()
        {
            return this._viewportRowStyleSaved
        },
        isHeaderRowStyleSaved: function()
        {
            return this._headerRowStyleSaved
        },
        isRangeGroupSaved: function()
        {
            return this._rangeGroupSaved
        }
    };
    function ActionBase()
    {
        this.canExecuteChanged = null
    }
    ActionBase.prototype = {
        execute: function(arg){},
        canExecute: function(arg){},
        canUndo: function()
        {
            return true
        },
        saveState: function(){},
        undo: function(arg){},
        _raiseCanExecuteChanged: function()
        {
            if(this.canExecuteChanged && typeof this.canExecuteChanged === 'function')
                this.canExecuteChanged(this)
        },
        _suspendInvalidate: function(sender)
        {
            if(sender && sender._suspendInvalidate)
                sender._suspendInvalidate()
        },
        _resumeInvalidate: function(sender)
        {
            if(sender && sender._resumeInvalidate)
                sender._resumeInvalidate()
        }
    };
    function ColumnResizeUndoAction(sheet, columns, size, rowHeader)
    {
        this._sheet = sheet;
        this._columns = columns;
        this._size = size;
        this._rowHeader = rowHeader;
        this._oldSizes = [];
        this._oldVisibles = []
    }
    ColumnResizeUndoAction.prototype = new ActionBase;
    function RowResizeUndoAction(sheet, rows, size, columnHeader)
    {
        this._sheet = sheet;
        this._rows = rows;
        this._size = size;
        this._columnHeader = columnHeader;
        this._oldSizes = [];
        this._oldVisibles = []
    }
    RowResizeUndoAction.prototype = new ActionBase;
    function ColumnAutoFitUndoAction(sheet, columns, rowHeader)
    {
        this._sheet = sheet;
        this._columns = columns;
        this._rowHeader = rowHeader;
        this._oldSizes = [];
        this._oldVisibles = []
    }
    ColumnAutoFitUndoAction.prototype = new ActionBase;
    function RowAutoFitUndoAction(sheet, rows, columnHeader)
    {
        this._sheet = sheet;
        this._rows = rows;
        this._columnHeader = columnHeader;
        this._oldSizes = [];
        this._oldVisibles = []
    }
    RowAutoFitUndoAction.prototype = new ActionBase;
    function GroupExtent(index, count)
    {
        this.index = index;
        this.count = count
    }
    function ColumnGroupUndoAction(sheet, groupExtent)
    {
        this._sheet = sheet;
        this._columnGroupExtent = groupExtent
    }
    ColumnGroupUndoAction.prototype = new ActionBase;
    function ColumnUngroupUndoAction(sheet, groupExtent)
    {
        this._sheet = sheet;
        this._columnUngroupExtent = groupExtent;
        this._canUndo = false
    }
    ColumnUngroupUndoAction.prototype = new ActionBase;
    function RowGroupUndoAction(sheet, groupExtent)
    {
        this._sheet = sheet;
        this._rowGroupExtent = groupExtent
    }
    RowGroupUndoAction.prototype = new ActionBase;
    function RowUngroupUndoAction(sheet, groupExtent)
    {
        this._sheet = sheet;
        this._rowUngroupExtent = groupExtent;
        this._canUndo = false
    }
    RowUngroupUndoAction.prototype = new ActionBase;
    function ColumnGroupExpandUndoAction(sheet, columnExpandExtent)
    {
        this._sheet = sheet;
        this._columnExpandExtent = columnExpandExtent
    }
    ColumnGroupExpandUndoAction.prototype = new ActionBase;
    function RowGroupExpandUndoAction(sheet, rowExpandExtent)
    {
        this._sheet = sheet;
        this._rowExpandExtent = rowExpandExtent
    }
    RowGroupExpandUndoAction.prototype = new ActionBase;
    function ColumnGroupHeaderExpandUndoAction(sheet, columnGroupHeaderExpandExtent)
    {
        this._sheet = sheet;
        this._columnGroupHeaderExpandExtent = columnGroupHeaderExpandExtent;
        this._oldStatus = {}
    }
    ColumnGroupHeaderExpandUndoAction.prototype = new ActionBase;
    function RowGroupHeaderExpandUndoAction(sheet, rowGroupHeaderExpandExtent)
    {
        this._sheet = sheet;
        this._rowGroupHeaderExpandExtent = rowGroupHeaderExpandExtent;
        this._oldStatus = {}
    }
    RowGroupHeaderExpandUndoAction.prototype = new ActionBase;
    function DragDropExtent(fromRow, fromColumn, toRow, toColumn, rowCount, columnCount)
    {
        this.fromRow = fromRow;
        this.fromColumn = fromColumn;
        this.toRow = toRow;
        this.toColumn = toColumn;
        this.rowCount = rowCount;
        this.columnCount = columnCount
    }
    function DragDropUndoAction(sheet, dragMoveExtent, copy, insert, option)
    {
        this._sheet = sheet;
        this._dragDropExtent = dragMoveExtent;
        this._copy = copy;
        this._insert = insert;
        this._option = option
    }
    DragDropUndoAction.prototype = new ActionBase;
    function CellEditUndoAction(sheet, cellEditInfo)
    {
        this._sheet = sheet;
        this._cellEditInfo = cellEditInfo;
        this._canUndo = false
    }
    CellEditUndoAction.prototype = new ActionBase;
    function SheetRenameUndoAction(sheet, newName)
    {
        this._sheet = sheet;
        this._newName = newName;
        this._oldName = null
    }
    SheetRenameUndoAction.prototype = new ActionBase;
    function ZoomUndoAction(sheet, newZoomFactor)
    {
        this._sheet = sheet;
        if(newZoomFactor < 0.25)
            newZoomFactor = 0.25;
        else if(newZoomFactor > 4)
            newZoomFactor = 4;
        this._zoomFactor = newZoomFactor;
        this._prevZoomFactor = -1
    }
    ZoomUndoAction.prototype = new ActionBase;
    function ClearRangeValueUndoAction(sheet, clearRange)
    {
        this._sheet = sheet;
        this._clearRange = clearRange;
        this._cachedFilteredColumns = null;
        this._cachedValues = null
    }
    ClearRangeValueUndoAction.prototype = new ActionBase;
    function ClearValueUndoAction(sheet, ranges)
    {
        this._sheet = sheet;
        this._cachedActions = null;
        if(ranges && ranges.length > 0)
        {
            this._cachedActions = [ranges.length];
            for(var i = 0; i < ranges.length; i++)
                this._cachedActions[i] = new ClearRangeValueUndoAction(sheet,ranges[i])
        }
    }
    ClearValueUndoAction.prototype = new ActionBase;
    function DragFillUndoAction(sheet, dragFillExtent)
    {
        this._workSheet = sheet;
        this._dragFillExtent = dragFillExtent;
        if(this._dragFillExtent.autoFillType === AutoFillType.ClearValues)
            this._clearValueUndoAction = new ClearValueUndoAction(sheet,[this._dragFillExtent.fillRange]);
        else
            this.initWholeFilledRange();
        if(this._dragFillExtent.fillDirection === FillDirection.Left || this._dragFillExtent.fillDirection === FillDirection.Right)
            this._fillSeries = FillSeries.Row;
        else
            this._fillSeries = FillSeries.Column
    }
    DragFillUndoAction.prototype = new ActionBase;
    function ClipboardPasteRangeUndoAction(sheet, srcSheet, destSheet, pasteExtent, option)
    {
        this._sheet = sheet;
        this._fromSheet = srcSheet;
        this._toSheet = destSheet;
        this._pasteExtent = pasteExtent;
        this._pasteOption = option
    }
    ClipboardPasteRangeUndoAction.prototype = new ActionBase;
    function ClipboardPasteUndoAction(sheet, srcSheet, destSheet, pasteExtent, option)
    {
        this._sheet = sheet;
        if(!destSheet)
            throw new Error("destSheet");
        if(!pasteExtent)
            throw new Error("pasteExtent");
        if(pasteExtent.pastedRanges && pasteExtent.pastedRanges.length > 0)
        {
            this._cachedActions = new Array(pasteExtent.pastedRanges.length);
            for(var i = 0; i < pasteExtent.pastedRanges.length; i++)
            {
                var extent = {
                        sourceRange: pasteExtent.fromRange,
                        targetRange: pasteExtent.pastedRanges[i],
                        isCutting: pasteExtent.isCutting,
                        clipboardText: pasteExtent.clipboardText
                    };
                this._cachedActions[i] = new ClipboardPasteRangeUndoAction(sheet,srcSheet,destSheet,extent,option)
            }
        }
    }
    ClipboardPasteUndoAction.prototype = new ActionBase;
    function CellValueEntry(value, isFormula)
    {
        this.value = value;
        this.isFormula = isFormula
    }
    UI.SpreadActions = {
        navigationLeft: function()
        {
            var processed = __navigateNoWrap(this,UI.Direction.left);
            return{processed: processed}
        },
        navigationRight: function()
        {
            var processed = __navigateNoWrap(this,UI.Direction.right);
            return{processed: processed}
        },
        navigationUp: function()
        {
            var processed = __navigateNoWrap(this,UI.Direction.up);
            return{processed: processed}
        },
        navigationDown: function()
        {
            var processed = __navigateNoWrap(this,UI.Direction.down);
            return{processed: processed}
        },
        commitInputNavigationDown: function()
        {
            this.moveActiveCell(UI.Direction.down,false)
        },
        commitInputNavigationUp: function()
        {
            this.moveActiveCell(UI.Direction.up,false)
        },
        navigationHome: function()
        {
            __navigateNoWrapFrom(this,UI.Direction.right,this._activeRowIndex,this.frozenColCount ? this.frozenColCount - 1 : -1)
        },
        navigationHome2: function()
        {
            __navigateNoWrapFrom(this,UI.Direction.right,this._activeRowIndex,-1)
        },
        navigationEnd: function()
        {
            __navigateNoWrapFrom(this,UI.Direction.left,this._activeRowIndex,this.getColumnCount())
        },
        navigationTop: function()
        {
            __navigateNoWrapFrom(this,UI.Direction.down,-1,this._activeColIndex)
        },
        navigationBottom: function()
        {
            __navigateNoWrapFrom(this,UI.Direction.up,this.getRowCount(),this._activeColIndex)
        },
        navigationPageUp: function()
        {
            if(this._editorStatus === UI.EditorStatus.Edit)
                return;
            else if(this._editorStatus === UI.EditorStatus.Enter)
                this.endEdit();
            var prevPageTopRow = this._getPrevPageTopRow();
            if(prevPageTopRow === null || prevPageTopRow === this._scrollTopRow)
                return;
            var rls = this._getRowLayout(1,UI.SheetArea.viewport);
            this._rowLayoutCache.viewport = {};
            var newActiveRow = this._getNextVisualRow(this._activeRowIndex - rls.length);
            if(newActiveRow < prevPageTopRow)
                newActiveRow = prevPageTopRow;
            else if(newActiveRow >= this._getPageBottomRow())
                newActiveRow = this._getPrevVisualRow(this._getPageBottomRow());
            this._activeRowIndex = newActiveRow;
            this._leadingCellRow = newActiveRow;
            var repaint = this.moveActiveCell();
            if(!repaint)
                this._setTopRow(prevPageTopRow,true);
            else
                this._setTopRow(prevPageTopRow)
        },
        navigationPageDown: function()
        {
            if(this._editorStatus === UI.EditorStatus.Edit)
                return;
            else if(this._editorStatus === UI.EditorStatus.Enter)
                this.endEdit();
            var nextPageTopRow = this._getNextPageTopRow();
            if(nextPageTopRow === null || nextPageTopRow === this._scrollTopRow)
                return;
            var rls = this._getRowLayout(1,UI.SheetArea.viewport);
            this._rowLayoutCache.viewport = {};
            var newActiveRow = this._getPrevVisualRow(this._activeRowIndex + rls.length);
            if(newActiveRow < nextPageTopRow)
                newActiveRow = nextPageTopRow;
            else if(newActiveRow >= this._getPageBottomRow())
                newActiveRow = this._getPrevVisualRow(this._getPageBottomRow());
            this._activeRowIndex = newActiveRow;
            this._leadingCellRow = newActiveRow;
            var repaint = this.moveActiveCell();
            if(!repaint)
                this._setTopRow(nextPageTopRow,true);
            else
                this._setTopRow(nextPageTopRow)
        },
        navigationNextSheet: function()
        {
            var spread = this.parent;
            if(spread || spread instanceof UI.GcSpread)
            {
                var count = spread.getSheetCount();
                var index = spread.getActiveSheetIndex();
                if(index < count - 1)
                {
                    index++;
                    spread.setActiveSheetIndex(index);
                    spread.repaint()
                }
            }
        },
        navigationPreviousSheet: function()
        {
            var spread = this.parent;
            if(spread || spread instanceof UI.GcSpread)
            {
                var index = spread.getActiveSheetIndex();
                if(index > 0)
                {
                    index--;
                    spread.setActiveSheetIndex(index);
                    spread.repaint()
                }
            }
        },
        navigationPrevious: function(){},
        navigationNext: function(){},
        navigationFirst: function()
        {
            if(this._editorStatus === UI.EditorStatus.Edit)
                return;
            var prevCol = this.frozenColCount ? this.frozenColCount - 1 : -1;
            var prevRow = this.frozenRowCount ? this.frozenRowCount - 1 : -1;
            this.moveActiveCell(UI.Direction.right,false,this._activeRowIndex,prevCol);
            this.moveActiveCell(UI.Direction.down,false,prevRow,this._activeColIndex)
        },
        navigationLast: function()
        {
            if(this._editorStatus === UI.EditorStatus.Edit)
                return;
            this.moveActiveCell(UI.Direction.left,false,this._activeRowIndex,this.getColumnCount());
            this.moveActiveCell(UI.Direction.up,false,this.getRowCount(),this._activeColIndex)
        },
        commitInputNavigationTabNext: function()
        {
            if(this._isNavigateInSelection)
                this._moveActiveCellInSelection(UI.Direction.right);
            else
                this.moveActiveCell(UI.Direction.right,true)
        },
        commitInputNavigationTabPrevious: function()
        {
            if(this._isNavigateInSelection)
                this._moveActiveCellInSelection(UI.Direction.left);
            else
                this.moveActiveCell(UI.Direction.left,true)
        },
        cancelInput: function()
        {
            if(this.isEditing())
            {
                var cacheValue = this.getValue(this._activeRowIndex,this._activeColIndex,UI.SheetArea.viewport);
                this.endEdit(true);
                this.setValue(this._activeRowIndex,this._activeColIndex,cacheValue,UI.SheetArea.viewport,true)
            }
        },
        clear: function()
        {
            if(!this.isEditing())
            {
                var sels = this.getSelections();
                for(var i = 0; i < sels.length; i++)
                    if(this.isProtected && this._isAnyCellInRangeLocked(sels[i]))
                        return;
                var ranges = sels.toArray();
                var action = new ClearValueUndoAction(this,ranges);
                if(action.canExecute(this))
                    this._doCommand(action)
            }
        },
        clearAndEditing: function()
        {
            var processed = false;
            if(!this.isEditing())
            {
                this.startEdit(true,"");
                processed = true
            }
            return{processed: processed}
        },
        copy: function()
        {
            if(!this.isEditing())
            {
                var sels = this.getSelections();
                if(sels && sels.length === 1)
                    this._clipboardCopy(sels[0],false)
            }
        },
        cut: function()
        {
            if(!this.isEditing())
            {
                var sels = this.getSelections();
                if(sels && sels.length === 1)
                    this._clipboardCopy(sels[0],true)
            }
        },
        paste: function()
        {
            var msg_spreadActionPasteSizeDifferent = "The pasted area should have the same size as the copy or cut area.";
            var sheet = this;
            if(sheet)
            {
                if(sheet.isEditing())
                    return;
                var ch = sheet._getClipboardHelper();
                var fromSheet = ch.fromSheet;
                var fromRange = ch.range;
                var clipboardText = sheet._getClipboardData();
                var isCutting = ch.isCutting;
                if(isCutting)
                {
                    ch.fromSheet = null;
                    ch.range = null;
                    ch.isCutting = false
                }
                if(isCutting && fromSheet && fromRange && fromSheet.isProtected && fromSheet._isAnyCellInRangeLocked(fromRange))
                    isCutting = false;
                var outPara = {
                        pastedRange: null,
                        pastedInternal: false
                    };
                var pastedRanges = [];
                var selections = sheet.getSelections();
                var toRange;
                if(selections.length > 1)
                    for(var i = 0; i < selections.length; i++)
                    {
                        toRange = selections[i];
                        if(!sheet._checkPastedRange(fromSheet,fromRange,toRange,isCutting,clipboardText,outPara))
                            return;
                        if(toRange.containsRange(outPara.pastedRange) && !toRange.equals(outPara.pastedRange))
                        {
                            sheet._raiseInvalidOperation(msg_spreadActionPasteSizeDifferent);
                            return
                        }
                        pastedRanges.push(outPara.pastedRange)
                    }
                else if(selections.length > 0)
                {
                    toRange = selections[0];
                    if(!sheet._checkPastedRange(fromSheet,fromRange,toRange,isCutting,clipboardText,outPara))
                        return;
                    pastedRanges.push(outPara.pastedRange)
                }
                else
                {
                    toRange = sheet._getSpanModel().find(sheet._activeRowIndex,sheet._activeColIndex);
                    if(!toRange)
                        toRange = new UI.Range(sheet._activeRowIndex,sheet._activeColIndex,1,1);
                    if(!sheet._checkPastedRange(fromSheet,fromRange,toRange,isCutting,clipboardText,outPara))
                        return;
                    pastedRanges.push(outPara.pastedRange)
                }
                if(pastedRanges.length > 0)
                {
                    if(!outPara.pastedInternal)
                    {
                        fromSheet = null;
                        fromRange = null
                    }
                    var pasteOption = sheet.clipBoardOptions();
                    if(isCutting)
                        pasteOption = UI.ClipboardPasteOptions.All;
                    var pasteExtent = {
                            fromRange: fromRange,
                            pastedRanges: pastedRanges.slice(0),
                            isCutting: isCutting,
                            clipboardText: clipboardText
                        };
                    var pasteUndoAction = new ClipboardPasteUndoAction(sheet,fromSheet,sheet,pasteExtent,pasteOption);
                    sheet._doCommand(pasteUndoAction)
                }
            }
        },
        selectionLeft: function()
        {
            __alterSelection(this,UI.Key.left)
        },
        selectionRight: function()
        {
            __alterSelection(this,UI.Key.right)
        },
        selectionUp: function()
        {
            __alterSelection(this,UI.Key.up)
        },
        selectionDown: function()
        {
            __alterSelection(this,UI.Key.down)
        },
        selectionHome: function()
        {
            __alterSelection(this,UI.Key.left,true)
        },
        selectionEnd: function()
        {
            __alterSelection(this,UI.Key.right,true)
        },
        selectionPageUp: function()
        {
            __alterSelection(this,UI.Key.pup)
        },
        selectionPageDown: function()
        {
            __alterSelection(this,UI.Key.pdn)
        },
        selectionTop: function()
        {
            __alterSelection(this,UI.Key.up,true)
        },
        selectionBottom: function()
        {
            __alterSelection(this,UI.Key.down,true)
        },
        selectionFirst: function()
        {
            __alterSelection(this,UI.Key.home,true)
        },
        selectionLast: function()
        {
            __alterSelection(this,UI.Key.end,true)
        },
        undo: function()
        {
            var undoManager = this.undoManager();
            if(this.isProtected === false && undoManager.canUndo())
                undoManager.undo()
        },
        redo: function()
        {
            var undoManager = this.undoManager();
            if(this.isProtected === false && undoManager.canRedo())
                undoManager.redo()
        }
    };
    ColumnResizeUndoAction.prototype.canExecute = function(arg)
    {
        return true
    };
    ColumnResizeUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        if(sheet && this._columns && this._columns.length > 0)
        {
            this._suspendInvalidate(arg);
            var resizedColumns = this._getColumnsReiszed(this._columns);
            var args = {
                    sheet: sheet,
                    sheetName: sheet._name,
                    colList: resizedColumns,
                    header: this._rowHeader,
                    cancel: false
                };
            sheet._trigger(UI.Events.ColumnWidthChanging,args);
            if(args && args.cancel === true)
            {
                this._canUndo = false;
                this._resumeInvalidate(arg);
                return
            }
            else
                this._canUndo = true;
            this.saveState();
            var columnCount = this._rowHeader ? sheet.getColumnCount(UI.SheetArea.rowHeader) : sheet.getColumnCount(UI.SheetArea.viewport);
            for(var c = 0; c < this._columns.length; c++)
            {
                var cw = this._columns[c];
                for(var c2 = cw.firstCol; c2 <= cw.lastCol; c2++)
                    if(0 <= c2 && c2 < columnCount)
                        if(this._rowHeader && sheet.getColumnResizable(c2,UI.SheetArea.rowHeader) && this._size !== sheet.getColumnWidth(c2,UI.SheetArea.rowHeader))
                            sheet.setColumnWidth(c2,this._size,UI.SheetArea.rowHeader);
                        else if(sheet.getColumnResizable(c2,UI.SheetArea.viewport) && this._size !== sheet.getColumnWidth(c2,UI.SheetArea.viewport))
                            sheet.setColumnWidth(c2,this._size,UI.SheetArea.viewport)
            }
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint();
            sheet._trigger(UI.Events.ColumnWidthChanged,{
                sheet: sheet,
                sheetName: sheet._name,
                colList: resizedColumns,
                header: this._rowHeader
            })
        }
    };
    ColumnResizeUndoAction.prototype.saveState = function()
    {
        var sizes = null;
        var visibles = null;
        var sheet = this._sheet;
        if(sheet && this._columns && this._columns.length > 0)
        {
            var columnCount = this._rowHeader ? sheet.getColumnCount(UI.SheetArea.rowHeader) : sheet.getColumnCount(UI.SheetArea.viewport);
            sizes = [this._columns.length];
            visibles = [this._columns.length];
            for(var c = 0; c < this._columns.length; c++)
            {
                var cw = this._columns[c];
                sizes[c] = [cw.lastCol - cw.firstCol + 1];
                visibles[c] = [cw.lastCol - cw.firstCol + 1];
                for(var c2 = cw.firstCol; c2 <= cw.lastCol; c2++)
                    if(0 <= c2 && c2 < columnCount)
                    {
                        sizes[c][c2 - cw.firstCol] = this._rowHeader ? sheet.getColumnWidth(c2,UI.SheetArea.rowHeader) : sheet.getColumnWidth(c2,UI.SheetArea.viewport);
                        visibles[c][c2 - cw.firstCol] = this._rowHeader ? sheet.getColumnVisible(c2,UI.SheetArea.rowHeader) : sheet.getColumnVisible(c2,UI.SheetArea.viewport)
                    }
                    else
                    {
                        sizes[c][c2 - cw.firstCol] = -1;
                        visibles[c][c2 - cw.firstCol] = false
                    }
            }
        }
        this._oldSizes = sizes;
        this._oldVisibles = visibles
    };
    ColumnResizeUndoAction.prototype.undo = function(arg)
    {
        var ret = false;
        var sheet = this._sheet;
        if(sheet && this._columns && this._columns.length > 0)
        {
            this._suspendInvalidate(arg);
            var resizedColumns = this._getColumnsReiszed(this._columns);
            var args = {
                    sheet: sheet,
                    sheetName: sheet._name,
                    colList: resizedColumns,
                    header: this._rowHeader,
                    cancel: false
                };
            sheet._trigger(UI.Events.ColumnWidthChanging,args);
            if(args && args.cancel === true)
            {
                this._resumeInvalidate(arg);
                return false
            }
            var columnCount = this._rowHeader ? sheet.getColumnCount(UI.SheetArea.rowHeader) : sheet.getColumnCount(UI.SheetArea.viewport);
            var oldsize,
                oldVisible;
            for(var c = 0; c < this._columns.length; c++)
            {
                var cw = this._columns[c];
                for(var c2 = cw.firstCol; c2 <= cw.lastCol; c2++)
                {
                    oldsize = this._oldSizes[c][c2 - cw.firstCol];
                    oldVisible = this._oldVisibles[c][c2 - cw.firstCol];
                    if(0 <= c2 && c2 < columnCount && oldsize !== -1)
                    {
                        if(this._rowHeader && sheet.getColumnResizable(c2,UI.SheetArea.rowHeader))
                        {
                            sheet.setColumnWidth(c2,oldsize,UI.SheetArea.rowHeader);
                            ret = true
                        }
                        else if(sheet.getColumnResizable(c2,UI.SheetArea.viewport))
                        {
                            sheet.setColumnWidth(c2,oldsize,UI.SheetArea.viewport);
                            ret = true
                        }
                        if(this._rowHeader && sheet.getColumnVisible(c2,UI.SheetArea.rowHeader) !== oldVisible)
                        {
                            sheet.setColumnVisible(c2,oldVisible,UI.SheetArea.rowHeader);
                            ret = true
                        }
                        else if(sheet.getColumnVisible(c2,UI.SheetArea.viewport) !== oldVisible)
                        {
                            sheet.setColumnVisible(c2,oldVisible,UI.SheetArea.viewport);
                            ret = true
                        }
                    }
                }
            }
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint();
            sheet._trigger(UI.Events.ColumnWidthChanged,{
                sheet: sheet,
                sheetName: sheet._name,
                colList: resizedColumns,
                header: this._rowHeader
            })
        }
        return ret
    };
    ColumnResizeUndoAction.prototype._getColumnsReiszed = function(columnWidthChangeExtents)
    {
        var columns = [];
        for(var i = 0; i < columnWidthChangeExtents.length; i++)
        {
            var item = columnWidthChangeExtents[i];
            for(var col = item.firstCol; col <= item.lastCol; col++)
                columns.push(col)
        }
        return columns
    };
    ColumnResizeUndoAction.prototype.canUndo = function()
    {
        return this._canUndo
    };
    RowResizeUndoAction.prototype.canExecute = function(arg)
    {
        return true
    };
    RowResizeUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        if(sheet && this._rows && this._rows.length > 0)
        {
            this._suspendInvalidate(arg);
            var resizedRows = this._getRowsReiszed(this._rows);
            var args = {
                    sheet: sheet,
                    sheetName: sheet._name,
                    rowList: resizedRows,
                    header: this._columnHeader,
                    cancel: false
                };
            sheet._trigger(UI.Events.RowHeightChanging,args);
            if(args && args.cancel === true)
            {
                this._canUndo = false;
                this._resumeInvalidate(arg);
                return
            }
            else
                this._canUndo = true;
            this.saveState();
            var rowCount = this._columnHeader ? sheet.getRowCount(UI.SheetArea.colHeader) : sheet.getRowCount(UI.SheetArea.viewport);
            for(var r = 0; r < this._rows.length; r++)
            {
                var rh = this._rows[r];
                for(var r2 = rh.firstRow; r2 <= rh.lastRow; r2++)
                    if(0 <= r2 && r2 < rowCount)
                        if(this._columnHeader && sheet.getRowResizable(r2,UI.SheetArea.colHeader) && this._size !== sheet.getRowHeight(r2,UI.SheetArea.colHeader))
                            sheet.setRowHeight(r2,this._size,UI.SheetArea.colHeader);
                        else if(sheet.getRowResizable(r2,UI.SheetArea.viewport) && this._size !== sheet.getRowHeight(r2,UI.SheetArea.viewport))
                        {
                            sheet.setRowHeight(r2,this._size,UI.SheetArea.viewport);
                            if(sheet.rowFilter())
                                sheet.rowFilter()._addRowFilteredIn(r2)
                        }
            }
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint();
            sheet._trigger(UI.Events.RowHeightChanged,{
                sheet: sheet,
                sheetName: sheet._name,
                rowList: resizedRows,
                header: this._columnHeader
            })
        }
    };
    RowResizeUndoAction.prototype.saveState = function()
    {
        var sizes = null;
        var visibles = null;
        var sheet = this._sheet;
        if(sheet && this._rows && this._rows.length > 0)
        {
            var rowCount = this._columnHeader ? sheet.getRowCount(UI.SheetArea.colHeader) : sheet.getRowCount(UI.SheetArea.viewport);
            sizes = [this._rows.length];
            visibles = [this._rows.length];
            for(var r = 0; r < this._rows.length; r++)
            {
                var rh = this._rows[r];
                sizes[r] = [rh.lastRow - rh.firstRow + 1];
                visibles[r] = [rh.lastRow - rh.firstRow + 1];
                for(var r2 = rh.firstRow; r2 <= rh.lastRow; r2++)
                    if(0 <= r2 && r2 < rowCount)
                    {
                        sizes[r][r2 - rh.firstRow] = this._columnHeader ? sheet.getRowHeight(r2,UI.SheetArea.colHeader) : sheet.getRowHeight(r2,UI.SheetArea.viewport);
                        visibles[r][r2 - rh.firstRow] = this._columnHeader ? sheet.getRowVisible(r2,UI.SheetArea.colHeader) : sheet.getRowVisible(r2,UI.SheetArea.viewport)
                    }
                    else
                    {
                        sizes[r][r2 - rh.firstRow] = -1;
                        visibles[r][r2 - rh.firstRow] = false
                    }
            }
        }
        this._oldVisibles = visibles;
        this._oldSizes = sizes
    };
    RowResizeUndoAction.prototype.undo = function(arg)
    {
        var ret = false;
        var sheet = this._sheet;
        if(sheet && this._rows && this._rows.length > 0)
        {
            this._suspendInvalidate(arg);
            var resizedRows = this._getRowsReiszed(this._rows);
            var args = {
                    sheet: sheet,
                    sheetName: sheet._name,
                    rowList: resizedRows,
                    header: this._columnHeader,
                    cancel: false
                };
            sheet._trigger(UI.Events.RowHeightChanging,args);
            if(args && args.cancel === true)
            {
                this._resumeInvalidate(arg);
                return false
            }
            var rowCount = this._columnHeader ? sheet.getRowCount(UI.SheetArea.colHeader) : sheet.getRowCount(UI.SheetArea.viewport);
            var oldsize,
                oldVisible;
            for(var r = 0; r < this._rows.length; r++)
            {
                var rh = this._rows[r];
                for(var r2 = rh.firstRow; r2 <= rh.lastRow; r2++)
                {
                    oldsize = this._oldSizes[r][r2 - rh.firstRow];
                    oldVisible = this._oldVisibles[r][r2 - rh.firstRow];
                    if(0 <= r2 && r2 < rowCount && oldsize !== -1)
                    {
                        if(this._columnHeader && sheet.getRowResizable(r2,UI.SheetArea.colHeader))
                        {
                            sheet.setRowHeight(r2,oldsize,UI.SheetArea.colHeader);
                            ret = true
                        }
                        else if(sheet.getRowResizable(r2,UI.SheetArea.viewport))
                        {
                            sheet.setRowHeight(r2,oldsize,UI.SheetArea.viewport);
                            ret = true
                        }
                        if(this._columnHeader && sheet.getRowVisible(r2,UI.SheetArea.colHeader) !== oldVisible)
                        {
                            sheet.setRowVisible(r2,oldVisible,UI.SheetArea.colHeader);
                            ret = true
                        }
                        else if(sheet.getRowVisible(r2,UI.SheetArea.viewport) !== oldVisible)
                        {
                            sheet.setRowVisible(r2,oldVisible,UI.SheetArea.viewport);
                            ret = true
                        }
                    }
                }
            }
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint();
            sheet._trigger(UI.Events.RowHeightChanged,{
                sheet: sheet,
                sheetName: sheet._name,
                rowList: resizedRows,
                header: this._columnHeader
            })
        }
        return ret
    };
    RowResizeUndoAction.prototype._getRowsReiszed = function(rowHeightChangeExtents)
    {
        var rows = [];
        for(var i = 0; i < rowHeightChangeExtents.length; i++)
        {
            var item = rowHeightChangeExtents[i];
            for(var row = item.firstRow; row <= item.lastRow; row++)
                rows.push(row)
        }
        return rows
    };
    RowResizeUndoAction.prototype.canUndo = function()
    {
        return this._canUndo
    };
    ColumnAutoFitUndoAction.prototype.canExecute = function(arg)
    {
        return this._sheet && this._columns && this._columns.length > 0
    };
    ColumnAutoFitUndoAction.prototype.execute = function(arg)
    {
        if(this.canExecute(arg))
        {
            var sheet = this._sheet;
            this._suspendInvalidate(arg);
            var resizedColumns = this._getColumnsReiszed(this._columns);
            var args = {
                    sheet: sheet,
                    sheetName: sheet._name,
                    colList: resizedColumns,
                    header: this._rowHeader,
                    cancel: false
                };
            sheet._trigger(UI.Events.ColumnWidthChanging,args);
            if(args && args.cancel === true)
            {
                this._canUndo = false;
                this._resumeInvalidate(arg);
                return
            }
            else
                this._canUndo = true;
            this.saveState();
            var columnCount = this._rowHeader ? sheet.getColumnCount(UI.SheetArea.rowHeader) : sheet.getColumnCount(UI.SheetArea.viewport);
            var oldState = sheet.isPaintSuspended();
            sheet.isPaintSuspended(true);
            var fitValue;
            for(var c = 0; c < this._columns.length; c++)
            {
                var ce = this._columns[c];
                if(0 <= ce.col && ce.col < columnCount)
                    if(this._rowHeader && sheet.getColumnResizable(ce.col,UI.SheetArea.rowHeader))
                    {
                        fitValue = this._getColumnAutoFitValue(ce.col,true);
                        if(fitValue !== sheet.getColumnWidth(ce.col,UI.SheetArea.rowHeader))
                        {
                            sheet.setColumnWidth(ce.col,fitValue,UI.SheetArea.rowHeader);
                            sheet._trigger(UI.Events.ColumnWidthChanged,{
                                sheet: sheet,
                                sheetName: sheet._name,
                                colList: [ce.col],
                                header: this._rowHeader
                            })
                        }
                    }
                    else if(sheet.getColumnResizable(ce.col,UI.SheetArea.viewport))
                    {
                        fitValue = this._getColumnAutoFitValue(ce.col,false);
                        if(fitValue !== sheet.getColumnWidth(ce.col,UI.SheetArea.viewport))
                        {
                            sheet.setColumnWidth(ce.col,fitValue,UI.SheetArea.viewport);
                            sheet._trigger(UI.Events.ColumnWidthChanged,{
                                sheet: sheet,
                                sheetName: sheet._name,
                                colList: [ce.col],
                                header: this._rowHeader
                            })
                        }
                    }
            }
            sheet.isPaintSuspended(oldState);
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
    };
    ColumnAutoFitUndoAction.prototype.saveState = function()
    {
        var sizes = null;
        var visibles = null;
        var sheet = this._sheet;
        if(sheet && this._columns && this._columns.length > 0)
        {
            var columnCount = this._rowHeader ? sheet.getColumnCount(UI.SheetArea.rowHeader) : sheet.getColumnCount(UI.SheetArea.viewport);
            sizes = [this._columns.length];
            visibles = [this._columns.length];
            for(var c = 0; c < this._columns.length; c++)
            {
                var ce = this._columns[c];
                if(0 <= ce.col && ce.col < columnCount)
                    if(this._rowHeader)
                    {
                        sizes[c] = sheet.getColumnWidth(ce.col,UI.SheetArea.rowHeader);
                        visibles[c] = sheet.getColumnVisible(ce.col,UI.SheetArea.rowHeader)
                    }
                    else
                    {
                        sizes[c] = sheet.getColumnWidth(ce.col,UI.SheetArea.viewport);
                        visibles[c] = sheet.getColumnVisible(ce.col,UI.SheetArea.viewport)
                    }
                else
                {
                    sizes[c] = -1;
                    visibles[c] = false
                }
            }
        }
        this._oldSizes = sizes;
        this._oldVisibles = visibles
    };
    ColumnAutoFitUndoAction.prototype.undo = function(arg)
    {
        var ret = false;
        if(this._sheet && this._columns && this._columns.length > 0)
        {
            var sheet = this._sheet;
            this._suspendInvalidate(arg);
            var resizedColumns = this._getColumnsReiszed(this._columns);
            var args = {
                    sheet: sheet,
                    sheetName: sheet._name,
                    colList: resizedColumns,
                    header: this._rowHeader,
                    cancel: false
                };
            sheet._trigger(UI.Events.ColumnWidthChanging,args);
            if(args && args.cancel === true)
            {
                this._resumeInvalidate(arg);
                return false
            }
            var columnCount = this._rowHeader ? sheet.getColumnCount(UI.SheetArea.rowHeader) : sheet.getColumnCount(UI.SheetArea.viewport);
            var oldSize,
                oldVisible;
            var oldState = sheet.isPaintSuspended();
            sheet.isPaintSuspended(true);
            for(var c = 0; c < this._columns.length; c++)
            {
                var ce = this._columns[c];
                oldSize = this._oldSizes[c];
                oldVisible = this._oldVisibles[c];
                if(0 <= c && c < columnCount && oldSize !== -1)
                    if(this._rowHeader && sheet.getColumnResizable(ce.col,UI.SheetArea.rowHeader))
                    {
                        sheet.setColumnWidth(ce.col,oldSize,UI.SheetArea.rowHeader);
                        sheet.setColumnVisible(ce.col,oldVisible,UI.SheetArea.rowHeader);
                        ret = true
                    }
                    else if(sheet.getColumnResizable(ce.col,UI.SheetArea.viewport))
                    {
                        sheet.setColumnWidth(ce.col,oldSize,UI.SheetArea.viewport);
                        sheet.setColumnVisible(ce.col,oldVisible,UI.SheetArea.viewport);
                        ret = true
                    }
            }
            sheet.isPaintSuspended(oldState);
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint();
            sheet._trigger(UI.Events.ColumnWidthChanged,{
                sheet: sheet,
                sheetName: sheet._name,
                colList: resizedColumns,
                header: this._rowHeader
            })
        }
        return ret
    };
    ColumnAutoFitUndoAction.prototype._getColumnsReiszed = function(columnWidthChangeExtents)
    {
        var columns = [];
        for(var i = 0; i < columnWidthChangeExtents.length; i++)
        {
            var item = columnWidthChangeExtents[i];
            columns.push(item.col)
        }
        return columns
    };
    ColumnAutoFitUndoAction.prototype._nextAutoFitRow = function(row, col, rowHeader)
    {
        var sheet = this._sheet;
        var nextNonNullRow = sheet._nextNonNullRow(row,rowHeader);
        var nextCellTypeRow = -1;
        var sheetArea = rowHeader ? UI.SheetArea.rowHeader : UI.SheetArea.viewport;
        var rowCount = sheet.getRowCount(sheetArea);
        for(var i = row + 1; i < rowCount; i++)
        {
            var style = sheet.getActualStyle(i,col,sheetArea);
            if(style && style.cellType)
            {
                nextCellTypeRow = i;
                break
            }
        }
        if(nextNonNullRow >= 0 && nextCellTypeRow >= 0)
            return Math.min(nextNonNullRow,nextCellTypeRow);
        else if(nextNonNullRow >= 0)
            return nextNonNullRow;
        else if(nextCellTypeRow >= 0)
            return nextCellTypeRow;
        else
            return-1
    };
    ColumnAutoFitUndoAction.prototype._getColumnAutoFitValue = function(col, rowHeader)
    {
        var sheet = this._sheet;
        var width = 0;
        var row = 0;
        var sheetArea = rowHeader ? UI.SheetArea.rowHeader : UI.SheetArea.viewport;
        while(row >= 0)
        {
            var span = sheet._getSpanModel(sheetArea).find(row,col);
            if(span && (span.col < col || span.colCount > 1))
            {
                row = span.row + span.rowCount;
                continue
            }
            var style = sheet.getActualStyle(row,col,sheetArea);
            var font = style.font || sheet._render._getDefaultFont(sheetArea);
            style.font = sheet._render._getZoomFont(font);
            var valueWidth = 0;
            var cellType = sheet.getCellType(row,col,sheetArea);
            if(cellType)
            {
                var isFilterHeader = !!(sheet.rowFilter() && sheet.rowFilter().isFilterHeader(row,col,sheetArea));
                var text = sheet.getText(row,col,sheetArea);
                var value = sheet.getValue(row,col,sheetArea);
                valueWidth = cellType._getAutoFitWidth(sheet,value,text,style,isFilterHeader)
            }
            if(valueWidth > width)
                width = valueWidth;
            row = this._nextAutoFitRow(row,col,rowHeader)
        }
        if(width === 0)
            width = sheet.defaults.colWidth;
        else
            width += Math.ceil(4 / sheet._zoomFactor);
        return width
    };
    ColumnAutoFitUndoAction.prototype.canUndo = function()
    {
        return this._canUndo
    };
    RowAutoFitUndoAction.prototype.canExecute = function(arg)
    {
        return this._sheet && this._rows && this._rows.length > 0
    };
    RowAutoFitUndoAction.prototype.execute = function(arg)
    {
        if(this.canExecute(arg))
        {
            var sheet = this._sheet;
            this._suspendInvalidate(arg);
            var resizedRows = this._getRowsResized(this._rows);
            var args = {
                    sheet: sheet,
                    sheetName: sheet._name,
                    rowList: resizedRows,
                    header: this._columnHeader,
                    cancel: false
                };
            sheet._trigger(UI.Events.RowHeightChanging,args);
            if(args && args.cancel === true)
            {
                this._canUndo = false;
                this._resumeInvalidate(arg);
                return
            }
            else
                this._canUndo = true;
            this.saveState();
            var rowCount = this._columnHeader ? sheet.getRowCount(UI.SheetArea.colHeader) : sheet.getRowCount(UI.SheetArea.viewport);
            var oldState = sheet.isPaintSuspended();
            sheet.isPaintSuspended(true);
            var fitValue;
            for(var r = 0; r < this._rows.length; r++)
            {
                var re = this._rows[r];
                if(0 <= re.row && re.row < rowCount)
                    if(this._columnHeader && sheet.getRowResizable(re.row,UI.SheetArea.colHeader))
                    {
                        fitValue = this._getRowAutoFitValue(re.row,true);
                        if(fitValue !== sheet.getRowHeight(re.row,UI.SheetArea.colHeader))
                        {
                            sheet.setRowHeight(re.row,fitValue,UI.SheetArea.colHeader);
                            sheet._trigger(UI.Events.RowHeightChanged,{
                                sheet: sheet,
                                sheetName: sheet._name,
                                rowList: [re.row],
                                header: this._columnHeader
                            })
                        }
                    }
                    else if(sheet.getRowResizable(re.row,UI.SheetArea.viewport))
                    {
                        fitValue = this._getRowAutoFitValue(re.row,false);
                        if(fitValue !== sheet.getRowHeight(re.row,UI.SheetArea.viewport))
                        {
                            sheet.setRowHeight(re.row,fitValue,UI.SheetArea.viewport);
                            sheet._trigger(UI.Events.RowHeightChanged,{
                                sheet: sheet,
                                sheetName: sheet._name,
                                rowList: [re.row],
                                header: this._columnHeader
                            })
                        }
                    }
            }
            sheet.isPaintSuspended(oldState);
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
    };
    RowAutoFitUndoAction.prototype.saveState = function()
    {
        var sizes = null;
        var visibles = null;
        var sheet = this._sheet;
        if(sheet && this._rows && this._rows.length > 0)
        {
            sizes = [this._rows.length];
            visibles = [this._rows.length];
            var rowCount = this._columnHeader ? sheet.getRowCount(UI.SheetArea.colHeader) : sheet.getRowCount(UI.SheetArea.viewport);
            for(var r = 0; r < this._rows.length; r++)
            {
                var re = this._rows[r];
                if(0 <= re.row && re.row < rowCount)
                    if(this._columnHeader)
                    {
                        sizes[r] = sheet.getRowHeight(re.row,UI.SheetArea.colHeader);
                        visibles[r] = sheet.getRowVisible(re.row,UI.SheetArea.colHeader)
                    }
                    else
                    {
                        sizes[r] = sheet.getRowHeight(re.row,UI.SheetArea.viewport);
                        visibles[r] = sheet.getRowVisible(re.row,UI.SheetArea.viewport)
                    }
                else
                {
                    sizes[r] = -1;
                    visibles[r] = false
                }
            }
        }
        this._oldSizes = sizes;
        this._oldVisibles = visibles
    };
    RowAutoFitUndoAction.prototype.undo = function(arg)
    {
        var ret = false;
        var sheet = this._sheet;
        if(sheet && this._rows && this._rows.length > 0)
        {
            this._suspendInvalidate(arg);
            var resizedRows = this._getRowsResized(this._rows);
            var args = {
                    sheet: sheet,
                    sheetName: sheet._name,
                    rowList: resizedRows,
                    header: this._columnHeader,
                    cancel: false
                };
            sheet._trigger(UI.Events.RowHeightChanging,args);
            if(args && args.cancel === true)
            {
                this._resumeInvalidate(arg);
                return false
            }
            var oldSize,
                oldVisible;
            var rowCount = this._columnHeader ? sheet.getRowCount(UI.SheetArea.colHeader) : sheet.getRowCount(UI.SheetArea.viewport);
            var oldState = sheet.isPaintSuspended();
            sheet.isPaintSuspended(true);
            for(var r = 0; r < this._rows.length; r++)
            {
                var re = this._rows[r];
                oldSize = this._oldSizes[r];
                oldVisible = this._oldVisibles[r];
                if(0 <= r && r < rowCount && oldSize !== -1)
                    if(this._columnHeader && sheet.getRowResizable(re.row,UI.SheetArea.colHeader))
                    {
                        sheet.setRowHeight(re.row,oldSize,UI.SheetArea.colHeader);
                        sheet.setRowVisible(re.row,oldVisible,UI.SheetArea.colHeader);
                        ret = true
                    }
                    else if(sheet.getRowResizable(re.row,UI.SheetArea.viewport))
                    {
                        sheet.setRowHeight(re.row,oldSize,UI.SheetArea.viewport);
                        sheet.setRowVisible(re.row,oldVisible,UI.SheetArea.viewport);
                        ret = true
                    }
            }
            sheet.isPaintSuspended(oldState);
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint();
            sheet._trigger(UI.Events.RowHeightChanged,{
                sheet: sheet,
                sheetName: sheet._name,
                rowList: resizedRows,
                header: this._columnHeader
            })
        }
        return ret
    };
    RowAutoFitUndoAction.prototype._getRowsResized = function(rowHeightChangeExtents)
    {
        var rows = [];
        for(var i = 0; i < rowHeightChangeExtents.length; i++)
        {
            var item = rowHeightChangeExtents[i];
            rows.push(item.row)
        }
        return rows
    };
    RowAutoFitUndoAction.prototype._nextAutoFitColumn = function(row, col, columnHeader)
    {
        var sheet = this._sheet;
        var nextNonNullCol = sheet._nextNonNullColumn(row,col,columnHeader);
        var nextCellTypeCol = -1;
        var sheetArea = columnHeader ? UI.SheetArea.colHeader : UI.SheetArea.viewport;
        var colCount = sheet.getColumnCount(sheetArea);
        for(var j = col + 1; j < colCount; j++)
        {
            var style = sheet.getActualStyle(row,j,sheetArea);
            if(style && style.cellType)
            {
                nextCellTypeCol = j;
                break
            }
        }
        if(nextNonNullCol >= 0 && nextCellTypeCol >= 0)
            return Math.min(nextNonNullCol,nextCellTypeCol);
        else if(nextNonNullCol >= 0)
            return nextNonNullCol;
        else if(nextCellTypeCol >= 0)
            return nextCellTypeCol;
        else
            return-1
    };
    RowAutoFitUndoAction.prototype._getRowAutoFitValue = function(row, columnHeader)
    {
        var sheet = this._sheet;
        var height = 0;
        var col = 0;
        var sheetArea = columnHeader ? UI.SheetArea.colHeader : UI.SheetArea.viewport;
        while(col >= 0)
        {
            var span = sheet._getSpanModel(sheetArea).find(row,col);
            if(span && (span.row < row || span.rowCount > 1))
            {
                col = span.col + span.colCount;
                continue
            }
            var style = sheet.getActualStyle(row,col,sheetArea);
            var font = style.font || sheet._render._getDefaultFont(sheetArea);
            style.font = sheet._render._getZoomFont(font);
            var valueHeight = 0;
            var cellType = sheet.getCellType(row,col,sheetArea);
            if(cellType)
            {
                var text = sheet.getText(row,col,sheetArea);
                var value = sheet.getValue(row,col,sheetArea);
                valueHeight = cellType._getAutoFitHeight(sheet,value,text,style)
            }
            if(valueHeight > height)
                height = valueHeight;
            col = this._nextAutoFitColumn(row,col,columnHeader)
        }
        if(height === 0)
            height = sheet.defaults.rowHeight;
        else
            height += Math.ceil(4 / sheet._zoomFactor);
        return height
    };
    RowAutoFitUndoAction.prototype.canUndo = function()
    {
        return this._canUndo
    };
    ColumnGroupUndoAction.prototype.canExecute = function(arg)
    {
        return true
    };
    ColumnGroupUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        if(sheet && this._columnGroupExtent && sheet.colRangeGroup)
        {
            this._suspendInvalidate(arg);
            var index = this._columnGroupExtent.index;
            var count = this._columnGroupExtent.count;
            sheet.colRangeGroup.group(index,count);
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
    };
    ColumnGroupUndoAction.prototype.saveState = function(){};
    ColumnGroupUndoAction.prototype.undo = function(arg)
    {
        var ret = false;
        var sheet = this._sheet;
        if(sheet && this._columnGroupExtent && sheet.colRangeGroup)
        {
            this._suspendInvalidate(arg);
            var index = this._columnGroupExtent.index;
            var count = this._columnGroupExtent.count;
            sheet.colRangeGroup.ungroupRange(index,count);
            ret = true;
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
        return ret
    };
    ColumnUngroupUndoAction.prototype.canExecute = function(arg)
    {
        return true
    };
    ColumnUngroupUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        if(sheet && this._columnUngroupExtent && sheet.colRangeGroup)
        {
            var index = this._columnUngroupExtent.index;
            var count = this._columnUngroupExtent.count;
            if(sheet.colRangeGroup.getLevel(index) >= 0 || sheet.colRangeGroup.getLevel(index + count - 1) >= 0)
            {
                this._suspendInvalidate(arg);
                this._canUndo = true;
                sheet.colRangeGroup.ungroupRange(index,count);
                this._resumeInvalidate(arg);
                sheet.invalidateLayout();
                sheet.repaint()
            }
            else
                this._canUndo = false
        }
    };
    ColumnUngroupUndoAction.prototype.canUndo = function()
    {
        return this._canUndo
    };
    ColumnUngroupUndoAction.prototype.saveState = function(){};
    ColumnUngroupUndoAction.prototype.undo = function(arg)
    {
        var ret = false;
        var sheet = this._sheet;
        if(this._canUndo === true && sheet && this._columnUngroupExtent && sheet.colRangeGroup)
        {
            this._suspendInvalidate(arg);
            var index = this._columnUngroupExtent.index;
            var count = this._columnUngroupExtent.count;
            sheet.colRangeGroup.group(index,count);
            ret = true;
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
        return ret
    };
    RowGroupUndoAction.prototype.canExecute = function(arg)
    {
        return true
    };
    RowGroupUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        if(sheet && this._rowGroupExtent && sheet.rowRangeGroup)
        {
            this._suspendInvalidate(arg);
            var index = this._rowGroupExtent.index;
            var count = this._rowGroupExtent.count;
            sheet.rowRangeGroup.group(index,count);
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
    };
    RowGroupUndoAction.prototype.saveState = function(){};
    RowGroupUndoAction.prototype.undo = function(arg)
    {
        var ret = false;
        var sheet = this._sheet;
        if(sheet && this._rowGroupExtent && sheet.rowRangeGroup)
        {
            this._suspendInvalidate(arg);
            var index = this._rowGroupExtent.index;
            var count = this._rowGroupExtent.count;
            sheet.rowRangeGroup.ungroupRange(index,count);
            ret = true;
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
        return ret
    };
    RowUngroupUndoAction.prototype.canExecute = function(arg)
    {
        return true
    };
    RowUngroupUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        if(sheet && this._rowUngroupExtent && sheet.rowRangeGroup)
        {
            var index = this._rowUngroupExtent.index;
            var count = this._rowUngroupExtent.count;
            if(sheet.rowRangeGroup.getLevel(index) >= 0 || sheet.rowRangeGroup.getLevel(index + count - 1) >= 0)
            {
                this._suspendInvalidate(arg);
                this._canUndo = true;
                sheet.rowRangeGroup.ungroupRange(index,count);
                this._resumeInvalidate(arg);
                sheet.invalidateLayout();
                sheet.repaint()
            }
            else
                this._canUndo = false
        }
    };
    RowUngroupUndoAction.prototype.canUndo = function()
    {
        return this._canUndo
    };
    RowUngroupUndoAction.prototype.saveState = function(){};
    RowUngroupUndoAction.prototype.undo = function(arg)
    {
        var ret = false;
        var sheet = this._sheet;
        if(this._canUndo === true && sheet && this._rowUngroupExtent && sheet.rowRangeGroup)
        {
            this._suspendInvalidate(arg);
            var index = this._rowUngroupExtent.index;
            var count = this._rowUngroupExtent.count;
            sheet.rowRangeGroup.group(index,count);
            ret = true;
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
        return ret
    };
    ColumnGroupExpandUndoAction.prototype.canExecute = function()
    {
        return true
    };
    ColumnGroupExpandUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        if(sheet && this._columnExpandExtent && sheet.colRangeGroup)
        {
            this._suspendInvalidate(arg);
            var index = this._columnExpandExtent.index;
            var collapsed = this._columnExpandExtent.collapsed;
            sheet.colRangeGroup.setCollapsed(index,collapsed);
            if(sheet._allowCellOverflow === true)
                sheet._clearCellOverflowModelCache();
            this._resumeInvalidate(arg);
            this._showColumnRangeGroup(collapsed);
            sheet.invalidateLayout();
            sheet.repaint()
        }
    };
    ColumnGroupExpandUndoAction.prototype.saveState = function(){};
    ColumnGroupExpandUndoAction.prototype.undo = function(arg)
    {
        var ret = false;
        var sheet = this._sheet;
        if(sheet && this._columnExpandExtent && sheet.colRangeGroup)
        {
            this._suspendInvalidate(arg);
            var index = this._columnExpandExtent.index;
            var collapsed = this._columnExpandExtent.collapsed;
            sheet.colRangeGroup.setCollapsed(index,!collapsed);
            if(sheet._allowCellOverflow === true)
                sheet._clearCellOverflowModelCache();
            ret = true;
            this._resumeInvalidate(arg);
            this._showColumnRangeGroup(!collapsed);
            sheet.invalidateLayout();
            sheet.repaint()
        }
        return ret
    };
    ColumnGroupExpandUndoAction.prototype._showColumnRangeGroup = function(collapsed)
    {
        var sheet = this._sheet;
        var summaryIndex = this._columnExpandExtent.index;
        if(summaryIndex < 0 || summaryIndex >= sheet.getColumnCount())
            return;
        var rangeGroup,
            startIndex,
            endIndex,
            viewportIndex,
            leftColumn,
            viewportWidth,
            needViewportWidth;
        if(sheet.colRangeGroup.direction === UI.RangeGroupDirection.Forward)
        {
            rangeGroup = sheet.colRangeGroup.find(summaryIndex - 1,this._columnExpandExtent.level);
            if(!rangeGroup)
                return;
            startIndex = rangeGroup.start;
            endIndex = summaryIndex;
            viewportIndex = 1;
            if(collapsed)
                startIndex = summaryIndex;
            else if(startIndex < sheet.frozenColCount)
                startIndex = sheet.frozenColCount;
            leftColumn = sheet.getViewportLeftColumn(viewportIndex);
            if(startIndex < leftColumn)
                leftColumn = startIndex;
            viewportWidth = sheet.getViewportWidth(viewportIndex);
            needViewportWidth = this._getColsTotalWidth(sheet,leftColumn,endIndex);
            if(needViewportWidth > viewportWidth)
                leftColumn = this._getNewLeftColumn(sheet,leftColumn,needViewportWidth - viewportWidth);
            sheet._scrollLeftCol = this._tryGetNextScrollableCol(sheet,leftColumn)
        }
        else
        {
            rangeGroup = sheet.colRangeGroup.find(summaryIndex + 1,this._columnExpandExtent.level);
            if(!rangeGroup)
                return;
            startIndex = summaryIndex;
            endIndex = rangeGroup.end;
            viewportIndex = 1;
            if(collapsed)
                endIndex = summaryIndex;
            else
            {
                var colCount = sheet.getColumnCount();
                if(endIndex >= colCount)
                    endIndex = colCount - 1
            }
            leftColumn = sheet.getViewportLeftColumn(viewportIndex);
            if(startIndex < leftColumn)
                sheet._scrollLeftCol = this._tryGetNextScrollableCol(sheet,startIndex);
            else
            {
                viewportWidth = sheet.getViewportWidth(viewportIndex);
                needViewportWidth = this._getColsTotalWidth(sheet,leftColumn,endIndex);
                if(needViewportWidth > viewportWidth)
                {
                    leftColumn = this._getNewLeftColumn(sheet,leftColumn,needViewportWidth - viewportWidth);
                    sheet._scrollLeftCol = this._tryGetNextScrollableCol(sheet,Math.min(startIndex,leftColumn))
                }
            }
        }
        sheet._syncHScollbarPosition()
    };
    ColumnGroupExpandUndoAction.prototype._getColsTotalWidth = function(sheet, leftColumn, rightColumn)
    {
        var columnWidthTotal = 0;
        var colCount = sheet.getColumnCount();
        for(var index = leftColumn; index <= rightColumn && index < colCount; index++)
            columnWidthTotal += sheet.getColumnWidth(index) * sheet._zoomFactor;
        return columnWidthTotal
    };
    ColumnGroupExpandUndoAction.prototype._getNewLeftColumn = function(sheet, oldLeftColumn, increasedWidth)
    {
        var columnWidthTotal = 0;
        var colCount = sheet.getColumnCount();
        for(var index = oldLeftColumn; index < colCount && columnWidthTotal < increasedWidth; index++)
        {
            oldLeftColumn++;
            columnWidthTotal += sheet.getColumnWidth(index) * sheet._zoomFactor
        }
        return oldLeftColumn >= colCount ? colCount - 1 : oldLeftColumn
    };
    ColumnGroupExpandUndoAction.prototype._tryGetNextScrollableCol = function(sheet, startColumn)
    {
        var min = sheet.frozenColCount;
        var max = sheet.getColumnCount() - 1;
        if(startColumn < min)
            return min;
        if(startColumn > max)
            return max;
        for(var i = startColumn; i <= max; i++)
        {
            var columnHeight = sheet.getColumnWidth(i);
            if(columnHeight > 0)
                return i
        }
        return-1
    };
    RowGroupExpandUndoAction.prototype.canExecute = function()
    {
        return true
    };
    RowGroupExpandUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        if(sheet && this._rowExpandExtent && sheet.rowRangeGroup)
        {
            this._suspendInvalidate(arg);
            var index = this._rowExpandExtent.index;
            var collapsed = this._rowExpandExtent.collapsed;
            sheet.rowRangeGroup.setCollapsed(index,collapsed);
            this._resumeInvalidate(arg);
            this._showRowRangeGroup(collapsed);
            sheet.invalidateLayout();
            sheet.repaint()
        }
    };
    RowGroupExpandUndoAction.prototype.saveState = function(){};
    RowGroupExpandUndoAction.prototype.undo = function(arg)
    {
        var ret = false;
        var sheet = this._sheet;
        if(sheet && this._rowExpandExtent && sheet.rowRangeGroup)
        {
            this._suspendInvalidate(arg);
            var index = this._rowExpandExtent.index;
            var collapsed = this._rowExpandExtent.collapsed;
            sheet.rowRangeGroup.setCollapsed(index,!collapsed);
            ret = true;
            this._resumeInvalidate(arg);
            this._showRowRangeGroup(!collapsed);
            sheet.invalidateLayout();
            sheet.repaint()
        }
        return ret
    };
    RowGroupExpandUndoAction.prototype._showRowRangeGroup = function(collapsed)
    {
        var sheet = this._sheet;
        var summaryIndex = this._rowExpandExtent.index;
        if(summaryIndex < 0 || summaryIndex >= sheet.getRowCount())
            return;
        var rangeGroup,
            startIndex,
            endIndex,
            viewportIndex,
            topRow,
            viewportHeight,
            needViewportHeight;
        if(sheet.rowRangeGroup.direction === UI.RangeGroupDirection.Forward)
        {
            rangeGroup = sheet.rowRangeGroup.find(summaryIndex - 1,this._rowExpandExtent.level);
            if(!rangeGroup)
                return;
            startIndex = rangeGroup.start;
            endIndex = summaryIndex;
            viewportIndex = 1;
            if(collapsed)
                startIndex = summaryIndex;
            else if(startIndex < sheet.frozenRowCount)
                startIndex = sheet.frozenRowCount;
            topRow = sheet.getViewportTopRow(viewportIndex);
            if(startIndex < topRow)
                topRow = startIndex;
            viewportHeight = sheet.getViewportHeight(viewportIndex);
            needViewportHeight = this._getRowsTotalHeight(sheet,topRow,endIndex);
            if(needViewportHeight > viewportHeight)
                topRow = this._getNewTopRow(sheet,topRow,needViewportHeight - viewportHeight);
            sheet._scrollTopRow = this._tryGetNextScrollableRow(sheet,topRow)
        }
        else
        {
            rangeGroup = sheet.rowRangeGroup.find(summaryIndex + 1,this._rowExpandExtent.level);
            if(!rangeGroup)
                return;
            startIndex = summaryIndex;
            endIndex = rangeGroup.end;
            viewportIndex = 1;
            if(collapsed)
                endIndex = summaryIndex;
            else
            {
                var rowCount = sheet.getRowCount();
                if(endIndex >= rowCount)
                    endIndex = rowCount - 1
            }
            topRow = sheet.getViewportTopRow(viewportIndex);
            if(startIndex < topRow)
                sheet._scrollTopRow = this._tryGetNextScrollableRow(sheet,startIndex);
            else
            {
                viewportHeight = sheet.getViewportHeight(viewportIndex);
                needViewportHeight = this._getRowsTotalHeight(sheet,topRow,endIndex);
                if(needViewportHeight > viewportHeight)
                {
                    topRow = this._getNewTopRow(sheet,topRow,needViewportHeight - viewportHeight);
                    sheet._scrollTopRow = this._tryGetNextScrollableRow(sheet,Math.min(startIndex,topRow))
                }
            }
        }
        sheet._syncVScrollbarPosition()
    };
    RowGroupExpandUndoAction.prototype._getRowsTotalHeight = function(sheet, topRow, bottomRow)
    {
        var rowHeightTotal = 0;
        var rowCount = sheet.getRowCount();
        for(var index = topRow; index <= bottomRow && index < rowCount; index++)
            rowHeightTotal += sheet.getRowHeight(index) * sheet._zoomFactor;
        return rowHeightTotal
    };
    RowGroupExpandUndoAction.prototype._getNewTopRow = function(sheet, oldTopRow, increasedHeight)
    {
        var rowHeightTotal = 0;
        var rowCount = sheet.getRowCount();
        for(var index = oldTopRow; index <= rowCount && rowHeightTotal < increasedHeight; index++)
        {
            oldTopRow++;
            rowHeightTotal += sheet.getRowHeight(index) * sheet._zoomFactor
        }
        return oldTopRow >= rowCount ? rowCount - 1 : oldTopRow
    };
    RowGroupExpandUndoAction.prototype._tryGetNextScrollableRow = function(sheet, startRow)
    {
        var min = sheet.frozenRowCount;
        var max = sheet.getRowCount() - 1;
        if(startRow < min)
            return min;
        if(startRow > max)
            return max;
        for(var i = startRow; i <= max; i++)
        {
            var rowHeight = sheet.getRowHeight(i);
            if(rowHeight > 0)
                return i
        }
        return-1
    };
    ColumnGroupHeaderExpandUndoAction.prototype.canExecute = function()
    {
        return true
    };
    ColumnGroupHeaderExpandUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        if(sheet && this._columnGroupHeaderExpandExtent && sheet.colRangeGroup)
        {
            this._suspendInvalidate(arg);
            this.saveState();
            var level = this._columnGroupHeaderExpandExtent.level;
            for(var index = 0; index < level; index++)
                sheet.colRangeGroup.expand(index,true);
            sheet.colRangeGroup.expand(level,false);
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
    };
    ColumnGroupHeaderExpandUndoAction.prototype.saveState = function()
    {
        var status = null;
        var sheet = this._sheet;
        if(sheet && this._columnGroupHeaderExpandExtent && sheet.colRangeGroup)
        {
            var level = this._columnGroupHeaderExpandExtent.level;
            status = {};
            for(var index = 0; index <= level; index++)
            {
                var columnIndex = 0;
                var columnCount = sheet.getColumnCount();
                var direction = sheet.colRangeGroup.direction;
                while(columnIndex < columnCount)
                {
                    var group = sheet.colRangeGroup.find(columnIndex,index);
                    if(group)
                    {
                        var summaryIndex = -1;
                        switch(direction)
                        {
                            case UI.RangeGroupDirection.Backward:
                                summaryIndex = group.start - 1;
                                break;
                            case UI.RangeGroupDirection.Forward:
                                summaryIndex = group.end + 1;
                                break;
                            default:
                                break
                        }
                        var isCollapsed = group.getState() === UI.GroupState.Collapsed;
                        if(!status[summaryIndex])
                            status[summaryIndex] = isCollapsed;
                        columnIndex += group.end - group.start + 1
                    }
                    columnIndex++
                }
            }
        }
        this._oldStatus = status
    };
    ColumnGroupHeaderExpandUndoAction.prototype.undo = function(arg)
    {
        var ret = false;
        var sheet = this._sheet;
        if(sheet && this._oldStatus && sheet.colRangeGroup)
        {
            this._suspendInvalidate(arg);
            for(var index in this._oldStatus)
                if(index)
                {
                    sheet.colRangeGroup.setCollapsed(index,this._oldStatus[index]);
                    ret = true
                }
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
        return ret
    };
    RowGroupHeaderExpandUndoAction.prototype.canExecute = function()
    {
        return true
    };
    RowGroupHeaderExpandUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        if(sheet && this._rowGroupHeaderExpandExtent && sheet.rowRangeGroup)
        {
            this._suspendInvalidate(arg);
            this.saveState();
            var level = this._rowGroupHeaderExpandExtent.level;
            for(var index = 0; index < level; index++)
                sheet.rowRangeGroup.expand(index,true);
            sheet.rowRangeGroup.expand(level,false);
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
    };
    RowGroupHeaderExpandUndoAction.prototype.saveState = function()
    {
        var status = null;
        var sheet = this._sheet;
        if(sheet && this._rowGroupHeaderExpandExtent && sheet.rowRangeGroup)
        {
            var level = this._rowGroupHeaderExpandExtent.level;
            status = {};
            for(var index = 0; index <= level; index++)
            {
                var rowIndex = 0;
                var rowCount = sheet.getRowCount();
                var direction = sheet.rowRangeGroup.direction;
                while(rowIndex < rowCount)
                {
                    var group = sheet.rowRangeGroup.find(rowIndex,index);
                    if(group)
                    {
                        var summaryIndex = -1;
                        switch(direction)
                        {
                            case UI.RangeGroupDirection.Backward:
                                summaryIndex = group.start - 1;
                                break;
                            case UI.RangeGroupDirection.Forward:
                                summaryIndex = group.end + 1;
                                break;
                            default:
                                break
                        }
                        var isCollapsed = group.getState() === UI.GroupState.Collapsed;
                        if(!status[summaryIndex])
                            status[summaryIndex] = isCollapsed;
                        rowIndex += group.end - group.start + 1
                    }
                    rowIndex++
                }
            }
        }
        this._oldStatus = status
    };
    RowGroupHeaderExpandUndoAction.prototype.undo = function(arg)
    {
        var ret = false;
        var sheet = this._sheet;
        if(sheet && this._oldStatus && sheet.rowRangeGroup)
        {
            this._suspendInvalidate(arg);
            for(var index in this._oldStatus)
                if(index)
                {
                    sheet.rowRangeGroup.setCollapsed(index,this._oldStatus[index]);
                    ret = true
                }
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
        return ret
    };
    DragDropUndoAction.prototype.canExecute = function(arg)
    {
        return true
    };
    DragDropUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        if(!sheet._isValidRange(this._dragDropExtent.fromRow,this._dragDropExtent.fromColumn,this._dragDropExtent.rowCount,this._dragDropExtent.columnCount,this._sheet.getRowCount(),this._sheet.getColumnCount()))
            return;
        if(!this._insert)
            if(!sheet._isValidRange(this._dragDropExtent.toRow,this._dragDropExtent.toColumn,this._dragDropExtent.rowCount,this._dragDropExtent.columnCount,this._sheet.getRowCount(),this._sheet.getColumnCount()))
                return;
        this.saveState();
        var from,
            to,
            count,
            oldSelections,
            newSelections;
        if(this._insert)
        {
            var ifr = this._dragDropExtent.fromRow,
                ifc = this._dragDropExtent.fromColumn;
            if(ifc >= 0 && ifr < 0)
            {
                from = this._dragDropExtent.fromColumn;
                to = this._dragDropExtent.toColumn;
                count = this._dragDropExtent.columnCount;
                var activeColumn = this._dragDropExtent.toColumn;
                this._suspendInvalidate(arg);
                try
                {
                    this._sheet.addColumns(to,count);
                    if(this._copy)
                        this._sheet.copyTo(-1,to <= from ? from + count : from,-1,to,-1,count,this._option);
                    else
                    {
                        this._sheet.moveTo(-1,to <= from ? from + count : from,-1,to,-1,count,this._option);
                        this._sheet.deleteColumns(to <= from ? from + count : from,count);
                        if(from < to)
                            activeColumn = to - count
                    }
                }
                finally
                {
                    this._resumeInvalidate(arg)
                }
                if(sheet)
                {
                    oldSelections = sheet.getSelections().toArray();
                    sheet._clearSelectionImp();
                    sheet.addSelection(-1,activeColumn,sheet.getRowCount(),count);
                    newSelections = sheet.getSelections();
                    sheet._trigger(UI.Events.SelectionChanging,{
                        sheet: sheet,
                        sheetName: sheet._name,
                        oldSelections: oldSelections,
                        newSelections: newSelections
                    });
                    sheet._trigger(UI.Events.SelectionChanged,{
                        sheet: sheet,
                        sheetName: sheet._name
                    });
                    sheet._setActiveCellImp(sheet._getFirstVisualRow(),activeColumn,sheet.activeRowViewportIndex,sheet.activeColViewportIndex);
                    sheet.invalidateLayout();
                    sheet._dragRect = {};
                    sheet.repaint()
                }
            }
            else if(this._dragDropExtent.fromRow >= 0 && this._dragDropExtent.fromColumn < 0)
            {
                from = this._dragDropExtent.fromRow;
                to = this._dragDropExtent.toRow;
                count = this._dragDropExtent.rowCount;
                var activeRow = this._dragDropExtent.toRow;
                this._suspendInvalidate(arg);
                try
                {
                    this._sheet.addRows(to,count);
                    if(this._copy)
                        this._sheet.copyTo(to <= from ? from + count : from,-1,to,-1,count,-1,this._option);
                    else
                    {
                        this._sheet.moveTo(to <= from ? from + count : from,-1,to,-1,count,-1,this._option);
                        this._sheet.deleteRows(to <= from ? from + count : from,count);
                        if(from < to)
                            activeRow = to - count
                    }
                }
                finally
                {
                    this._resumeInvalidate(arg)
                }
                if(sheet)
                {
                    oldSelections = sheet.getSelections().toArray();
                    sheet._clearSelectionImp();
                    sheet.addSelection(activeRow,-1,count,sheet.getColumnCount());
                    newSelections = sheet.getSelections();
                    sheet._trigger(UI.Events.SelectionChanging,{
                        sheet: sheet,
                        sheetName: sheet._name,
                        oldSelections: oldSelections,
                        newSelections: newSelections
                    });
                    sheet._trigger(UI.Events.SelectionChanged,{
                        sheet: sheet,
                        sheetName: sheet._name
                    });
                    sheet._setActiveCellImp(activeRow,sheet._getFirstVisualColumn(),sheet.activeRowViewportIndex,sheet.activeColViewportIndex);
                    sheet.invalidateLayout();
                    sheet._dragRect = {};
                    sheet.repaint()
                }
            }
        }
        else
        {
            var fromRow = this._dragDropExtent.fromRow;
            var fromColumn = this._dragDropExtent.fromColumn;
            var toRow = this._dragDropExtent.toRow;
            var toColumn = this._dragDropExtent.toColumn;
            var rowCount = this._dragDropExtent.rowCount;
            var columnCount = this._dragDropExtent.columnCount;
            this._suspendInvalidate(arg);
            try
            {
                if(this._copy)
                    this._sheet.copyTo(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount,this._option);
                else
                    this._sheet.moveTo(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount,this._option);
                if(sheet)
                {
                    oldSelections = sheet.getSelections().toArray();
                    sheet._clearSelectionImp();
                    sheet.addSelection(toRow,toColumn,rowCount,columnCount);
                    newSelections = sheet.getSelections().toArray();
                    sheet._trigger(UI.Events.SelectionChanging,{
                        sheet: sheet,
                        sheetName: sheet._name,
                        oldSelections: oldSelections,
                        newSelections: newSelections
                    });
                    sheet._trigger(UI.Events.SelectionChanged,{
                        sheet: sheet,
                        sheetName: sheet._name
                    });
                    sheet._setActiveCellImp(Math.max(sheet._getFirstVisualRow(),toRow),Math.max(sheet._getFirstVisualColumn(),toColumn),sheet.activeRowViewportIndex,sheet.activeColViewportIndex);
                    if(!this._copy && this._savedFromViewportCells && this._savedFromViewportCells.isValueSaved())
                        CopyMoveHelper.raiseRangeDataChanged(sheet,fromRow,fromColumn,rowCount,columnCount,this._savedFromViewportCells.getValues());
                    if(this._savedToViewportCells && this._savedToViewportCells.isValueSaved())
                        CopyMoveHelper.raiseRangeDataChanged(sheet,toRow,toColumn,rowCount,columnCount,this._savedToViewportCells.getValues())
                }
            }
            finally
            {
                this._resumeInvalidate(arg)
            }
            if(sheet)
            {
                sheet.invalidateLayout();
                sheet._dragRect = {};
                sheet.repaint()
            }
        }
    };
    DragDropUndoAction.prototype.saveState = function()
    {
        this.initSaveState();
        var fromRow = this._dragDropExtent.fromRow < 0 ? 0 : this._dragDropExtent.fromRow;
        var fromColumn = this._dragDropExtent.fromColumn < 0 ? 0 : this._dragDropExtent.fromColumn;
        var toRow = this._dragDropExtent.toRow < 0 ? 0 : this._dragDropExtent.toRow;
        var toColumn = this._dragDropExtent.toColumn < 0 ? 0 : this._dragDropExtent.toColumn;
        var rowCount = this._dragDropExtent.fromRow < 0 ? this._sheet.getRowCount() : this._dragDropExtent.rowCount;
        var columnCount = this._dragDropExtent.fromColumn < 0 ? this._sheet.getColumnCount() : this._dragDropExtent.columnCount;
        if(!this._insert)
        {
            if(this._dragDropExtent.fromRow < 0)
            {
                var toColumnHeaderCellsInfo = new CopyMoveCellsInfo(this._sheet.getRowCount(UI.SheetArea.colHeader),columnCount);
                var toColumnsInfo = new CopyMoveColumnsInfo(columnCount);
                CopyMoveHelper.saveColumnHeaderInfo(this._sheet,toColumnHeaderCellsInfo,toColumnsInfo,toColumn,this._option);
                this._savedToColumnHeaderCells = toColumnHeaderCellsInfo;
                this._savedToColumns = toColumnsInfo;
                if(!this._copy)
                {
                    var fromColumnHeaderCellsInfo = new CopyMoveCellsInfo(this._sheet.getRowCount(UI.SheetArea.colHeader),columnCount);
                    var fromColumnsInfo = new CopyMoveColumnsInfo(columnCount);
                    CopyMoveHelper.saveColumnHeaderInfo(this._sheet,fromColumnHeaderCellsInfo,fromColumnsInfo,fromColumn,this._option);
                    this._savedFromColumnHeaderCells = fromColumnHeaderCellsInfo;
                    this._savedFromColumns = fromColumnsInfo
                }
            }
            if(this._dragDropExtent.fromColumn < 0)
            {
                var toRowHeaderCellsInfo = new CopyMoveCellsInfo(rowCount,this._sheet.getColumnCount(UI.SheetArea.rowHeader));
                var toRowsInfo = new CopyMoveRowsInfo(rowCount);
                CopyMoveHelper.saveRowHeaderInfo(this._sheet,toRowHeaderCellsInfo,toRowsInfo,toRow,this._option);
                this._savedToRowHeaderCells = toRowHeaderCellsInfo;
                this._savedToRows = toRowsInfo;
                if(!this._copy)
                {
                    var fromRowHeaderCellsInfo = new CopyMoveCellsInfo(rowCount,this._sheet.getColumnCount(UI.SheetArea.rowHeader));
                    var fromRowsInfo = new CopyMoveRowsInfo(rowCount);
                    CopyMoveHelper.saveRowHeaderInfo(this._sheet,fromRowHeaderCellsInfo,fromRowsInfo,fromRow,this._option);
                    this._savedFromRowHeaderCells = fromRowHeaderCellsInfo;
                    this._savedFromRows = fromRowsInfo
                }
            }
            var toViewportCellsInfo = new CopyMoveCellsInfo(rowCount,columnCount);
            CopyMoveHelper.saveViewportInfo(this._sheet,toViewportCellsInfo,toRow,toColumn,this._option);
            this._savedToViewportCells = toViewportCellsInfo;
            if(!this._copy)
            {
                var fromViewportCellsInfo = new CopyMoveCellsInfo(rowCount,columnCount);
                CopyMoveHelper.saveViewportInfo(this._sheet,fromViewportCellsInfo,fromRow,fromColumn,this._option);
                this._savedFromViewportCells = fromViewportCellsInfo
            }
        }
        this._savedAcitveRowViewportIndex = this._sheet.activeRowViewportIndex;
        this._savedAcitveColumnViewportIndex = this._sheet.activeColViewportIndex;
        this._savedActiveRow = this._sheet._activeRowIndex;
        this._savedActiveColumn = this._sheet._activeColIndex
    };
    DragDropUndoAction.prototype.initSaveState = function()
    {
        this._savedFromColumnHeaderCells = null;
        this._savedFromColumns = null;
        this._savedFromViewportCells = null;
        this._savedFromRowHeaderCells = null;
        this._savedFromRows = null;
        this._savedToColumnHeaderCells = null;
        this._savedToColumns = null;
        this._savedToViewportCells = null;
        this._savedToRowHeaderCells = null;
        this._savedToRows = null;
        this._savedAcitveRowViewportIndex = -2;
        this._savedAcitveColumnViewportIndex = -2;
        this._savedActiveRow = -1;
        this._savedActiveColumn = -1
    };
    DragDropUndoAction.prototype.undo = function(arg)
    {
        var sheet = this._sheet;
        if(!sheet._isValidRange(this._dragDropExtent.fromRow,this._dragDropExtent.fromColumn,this._dragDropExtent.rowCount,this._dragDropExtent.columnCount,this._sheet.getRowCount(),this._sheet.getColumnCount()))
            return false;
        if(!this._insert)
            if(!sheet._isValidRange(this._dragDropExtent.toRow,this._dragDropExtent.toColumn,this._dragDropExtent.rowCount,this._dragDropExtent.columnCount,this._sheet.getRowCount(),this._sheet.getColumnCount()))
                return false;
        var ret = false,
            oldSelections,
            newSelections,
            count,
            from,
            to;
        if(this._insert)
        {
            if(!(this._dragDropExtent.fromColumn >= 0 && this._dragDropExtent.fromRow >= 0))
                if(this._dragDropExtent.fromColumn >= 0)
                {
                    var activeColumn = this._dragDropExtent.fromColumn;
                    count = this._dragDropExtent.columnCount;
                    this._suspendInvalidate(arg);
                    try
                    {
                        if(this._copy)
                            this._sheet.deleteColumns(this._dragDropExtent.toColumn,count);
                        else
                        {
                            from = this._dragDropExtent.toColumn;
                            to = this._dragDropExtent.fromColumn;
                            if(this._dragDropExtent.fromColumn < this._dragDropExtent.toColumn)
                                from = this._dragDropExtent.toColumn - count;
                            else
                                to = this._dragDropExtent.fromColumn + count;
                            this._sheet.addColumns(to,count);
                            this._sheet.copyTo(-1,to <= from ? from + count : from,-1,to,-1,count,this._option);
                            this._sheet.deleteColumns(to <= from ? from + count : from,count);
                            if(from < to)
                                activeColumn = to - count
                        }
                    }
                    finally
                    {
                        this._resumeInvalidate(arg)
                    }
                    if(sheet)
                    {
                        oldSelections = sheet.getSelections().toArray();
                        sheet._clearSelectionImp();
                        sheet.addSelection(-1,activeColumn,sheet.getRowCount(),count);
                        newSelections = sheet.getSelections();
                        sheet._trigger(UI.Events.SelectionChanging,{
                            sheet: sheet,
                            sheetName: sheet._name,
                            oldSelections: oldSelections,
                            newSelections: newSelections
                        });
                        sheet._trigger(UI.Events.SelectionChanged,{
                            sheet: sheet,
                            sheetName: sheet._name
                        });
                        sheet.invalidateLayout();
                        sheet.repaint()
                    }
                    ret = true
                }
                else if(this._dragDropExtent.fromRow >= 0)
                {
                    count = this._dragDropExtent.rowCount;
                    var activeRow = this._dragDropExtent.fromRow;
                    this._suspendInvalidate(arg);
                    try
                    {
                        if(this._copy)
                            this._sheet.deleteRows(this._dragDropExtent.toRow,count);
                        else
                        {
                            from = this._dragDropExtent.toRow;
                            to = this._dragDropExtent.fromRow;
                            if(this._dragDropExtent.fromRow < this._dragDropExtent.toRow)
                                from = this._dragDropExtent.toRow - count;
                            else
                                to = this._dragDropExtent.fromRow + count;
                            this._sheet.addRows(to,count);
                            if(this._savedFromViewportCells)
                            {
                                CopyMoveHelper.undoCellsInfo(this._sheet,this._savedFromViewportCells,to,0,UI.SheetArea.viewport);
                                ret = true
                            }
                            if(this._savedFromRowHeaderCells)
                            {
                                CopyMoveHelper.undoCellsInfo(this._sheet,this._savedFromRowHeaderCells,to,0,UI.SheetArea.rowHeader);
                                ret = true
                            }
                            if(this._savedFromRows)
                            {
                                CopyMoveHelper.undoRowsInfo(this._sheet,this._savedFromRows,to);
                                ret = true
                            }
                            if(!ret)
                                this._sheet.copyTo(to <= from ? from + count : from,-1,to,-1,count,-1,this._option);
                            this._sheet.deleteRows(to <= from ? from + count : from,count);
                            if(from < to)
                                activeRow = to - count
                        }
                    }
                    finally
                    {
                        this._resumeInvalidate(arg)
                    }
                    if(sheet)
                    {
                        oldSelections = sheet.getSelections().toArray();
                        sheet._clearSelectionImp();
                        sheet.addSelection(activeRow,-1,count,sheet.getColumnCount());
                        newSelections = sheet.getSelections();
                        sheet._trigger(UI.Events.SelectionChanging,{
                            sheet: sheet,
                            sheetName: sheet._name,
                            oldSelections: oldSelections,
                            newSelections: newSelections
                        });
                        sheet._trigger(UI.Events.SelectionChanged,{
                            sheet: sheet,
                            sheetName: sheet._name
                        });
                        sheet.invalidateLayout();
                        sheet.repaint()
                    }
                    ret = true
                }
        }
        else
        {
            var fromRow = this._dragDropExtent.fromRow < 0 ? 0 : this._dragDropExtent.fromRow;
            var fromColumn = this._dragDropExtent.fromColumn < 0 ? 0 : this._dragDropExtent.fromColumn;
            var toRow = this._dragDropExtent.toRow < 0 ? 0 : this._dragDropExtent.toRow;
            var toColumn = this._dragDropExtent.toColumn < 0 ? 0 : this._dragDropExtent.toColumn;
            var rowCount = this._dragDropExtent.fromRow < 0 ? this._sheet.getRowCount() : this._dragDropExtent.rowCount;
            var columnCount = this._dragDropExtent.fromColumn < 0 ? this._sheet.getColumnCount() : this._dragDropExtent.columnCount;
            var oldToValues = null;
            var oldFromValues = null;
            if(!this._copy && this._savedFromViewportCells && this._savedFromViewportCells.isValueSaved())
                oldFromValues = CopyMoveHelper.getValues(this._sheet,fromRow,fromColumn,rowCount,columnCount);
            if(this._savedToViewportCells && this._savedToViewportCells.isValueSaved())
                oldToValues = CopyMoveHelper.getValues(this._sheet,toRow,toColumn,rowCount,columnCount);
            this._suspendInvalidate(arg);
            try
            {
                if(this._savedToColumnHeaderCells)
                {
                    CopyMoveHelper.undoCellsInfo(this._sheet,this._savedToColumnHeaderCells,0,toColumn,UI.SheetArea.colHeader);
                    ret = true
                }
                if(this._savedToColumns)
                {
                    CopyMoveHelper.undoColumnsInfo(this._sheet,this._savedToColumns,toColumn);
                    ret = true
                }
                if(this._savedToViewportCells)
                {
                    CopyMoveHelper.undoCellsInfo(this._sheet,this._savedToViewportCells,toRow,toColumn,UI.SheetArea.viewport);
                    ret = true
                }
                if(this._savedToRowHeaderCells)
                {
                    CopyMoveHelper.undoCellsInfo(this._sheet,this._savedToRowHeaderCells,toRow,0,UI.SheetArea.rowHeader);
                    ret = true
                }
                if(this._savedToRows)
                {
                    CopyMoveHelper.undoRowsInfo(this._sheet,this._savedToRows,toRow);
                    ret = true
                }
                if(this._savedFromColumnHeaderCells)
                {
                    CopyMoveHelper.undoCellsInfo(this._sheet,this._savedFromColumnHeaderCells,0,fromColumn,UI.SheetArea.colHeader);
                    ret = true
                }
                if(this._savedFromColumns)
                {
                    CopyMoveHelper.undoColumnsInfo(this._sheet,this._savedFromColumns,fromColumn);
                    ret = true
                }
                if(this._savedFromViewportCells)
                {
                    CopyMoveHelper.undoCellsInfo(this._sheet,this._savedFromViewportCells,fromRow,fromColumn,UI.SheetArea.viewport);
                    ret = true
                }
                if(this._savedFromRowHeaderCells)
                {
                    CopyMoveHelper.undoCellsInfo(this._sheet,this._savedFromRowHeaderCells,fromRow,0,UI.SheetArea.rowHeader);
                    ret = true
                }
                if(this._savedFromRows)
                {
                    CopyMoveHelper.undoRowsInfo(this._sheet,this._savedFromRows,fromRow);
                    ret = true
                }
            }
            finally
            {
                this._resumeInvalidate(arg)
            }
            if(ret && sheet)
            {
                oldSelections = sheet.getSelections().toArray();
                sheet._clearSelectionImp();
                sheet.addSelection(this._dragDropExtent.fromRow,this._dragDropExtent.fromColumn,this._dragDropExtent.rowCount,this._dragDropExtent.columnCount);
                newSelections = sheet.getSelections();
                sheet._trigger(UI.Events.SelectionChanging,{
                    sheet: sheet,
                    sheetName: sheet._name,
                    oldSelections: oldSelections,
                    newSelections: newSelections
                });
                sheet._trigger(UI.Events.SelectionChanged,{
                    sheet: sheet,
                    sheetName: sheet._name
                });
                if(oldToValues)
                    CopyMoveHelper.raiseRangeDataChanged(sheet,toRow,toColumn,rowCount,columnCount,oldToValues);
                if(oldFromValues)
                    CopyMoveHelper.raiseRangeDataChanged(sheet,fromRow,fromColumn,rowCount,columnCount,oldFromValues)
            }
        }
        if(ret)
            if(sheet)
            {
                if(this._savedActiveRow !== -1 && this._savedActiveColumn !== -1)
                {
                    var selection = sheet.getSelections()[0];
                    if(selection.contains(this._savedActiveRow,this._savedActiveColumn))
                        sheet._setActiveCellImp(this._savedActiveRow,this._savedActiveColumn,false);
                    else
                        sheet._setActiveCellImp(Math.max(sheet._getFirstVisualRow(),selection.row),Math.max(sheet._getFirstVisualColumn(),selection.col),false)
                }
                if(this._savedAcitveRowViewportIndex !== -2 && this._savedAcitveColumnViewportIndex !== -2 && this._savedActiveRow !== -1 && this._savedActiveColumn !== -1)
                    sheet.showCell(this._savedActiveRow,this._savedActiveColumn,UI.VerticalPosition.nearest,UI.HorizontalPosition.nearest);
                sheet.invalidateLayout();
                sheet.repaint()
            }
        return ret
    };
    CellEditUndoAction.prototype.canExecute = function(arg)
    {
        return true
    };
    CellEditUndoAction.prototype.saveState = function()
    {
        var oldFormula = this._sheet.getFormula(this._cellEditInfo.row,this._cellEditInfo.col);
        if(!(oldFormula === null || oldFormula === ""))
        {
            this._oldValue = oldFormula;
            this._oldValueIsFormula = true
        }
        else
        {
            this._oldValue = this._sheet.getValue(this._cellEditInfo.row,this._cellEditInfo.col);
            this._oldValueIsFormula = false
        }
    };
    CellEditUndoAction.prototype.undo = function(arg)
    {
        var sheet = this._sheet;
        var row = this._cellEditInfo.row;
        var col = this._cellEditInfo.col;
        try
        {
            this._suspendInvalidate(arg);
            sheet._bind(UI.Events.CellChanged,function(event, data)
            {
                if(data.propertyName === "value")
                    sheet._trigger(UI.Events.ValueChanged,{
                        sheet: data.sheet,
                        sheetName: data.sheetName,
                        row: data.row,
                        col: data.col,
                        oldValue: data._oldValue,
                        newValue: data.sheet.getValue(data.row,data.col)
                    })
            });
            if(this._oldValueIsFormula)
                sheet.setFormula(row,col,this._oldValue);
            else
            {
                var formula = sheet.getFormula(row,col);
                if(!(formula === null || formula === ""))
                    sheet.setFormula(row,col,null);
                sheet.setValue(row,col,this._oldValue)
            }
            this._resumeInvalidate(arg);
            this._sheet.repaint();
            return true
        }
        catch(ex)
        {
            return false
        }
        finally
        {
            sheet._unbind(UI.Events.CellChanged)
        }
    };
    CellEditUndoAction.prototype.execute = function(arg)
    {
        this._suspendInvalidate(arg);
        this.saveState();
        var sheet = this._sheet;
        var old = sheet.isPaintSuspended();
        try
        {
            sheet.isPaintSuspended(true);
            sheet._bind(UI.Events.CellChanged,function(event, data)
            {
                if(data.propertyName === "value")
                    sheet._trigger(UI.Events.ValueChanged,{
                        sheet: data.sheet,
                        sheetName: data.sheetName,
                        row: data.row,
                        col: data.col,
                        oldValue: data._oldValue,
                        newValue: data.sheet.getValue(data.row,data.col)
                    })
            });
            this.applyResult = this._applyEditing(arg)
        }
        finally
        {
            this._sheet._unbind(UI.Events.CellChanged);
            this._resumeInvalidate(arg);
            sheet.isPaintSuspended(old)
        }
        this._canUndo = this.applyResult === UI.DataValidationResult.ForceApply
    };
    CellEditUndoAction.prototype._applyEditing = function(arg)
    {
        var sheet = this._sheet;
        var row = this._cellEditInfo.row;
        var col = this._cellEditInfo.col;
        var text = this._cellEditInfo.newValue;
        var autoFormat = this._cellEditInfo.hasOwnProperty("autoFormat") ? this._cellEditInfo.autoFormat : true;
        var action = UI.DataValidationResult.ForceApply;
        var canUserEditFormula = sheet.parent ? sheet.parent.canUserEditFormula() : true;
        var value;
        if(text && text.length > 0 && text[0] === "=")
        {
            if(sheet.getDataValidator(row,col))
            {
                var newFormula = text.substring(1);
                if(newFormula !== '' && newFormula !== sheet.getFormula(row,col))
                {
                    var svc = sheet.getCalcService();
                    if(svc)
                    {
                        var expr = svc.parse(newFormula,row >= 0 ? row : 0,col >= 0 ? col : 0);
                        if(expr)
                        {
                            var calc = svc.evaluateParsedFormula(sheet._getSheetSource(),expr,row,col);
                            if(!sheet.isValid(row,col,calc))
                                action = sheet._validationError(row,col,calc)
                        }
                    }
                }
            }
        }
        else
        {
            value = this._getValueFromEditing(row,col,text,autoFormat);
            var valid = sheet.isValid(row,col,value);
            if(!valid)
            {
                action = sheet._validationError(row,col,text);
                sheet._eventHandler._forceCancelSelectiong = true
            }
        }
        if(!(action === UI.DataValidationResult.Discard || action === UI.DataValidationResult.Retry))
            if(action === UI.DataValidationResult.ForceApply)
                if(canUserEditFormula && text && text.length > 0 && text[0] === "=")
                {
                    try
                    {
                        sheet.setFormula(row,col,text.substring(1))
                    }
                    catch(e)
                    {
                        sheet._trigger(UI.Events.InvalidOperation,{
                            sheet: sheet,
                            sheetName: sheet._name,
                            message: e.message
                        });
                        throw e;
                    }
                    sheet._trigger(UI.Events.UserFormulaEntered,{
                        sheet: sheet,
                        sheetName: sheet._name,
                        row: row,
                        col: col,
                        formula: text.substring(1).toUpperCase()
                    })
                }
                else
                {
                    if(sheet.hasFormula(row,col))
                        sheet.setFormula(row,col,null);
                    try
                    {
                        if(/^('=)/ig.test(text))
                            text = text.substring(1);
                        if(value === undefined || value === null)
                            value = this._getValueFromEditing(row,col,text,autoFormat);
                        sheet.setValue(row,col,value)
                    }
                    catch(ex)
                    {
                        sheet.setValue(row,col,text)
                    }
                }
        return action
    };
    CellEditUndoAction.prototype.canUndo = function(arg)
    {
        return this._canUndo
    };
    CellEditUndoAction.prototype._getValueFromEditing = function(row, col, text, autoFormat)
    {
        var cellFormatter = null;
        var style = this._sheet.getActualStyle(row,col);
        if(style)
            if(style.formatter)
            {
                cellFormatter = style.formatter;
                if(typeof cellFormatter === 'string')
                {
                    cellFormatter = new UI.GeneralFormatter(cellFormatter);
                    if(this._sheet._editingTimeValue)
                        cellFormatter = new UI.GeneralFormatter("dd/mm/yyyy hh:mm:ss")
                }
            }
            else
                cellFormatter = style._autoFormatter;
        if(cellFormatter && !(cellFormatter instanceof UI.AutoFormatter))
        {
            var customerValue = null;
            try
            {
                customerValue = cellFormatter.Parse(text)
            }
            catch(ex){}
            return customerValue === undefined || customerValue === null ? text : customerValue
        }
        else if(autoFormat)
        {
            var aValRef = {};
            var autoDisplayFormatter = null;
            try
            {
                autoDisplayFormatter = (new UI.GeneralFormatter).GetPreferredDisplayFormatter(text,aValRef)
            }
            catch(ex){}
            var af = null;
            if(cellFormatter && cellFormatter instanceof UI.AutoFormatter && aValRef.value !== null && autoDisplayFormatter && autoDisplayFormatter.FormatString() !== "General")
                af = new UI.AutoFormatter(autoDisplayFormatter);
            else if(!cellFormatter && autoDisplayFormatter)
                af = new UI.AutoFormatter(autoDisplayFormatter);
            if(af)
            {
                var storedStyle = this._sheet.getStyle(row,col);
                if(!storedStyle)
                    storedStyle = new UI.Style;
                storedStyle._autoFormatter = af;
                this._sheet.setStyle(row,col,storedStyle)
            }
            return aValRef.value !== null ? aValRef.value : text
        }
        else
            return text
    };
    SheetRenameUndoAction.prototype.canUndo = function()
    {
        return!!this._oldName
    };
    SheetRenameUndoAction.prototype.canExecute = function(arg)
    {
        return this._sheet && this._newName && this._newName !== this._oldName
    };
    SheetRenameUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        if(sheet && this._newName && this._newName !== this._oldName)
        {
            this.saveState();
            this._suspendInvalidate(arg);
            sheet._name = this._newName;
            this._resumeInvalidate(arg);
            sheet._refreshTabStrip()
        }
    };
    SheetRenameUndoAction.prototype.canUndo = function()
    {
        return!!this._oldName
    };
    SheetRenameUndoAction.prototype.saveState = function()
    {
        if(this._sheet)
            this._oldName = this._sheet._name
    };
    SheetRenameUndoAction.prototype.undo = function(arg)
    {
        var sheet = this._sheet;
        if(sheet)
        {
            this._suspendInvalidate(arg);
            sheet._name = this._oldName;
            this._resumeInvalidate(arg);
            sheet._refreshTabStrip();
            return true
        }
        return false
    };
    ZoomUndoAction.prototype.canExecute = function(arg)
    {
        return this._sheet && this._sheet.parent._allowUserZoom && this._sheet._zoomFactor !== this._zoomFactor
    };
    ZoomUndoAction.prototype.execute = function(arg)
    {
        var sheet = this._sheet;
        this.saveState();
        if(sheet && (!sheet.parent || sheet.parent._allowUserZoom === true) && sheet._zoomFactor !== this._zoomFactor)
        {
            if(sheet.isEditing() === true)
                sheet.endEdit();
            this._suspendInvalidate(arg);
            sheet.zoom(this._zoomFactor);
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint()
        }
    };
    ZoomUndoAction.prototype.canUndo = function()
    {
        return this._sheet && this._prevZoomFactor > 0
    };
    ZoomUndoAction.prototype.saveState = function()
    {
        if(this._sheet)
            this._prevZoomFactor = this._sheet._zoomFactor
    };
    ZoomUndoAction.prototype.undo = function(arg)
    {
        var sheet = this._sheet;
        if(sheet)
        {
            this._suspendInvalidate(arg);
            sheet.zoom(this._prevZoomFactor);
            this._resumeInvalidate(arg);
            sheet.invalidateLayout();
            sheet.repaint();
            return true
        }
        return false
    };
    ClearValueUndoAction.prototype.canExecute = function(arg)
    {
        if(this._cachedActions)
        {
            for(var i = 0; i < this._cachedActions.length; i++)
            {
                var item = this._cachedActions[i];
                if(!item.canExecute(arg))
                    return false
            }
            return true
        }
        return false
    };
    ClearValueUndoAction.prototype.execute = function(arg)
    {
        this.saveState();
        if(this._cachedActions)
        {
            this._suspendInvalidate(arg);
            for(var i = 0; i < this._cachedActions.length; i++)
            {
                var item = this._cachedActions[i];
                item.execute(arg)
            }
            this._resumeInvalidate(arg);
            this._sheet.repaint()
        }
    };
    ClearValueUndoAction.prototype.canUndo = function()
    {
        if(this._cachedActions)
        {
            for(var i = 0; i < this._cachedActions.length; i++)
            {
                var item = this._cachedActions[i];
                if(!item.canUndo())
                    return false
            }
            return true
        }
        return false
    };
    ClearValueUndoAction.prototype.saveState = function()
    {
        if(this._cachedActions)
            for(var i = 0; i < this._cachedActions.length; i++)
            {
                var item = this._cachedActions[i];
                item.saveState()
            }
    };
    ClearValueUndoAction.prototype.undo = function(arg)
    {
        if(this._cachedActions)
        {
            for(var i = this._cachedActions.length - 1; i >= 0; i--)
            {
                var action = this._cachedActions[i];
                this._suspendInvalidate(arg);
                var ret = action.undo(arg);
                this._resumeInvalidate(arg);
                if(!ret)
                    return false;
                else
                    this._sheet.repaint()
            }
            return true
        }
        return false
    };
    ClearRangeValueUndoAction.prototype.execute = function(arg)
    {
        this.saveState();
        var sheet = this._sheet;
        if(sheet)
        {
            var range = sheet._getActualRange(this._clearRange);
            if(range.colCount > 0 && range.rowCount > 0)
                try
                {
                    sheet._bind(UI.Events.CellChanged,function(event, data)
                    {
                        if(data.propertyName === "value")
                            sheet._trigger(UI.Events.ValueChanged,{
                                sheet: data.sheet,
                                sheetName: data.sheetName,
                                row: data.row,
                                col: data.col
                            })
                    });
                    var oldState = sheet.isPaintSuspended();
                    sheet.isPaintSuspended(true);
                    sheet.suspendEvent();
                    sheet.clear(range.row,range.col,range.rowCount,range.colCount,UI.SheetArea.viewport,StorageType.Data);
                    sheet.resumeEvent();
                    sheet.isPaintSuspended(oldState);
                    sheet.repaint();
                    CopyMoveHelper.raiseRangeDataChanged(sheet,range.row,range.col,range.rowCount,range.colCount)
                }
                finally
                {
                    this._sheet._unbind(UI.Events.CellChanged)
                }
        }
    };
    ClearRangeValueUndoAction.prototype.canExecute = function(arg)
    {
        var sheet = this._sheet;
        return sheet.isProtected === true && sheet._isAnyCellInRangeLocked(this._clearRange) ? false : true
    };
    ClearRangeValueUndoAction.prototype.canUndo = function()
    {
        return!!this._cachedValues
    };
    ClearRangeValueUndoAction.prototype.saveState = function()
    {
        var sheet = this._sheet;
        if(sheet)
        {
            this._cachedFilteredColumns = [];
            var rowFilter = sheet.rowFilter(),
                row,
                column;
            if(rowFilter && rowFilter.range && rowFilter.isFiltered())
            {
                row = rowFilter.range.row;
                var rowCount = rowFilter.range.rowCount;
                if(rowFilter.showFilterButton)
                {
                    row = rowFilter.range.row - 1;
                    rowCount = rowFilter.range.rowCount + 1;
                    if(row < 0)
                    {
                        row = -1;
                        rowCount = -1
                    }
                }
                if((this._clearRange.row === -1 || this._clearRange.row <= row && row + rowCount <= this._clearRange.row + this._clearRange.rowCount) && (this._clearRange.col === -1 || this._clearRange.col <= rowFilter.range.col && rowFilter.range.col + rowFilter.range.colCount <= this._clearRange.col + this._clearRange.colCount))
                {
                    column = rowFilter.range.col < 0 ? 0 : rowFilter.range.col;
                    var columnCount = rowFilter.range.colCount < 0 ? sheet.getColumnCount() : rowFilter.range.colCount;
                    for(var c = 0; c < columnCount; c++)
                        if(rowFilter.isColumnFiltered(column + c))
                            this._cachedFilteredColumns.push(column + c)
                }
            }
            var range = sheet._getActualRange(this._clearRange);
            if(range.colCount > 0 && range.rowCount > 0)
            {
                this._cachedValues = [range.row + range.rowCount];
                for(var i = 0; i < range.rowCount; i++)
                {
                    this._cachedValues[range.row + i] = [range.col + range.colCount];
                    for(var j = 0; j < range.colCount; j++)
                    {
                        row = range.row + i;
                        column = range.col + j;
                        var formula = sheet.getFormula(row,column);
                        if(formula && formula !== "")
                            this._cachedValues[row][column] = new CellValueEntry(formula,true);
                        else
                        {
                            var value = sheet.getValue(row,column);
                            if(value !== undefined && value !== null)
                                this._cachedValues[row][column] = new CellValueEntry(value,false)
                        }
                    }
                }
            }
        }
    };
    ClearRangeValueUndoAction.prototype.undo = function(arg)
    {
        var sheet = this._sheet;
        if(sheet)
        {
            var ret = false,
                i;
            var range = sheet._getActualRange(this._clearRange);
            if(this._cachedValues && range.colCount > 0 && range.rowCount > 0)
                try
                {
                    sheet._bind(UI.Events.CellChanged,function(event, data)
                    {
                        if(data.propertyName === "value")
                            sheet._trigger(UI.Events.ValueChanged,{
                                sheet: data.sheet,
                                sheetName: data.sheetName,
                                row: data.row,
                                col: data.col
                            })
                    });
                    var rc = range.rowCount;
                    var cc = range.colCount;
                    var oldAutoRefresh = sheet.isPaintSuspended();
                    sheet.isPaintSuspended(true);
                    sheet.suspendEvent();
                    for(i = 0; i < rc; i++)
                        for(var j = 0; j < cc; j++)
                        {
                            var row = range.row + i;
                            var column = range.col + j;
                            if(this._cachedValues[row][column])
                            {
                                var entry = this._cachedValues[row][column];
                                if(entry.isFormula)
                                    sheet.setFormula(row,column,entry.value);
                                else
                                {
                                    sheet.setFormula(row,column,null);
                                    sheet.setValue(row,column,entry.value)
                                }
                            }
                            else
                            {
                                sheet.setFormula(row,column,null);
                                sheet.setValue(row,column,null)
                            }
                        }
                    sheet.resumeEvent();
                    sheet.isPaintSuspended(oldAutoRefresh);
                    CopyMoveHelper.raiseRangeDataChanged(sheet,range.row,range.col,range.rowCount,range.colCount);
                    ret = true
                }
                finally
                {
                    sheet._unbind(UI.Events.CellChanged)
                }
            var filter = sheet.rowFilter();
            if(filter && this._cachedFilteredColumns && this._cachedFilteredColumns.length > 0)
                for(i = 0; i < this._cachedFilteredColumns.length; i++)
                {
                    var col = this._cachedFilteredColumns[i];
                    filter.filter(col)
                }
            if(ret)
            {
                sheet.repaint();
                return true
            }
        }
        return false
    };
    DragFillUndoAction.prototype.canExecute = function(arg)
    {
        if(this._dragFillExtent.autoFillType === AutoFillType.ClearValues)
            return this.canExecuteDragClear();
        else
            return this.canExecuteDragFill()
    };
    DragFillUndoAction.prototype.canExecuteDragClear = function()
    {
        return true
    };
    DragFillUndoAction.prototype.canExecuteDragFill = function()
    {
        var startRange = this._dragFillExtent.startRange;
        var fillRange = this._dragFillExtent.fillRange;
        return!fillRange.intersect(startRange.row,startRange.col,startRange.rowCount,startRange.colCount)
    };
    DragFillUndoAction.prototype.execute = function(arg)
    {
        if(this.canExecute(arg))
        {
            var sheet = this._workSheet;
            try
            {
                this._suspendInvalidate(sheet);
                sheet.suspendCalcService();
                this.saveState();
                this._execute(sheet)
            }
            finally
            {
                sheet.resumeCalcService();
                this._resumeInvalidate(sheet)
            }
            sheet.invalidateLayout();
            sheet.repaint()
        }
    };
    DragFillUndoAction.prototype._execute = function(sheetView)
    {
        var sheet = this._workSheet;
        var oldSelections = sheet.getSelections() ? sheet.getSelections().toArray() : null;
        if(this._dragFillExtent.autoFillType === AutoFillType.ClearValues)
            this.executeDragFillClear(sheetView);
        else
            this.executeDragFill(sheetView);
        if(this._savedFilledViewportCells && this._savedFilledViewportCells.isValueSaved() && sheetView === this._workSheet)
        {
            var fillRange = this._dragFillExtent.fillRange;
            CopyMoveHelper.raiseRangeDataChanged(sheetView,fillRange.row,fillRange.col,fillRange.rowCount,fillRange.colCount,this._savedFilledViewportCells.getValues())
        }
        var newSelections = sheet.getSelections() ? sheet.getSelections().toArray() : null;
        if(sheet._raiseSelectionChanging(oldSelections,newSelections))
            sheet._raiseSelectionChanged()
    };
    DragFillUndoAction.prototype.executeDragFillClear = function(sheetView)
    {
        this._clearValueUndoAction.execute(sheetView);
        var startRange = this._dragFillExtent.startRange;
        var clearRange = this._dragFillExtent.fillRange;
        if(!startRange.equals(clearRange))
        {
            var newRange;
            if(this._fillSeries === FillSeries.Column)
            {
                newRange = new UI.Range(startRange.row,startRange.col,Math.max(1,startRange.rowCount - clearRange.rowCount),startRange.colCount);
                sheetView._setActiveCellImp(Math.max(sheetView._getFirstVisualRow(),newRange.row),Math.max(sheetView._getFirstVisualColumn(),newRange.col),sheetView.activeRowViewportIndex,sheetView.activeColViewportIndex);
                sheetView._clearSelectionImp();
                sheetView.addSelection(newRange.row,newRange.col,newRange.rowCount,newRange.colCount)
            }
            else
            {
                newRange = new UI.Range(startRange.row,startRange.col,startRange.rowCount,Math.max(1,startRange.colCount - clearRange.colCount));
                sheetView._setActiveCellImp(Math.max(sheetView._getFirstVisualRow(),newRange.row),Math.max(sheetView._getFirstVisualColumn(),newRange.col),sheetView.activeRowViewportIndex,sheetView.activeColViewportIndex);
                sheetView._clearSelectionImp();
                sheetView.addSelection(newRange.row,newRange.col,newRange.rowCount,newRange.colCount)
            }
        }
    };
    DragFillUndoAction.prototype.executeDragFill = function(sheetView)
    {
        var sheet = this._workSheet;
        var startRange = this._dragFillExtent.startRange;
        var fillRange = this._dragFillExtent.fillRange;
        var r,
            c,
            isIncrease;
        if(this._dragFillExtent.autoFillType === AutoFillType.FillSeries)
        {
            this.clearData(fillRange);
            isIncrease = this._dragFillExtent.fillDirection === FillDirection.Down || this._dragFillExtent.fillDirection === FillDirection.Right;
            if(isIncrease)
                sheet.fillAuto(startRange,this._wholeFillRange,this._fillSeries);
            else
                sheet.fillLinear(startRange,this._wholeFillRange,this._fillSeries)
        }
        else if(this._dragFillExtent.autoFillType === AutoFillType.CopyCells)
            this.copyCells(startRange,fillRange,UI.CopyToOption.All);
        else if(this._dragFillExtent.autoFillType === AutoFillType.FillFormattingOnly)
            this.copyCells(startRange,fillRange,UI.CopyToOption.Style);
        else if(this._dragFillExtent.autoFillType === AutoFillType.FillWithoutFormatting)
        {
            this.clearData(fillRange);
            var isDragSingleCell = startRange.rowCount === 1 && startRange.colCount === 1 && !(startRange.row === -1 && startRange.col !== -1) && !(startRange.col === -1 && startRange.row !== -1);
            if(isDragSingleCell)
            {
                var noStyleOption = UI.CopyToOption.Formula | UI.CopyToOption.RangeGroup | UI.CopyToOption.Span | UI.CopyToOption.Sparkline | UI.CopyToOption.Tag | UI.CopyToOption.Value;
                this.copyCells(startRange,fillRange,noStyleOption)
            }
            else
            {
                var fixedRange = this.adjustRange(this._wholeFillRange);
                var savedStyle = new UI._GcSheetModel(fixedRange.rowCount,fixedRange.colCount,null);
                for(r = 0; r < fixedRange.rowCount; r++)
                    for(c = 0; c < fixedRange.colCount; c++)
                        savedStyle.setStyle(r,c,CopyMoveHelper.getStyleObject(this._workSheet,fixedRange.row + r,fixedRange.col + c,UI.SheetArea.viewport));
                isIncrease = this._dragFillExtent.fillDirection === FillDirection.Down || this._dragFillExtent.fillDirection === FillDirection.Right;
                if(isIncrease)
                    sheet.fillAuto(startRange,this._wholeFillRange,this._fillSeries);
                else
                    sheet.fillLinear(startRange,this._wholeFillRange,this._fillSeries);
                for(r = 0; r < fixedRange.rowCount; r++)
                    for(c = 0; c < fixedRange.colCount; c++)
                        CopyMoveHelper.setStyleObject(this._workSheet,fixedRange.row + r,fixedRange.col + c,UI.SheetArea.viewport,savedStyle.getStyle(r,c))
            }
        }
        sheet._setActiveCellImp(Math.max(sheetView._getFirstVisualRow(),this._wholeFillRange.row),Math.max(sheetView._getFirstVisualColumn(),this._wholeFillRange.col),sheetView.activeRowViewportIndex,sheetView.activeColViewportIndex);
        if(sheetView._selectionModel)
            sheetView._clearSelectionImp();
        sheet.addSelection(this._wholeFillRange.row,this._wholeFillRange.col,this._wholeFillRange.rowCount,this._wholeFillRange.colCount)
    };
    DragFillUndoAction.prototype.clearData = function(cellRange)
    {
        var allStorages = StorageType.Data;
        this._workSheet.clear(cellRange.row,cellRange.col,cellRange.rowCount,cellRange.colCount,UI.SheetArea.viewport,allStorages)
    };
    DragFillUndoAction.prototype.adjustRange = function(range)
    {
        var row = range.row !== -1 ? range.row : 0;
        var column = range.col !== -1 ? range.col : 0;
        var rowCount = range.rowCount !== -1 ? range.rowCount : this._workSheet.getRowCount();
        var columnCount = range.colCount !== -1 ? range.colCount : this._workSheet.getColumnCount();
        return new UI.Range(row,column,rowCount,columnCount)
    };
    DragFillUndoAction.prototype.copyCells = function(fromRange, toRange, copyOption)
    {
        var sheet = this._workSheet;
        var adjustedFromRange = this.adjustRange(fromRange);
        var adjustedToRange = this.adjustRange(toRange);
        var i,
            fromRow,
            fromColumn,
            toRow,
            toColumn,
            bottomRow,
            rowCount,
            columnCount;
        if(this._fillSeries === FillSeries.Column)
        {
            var rows = Math.floor(adjustedToRange.rowCount / adjustedFromRange.rowCount);
            for(i = 0; i < rows; i++)
            {
                fromRow = adjustedFromRange.row;
                fromColumn = adjustedFromRange.col;
                if(this._dragFillExtent.fillDirection === FillDirection.Down)
                    toRow = adjustedToRange.row + i * adjustedFromRange.rowCount;
                else
                {
                    bottomRow = adjustedToRange.row + adjustedToRange.rowCount;
                    toRow = bottomRow - (i + 1) * adjustedFromRange.rowCount
                }
                toColumn = adjustedToRange.col;
                rowCount = adjustedFromRange.rowCount;
                columnCount = adjustedFromRange.colCount;
                sheet.copyTo(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount,copyOption)
            }
            var lastCopyRows = adjustedToRange.rowCount % adjustedFromRange.rowCount;
            if(lastCopyRows !== 0)
            {
                if(this._dragFillExtent.fillDirection === FillDirection.Down)
                    fromRow = adjustedFromRange.row;
                else
                {
                    var lastRow = adjustedFromRange.rowCount - (adjustedToRange.rowCount - adjustedFromRange.rowCount * rows);
                    fromRow = adjustedFromRange.row + lastRow
                }
                fromColumn = adjustedFromRange.col;
                if(this._dragFillExtent.fillDirection === FillDirection.Down)
                    toRow = adjustedToRange.row + adjustedFromRange.rowCount * rows;
                else
                {
                    bottomRow = adjustedToRange.row + adjustedToRange.rowCount;
                    toRow = bottomRow - rows * adjustedFromRange.rowCount - lastCopyRows
                }
                toColumn = adjustedToRange.col;
                rowCount = lastCopyRows;
                columnCount = adjustedFromRange.colCount;
                sheet.copyTo(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount,copyOption)
            }
        }
        else
        {
            var columns = Math.floor(adjustedToRange.colCount / adjustedFromRange.colCount);
            for(i = 0; i < columns; i++)
            {
                fromRow = adjustedFromRange.row;
                fromColumn = adjustedFromRange.col;
                toRow = adjustedToRange.row;
                if(this._dragFillExtent.fillDirection === FillDirection.Right)
                    toColumn = adjustedToRange.col + i * adjustedFromRange.colCount;
                else
                {
                    var bottomColumn = adjustedToRange.col + adjustedToRange.colCount;
                    toColumn = bottomColumn - (i + 1) * adjustedFromRange.colCount
                }
                rowCount = adjustedFromRange.rowCount;
                columnCount = adjustedFromRange.colCount;
                sheet.copyTo(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount,copyOption)
            }
            var lastCopyColumns = adjustedToRange.colCount % adjustedFromRange.colCount;
            if(lastCopyColumns !== 0)
            {
                fromRow = adjustedFromRange.row;
                if(this._dragFillExtent.fillDirection === FillDirection.Right)
                    fromColumn = adjustedFromRange.col;
                else
                {
                    var lastColumn = adjustedFromRange.colCount - (adjustedToRange.colCount - adjustedFromRange.colCount * columns);
                    fromColumn = adjustedFromRange.col + lastColumn
                }
                toRow = adjustedToRange.row;
                if(this._dragFillExtent.fillDirection === FillDirection.Right)
                    toColumn = adjustedToRange.col + adjustedFromRange.colCount * columns;
                else
                {
                    var leftColumn = adjustedToRange.col + adjustedToRange.colCount;
                    toColumn = leftColumn - columns * adjustedFromRange.colCount - lastCopyColumns
                }
                rowCount = adjustedFromRange.rowCount;
                columnCount = lastCopyColumns;
                sheet.copyTo(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount,copyOption)
            }
        }
    };
    DragFillUndoAction.prototype.saveState = function()
    {
        if(this._dragFillExtent.AutoFillType === AutoFillType.ClearValues)
            this.saveDragClearState();
        else
            this.saveDragFillState()
    };
    DragFillUndoAction.prototype.saveDragClearState = function()
    {
        this._clearValueUndoAction.saveState()
    };
    DragFillUndoAction.prototype.saveDragFillState = function()
    {
        this._savedFilledViewportCells = this.saveRangeStates(this._dragFillExtent.fillRange);
        this._savedStartViewportCells = this.saveRangeStates(this._dragFillExtent.startRange)
    };
    DragFillUndoAction.prototype.saveRangeStates = function(range)
    {
        var savedRange = this.adjustRange(range);
        var savedCellsInfo = new CopyMoveCellsInfo(savedRange.rowCount,savedRange.colCount);
        CopyMoveHelper.saveViewportInfo(this._workSheet,savedCellsInfo,savedRange.row,savedRange.col,UI.CopyToOption.All);
        return savedCellsInfo
    };
    DragFillUndoAction.prototype.undo = function(arg)
    {
        var sheet = this._workSheet;
        var result = false;
        try
        {
            result = this._undo(sheet)
        }
        finally
        {
            this._workSheet.resumeCalcService();
            this._resumeInvalidate(sheet)
        }
        return result
    };
    DragFillUndoAction.prototype._undo = function(sheet)
    {
        var result;
        var oldSelections = sheet.getSelections() ? sheet.getSelections().toArray() : null;
        var filledRangeCellValues = null;
        var fillRange = this._dragFillExtent.fillRange;
        if(this._savedFilledViewportCells && this._savedFilledViewportCells.isValueSaved())
            filledRangeCellValues = CopyMoveHelper.getValues(this._workSheet,fillRange.row,fillRange.col,fillRange.rowCount,fillRange.colCount);
        if(this._dragFillExtent.autoFillType === AutoFillType.ClearValues)
            result = this.undoDragClear(sheet);
        else
            result = this.undoDragFill(sheet);
        if(sheet._skipCloseDragFillSmartTag !== true)
            sheet._closeDragFillPopup();
        if(filledRangeCellValues)
            CopyMoveHelper.raiseRangeDataChanged(sheet,fillRange.row,fillRange.col,fillRange.rowCount,fillRange.colCount,filledRangeCellValues);
        var newSelections = sheet.getSelections() ? sheet.getSelections().toArray() : null;
        if(sheet._raiseSelectionChanging(oldSelections,newSelections))
            sheet._raiseSelectionChanged();
        sheet.repaint();
        return result
    };
    DragFillUndoAction.prototype.undoDragClear = function(sheet)
    {
        var result = this._clearValueUndoAction.undo(sheet);
        sheet._setActiveCellImp(Math.max(sheet._getFirstVisualRow(),this._dragFillExtent.startRange.row),Math.max(sheet._getFirstVisualColumn(),this._dragFillExtent.startRange.col),sheet.activeRowViewportIndex,sheet.activeColViewportIndex);
        sheet._clearSelectionImp();
        sheet.addSelection(this._dragFillExtent.startRange.row,this._dragFillExtent.startRange.col,this._dragFillExtent.startRange.rowCount,this._dragFillExtent.startRange.colCount);
        return result
    };
    DragFillUndoAction.prototype.undoDragFill = function(sheet)
    {
        this._suspendInvalidate(sheet);
        sheet.suspendCalcService();
        try
        {
            this.undoRangeStates(this._savedFilledViewportCells,this._dragFillExtent.fillRange);
            this.undoRangeStates(this._savedStartViewportCells,this._dragFillExtent.startRange);
            sheet._setActiveCellImp(Math.max(sheet._getFirstVisualRow(),this._dragFillExtent.startRange.row),Math.max(sheet._getFirstVisualColumn(),this._dragFillExtent.startRange.col),sheet.activeRowViewportIndex,sheet.activeColViewportIndex);
            sheet._clearSelectionImp();
            sheet.addSelection(this._dragFillExtent.startRange.row,this._dragFillExtent.startRange.col,this._dragFillExtent.startRange.rowCount,this._dragFillExtent.startRange.colCount)
        }
        finally
        {
            sheet.resumeCalcService();
            this._resumeInvalidate(sheet)
        }
        sheet.invalidateLayout();
        return true
    };
    DragFillUndoAction.prototype.undoRangeStates = function(savedInfo, range)
    {
        var savedRange = this.adjustRange(range);
        CopyMoveHelper.undoCellsInfo(this._workSheet,savedInfo,savedRange.row,savedRange.col,UI.SheetArea.viewport)
    };
    DragFillUndoAction.prototype.initWholeFilledRange = function()
    {
        var row = 0,
            rowCount = 0,
            column = 0,
            columnCount = 0;
        var direction = this._dragFillExtent.fillDirection;
        var startRange = this._dragFillExtent.startRange;
        var fillRange = this._dragFillExtent.fillRange;
        if(direction === FillDirection.Left || direction === FillDirection.Right)
        {
            row = startRange.row;
            rowCount = startRange.rowCount;
            column = direction === FillDirection.Left ? fillRange.col : startRange.col;
            columnCount = startRange.colCount + fillRange.colCount
        }
        else
        {
            row = direction === FillDirection.Up ? fillRange.row : startRange.row;
            rowCount = startRange.rowCount + fillRange.rowCount;
            column = startRange.col;
            columnCount = startRange.colCount
        }
        this._wholeFillRange = new UI.Range(row,column,rowCount,columnCount)
    };
    ClipboardPasteUndoAction.prototype.execute = function(sender)
    {
        if(this._cachedActions)
        {
            var sheetView = this._sheet;
            for(var i = 0; i < this._cachedActions.length; i++)
            {
                var action = this._cachedActions[i];
                if(!sheetView)
                    action.execute(sender);
                else
                {
                    var cancel = sheetView._raiseClipboardPasting(action.pasteRange(),action.pasteOption());
                    if(!cancel)
                    {
                        action.execute(sender);
                        sheetView._raiseClipboardPasted(action.pasteRange(),action.pasteOption())
                    }
                }
            }
            this.refreshSelection(sender);
            this.refreshUI(sender)
        }
    };
    ClipboardPasteUndoAction.prototype.refreshSelection = function(sender)
    {
        var sheetView = this._sheet;
        if(sheetView)
            if(this._cachedActions)
            {
                var oldSelection = sheetView.getSelections().toArray();
                sheetView._clearSelectionImp();
                if(this._cachedActions.length > 1)
                    for(var i = 0; i < this._cachedActions.length; i++)
                    {
                        var action = this._cachedActions[i];
                        var range = action.pasteRange();
                        sheetView.addSelection(range.row,range.col,range.rowCount,range.colCount)
                    }
                else if(this._cachedActions.length > 0)
                {
                    var toRange = this._cachedActions[0].pasteRange();
                    sheetView.addSelection(toRange.row,toRange.col,toRange.rowCount,toRange.colCount);
                    if(!toRange.contains(sheetView._activeRowIndex,sheetView._activeColIndex))
                        sheetView._setActiveCellImp(Math.max(sheetView._getFirstVisualRow(),toRange.row),Math.max(sheetView._getFirstVisualColumn(),toRange.col),sheetView.activeRowViewportIndex,sheetView.activeColViewportIndex)
                }
                if(sheetView._raiseSelectionChanging(oldSelection,sheetView.getSelections().toArray()))
                    sheetView._raiseSelectionChanged()
            }
    };
    ClipboardPasteUndoAction.prototype.refreshUI = function(sender)
    {
        var sheetView = this._sheet;
        if(sheetView)
        {
            sheetView.invalidateLayout();
            sheetView.repaint()
        }
    };
    ClipboardPasteUndoAction.prototype.canExecute = function(sender)
    {
        if(this._cachedActions)
        {
            for(var i = 0; i < this._cachedActions.length; i++)
            {
                var action = this._cachedActions[i];
                if(!action.canExecute(sender))
                    return false
            }
            return true
        }
        return false
    };
    ClipboardPasteUndoAction.prototype.canUndo = function()
    {
        if(this._cachedActions)
        {
            for(var i = 0; i < this._cachedActions.length; i++)
            {
                var action = this._cachedActions[i];
                if(!action.canUndo())
                    return false
            }
            return true
        }
        return false
    };
    ClipboardPasteUndoAction.prototype.saveState = function()
    {
        if(this._cachedActions)
            for(var i = 0; i < this._cachedActions.length; i++)
            {
                var action = this._cachedActions[i];
                action.saveState()
            }
    };
    ClipboardPasteUndoAction.prototype.undo = function(sender)
    {
        if(this._cachedActions)
        {
            var ret = true;
            for(var i = 0; i < this._cachedActions.length; i++)
            {
                var action = this._cachedActions[i];
                ret &= action.undo(sender)
            }
            this.refreshUI(sender);
            return ret
        }
        return false
    };
    ClipboardPasteRangeUndoAction.prototype.canUndo = function()
    {
        return true
    };
    ClipboardPasteRangeUndoAction.prototype.canExecute = function(sender)
    {
        return true
    };
    ClipboardPasteRangeUndoAction.prototype.saveState = function()
    {
        this.initSaveState();
        var isCutting = this._pasteExtent.isCutting;
        var option = ClipboardPasteRangeUndoAction.convertPasteOption(this._pasteOption);
        var fromRange = this._pasteExtent.sourceRange;
        var toRange = this._pasteExtent.targetRange;
        if(this._fromSheet && fromRange && isCutting)
        {
            var fromRow = fromRange.row < 0 ? 0 : fromRange.row;
            var fromColumn = fromRange.col < 0 ? 0 : fromRange.col;
            var fromRowCount = fromRange.row < 0 ? this._fromSheet.getRowCount() : fromRange.rowCount;
            var fromColumnCount = fromRange.col < 0 ? this._fromSheet.getColumnCount() : fromRange.colCount;
            if(fromRange.row < 0 && fromRange.col < 0 && toRange.row < 0 && toRange.col < 0)
                if(this._fromSheet._name !== this._toSheet._name)
                {
                    var fromSheetInfo = new CopyMoveSheetInfo;
                    CopyMoveHelper.saveSheetInfo(this._fromSheet,fromSheetInfo,option);
                    this._savedFromSheetInfo = fromSheetInfo
                }
            if(fromRange.row < 0)
            {
                var fromColumnHeaderCellsInfo = new CopyMoveCellsInfo(this._fromSheet.getRowCount(UI.SheetArea.colHeader),fromColumnCount);
                var fromColumnsInfo = new CopyMoveColumnsInfo(fromColumnCount);
                CopyMoveHelper.saveColumnHeaderInfo(this._fromSheet,fromColumnHeaderCellsInfo,fromColumnsInfo,fromColumn,option);
                this._savedFromColumnHeaderCells = fromColumnHeaderCellsInfo;
                this._savedFromColumns = fromColumnsInfo
            }
            if(fromRange.col < 0)
            {
                var fromRowHeaderCellsInfo = new CopyMoveCellsInfo(fromRowCount,this._fromSheet.getColumnCount(UI.SheetArea.rowHeader));
                var fromRowsInfo = new CopyMoveRowsInfo(fromRowCount);
                CopyMoveHelper.saveRowHeaderInfo(this._fromSheet,fromRowHeaderCellsInfo,fromRowsInfo,fromRow,option);
                this._savedFromRowHeaderCells = fromRowHeaderCellsInfo;
                this._savedFromRows = fromRowsInfo
            }
            var fromViewportCellsInfo = new CopyMoveCellsInfo(fromRowCount,fromColumnCount);
            CopyMoveHelper.saveViewportInfo(this._fromSheet,fromViewportCellsInfo,fromRow,fromColumn,option);
            this._savedFromViewportCells = fromViewportCellsInfo
        }
        var toRow = toRange.row < 0 ? 0 : toRange.row;
        var toColumn = toRange.col < 0 ? 0 : toRange.col;
        var toRowCount = toRange.row < 0 ? this._toSheet.getRowCount() : toRange.rowCount;
        var toColumnCount = toRange.col < 0 ? this._toSheet.getColumnCount() : toRange.colCount;
        if(this._fromSheet && fromRange)
        {
            if(fromRange.row < 0 && fromRange.col < 0 && toRange.row < 0 && toRange.col < 0)
                if(this._fromSheet._name !== this._toSheet._name)
                {
                    var toSheetInfo = new CopyMoveSheetInfo;
                    CopyMoveHelper.saveSheetInfo(this._toSheet,toSheetInfo,option);
                    this._savedToSheetInfo = toSheetInfo
                }
            if(fromRange.row < 0)
            {
                var toColumnHeaderCellsInfo = new CopyMoveCellsInfo(this._toSheet.getRowCount(UI.SheetArea.colHeader),toColumnCount);
                var toColumnsInfo = new CopyMoveColumnsInfo(toColumnCount);
                CopyMoveHelper.saveColumnHeaderInfo(this._toSheet,toColumnHeaderCellsInfo,toColumnsInfo,toColumn,option);
                this._savedToColumnHeaderCells = toColumnHeaderCellsInfo;
                this._savedToColumns = toColumnsInfo
            }
            if(fromRange.col < 0)
            {
                var toRowHeaderCellsInfo = new CopyMoveCellsInfo(toRowCount,this._toSheet.getColumnCount(UI.SheetArea.rowHeader));
                var toRowsInfo = new CopyMoveRowsInfo(toRowCount);
                CopyMoveHelper.saveRowHeaderInfo(this._toSheet,toRowHeaderCellsInfo,toRowsInfo,toRow,option);
                this._savedToRowHeaderCells = toRowHeaderCellsInfo;
                this._savedToRows = toRowsInfo
            }
        }
        var toViewportCellsInfo = new CopyMoveCellsInfo(toRowCount,toColumnCount);
        CopyMoveHelper.saveViewportInfo(this._toSheet,toViewportCellsInfo,toRow,toColumn,option);
        this._savedToViewportCells = toViewportCellsInfo
    };
    ClipboardPasteRangeUndoAction.prototype.initSaveState = function()
    {
        this._savedFromSheetInfo = null;
        this._savedFromColumnHeaderCells = null;
        this._savedFromColumns = null;
        this._savedFromViewportCells = null;
        this._savedFromRowHeaderCells = null;
        this._savedFromRows = null;
        this._savedToSheetInfo = null;
        this._savedToColumnHeaderCells = null;
        this._savedToColumns = null;
        this._savedToViewportCells = null;
        this._savedToRowHeaderCells = null;
        this._savedToRows = null
    };
    ClipboardPasteRangeUndoAction.prototype.pasteOption = function()
    {
        return this._pasteOption
    };
    ClipboardPasteRangeUndoAction.prototype.pasteRange = function()
    {
        return this._pasteExtent.targetRange
    };
    ClipboardPasteRangeUndoAction.prototype.execute = function(sender)
    {
        var fromRange = this._pasteExtent.sourceRange;
        var toRange = this._pasteExtent.targetRange;
        if(this._fromSheet && fromRange && !this._fromSheet._isValidRange(fromRange.row,fromRange.col,fromRange.rowCount,fromRange.colCount,this._fromSheet.getRowCount(),this._fromSheet.getColumnCount()))
            return;
        if(!this._toSheet || !toRange)
            return;
        if(!this._toSheet._isValidRange(toRange.row,toRange.col,toRange.rowCount,toRange.colCount,this._toSheet.getRowCount(),this._toSheet.getColumnCount()))
            return;
        this.saveState();
        this._suspendInvalidate(sender);
        try
        {
            this._toSheet._clipboardPaste(this._fromSheet,fromRange,this._toSheet,toRange,this._pasteExtent.isCutting,this._pasteExtent.clipboardText,this._pasteOption);
            var sheet = this._sheet;
            if(sheet)
            {
                if(this._pasteExtent.isCutting && this._savedFromViewportCells && this._savedFromViewportCells.isValueSaved() && this._fromSheet)
                    CopyMoveHelper.raiseRangeDataChanged(this._fromSheet,fromRange.row,fromRange.col,fromRange.rowCount,fromRange.colCount,this._savedFromViewportCells.getValues());
                if(this._savedToViewportCells && this._savedToViewportCells.isValueSaved() && this._toSheet)
                    CopyMoveHelper.raiseRangeDataChanged(this._toSheet,toRange.row,toRange.col,toRange.rowCount,toRange.colCount,this._savedToViewportCells.getValues())
            }
        }
        finally
        {
            this._resumeInvalidate(sender)
        }
    };
    ClipboardPasteRangeUndoAction.prototype.undo = function(sender)
    {
        var fromRange = this._pasteExtent.sourceRange;
        var toRange = this._pasteExtent.targetRange;
        if(!this._toSheet || !toRange)
            return false;
        if(!this._toSheet._isValidRange(toRange.row,toRange.col,toRange.rowCount,toRange.colCount,this._toSheet.getRowCount(),this._toSheet.getColumnCount()))
            return false;
        if(this._fromSheet && fromRange)
        {
            if(!this._fromSheet._isValidRange(fromRange.row,fromRange.col,fromRange.rowCount,fromRange.colCount,this._fromSheet.getRowCount(),this._fromSheet.getColumnCount()))
                return false;
            if(this._fromSheet && this._fromSheet._name === this._toSheet._name && this._toSheet.parent && !this._toSheet.parent.sheets.contains(this._fromSheet))
                return false
        }
        this._suspendInvalidate(sender);
        var ret = false;
        try
        {
            var oldToValues = null;
            var oldFromValues = null;
            var toRow = toRange.row < 0 ? 0 : toRange.row;
            var toColumn = toRange.col < 0 ? 0 : toRange.col;
            var toRowCount = toRange.row < 0 ? this._toSheet.getRowCount() : toRange.rowCount;
            var toColumnCount = toRange.col < 0 ? this._toSheet.getColumnCount() : toRange.colCount;
            if(this._savedToSheetInfo)
            {
                CopyMoveHelper.undoSheetInfo(this._toSheet,this._savedToSheetInfo);
                ret = true
            }
            if(this._savedToViewportCells && this._savedToViewportCells.isValueSaved())
                oldToValues = CopyMoveHelper.getValues(this._toSheet,toRow,toColumn,toRowCount,toColumnCount);
            if(this._savedToColumnHeaderCells)
            {
                CopyMoveHelper.undoCellsInfo(this._toSheet,this._savedToColumnHeaderCells,0,toColumn,UI.SheetArea.colHeader);
                ret = true
            }
            if(this._savedToColumns)
            {
                CopyMoveHelper.undoColumnsInfo(this._toSheet,this._savedToColumns,toColumn);
                ret = true
            }
            if(this._savedToViewportCells)
            {
                CopyMoveHelper.undoCellsInfo(this._toSheet,this._savedToViewportCells,toRow,toColumn,UI.SheetArea.viewport);
                ret = true
            }
            if(this._savedToRowHeaderCells)
            {
                CopyMoveHelper.undoCellsInfo(this._toSheet,this._savedToRowHeaderCells,toRow,0,UI.SheetArea.rowHeader);
                ret = true
            }
            if(this._savedToRows)
            {
                CopyMoveHelper.undoRowsInfo(this._toSheet,this._savedToRows,toRow);
                ret = true
            }
            var fromRow = 0;
            var fromColumn = 0;
            var fromRowCount = 0;
            var fromColumnCount = 0;
            if(this._fromSheet && fromRange)
            {
                fromRow = fromRange.row < 0 ? 0 : fromRange.row;
                fromColumn = fromRange.col < 0 ? 0 : fromRange.col;
                fromRowCount = fromRange.row < 0 ? this._fromSheet.getRowCount() : fromRange.rowCount;
                fromColumnCount = fromRange.col < 0 ? this._fromSheet.getColumnCount() : fromRange.colCount;
                if(this._savedFromViewportCells && this._savedFromViewportCells.isValueSaved())
                    oldFromValues = CopyMoveHelper.getValues(this._fromSheet,fromRow,fromColumn,fromRowCount,fromColumnCount);
                if(this._savedFromColumnHeaderCells)
                {
                    CopyMoveHelper.undoCellsInfo(this._fromSheet,this._savedFromColumnHeaderCells,0,fromColumn,UI.SheetArea.colHeader);
                    ret = true
                }
                if(this._savedFromColumns)
                {
                    CopyMoveHelper.undoColumnsInfo(this._fromSheet,this._savedFromColumns,fromColumn);
                    ret = true
                }
                if(this._savedFromViewportCells)
                {
                    CopyMoveHelper.undoCellsInfo(this._fromSheet,this._savedFromViewportCells,fromRow,fromColumn,UI.SheetArea.viewport);
                    ret = true
                }
                if(this._savedFromRowHeaderCells)
                {
                    CopyMoveHelper.undoCellsInfo(this._fromSheet,this._savedFromRowHeaderCells,fromRow,0,UI.SheetArea.rowHeader);
                    ret = true
                }
                if(this._savedFromRows)
                {
                    CopyMoveHelper.undoRowsInfo(this._fromSheet,this._savedFromRows,fromRow);
                    ret = true
                }
            }
            var sheet = sender;
            if(ret && sheet)
            {
                if(oldToValues && this._toSheet)
                    CopyMoveHelper.raiseRangeDataChanged(this._toSheet,toRow,toColumn,toRowCount,toColumnCount,oldToValues);
                if(oldFromValues && this._fromSheet)
                    CopyMoveHelper.raiseRangeDataChanged(this._fromSheet,fromRow,fromColumn,fromRowCount,fromColumnCount,oldFromValues)
            }
        }
        finally
        {
            this._resumeInvalidate(sender)
        }
        return ret
    };
    ClipboardPasteRangeUndoAction.convertPasteOption = function(pasteOption)
    {
        var copyOption = 0;
        if(pasteOption === UI.ClipboardPasteOptions.All || pasteOption === UI.ClipboardPasteOptions.Values)
            copyOption |= UI.CopyToOption.Value;
        if(pasteOption === UI.ClipboardPasteOptions.All || pasteOption === UI.ClipboardPasteOptions.Formatting)
            copyOption |= UI.CopyToOption.Style;
        if(pasteOption === UI.ClipboardPasteOptions.All || pasteOption === UI.ClipboardPasteOptions.Formulas)
            copyOption |= UI.CopyToOption.Formula;
        if(pasteOption === UI.ClipboardPasteOptions.All)
        {
            copyOption |= UI.CopyToOption.Span;
            copyOption |= UI.CopyToOption.Sparkline;
            copyOption |= UI.CopyToOption.BindingPath
        }
        return copyOption
    };
    CopyMoveSheetInfo.prototype = {
        saveDefaultStyle: function(style)
        {
            this._defaultStyle = style;
            this._defaultStyleSaved = true
        },
        getDefaultStyle: function()
        {
            return this._defaultStyle
        },
        saveDefaultColumnWidth: function(width)
        {
            this._defaultColumnWidth = width;
            this._defaultColumnWidthSaved = true
        },
        getDefaultColumnWidth: function()
        {
            return this._defaultColumnWidth
        },
        saveDefaultRowHeight: function(height)
        {
            this._defaultRowHeight = height;
            this._defaultRowHeightSaved = true
        },
        getDefaultRowHeight: function()
        {
            return this._defaultRowHeight
        },
        saveColumnHeaderDefaultStyle: function(style)
        {
            this._columnHeaderDefaultStyle = style;
            this._columnHeaderDefaultStyleSaved = true
        },
        getColumnHeaderDefaultStyle: function()
        {
            return this._columnHeaderDefaultStyle
        },
        saveColumnHeaderDefaultRowHeight: function(height)
        {
            this._columnHeaderDefaultRowHeight = height;
            this._columnHeaderDefaultRowHeightSaved = true
        },
        getColumnHeaderDefaultRowHeight: function()
        {
            return this._columnHeaderDefaultRowHeight
        },
        saveRowHeaderDefaultStyle: function(style)
        {
            this._rowHeaderDefaultStyle = style;
            this._rowHeaderDefaultStyleSaved = true
        },
        getRowHeaderDefaultStyle: function()
        {
            return this._rowHeaderDefaultStyle
        },
        saveRowHeaderDefaultColumnWidth: function(width)
        {
            this._rowHeaderDefaultColumnWidth = width;
            this._rowHeaderDefaultColumnWidthSaved = true
        },
        getRowHeaderDefaultColumnWidth: function()
        {
            return this._rowHeaderDefaultColumnWidth
        },
        isDefaultStyleSaved: function()
        {
            return this._defaultStyleSaved
        },
        isDefaultColumnWidthSaved: function()
        {
            return this._defaultColumnWidthSaved
        },
        isDefaultRowHeightSaved: function()
        {
            return this._defaultRowHeightSaved
        },
        isColumnHeaderDefaultStyleSaved: function()
        {
            return this._columnHeaderDefaultStyleSaved
        },
        isColumnHeaderDefaultRowHeightSaved: function()
        {
            return this._columnHeaderDefaultRowHeightSaved
        },
        isRowHeaderDefaultStyleSaved: function()
        {
            return this._rowHeaderDefaultStyleSaved
        },
        isRowHeaderDefaultColumnWidthSaved: function()
        {
            return this._rowHeaderDefaultColumnWidthSaved
        }
    };
    UI.CopyMoveCellsInfo = CopyMoveCellsInfo;
    UI.CopyMoveHelper = CopyMoveHelper;
    UI.UndoRedo = {
        DragDropExtent: DragDropExtent,
        GroupExtent: GroupExtent,
        ActionBase: ActionBase,
        ColumnResizeUndoAction: ColumnResizeUndoAction,
        RowResizeUndoAction: RowResizeUndoAction,
        ColumnAutoFitUndoAction: ColumnAutoFitUndoAction,
        RowAutoFitUndoAction: RowAutoFitUndoAction,
        ColumnGroupUndoAction: ColumnGroupUndoAction,
        ColumnUngroupUndoAction: ColumnUngroupUndoAction,
        RowGroupUndoAction: RowGroupUndoAction,
        RowUngroupUndoAction: RowUngroupUndoAction,
        ColumnGroupExpandUndoAction: ColumnGroupExpandUndoAction,
        RowGroupExpandUndoAction: RowGroupExpandUndoAction,
        ColumnGroupHeaderExpandUndoAction: ColumnGroupHeaderExpandUndoAction,
        RowGroupHeaderExpandUndoAction: RowGroupHeaderExpandUndoAction,
        DragDropUndoAction: DragDropUndoAction,
        CellEditUndoAction: CellEditUndoAction,
        SheetRenameUndoAction: SheetRenameUndoAction,
        ZoomUndoAction: ZoomUndoAction,
        ClearValueUndoAction: ClearValueUndoAction,
        DragFillUndoAction: DragFillUndoAction,
        ClipboardPasteUndoAction: ClipboardPasteUndoAction,
        ClipboardPasteRangeUndoAction: ClipboardPasteRangeUndoAction
    }
})(window);
(function(window)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var LineStyle = GrapeCity.UI.LineStyle;
    var ComposedLineSide = {
            first: 1,
            second: 2
        };
    var Line = function(){};
    GrapeCity.UI.Line = Line;
    Line.prototype = {
        adjust: function(opt)
        {
            if(opt.orientation === 0)
            {
                if(opt.offsetEnd)
                    this._x2 += opt.offsetEnd;
                if(opt.offsetStart)
                    this._x1 += opt.offsetStart
            }
            else if(opt.orientation === 1)
            {
                if(opt.offsetEnd)
                    this._y2 += opt.offsetEnd;
                if(opt.offsetStart)
                    this._y1 += opt.offsetStart
            }
        },
        paint: function(ctx)
        {
            var needStroke = false;
            if(ctx.lineWidth !== this._lineWidth)
                needStroke = true;
            var color = this._color ? this._color : "#9EB6CE";
            if(ctx.strokeStyle !== color)
                needStroke = true;
            if(needStroke)
            {
                ctx.closePath();
                ctx.stroke();
                ctx.beginPath();
                ctx.lineWidth = this._lineWidth;
                ctx.strokeStyle = color
            }
            this.paintLine(ctx)
        },
        paintLine: function(ctx)
        {
            ctx.moveTo(this._x1,this._y1);
            ctx.lineTo(this._x2,this._y2)
        }
    };
    var SolidLine = function(x1, y1, x2, y2, color, width)
        {
            var isSnapToPixel = width % 2;
            if(!isSnapToPixel)
                if(x1 !== x2)
                {
                    y1 -= 0.5;
                    y2 -= 0.5
                }
                else
                {
                    x1 -= 0.5;
                    x2 -= 0.5
                }
            this._x1 = x1;
            this._y1 = y1;
            this._x2 = x2;
            this._y2 = y2;
            this._color = color;
            this._lineWidth = width
        };
    SolidLine.prototype = new Line;
    var DashedLine = function(x1, y1, x2, y2, color, width, pattern)
        {
            var isSnapToPixel = width % 2;
            if(!isSnapToPixel)
                if(x1 !== x2)
                {
                    y1 -= 0.5;
                    y2 -= 0.5
                }
                else
                {
                    x1 -= 0.5;
                    x2 -= 0.5
                }
            this._x1 = x1;
            this._y1 = y1;
            this._x2 = x2;
            this._y2 = y2;
            this._color = color;
            this._lineWidth = width;
            this._pattern = pattern
        };
    DashedLine.prototype = new Line;
    DashedLine.prototype.paintLine = function(ctx)
    {
        DashedLine.render(ctx,this._x1,this._y1,this._x2,this._y2,this._pattern)
    };
    DashedLine.render = function(ctx, x0, y0, x1, y1, pattern)
    {
        var length = Math.sqrt(Math.pow(x1 - x0,2) + Math.pow(y1 - y0,2));
        var vector = {
                x: (x1 - x0) / length,
                y: (y1 - y0) / length
            };
        var dist = 0,
            i = 0;
        pattern = pattern && pattern.length ? pattern : [4,4];
        while(dist < length)
        {
            var dashLength = Math.min(pattern[i++ % pattern.length],length - dist);
            var draw = i % 2;
            dist += dashLength;
            if(draw)
                ctx.moveTo(x0,y0);
            x0 += dashLength * vector.x;
            y0 += dashLength * vector.y;
            if(draw)
                ctx.lineTo(x0,y0)
        }
    };
    var SlantedLine = function(x1, y1, x2, y2, color, horizental, pattern1, pattern2)
        {
            if(horizental)
            {
                x1 -= 1;
                x2 -= 1
            }
            else
            {
                y1 -= 1;
                y2 -= 1
            }
            this._x1 = x1;
            this._y1 = y1;
            this._x2 = x2;
            this._y2 = y2;
            this._color = color;
            this._horizental = horizental;
            this._lineWidth = 1;
            this._pattern1 = pattern1;
            this._pattern2 = pattern2
        };
    SlantedLine.prototype = new Line;
    SlantedLine.prototype.paintLine = function(ctx)
    {
        var xOff = this._horizental ? 0 : 1;
        var yOff = this._horizental ? 1 : 0;
        DashedLine.render(ctx,this._x1 - xOff,this._y1 - yOff,this._x2 - xOff,this._y2 - yOff,this._pattern1);
        DashedLine.render(ctx,this._x1,this._y1,this._x2,this._y2,this._pattern2)
    };
    var DoubleLine = function(x1, y1, x2, y2, color, horizental)
        {
            var xOff = horizental ? 0 : 1;
            var yOff = horizental ? 1 : 0;
            this._line1 = new SolidLine(x1 - xOff,y1 - yOff,x2 - xOff,y2 - yOff,color,1);
            this._line2 = new SolidLine(x1 + xOff,y1 + yOff,x2 + xOff,y2 + yOff,color,1);
            this._color = color;
            this._horizental = horizental;
            this._lineWidth = 1
        };
    DoubleLine.prototype = new Line;
    DoubleLine.prototype.paintLine = function(ctx)
    {
        this._line1.paintLine(ctx);
        this._line2.paintLine(ctx)
    };
    DoubleLine.prototype.adjust = function(opt)
    {
        if(!opt.lineSide)
        {
            this._line1.adjust(opt);
            this._line2.adjust(opt)
        }
        else if(opt.lineSide === ComposedLineSide.first)
            this._line1.adjust(opt);
        else if(opt.lineSide === ComposedLineSide.second)
            this._line2.adjust(opt)
    };
    Line.Create = function(x1, y1, x2, y2, color, lineStyle)
    {
        if(lineStyle === undefined || lineStyle === null)
            lineStyle = LineStyle.thin;
        switch(lineStyle)
        {
            case LineStyle.thin:
                return new SolidLine(x1,y1,x2,y2,color,1);
            case LineStyle.medium:
                return new SolidLine(x1,y1,x2,y2,color,2);
            case LineStyle.thick:
                return new SolidLine(x1,y1,x2,y2,color,3);
            case LineStyle.dashed:
                return new DashedLine(x1,y1,x2,y2,color,1,[3,1]);
            case LineStyle.dashDot:
                return new DashedLine(x1,y1,x2,y2,color,1,[8,2,2,2]);
            case LineStyle.dotted:
                return new DashedLine(x1,y1,x2,y2,color,1,[2,2]);
            case LineStyle.dashDotDot:
                return new DashedLine(x1,y1,x2,y2,color,1,[9,3,3,3,3,3]);
            case LineStyle.slantedDashDot:
                return new SlantedLine(x1,y1,x2,y2,color,x1 !== x2,[11,1,5,1],[10,2,4,2]);
            case LineStyle.mediumDashDot:
                return new DashedLine(x1,y1,x2,y2,color,2,[9,3,3,3]);
            case LineStyle.mediumDashDotDot:
                return new DashedLine(x1,y1,x2,y2,color,2,[9,3,3,3,3,3]);
            case LineStyle.mediumDashed:
                return new DashedLine(x1,y1,x2,y2,color,2,[9,3]);
            case LineStyle.hair:
                return new DashedLine(x1,y1,x2,y2,color,1,[1]);
            case LineStyle.double:
                return new DoubleLine(x1,y1,x2,y2,color,x1 !== x2)
        }
        return null
    };
    var compareColor = function(color1, color2)
        {
            return 0
        };
    var compareLineStyle = function(line1, line2)
        {
            if(!line1)
            {
                if(!line2)
                    return 0;
                return-1
            }
            else if(!line2)
                return 1;
            var lw1 = GrapeCity.UI.LineBorder.prototype._weight(line1);
            var lw2 = GrapeCity.UI.LineBorder.prototype._weight(line2);
            if(lw1 === lw2)
                return compareColor(line1.color,line2.color);
            else
                return lw1 - lw2
        };
    var getDrawingThickness = function(lineItem)
        {
            if(lineItem)
            {
                if(lineItem.isGridLine)
                    return 1;
                if(lineItem.style)
                    return GrapeCity.UI.LineBorder.prototype.width(lineItem.style)
            }
            return 0
        };
    var isDoubleLine = function(line)
        {
            return line && line.style === LineStyle.double
        };
    var isSlantedLine = function(line)
        {
            return line && line.style === LineStyle.slantedDashDot
        };
    var LayoutEngine = function()
        {
            var LineType = {
                    previous: -1,
                    next: 1
                };
            var getMaxLine = function(line1, line2)
                {
                    if(!line1)
                    {
                        if(!line2)
                            return null;
                        return line2
                    }
                    else if(!line2)
                        return line1;
                    return compareLineStyle(line1.style,line2.style) > 0 ? line1 : line2
                };
            var adjustLine = function(lineItem, opt)
                {
                    if(lineItem && lineItem.line)
                        lineItem.line.adjust(opt)
                };
            var compareLine = function(lineItem1, lineItem2)
                {
                    if(lineItem1 === null || lineItem1 === undefined)
                        if(lineItem2 === null || lineItem2 === undefined)
                            return 0;
                        else
                            return-1;
                    else if(lineItem2 === null || lineItem2 === undefined)
                        return 1;
                    else
                    {
                        if(lineItem1.isGridLine)
                            if(lineItem2.isGridLine)
                                return 0;
                            else
                                return-1;
                        else if(lineItem2.isGridLine)
                            return 1;
                        return compareLineStyle(lineItem1.style,lineItem2.style)
                    }
                };
            var isDoubleLineItem = function(lineItem)
                {
                    return lineItem && isDoubleLine(lineItem.style)
                };
            var isAllowExtend = function(current, line, break1, break2)
                {
                    if(!isDoubleLineItem(current))
                    {
                        var doubleCnt = 0;
                        doubleCnt += isDoubleLineItem(line) ? 1 : 0;
                        doubleCnt += isDoubleLineItem(break1) ? 1 : 0;
                        doubleCnt += isDoubleLineItem(break2) ? 1 : 0;
                        return doubleCnt < 2
                    }
                    return true
                };
            var layout4CrossRoad = function(current, line, break1, break2, lineType, vertical)
                {
                    var opt = {
                            orientation: vertical ? 1 : 0,
                            offsetStart: 0,
                            offsetEnd: 0
                        };
                    var diff1 = compareLine(current,break1),
                        diff2 = compareLine(current,break2);
                    if(diff1 >= 0 && diff2 >= 0)
                    {
                        if(diff1 === 0 && diff2 === 0)
                        {
                            if(lineType !== LineType.previous)
                                if(lineType === LineType.next)
                                    opt.offsetEnd -= lineType
                        }
                        else if(lineType === LineType.previous)
                            opt.offsetStart -= lineType;
                        else if(lineType === lineType.next)
                            opt.offsetEnd -= 2 * lineType
                    }
                    else if(diff2 >= 0)
                    {
                        opt.lineSide = ComposedLineSide.second;
                        opt.offsetStart += lineType
                    }
                    else if(diff1 >= 0)
                    {
                        opt.lineSide = ComposedLineSide.first;
                        opt.offsetStart += lineType
                    }
                    adjustLine(current,opt)
                };
            var layout4TurnRoad = function(current, prevLine, nextLine, break1, break2, type, lineType, vertical, swap)
                {
                    var optFirst = {
                            orientation: vertical ? 1 : 0,
                            lineSide: swap ? ComposedLineSide.second : ComposedLineSide.first,
                            offsetStart: 0,
                            offsetEnd: 0
                        };
                    var optSecond = {
                            orientation: vertical ? 1 : 0,
                            lineSide: swap ? ComposedLineSide.first : ComposedLineSide.second,
                            offsetStart: 0,
                            offsetEnd: 0
                        };
                    var breakLine = type === 1 ? break1 : break2;
                    var neighborLine = lineType === LineType.next ? nextLine : prevLine;
                    var hasNeighborDoubleLine = isDoubleLineItem(neighborLine);
                    if(!hasNeighborDoubleLine)
                    {
                        if(compareLine(current,breakLine) >= 0)
                        {
                            if(lineType === LineType.previous)
                                optSecond.offsetStart -= 2 * lineType;
                            else if(lineType === LineType.next)
                                optSecond.offsetEnd -= 2 * lineType
                        }
                        else if(lineType === LineType.previous)
                            optFirst.offsetStart += 2 * lineType;
                        else if(lineType === LineType.next)
                            optFirst.offsetEnd += 2 * lineType
                    }
                    else
                    {
                        var diff = compareLine(current,breakLine);
                        if(diff === 0)
                        {
                            var diff1 = compareLine(current,neighborLine);
                            if(diff1 === 0)
                            {
                                if(lineType === LineType.next)
                                    optSecond.offsetEnd -= lineType
                            }
                            else if(diff1 > 0)
                                if(lineType === LineType.previous)
                                    optSecond.offsetStart -= 2 * lineType;
                                else if(lineType === LineType.next)
                                    optSecond.offsetEnd -= 2 * lineType
                        }
                        else if(diff > 0)
                        {
                            var diff2 = compareLine(current,neighborLine);
                            if(diff2 === 0)
                            {
                                if(lineType === LineType.next)
                                    optSecond.offsetEnd -= lineType
                            }
                            else if(diff2 > 0)
                                if(lineType === LineType.previous)
                                    optSecond.offsetStart -= 2 * lineType;
                                else if(lineType === LineType.next)
                                    optSecond.offsetEnd -= 2 * lineType
                        }
                        else
                        {
                            var diff3 = compareLine(current,neighborLine);
                            if(diff3 === 0)
                            {
                                if(lineType === LineType.previous)
                                    optFirst.offsetStart += 2 * lineType;
                                else if(lineType === LineType.next)
                                    optFirst.offsetEnd += 2 * lineType
                            }
                            else if(diff3 > 0)
                                if(lineType === LineType.previous)
                                    optFirst.offsetStart -= 3 * lineType;
                                else if(lineType === LineType.next)
                                    optFirst.offsetEnd -= 3 * lineType
                        }
                    }
                    var otherType = type === 1 ? 2 : 1;
                    var otherBreakLine = otherType === 1 ? break1 : break2;
                    var needMakeupCorner = false;
                    var thickness = 0;
                    if(!needMakeupCorner && isDoubleLineItem(otherBreakLine) && compareLine(otherBreakLine,current) > 0)
                    {
                        needMakeupCorner = true;
                        var drawThickness = getDrawingThickness(otherBreakLine);
                        if(compareLine(otherBreakLine,breakLine) > 0)
                        {
                            if(drawThickness > 0)
                                if(lineType === LineType.next && isDoubleLineItem(nextLine))
                                    thickness = drawThickness >= 2 ? 2 : 1;
                                else if(lineType === LineType.previous && isDoubleLineItem(prevLine))
                                    thickness = drawThickness >= 3 ? 2 : 1;
                                else
                                    thickness = drawThickness === 3 ? 3 : 2
                        }
                        else
                            thickness = drawThickness === 3 ? 3 : 2
                    }
                    if(!needMakeupCorner && !isDoubleLineItem(neighborLine) && (neighborLine && !neighborLine.isGridLine || otherBreakLine && !otherBreakLine.isGridLine))
                    {
                        needMakeupCorner = true;
                        if(lineType === LineType.previous)
                            thickness = 2;
                        else
                            thickness = 1
                    }
                    if(needMakeupCorner)
                        if(lineType === LineType.previous)
                        {
                            optFirst.offsetStart += lineType * thickness;
                            optSecond.offsetStart += lineType * thickness
                        }
                        else if(lineType === LineType.next)
                        {
                            optFirst.offsetEnd += lineType * thickness;
                            optSecond.offsetEnd += lineType * thickness
                        }
                    if(optFirst.offsetStart || optFirst.offsetEnd)
                        adjustLine(current,optFirst);
                    if(optSecond.offsetStart || optSecond.offsetEnd)
                        adjustLine(current,optSecond)
                };
            var layout4TRoad = function(current, line, break1, break2, lineType, vertical)
                {
                    if(compareLine(current,break1) >= 0 && compareLine(current,break2) >= 0)
                    {
                        var opt = {
                                orientation: vertical ? 1 : 0,
                                offsetStart: 0,
                                offsetEnd: 0
                            };
                        if(lineType === LineType.previous)
                        {
                            opt.offsetStart -= 2 * lineType;
                            if(isDoubleLineItem(current) && compareLine(line,current) > 0)
                                opt.offsetStart -= 1
                        }
                        else if(lineType === LineType.next)
                        {
                            opt.offsetEnd -= 2 * lineType;
                            if(isDoubleLineItem(current) && compareLine(line,current) > 0)
                                opt.offsetEnd += 1
                        }
                        adjustLine(current,opt)
                    }
                };
            var layout4Connected = function(current, line, break1, break2, lineType, vertical)
                {
                    if(isDoubleLineItem(current))
                    {
                        var breakLine = getMaxLine(break1,break2);
                        if(breakLine && !breakLine.isGridLine)
                        {
                            var drawThickness = getDrawingThickness(breakLine);
                            if(drawThickness > 0)
                            {
                                var opt = {
                                        orientation: vertical ? 1 : 0,
                                        offsetStart: 0,
                                        offsetEnd: 0
                                    };
                                if(lineType === LineType.previous)
                                    opt.offsetStart += drawThickness * lineType;
                                else
                                    opt.offsetEnd += drawThickness * lineType;
                                if(opt.offsetStart || opt.offsetEnd)
                                    adjustLine(current,opt)
                            }
                        }
                    }
                };
            return{
                    calcLayoutHorizontal: function(current, prevLine, prevBreak1, prevBreak2, nextLine, nextBreak1, nextBreak2)
                    {
                        var drawThickness;
                        var diff;
                        if(prevLine || prevBreak1 || prevBreak2)
                        {
                            var prevBreak = getMaxLine(prevBreak1,prevBreak2);
                            if(!isAllowExtend(current,prevLine,prevBreak1,prevBreak2))
                                adjustLine(current,{
                                    orientation: 0,
                                    offsetStart: 1
                                });
                            else if((diff = compareLine(prevBreak,current)) > 0)
                            {
                                drawThickness = getDrawingThickness(prevBreak);
                                if(!(isDoubleLineItem(current) && (isDoubleLineItem(prevBreak1) || isDoubleLineItem(prevBreak2) || isDoubleLineItem(prevLine))))
                                    if(drawThickness === 3)
                                        adjustLine(current,{
                                            orientation: 0,
                                            offsetStart: 1
                                        })
                            }
                            else if(prevBreak && diff < 0)
                            {
                                if((diff = compareLine(current,prevLine)) > 0)
                                {
                                    drawThickness = getDrawingThickness(prevBreak);
                                    if(drawThickness === 3 || drawThickness === 2)
                                        adjustLine(current,{
                                            orientation: 0,
                                            offsetStart: -2
                                        });
                                    else if(drawThickness === 1)
                                        adjustLine(current,{
                                            orientation: 0,
                                            offsetStart: -1
                                        })
                                }
                                else if(diff !== 0)
                                    if(compareLine(prevLine,prevBreak) > 0)
                                    {
                                        drawThickness = getDrawingThickness(prevBreak);
                                        if(drawThickness === 3)
                                            adjustLine(current,{
                                                orientation: 0,
                                                offsetStart: 1
                                            })
                                    }
                            }
                            else if(diff === 0)
                                if(!prevLine || compareLine(current,prevLine) > 0)
                                {
                                    drawThickness = getDrawingThickness(prevBreak);
                                    if(drawThickness === 3 || drawThickness === 2)
                                        adjustLine(current,{
                                            orientation: 0,
                                            offsetStart: -2
                                        });
                                    else if(drawThickness === 1)
                                        adjustLine(current,{
                                            orientation: 0,
                                            offsetStart: -1
                                        })
                                }
                        }
                        if(nextLine || nextBreak1 || nextBreak2)
                        {
                            var nextBreak = getMaxLine(nextBreak1,nextBreak2);
                            if(!isAllowExtend(current,nextLine,nextBreak1,nextBreak2))
                                adjustLine(current,{
                                    orientation: 0,
                                    offsetEnd: -2
                                });
                            else if((diff = compareLine(nextBreak,current)) > 0)
                            {
                                drawThickness = getDrawingThickness(nextBreak);
                                if(!(isDoubleLineItem(current) && (isDoubleLineItem(nextBreak1) || isDoubleLineItem(nextBreak2) || isDoubleLineItem(nextLine))))
                                    if(drawThickness === 3)
                                        adjustLine(current,{
                                            orientation: 0,
                                            offsetEnd: -2
                                        });
                                    else if(drawThickness === 2 || drawThickness === 1)
                                        adjustLine(current,{
                                            orientation: 0,
                                            offsetEnd: -1
                                        })
                            }
                            else if(diff < 0)
                            {
                                if((diff = compareLine(current,nextLine)) > 0)
                                {
                                    drawThickness = getDrawingThickness(nextBreak);
                                    if(drawThickness === 3)
                                        adjustLine(current,{
                                            orientation: 0,
                                            offsetEnd: 1
                                        })
                                }
                                else if(diff !== 0)
                                    adjustLine(current,{
                                        orientation: 0,
                                        offsetEnd: -1
                                    })
                            }
                            else if(diff === 0)
                                if((diff = compareLine(current,nextLine)) > 0)
                                {
                                    drawThickness = getDrawingThickness(nextBreak);
                                    if(drawThickness === 3)
                                        adjustLine(current,{
                                            orientation: 0,
                                            offsetEnd: 1
                                        })
                                }
                                else if(diff !== 0)
                                    adjustLine(current,{
                                        orientation: 0,
                                        offsetEnd: -1
                                    })
                        }
                    },
                    calcLayoutVertical: function(current, prevLine, prevBreak1, prevBreak2, nextLine, nextBreak1, nextBreak2)
                    {
                        var drawThickness;
                        var diff;
                        if(prevLine || prevBreak1 || prevBreak2)
                        {
                            var prevBreak = getMaxLine(prevBreak1,prevBreak2);
                            if(!isAllowExtend(current,prevLine,prevBreak1,prevBreak2))
                                adjustLine(current,{
                                    orientation: 1,
                                    offsetStart: 1
                                });
                            else if((diff = compareLine(prevBreak,current)) > 0)
                            {
                                drawThickness = getDrawingThickness(prevBreak);
                                if(!(isDoubleLineItem(current) && (isDoubleLineItem(prevBreak1) || isDoubleLineItem(prevBreak2) || isDoubleLineItem(prevLine))))
                                    if(drawThickness === 3)
                                        adjustLine(current,{
                                            orientation: 1,
                                            offsetStart: 1
                                        })
                            }
                            else if(diff < 0)
                            {
                                if((diff = compareLine(current,prevLine)) > 0)
                                {
                                    drawThickness = getDrawingThickness(prevBreak);
                                    if(drawThickness === 3 || drawThickness === 2)
                                        adjustLine(current,{
                                            orientation: 1,
                                            offsetStart: -2
                                        });
                                    else if(drawThickness === 1)
                                        adjustLine(current,{
                                            orientation: 1,
                                            offsetStart: -1
                                        })
                                }
                                else if(diff !== 0)
                                    if(compareLine(prevLine,prevBreak) > 0)
                                    {
                                        drawThickness = getDrawingThickness(prevBreak);
                                        if(drawThickness === 3)
                                            adjustLine(current,{
                                                orientation: 1,
                                                offsetStart: 1
                                            })
                                    }
                            }
                            else if(diff === 0)
                                if(compareLine(current,prevLine) > 0)
                                {
                                    drawThickness = getDrawingThickness(prevBreak);
                                    if(drawThickness === 3 || drawThickness === 2)
                                        adjustLine(current,{
                                            orientation: 1,
                                            offsetStart: -2
                                        });
                                    else if(drawThickness === 1)
                                        adjustLine(current,{
                                            orientation: 1,
                                            offsetStart: -1
                                        })
                                }
                        }
                        if(nextLine || nextBreak1 || nextBreak2)
                        {
                            var nextBreak = getMaxLine(nextBreak1,nextBreak2);
                            if(!isAllowExtend(current,nextLine,nextBreak1,nextBreak2))
                                adjustLine(current,{
                                    orientation: 1,
                                    offsetEnd: -2
                                });
                            else if((diff = compareLine(current,nextBreak)) < 0)
                            {
                                drawThickness = getDrawingThickness(nextBreak);
                                if(!(isDoubleLineItem(current) && (isDoubleLineItem(nextBreak1) || isDoubleLineItem(nextBreak2) || isDoubleLineItem(nextLine))))
                                    if(drawThickness === 3 || drawThickness === 2)
                                        adjustLine(current,{
                                            orientation: 1,
                                            offsetEnd: -2
                                        });
                                    else if(drawThickness === 1)
                                        adjustLine(current,{
                                            orientation: 1,
                                            offsetEnd: -1
                                        })
                            }
                            else if(nextBreak !== null && diff > 0)
                            {
                                if((diff = compareLine(current,nextLine)) > 0)
                                {
                                    drawThickness = getDrawingThickness(nextBreak);
                                    if(drawThickness === 3)
                                        adjustLine(current,{
                                            orientation: 1,
                                            offsetEnd: 1
                                        })
                                }
                                else if(diff !== 0)
                                    adjustLine(current,{
                                        orientation: 1,
                                        offsetEnd: -1
                                    })
                            }
                            else if(diff === 0)
                                if((diff = compareLine(current,nextLine)) > 0)
                                {
                                    drawThickness = getDrawingThickness(nextBreak);
                                    if(drawThickness === 3)
                                        adjustLine(current,{
                                            orientation: 1,
                                            offsetEnd: 1
                                        })
                                }
                                else if(diff !== 0)
                                    adjustLine(current,{
                                        orientation: 1,
                                        offsetEnd: -1
                                    })
                        }
                    },
                    calcDoubleLayout: function(current, prevLine, prevBreak1, prevBreak2, nextLine, nextBreak1, nextBreak2, vertical)
                    {
                        if(vertical)
                            this.calcLayoutVertical(current,prevLine,prevBreak1,prevBreak2,nextLine,nextBreak1,nextBreak2);
                        else
                            this.calcLayoutHorizontal(current,prevLine,prevBreak1,prevBreak2,nextLine,nextBreak1,nextBreak2);
                        var break1DoubleLine = isDoubleLineItem(prevBreak1),
                            break2DoubleLine = isDoubleLineItem(prevBreak2),
                            prevDoubleLine = isDoubleLineItem(prevLine);
                        if(break1DoubleLine && break2DoubleLine && prevDoubleLine)
                            layout4CrossRoad(current,prevLine,prevBreak1,prevBreak2,LineType.previous,vertical);
                        else if(break1DoubleLine && !break2DoubleLine)
                            layout4TurnRoad(current,prevLine,nextLine,prevBreak1,prevBreak2,1,LineType.previous,vertical,true);
                        else if(!break1DoubleLine && break2DoubleLine)
                            layout4TurnRoad(current,prevLine,nextLine,prevBreak1,prevBreak2,2,LineType.previous,vertical,false);
                        else if(break1DoubleLine && break2DoubleLine && !prevDoubleLine)
                            layout4TRoad(current,prevLine,prevBreak1,prevBreak2,LineType.previous,vertical);
                        else if(prevDoubleLine)
                            layout4Connected(current,prevLine,prevBreak1,prevBreak2,LineType.previous,vertical);
                        break1DoubleLine = isDoubleLineItem(nextBreak1);
                        break2DoubleLine = isDoubleLineItem(nextBreak2);
                        var nextDoubleLine = isDoubleLineItem(nextLine);
                        if(break1DoubleLine && break2DoubleLine && nextDoubleLine)
                            layout4CrossRoad(current,nextLine,nextBreak1,nextBreak2,LineType.next,vertical);
                        else if(break1DoubleLine && !break2DoubleLine)
                            layout4TurnRoad(current,prevLine,nextLine,nextBreak1,nextBreak2,1,LineType.next,vertical,true);
                        else if(!break1DoubleLine && break2DoubleLine)
                            layout4TurnRoad(current,prevLine,nextLine,nextBreak1,nextBreak2,2,LineType.next,vertical,false);
                        else if(break1DoubleLine && break2DoubleLine && !nextDoubleLine)
                            layout4TRoad(current,nextLine,nextBreak1,nextBreak2,LineType.next,vertical);
                        else if(nextDoubleLine)
                            layout4Connected(current,nextLine,nextBreak1,nextBreak2,LineType.next,vertical)
                    }
                }
        }();
    var DefaultLineLayout = {
            layoutHorizontal: function(line, prevLine, prevBreak1, prevBreak2, nextLine, nextBreak1, nextBreak2)
            {
                LayoutEngine.calcLayoutHorizontal(line,prevLine,prevBreak1,prevBreak2,nextLine,nextBreak1,nextBreak2)
            },
            layoutVertical: function(line, prevLine, prevBreak1, prevBreak2, nextLine, nextBreak1, nextBreak2)
            {
                LayoutEngine.calcLayoutVertical(line,prevLine,prevBreak1,prevBreak2,nextLine,nextBreak1,nextBreak2)
            }
        };
    var DoubleLineLayout = {
            layoutHorizontal: function(line, prevLine, prevBreak1, prevBreak2, nextLine, nextBreak1, nextBreak2)
            {
                LayoutEngine.calcDoubleLayout(line,prevLine,prevBreak1,prevBreak2,nextLine,nextBreak1,nextBreak2,false)
            },
            layoutVertical: function(line, prevLine, prevBreak1, prevBreak2, nextLine, nextBreak1, nextBreak2)
            {
                LayoutEngine.calcDoubleLayout(line,prevLine,prevBreak1,prevBreak2,nextLine,nextBreak1,nextBreak2,true)
            }
        };
    var SlantedLineLayout = {
            layoutHorizontal: function(line, prevLine, prevBreak1, prevBreak2, nextLine, nextBreak1, nextBreak2){},
            layoutVertical: function(line, prevLine, prevBreak1, prevBreak2, nextLine, nextBreak1, nextBreak2){}
        };
    GrapeCity.UI._GcBorders = function(sheet, rowViewportIndex, colViewportIndex, sheetArea)
    {
        this._sheet = sheet;
        this._sheetArea = sheetArea;
        this._rowViewportIndex = rowViewportIndex;
        this._colViewportIndex = colViewportIndex
    };
    GrapeCity.UI._GcBorders.prototype = {
        _isInitialized: false,
        _isMerged: false,
        _initialize: function()
        {
            this._spanCells = [];
            this._rowIndecies = [];
            this._colIndecies = [];
            this._overflowedCells = [];
            this._adjustingRanges = [];
            this._hGridLine = [];
            this._vGridLine = [];
            this._hBorders = [];
            this._vBorders = [];
            var leftCol = 0,
                rightCol = 0,
                topRow = 0,
                bottomRow = 0;
            if(this._sheetArea === GrapeCity.UI.SheetArea.viewport)
            {
                leftCol = this._sheet.getViewportLeftColumn(this._colViewportIndex);
                rightCol = this._sheet.getViewportRightColumn(this._colViewportIndex);
                topRow = this._sheet.getViewportTopRow(this._rowViewportIndex);
                bottomRow = this._sheet.getViewportBottomRow(this._rowViewportIndex)
            }
            else if(this._sheetArea === GrapeCity.UI.SheetArea.colHeader)
            {
                leftCol = this._sheet.getViewportLeftColumn(this._colViewportIndex);
                rightCol = this._sheet.getViewportRightColumn(this._colViewportIndex);
                bottomRow = this._sheet.getRowCount(this._sheetArea)
            }
            else if(this._sheetArea === GrapeCity.UI.SheetArea.rowHeader)
            {
                rightCol = this._sheet.getColumnCount(this._sheetArea);
                topRow = this._sheet.getViewportTopRow(this._rowViewportIndex);
                bottomRow = this._sheet.getViewportBottomRow(this._rowViewportIndex)
            }
            for(var r = topRow; r <= bottomRow; r++)
                if(this._sheet.getRowVisible(r,this._sheetArea))
                    this._rowIndecies.push(r);
            for(var c = leftCol; c <= rightCol; c++)
                if(this._sheet.getColumnVisible(c,this._sheetArea))
                    this._colIndecies.push(c);
            this._isInitialized = true
        },
        addCellLines: function(row, col, x, y, width, height, style, spanCellLayout, cellOverflowLayout)
        {
            if(!this._isInitialized)
                this._initialize();
            if(cellOverflowLayout)
            {
                var overflowedCell = {
                        row: row,
                        startCol: cellOverflowLayout.startCol,
                        endCol: cellOverflowLayout.endCol
                    };
                if(!this._overflowedCells.contains(overflowedCell))
                    this._overflowedCells.push(overflowedCell)
            }
            if(spanCellLayout)
                this._spanCells.push(spanCellLayout);
            else
            {
                var borderLeft,
                    borderTop,
                    borderRight,
                    borderBottom;
                var backColor;
                if(style)
                {
                    borderLeft = style.borderLeft;
                    borderTop = style.borderTop;
                    borderRight = style.borderRight;
                    borderBottom = style.borderBottom;
                    backColor = style.backColor;
                    if((backColor === undefined || backColor === null) && (style._backColor !== undefined || style._backColor !== null))
                        backColor = style._backColor
                }
                if(backColor && !borderLeft && !borderRight && !borderTop && !borderBottom)
                    this._adjustingRanges.push({
                        r: row,
                        c: col,
                        rc: 1,
                        cc: 1
                    });
                this._addCellLineImp(row,col,x,y,width,height,borderLeft,borderTop,borderRight,borderBottom,backColor)
            }
        },
        _addCellLineImp: function(row, col, x, y, width, height, borderLeft, borderTop, borderRight, borderBottom, backColor, noHGridLine, noVGridLine)
        {
            var addTopLineSucceeded = this._addCellLineSide(row,col,x,y - 0.5,x + width,y - 0.5,borderTop,this._hBorders);
            var addLeftLineSucceeded = this._addCellLineSide(row,col,x - 0.5,y,x - 0.5,y + height,borderLeft,this._vBorders);
            var addBottomLineSucceeded = this._addCellLineSide(row + 1,col,x,y + height - 0.5,x + width,y + height - 0.5,borderBottom,this._hBorders);
            var addRightLineSucceeded = this._addCellLineSide(row,col + 1,x + width - 0.5,y,x + width - 0.5,y + height,borderRight,this._vBorders);
            if(!backColor || this._sheet.getRowHeight(row) === 0 || this._sheet.getColumnWidth(col) === 0)
            {
                var needRenderHGridLine = !addBottomLineSucceeded && !noHGridLine,
                    needRenderVGridLine = !addRightLineSucceeded && !noVGridLine;
                if(needRenderHGridLine || needRenderVGridLine)
                    this._addGridLine(row,col,x,y,width,height,needRenderHGridLine,needRenderVGridLine)
            }
            if(addTopLineSucceeded || backColor !== undefined)
                this._removeLineItem(row - 1,col,this._hGridLine);
            if(addLeftLineSucceeded || backColor !== undefined)
                this._removeLineItem(row,col - 1,this._vGridLine);
            if(backColor && !borderLeft && !borderRight && !borderTop && !borderBottom)
            {
                this._adjustLineItem(row - 1,col - 1,this._vGridLine,false);
                this._adjustLineItem(row - 1,col - 1,this._hGridLine,true)
            }
        },
        _addCellLineSide: function(row, col, x, y, width, height, lineStyle, store)
        {
            if(lineStyle)
            {
                var borderLine;
                var query = this._queryLineItem(row,col,store,true);
                if(query.success)
                {
                    borderLine = query.lineItem;
                    if(borderLine.style.style !== LineStyle.double)
                        if(lineStyle.style === LineStyle.double || compareLineStyle(lineStyle,borderLine.style) > 0)
                        {
                            borderLine.line = Line.Create(x,y,width,height,lineStyle.color,lineStyle.style);
                            borderLine.style = lineStyle
                        }
                }
                else
                {
                    borderLine = this._queryLineItem(row,col,store).lineItem;
                    borderLine.line = Line.Create(x,y,width,height,lineStyle.color,lineStyle.style);
                    borderLine.style = lineStyle
                }
                return borderLine.line !== null
            }
            return false
        },
        _addGridLine: function(row, col, x, y, width, height, hLine, vLine)
        {
            var atViewport = this._sheetArea === GrapeCity.UI.SheetArea.viewport;
            var gridLine = this._sheet.gridline;
            if(!atViewport || gridLine)
            {
                var gridLineColor = !atViewport ? "#9EB6CE" : gridLine.color;
                if(hLine && (gridLine.showHorizontalGridline || !atViewport))
                {
                    var hGridLineItem = this._queryLineItem(row,col,this._hGridLine).lineItem;
                    hGridLineItem.isGridLine = true;
                    hGridLineItem.line = Line.Create(x,y + height - 0.5,x + width,y + height - 0.5,gridLineColor)
                }
                if(vLine && (gridLine.showVerticalGridline || !atViewport))
                {
                    var vGridLineItem = this._queryLineItem(row,col,this._vGridLine).lineItem;
                    vGridLineItem.isGridLine = true;
                    vGridLineItem.line = Line.Create(x + width - 0.5,y,x + width - 0.5,y + height,gridLineColor)
                }
            }
        },
        _adjustLineItem: function(r, c, store, h)
        {
            var query = this._queryLineItem(r,c,store,true);
            if(query.success)
            {
                var lineItem = query.lineItem;
                if(lineItem && lineItem.line)
                    lineItem.line.adjust({
                        orientation: h ? 0 : 1,
                        offsetEnd: -1
                    })
            }
        },
        _removeLineItem: function(r, c, store)
        {
            if(r >= 0 && c >= 0)
            {
                var firstDim = store[r];
                if(firstDim)
                {
                    var secondDim = firstDim[c];
                    if(secondDim)
                        firstDim[c] = null
                }
            }
        },
        _queryLineItem: function(r, c, store, noCreate)
        {
            if(r === -1 || c === -1)
                return{success: false};
            var firstDim = store[r];
            if(!firstDim)
            {
                if(noCreate)
                    return{success: false};
                store[r] = firstDim = []
            }
            var secondDim = firstDim[c];
            if(!secondDim)
            {
                if(noCreate)
                    return{success: false};
                firstDim[c] = secondDim = {}
            }
            return{
                    success: true,
                    lineItem: secondDim
                }
        },
        _processOverflowCells: function()
        {
            for(var i = 0; i < this._overflowedCells.length; i++)
            {
                var overflowCell = this._overflowedCells[i];
                var r = overflowCell.row,
                    sc = overflowCell.startCol,
                    ec = overflowCell.endCol;
                var query;
                for(var c = sc; c < ec; c++)
                {
                    query = this._queryLineItem(r,c + 1,this._vBorders,true);
                    if(query.success)
                        this._removeLineItem(r,c + 1,this._vBorders);
                    else
                    {
                        query = this._queryLineItem(r,c,this._vGridLine,true);
                        if(query.success)
                            this._removeLineItem(r,c,this._vGridLine)
                    }
                }
            }
        },
        _processSpans: function()
        {
            if(this._spanCells.length > 0)
                for(var i = 0; i < this._spanCells.length; i++)
                {
                    var spanLayout = this._spanCells[i];
                    this._processSpanCell(spanLayout)
                }
        },
        _processSpanCell: function(spanCellLayout)
        {
            var row = spanCellLayout.row,
                col = spanCellLayout.col;
            var x = spanCellLayout.x,
                y = spanCellLayout.y;
            var borderLeft,
                borderTop,
                borderRight,
                borderBottom;
            var spanCellStyle = this._sheet.getActualStyle(row,col,this._sheetArea);
            var backColor = spanCellStyle ? spanCellStyle.backColor : null;
            var i,
                r,
                c,
                rowHeight,
                colWidth,
                firstCol,
                firstRow,
                lastCol,
                lastRow,
                cellStyle;
            var ix = x,
                iy = y;
            if(spanCellLayout.rowCount === 1)
            {
                r = row;
                rowHeight = this._sheet._getZoomRowHeight(r,this._sheetArea);
                for(i = 0; i < spanCellLayout.colCount; i++)
                {
                    firstCol = i === 0;
                    lastCol = i === spanCellLayout.colCount - 1;
                    c = col + i;
                    colWidth = this._sheet._getZoomColumnWidth(c,this._sheetArea);
                    cellStyle = this._sheet.getActualStyle(r,c,this._sheetArea);
                    if(cellStyle)
                    {
                        borderLeft = cellStyle.borderLeft;
                        borderTop = cellStyle.borderTop;
                        borderRight = cellStyle.borderRight;
                        borderBottom = cellStyle.borderBottom
                    }
                    else
                        borderLeft = borderTop = borderRight = borderBottom = null;
                    if(firstCol)
                        this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,borderLeft,borderTop,null,borderBottom,backColor,false,true);
                    else if(lastCol)
                        this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,null,borderTop,borderRight,borderBottom,backColor);
                    else
                        this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,null,borderTop,null,borderBottom,backColor,false,true);
                    ix += colWidth
                }
            }
            else if(spanCellLayout.colCount === 1)
            {
                c = col;
                colWidth = this._sheet._getZoomColumnWidth(c,this._sheetArea);
                for(i = 0; i < spanCellLayout.rowCount; i++)
                {
                    firstRow = i === 0;
                    lastRow = i === spanCellLayout.rowCount - 1;
                    r = row + i;
                    rowHeight = this._sheet._getZoomRowHeight(r,this._sheetArea);
                    cellStyle = this._sheet.getActualStyle(r,c,this._sheetArea);
                    if(cellStyle)
                    {
                        borderLeft = cellStyle.borderLeft;
                        borderTop = cellStyle.borderTop;
                        borderRight = cellStyle.borderRight;
                        borderBottom = cellStyle.borderBottom
                    }
                    else
                        borderLeft = borderTop = borderRight = borderBottom = null;
                    if(firstRow)
                        this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,borderLeft,borderTop,borderRight,null,backColor,true,false);
                    else if(lastRow)
                        this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,borderLeft,null,borderRight,borderBottom,backColor);
                    else
                        this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,borderLeft,null,borderRight,null,backColor,true,false);
                    iy += rowHeight
                }
            }
            else
                for(i = 0; i < spanCellLayout.rowCount; i++)
                {
                    r = row + i;
                    firstRow = i === 0;
                    lastRow = i === spanCellLayout.rowCount - 1;
                    rowHeight = this._sheet._getZoomRowHeight(r,this._sheetArea);
                    for(var j = 0; j < spanCellLayout.colCount; j++)
                    {
                        c = col + j;
                        firstCol = j === 0;
                        lastCol = j === spanCellLayout.colCount - 1;
                        colWidth = this._sheet._getZoomColumnWidth(c,this._sheetArea);
                        cellStyle = this._sheet.getActualStyle(r,c,this._sheetArea);
                        if(cellStyle)
                        {
                            borderLeft = cellStyle.borderLeft;
                            borderTop = cellStyle.borderTop;
                            borderRight = cellStyle.borderRight;
                            borderBottom = cellStyle.borderBottom
                        }
                        else
                            borderLeft = borderTop = borderRight = borderBottom = null;
                        if(firstRow)
                            if(firstCol)
                                this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,borderLeft,borderTop,null,null,backColor,true,true);
                            else if(lastCol)
                                this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,null,borderTop,borderRight,null,backColor,true,false);
                            else
                                this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,null,borderTop,null,null,backColor,true,true);
                        else if(lastRow)
                            if(firstCol)
                                this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,borderLeft,null,null,borderBottom,backColor,false,true);
                            else if(lastCol)
                                this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,null,null,borderRight,borderBottom,backColor);
                            else
                                this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,null,null,null,borderBottom,backColor,false,true);
                        else if(firstCol)
                            this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,borderLeft,null,null,null,backColor,true,true);
                        else if(lastCol)
                            this._addCellLineImp(r,c,ix,iy,colWidth,rowHeight,null,null,borderRight,null,backColor,true,false);
                        ix += colWidth
                    }
                    ix = x;
                    iy += rowHeight
                }
            if(backColor && !borderLeft && !borderRight && !borderTop && !borderBottom)
                this._adjustingRanges.push({
                    r: row,
                    c: col,
                    rc: spanCellLayout.rowCount,
                    cc: spanCellLayout.colCount
                })
        },
        _adjust: function()
        {
            if(this._vBorders.length > 0 || this._hBorders.length > 0)
                this._adjustBorders();
            if(this._adjustingRanges.length > 0)
                this._adjustGridlines()
        },
        _adjustGridlines: function()
        {
            for(var i = 0; i < this._adjustingRanges.length; i++)
            {
                var range = this._adjustingRanges[i];
                var r = range.r,
                    c = range.c;
                var ar1 = r,
                    ar2 = r - 1;
                var ac1 = c - 1,
                    ac2 = c;
                if(range.rc > 1)
                    ar1 += range.rc - 1;
                if(range.cc > 1)
                    ac2 += range.cc - 1;
                var skipLeftBottom = false,
                    skipRightTop = false;
                for(var j = 0; j < this._adjustingRanges.length; j++)
                {
                    var next = this._adjustingRanges[j];
                    if(next.c === ac2 + 1 && next.r === r)
                        skipRightTop = true;
                    if(next.r === ar1 + 1 && next.c === c)
                        skipLeftBottom = true;
                    if(skipLeftBottom && skipRightTop)
                        break;
                    if(next.r > r + 1 && next.c > c + 1)
                        break
                }
                if(!skipLeftBottom)
                    this._adjustLineItem(ar1,ac1,this._hGridLine,true);
                if(!skipRightTop)
                    this._adjustLineItem(ar2,ac2,this._vGridLine,false)
            }
        },
        _adjustBorders: function()
        {
            var r,
                c,
                row,
                col;
            for(r = 0; r <= this._rowIndecies.length; r++)
            {
                row = this._rowIndecies[r];
                if(row === undefined && r === this._rowIndecies.length)
                    row = this._rowIndecies[r - 1] + 1;
                for(c = 0; c <= this._colIndecies.length; c++)
                {
                    col = this._colIndecies[c];
                    if(col === undefined && c === this._colIndecies.length)
                        col = this._colIndecies[c - 1] + 1;
                    this._adjustHorizontalBorderLine(r,c,row,col)
                }
            }
            for(c = 0; c <= this._colIndecies.length; c++)
            {
                col = this._colIndecies[c];
                if(col === undefined && c === this._colIndecies.length)
                    col = this._colIndecies[c - 1] + 1;
                for(r = 0; r <= this._rowIndecies.length; r++)
                {
                    row = this._rowIndecies[r];
                    if(row === undefined && r === this._rowIndecies.length)
                        row = this._rowIndecies[r - 1] + 1;
                    this._adjustVerticalBorderLine(r,c,row,col)
                }
            }
        },
        _adjustHorizontalBorderLine: function(r, c, row, col)
        {
            var line,
                prevLine,
                prevBreak1,
                prevBreak2,
                nextLine,
                nextBreak1,
                nextBreak2;
            var lineQuery = this._queryLineItem(row,col,this._hBorders,true);
            var borderLineCount = 0;
            if(!lineQuery.success)
            {
                lineQuery = this._queryLineItem(row - 1,col,this._hGridLine,true);
                if(!lineQuery.success)
                    return
            }
            else
                borderLineCount++;
            line = lineQuery.lineItem;
            prevLine = this._getLineItemImp(row,this._prevCol(c),false);
            if(this._isBorderLineInLayout(prevLine))
                borderLineCount++;
            prevBreak1 = this._getLineItemImp(this._prevRow(r),col,true);
            if(this._isBorderLineInLayout(prevBreak1))
                borderLineCount++;
            prevBreak2 = this._getLineItemImp(row,col,true);
            if(this._isBorderLineInLayout(prevBreak2))
                borderLineCount++;
            nextLine = this._getLineItemImp(row,this._nextCol(c),false);
            if(this._isBorderLineInLayout(nextLine))
                borderLineCount++;
            nextBreak1 = this._getLineItemImp(this._prevRow(r),this._nextCol(c),true);
            if(this._isBorderLineInLayout(nextBreak1))
                borderLineCount++;
            nextBreak2 = this._getLineItemImp(row,this._nextCol(c),true);
            if(this._isBorderLineInLayout(nextBreak2))
                borderLineCount++;
            if(borderLineCount > 0)
                if(isDoubleLine(line.style))
                    DoubleLineLayout.layoutHorizontal(line,prevLine,prevBreak1,prevBreak2,nextLine,nextBreak1,nextBreak2);
                else if(isSlantedLine(line.style))
                    SlantedLineLayout.layoutHorizontal(line,prevLine,prevBreak1,prevBreak2,nextLine,nextBreak1,nextBreak2);
                else
                    DefaultLineLayout.layoutHorizontal(line,prevLine,prevBreak1,prevBreak2,nextLine,nextBreak1,nextBreak2)
        },
        _adjustVerticalBorderLine: function(r, c, row, col)
        {
            var line,
                prevLine,
                prevBreak1,
                prevBreak2,
                nextLine,
                nextBreak1,
                nextBreak2;
            var lineQuery = this._queryLineItem(row,col,this._vBorders,true);
            var borderLineCount = 0;
            if(!lineQuery.success)
            {
                lineQuery = this._queryLineItem(row,col - 1,this._vGridLine,true);
                if(!lineQuery.success)
                    return
            }
            else
                borderLineCount++;
            line = lineQuery.lineItem;
            prevLine = this._getLineItemImp(this._prevRow(r),col,true);
            if(this._isBorderLineInLayout(prevLine))
                borderLineCount++;
            prevBreak1 = this._getLineItemImp(row,this._prevCol(c),false);
            if(this._isBorderLineInLayout(prevBreak1))
                borderLineCount++;
            prevBreak2 = this._getLineItemImp(row,col,false);
            if(this._isBorderLineInLayout(prevBreak2))
                borderLineCount++;
            nextLine = this._getLineItemImp(this._nextRow(r),col,true);
            if(this._isBorderLineInLayout(nextLine))
                borderLineCount++;
            nextBreak1 = this._getLineItemImp(this._nextRow(r),this._prevCol(c),false);
            if(this._isBorderLineInLayout(nextBreak1))
                borderLineCount++;
            nextBreak2 = this._getLineItemImp(this._nextRow(r),col,false);
            if(this._isBorderLineInLayout(nextBreak2))
                borderLineCount++;
            if(borderLineCount > 0)
                if(isDoubleLine(line.style))
                    DoubleLineLayout.layoutVertical(line,prevLine,prevBreak1,prevBreak2,nextLine,nextBreak1,nextBreak2);
                else if(isSlantedLine(line.style))
                    SlantedLineLayout.layoutVertical(line,prevLine,prevBreak1,prevBreak2,nextLine,nextBreak1,nextBreak2);
                else
                    DefaultLineLayout.layoutVertical(line,prevLine,prevBreak1,prevBreak2,nextLine,nextBreak1,nextBreak2)
        },
        _isBorderLineInLayout: function(line)
        {
            return line && !line.isGridLine && line.line
        },
        _prevCol: function(c)
        {
            if(c < 0)
                return-1;
            return this._colIndecies[c - 1]
        },
        _nextCol: function(c)
        {
            var col = this._colIndecies[c + 1];
            if(col === undefined && c + 1 >= this._colIndecies.length)
                return this._colIndecies[this._colIndecies.length - 1] + 1;
            return col
        },
        _prevRow: function(r)
        {
            if(r < 0)
                return-1;
            return this._rowIndecies[r - 1]
        },
        _nextRow: function(r)
        {
            var row = this._rowIndecies[r + 1];
            if(row === undefined && r + 1 >= this._rowIndecies.length)
                return this._rowIndecies[this._rowIndecies.length - 1] + 1;
            return row
        },
        _getLineItemImp: function(r, c, vertical)
        {
            if(r === undefined || r < 0 || c === undefined || c < 0)
                return null;
            var store = vertical ? this._vBorders : this._hBorders;
            var query = this._queryLineItem(r,c,store,true);
            if(query.success)
                return query.lineItem;
            store = vertical ? this._vGridLine : this._hGridLine;
            if(vertical)
            {
                c--;
                if(c < 0)
                    return null
            }
            else
            {
                r--;
                if(r < 0)
                    return null
            }
            query = this._queryLineItem(r,c,store,true);
            if(query.success)
                return query.lineItem;
            return null
        },
        paint: function(context, clipRect)
        {
            if(!this._isInitialized)
                return;
            if(!this._isMerged)
            {
                this._processSpans();
                this._processOverflowCells();
                this._adjust();
                this._isMerged = true
            }
            context.save();
            context.beginPath();
            this._paint(context,clipRect);
            context.closePath();
            context.stroke();
            context.restore()
        },
        _paint: function(context, clipRect)
        {
            this._paintGridLine(context);
            this._paintBorderLines(context)
        },
        _paintGridLine: function(context)
        {
            this._paintLines(context,this._hGridLine);
            this._paintLines(context,this._vGridLine)
        },
        _paintBorderLines: function(context)
        {
            this._paintLines(context,this._hBorders);
            this._paintLines(context,this._vBorders)
        },
        _paintLines: function(context, store)
        {
            if(store && store.length > 0)
                for(var i in store)
                    if(i)
                    {
                        var firstDim = store[i];
                        if(firstDim && firstDim.length > 0)
                            for(var j in firstDim)
                                if(j)
                                {
                                    var secondDim = firstDim[j];
                                    if(secondDim && secondDim.line)
                                        secondDim.line.paint(context)
                                }
                    }
        }
    }
})(window);
(function(window, $)
{
    var GrapeCity = window.GrapeCity;
    var const_undefined = "undefined";
    if(typeof GrapeCity === const_undefined)
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === const_undefined)
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    var document = window.document;
    GrapeCity.UI._SheetEventHandler = function(sheet)
    {
        this._init(sheet)
    };
    GrapeCity.UI._SheetEventHandler.prototype = {
        _init: function(sheet)
        {
            this._sheet = sheet;
            this._elem = null;
            this._eventSuspended = 0
        },
        _getElem: function()
        {
            if(!this._elem)
                this._elem = document.createElement("input");
            return this._elem
        },
        _dispose: function()
        {
            if(this._hScrollTimer)
                this._hScrollTimer._dispose();
            if(this._vScrollTimer)
                this._vScrollTimer._dispose()
        },
        _getHScrollTimer: function()
        {
            if(!this._hScrollTimer)
                this._hScrollTimer = new GrapeCity.UI.Timer(this._sheet);
            return this._hScrollTimer
        },
        _getVScrollTimer: function()
        {
            if(!this._vScrollTimer)
                this._vScrollTimer = new GrapeCity.UI.Timer(this._sheet);
            return this._vScrollTimer
        },
        doResize: function()
        {
            var sheet = this._sheet;
            var canvas = sheet._getCanvas();
            if(!canvas || !canvas.parentNode)
                return;
            if(canvas.parentNode.clientWidth === 0 || canvas.parentNode.clientHeight === 0)
                return;
            var deltx = 0;
            var delty = 0;
            canvas.style.display = "none";
            canvas.width = Math.max(canvas.parentNode.clientWidth - deltx,0);
            canvas.height = Math.max(canvas.parentNode.clientHeight - delty,0);
            canvas.style.display = "";
            sheet._bounds.width = canvas.clientWidth || canvas.width;
            sheet._bounds.height = canvas.clientHeight || canvas.height;
            sheet.invalidateLayout();
            sheet.repaint()
        },
        _getCanvasOffset: function()
        {
            var sheet = this._sheet;
            var t = $(sheet._getCanvas()).offset();
            if(t)
            {
                if(!isNaN(document.body.clientTop))
                    t.top += document.body.clientTop;
                if(!isNaN(document.body.clientLeft))
                    t.left += document.body.clientLeft
            }
            else
                t = {
                    top: 0,
                    left: 0
                };
            return t
        },
        doMouseDown: function(e)
        {
            if(e.button === 2)
                return;
            var sheet = this._sheet;
            if(!sheet.inCanvas)
                this.handleDocumentMouseMove();
            var t = this._getCanvasOffset();
            var ret = this.doMouseDownImp(e,e.pageX - t.left,e.pageY - t.top);
            if(!sheet.isEditing() && !ret)
                this._setFocus();
            sheet._isMouseDownInSheet = true;
            return false
        },
        handleDocumentMouseMove: function()
        {
            var self = this;
            if(!this._isMouseCapture)
            {
                $(document).bind("mousemove.gcSheet",function(e)
                {
                    self.doMouseMove(e)
                });
                $(document).bind("mouseup.gcSheet",function(e)
                {
                    self.doMouseUp(e)
                });
                this._isMouseCapture = true
            }
        },
        unhandleDocumentMouseMove: function()
        {
            if(this._isMouseCapture)
            {
                this._isMouseCapture = false;
                $(document).unbind("mousemove.gcSheet");
                $(document).unbind("mouseup.gcSheet")
            }
        },
        doMouseDownImp: function(e, x, y)
        {
            var sheet = this._sheet;
            var target = sheet.hitTest(x,y);
            var hitInfo = target.groupHitInfo;
            if(hitInfo)
            {
                if(!sheet.isEditing())
                    this._doClickRangeGroup(hitInfo)
            }
            else
            {
                sheet._currentTarget = target;
                this.isMouseDown = true;
                if(target.resizeInfo)
                {
                    sheet.endEdit();
                    this.isWorking = true
                }
                else if(target.dragInfo && target.dragInfo.side && target.dragInfo.side !== "corner")
                {
                    if(!sheet.endEdit())
                        return;
                    this.startDragDropping(e,x,y,target)
                }
                else if(target.dragInfo && target.dragInfo.side === "corner")
                {
                    if(!sheet.endEdit())
                        return;
                    this.startDragFill(e,x,y,target)
                }
                else if(target.filterButtonHitInfo)
                {
                    if(!sheet.endEdit())
                        return;
                    this._openFilterDialog(target.filterButtonHitInfo)
                }
                else
                {
                    var oldActiveRow = sheet.getActiveRowIndex();
                    var oldActiveCol = sheet.getActiveColumnIndex();
                    if(target.cellTypeHitInfo)
                    {
                        var r = target.row,
                            c = target.col;
                        if(r !== oldActiveRow || c !== oldActiveCol)
                        {
                            var oldState = sheet.isPaintSuspended();
                            sheet.isPaintSuspended(true);
                            try
                            {
                                if(!sheet.endEdit())
                                    return;
                                var args = {
                                        sheet: sheet,
                                        sheetName: sheet._name,
                                        row: oldActiveRow,
                                        col: oldActiveCol,
                                        cancel: false
                                    };
                                sheet._trigger(GrapeCity.UI.Events.LeaveCell,args);
                                if(args && args.cancel === true)
                                    return;
                                var oldSels = sheet._selectionModel.toArray();
                                var newSels = null;
                                var span = sheet._spanModel.find(r,c);
                                if(span)
                                    newSels = [new UI.Range(span.row,span.col,span.rowCount,span.colCount)];
                                else
                                    newSels = [new UI.Range(r,c,1,1)];
                                sheet._trigger(GrapeCity.UI.Events.SelectionChanging,{
                                    sheet: sheet,
                                    sheetName: sheet._name,
                                    oldSelections: oldSels,
                                    newSelections: newSels
                                });
                                sheet.setActiveCell(r,c);
                                sheet._trigger(GrapeCity.UI.Events.EnterCell,{
                                    sheet: sheet,
                                    sheetName: sheet._name,
                                    row: r,
                                    col: c
                                });
                                this._lastSelections = newSels;
                                sheet._trigger(GrapeCity.UI.Events.SelectionChanged,{
                                    sheet: sheet,
                                    sheetName: sheet._name
                                });
                                this._updateValidationUI(r,c)
                            }
                            finally
                            {
                                sheet.isPaintSuspended(oldState)
                            }
                        }
                        var ct = sheet.getCellType(r,c,target.hitTestType);
                        target.cellTypeHitInfo.sheet = sheet;
                        ct.processMouseDown(target.cellTypeHitInfo)
                    }
                    if(target.cellTypeHitInfo && target.cellTypeHitInfo.isReservedLocation)
                        return true;
                    else
                    {
                        this.setMetaKeyState(e);
                        try
                        {
                            this._hitTestResult = target;
                            if(sheet.isEditing() && oldActiveRow === sheet.getActiveRowIndex() && oldActiveCol === sheet.getActiveColumnIndex() && !sheet.endEdit())
                                return
                        }
                        finally
                        {
                            this._hitTestResult = null
                        }
                        if(target.row === undefined || target.row === null || target.col === undefined || target.col === null)
                            return;
                        if(target.hitTestType === GrapeCity.UI.SheetArea.viewport)
                            this._updateValidationUI(target.row,target.col);
                        var oldSelections = sheet._selectionModel.toArray();
                        this.startSelecting(e,x,y,target);
                        var newSelections = sheet._selectionModel.toArray();
                        if(this._notEqualSelecions(oldSelections,newSelections))
                            sheet._trigger(GrapeCity.UI.Events.SelectionChanging,{
                                sheet: sheet,
                                sheetName: sheet._name,
                                oldSelections: oldSelections,
                                newSelections: newSelections
                            })
                    }
                }
            }
        },
        _openFilterDialog: function(filterButtonInfo)
        {
            if(!filterButtonInfo)
                return;
            this._sheet._filterDialiog = new GrapeCity.UI._GcFilterDialog(this._sheet,filterButtonInfo);
            this._sheet._filterDialiog.open()
        },
        _doClickRangeGroup: function(hitInfo)
        {
            var sheet = this._sheet;
            if(!sheet || !hitInfo)
                return;
            var level,
                args,
                action,
                index,
                isExpanded;
            if(hitInfo.what === "rgh")
            {
                if(sheet && !sheet.isEditing())
                {
                    level = hitInfo.info.index;
                    args = {
                        sheet: sheet,
                        sheetName: sheet._name,
                        isRowGroup: true,
                        index: -1,
                        level: level,
                        cancel: false
                    };
                    sheet._trigger(GrapeCity.UI.Events.RangeGroupStateChanging,args);
                    if(args && args.cancel === false)
                    {
                        action = new GrapeCity.UI.UndoRedo.RowGroupHeaderExpandUndoAction(sheet,{level: level});
                        sheet._doCommand(action);
                        sheet._trigger(GrapeCity.UI.Events.RangeGroupStateChanged,{
                            sheet: sheet,
                            sheetName: sheet._name,
                            isRowGroup: true,
                            index: -1,
                            level: level
                        })
                    }
                }
            }
            else if(hitInfo.what === "cgh")
            {
                if(!sheet.isEditing())
                {
                    level = hitInfo.info.index;
                    args = {
                        sheet: sheet,
                        sheetName: sheet._name,
                        isRowGroup: false,
                        index: -1,
                        level: level,
                        cancel: false
                    };
                    sheet._trigger(GrapeCity.UI.Events.RangeGroupStateChanging,args);
                    if(args && args.cancel === false)
                    {
                        action = new GrapeCity.UI.UndoRedo.ColumnGroupHeaderExpandUndoAction(sheet,{level: level});
                        sheet._doCommand(action);
                        sheet._trigger(GrapeCity.UI.Events.RangeGroupStateChanged,{
                            sheet: sheet,
                            sheetName: sheet._name,
                            isRowGroup: false,
                            index: -1,
                            level: level
                        })
                    }
                }
            }
            else if(hitInfo.what === "rg")
            {
                if(sheet.rowRangeGroup)
                {
                    index = hitInfo.info.index;
                    isExpanded = hitInfo.info.isExpanded;
                    if(hitInfo.info.lineDirection === GrapeCity.UI.RangeGroupDirection.Forward)
                        index--;
                    else
                        index++;
                    args = {
                        sheet: sheet,
                        sheetName: sheet._name,
                        isRowGroup: true,
                        index: index,
                        level: hitInfo.info.level,
                        cancel: false
                    };
                    sheet._trigger(GrapeCity.UI.Events.RangeGroupStateChanging,args);
                    if(args && args.cancel === false)
                    {
                        var rowGroupExpandUndoAction = new GrapeCity.UI.UndoRedo.RowGroupExpandUndoAction(sheet,{
                                index: hitInfo.info.index,
                                level: hitInfo.info.level,
                                collapsed: isExpanded
                            });
                        sheet._doCommand(rowGroupExpandUndoAction);
                        sheet._trigger(GrapeCity.UI.Events.RangeGroupStateChanged,{
                            sheet: sheet,
                            sheetName: sheet._name,
                            isRowGroup: true,
                            index: index,
                            level: hitInfo.info.level
                        })
                    }
                }
            }
            else if(hitInfo.what === "cg")
                if(sheet.colRangeGroup)
                {
                    index = hitInfo.info.index;
                    isExpanded = hitInfo.info.isExpanded;
                    if(hitInfo.info.lineDirection === GrapeCity.UI.RangeGroupDirection.Forward)
                        index--;
                    else
                        index++;
                    args = {
                        sheet: sheet,
                        sheetName: sheet._name,
                        isRowGroup: false,
                        index: index,
                        level: hitInfo.info.level,
                        cancel: false
                    };
                    sheet._trigger(GrapeCity.UI.Events.RangeGroupStateChanging,args);
                    if(args && args.cancel === false)
                    {
                        var columnGroupExpandUndoAction = new GrapeCity.UI.UndoRedo.ColumnGroupExpandUndoAction(sheet,{
                                index: hitInfo.info.index,
                                level: hitInfo.info.level,
                                collapsed: isExpanded
                            });
                        sheet._doCommand(columnGroupExpandUndoAction);
                        sheet._trigger(GrapeCity.UI.Events.RangeGroupStateChanged,{
                            sheet: sheet,
                            sheetName: sheet._name,
                            isRowGroup: false,
                            index: index,
                            level: hitInfo.info.level
                        })
                    }
                }
        },
        _updateValidationUI: function(row, col)
        {
            var sheet = this._sheet;
            if(!sheet || !sheet.parent)
                return;
            sheet._disposeValidationUI();
            var activeSheet = sheet.parent.getActiveSheet();
            if(!activeSheet || sheet.getName() !== activeSheet.getName())
                return;
            var dv = sheet.getDataValidator(row,col);
            var offset = this._getCanvasOffset();
            var rect = sheet.getCellRect(row,col);
            var sheetLayout = sheet._getSheetLayout();
            if(rect.x === null || rect.x === undefined || rect.y === null || rect.y === undefined || rect.width === null || rect.width === undefined || rect.height === null || rect.height === undefined || rect.x + rect.width > sheetLayout.viewportX + sheetLayout.viewportWidth || rect.y + rect.height > sheetLayout.viewportY + sheetLayout.viewportHeight)
                return;
            if(dv && dv.showInputMessage && dv.inputMessage)
            {
                var span = document.createElement("span");
                $(span).css("position","absolute").css("border","1px #C0C0C0 solid").css("padding","3px 8px 3px 8px").css("background-color","#FFFFFF").css("box-shadow","1px 2px 5px rgba(0,0,0,0.4)").css("font","normal normal normal 12px/normal Arial").width("auto").height("auto").css("top",rect.y + rect.height + offset.top + 5).css("left",rect.x + rect.width / 2 + offset.left).html("<b>" + dv.inputTitle + "</b><br/>" + dv.inputMessage).attr("gcUIElement","gcValidationInputMessage").appendTo(document.body);
                sheet._validationInputMessage = span
            }
            if(dv && dv.type === UI.CriteriaType.List && dv.inCellDropdown)
            {
                var validList = dv.condition.getValidList(sheet,row,col);
                var length = validList.length;
                var select = document.createElement("select");
                var innerHtml = "";
                for(var i = 0; i < length; i++)
                {
                    var v = validList[i];
                    if(v === null || v === undefined)
                        v = "";
                    else if(v instanceof Date)
                        v = new UI._DateTimeHelper(v).localeFormat("d/M/yyyy h:mm:ss");
                    innerHtml += "<option value=\"" + v + "\">" + v + "</option>"
                }
                var cellStyle = sheet.getActualStyle(row,col);
                var font = cellStyle && cellStyle.font ? cellStyle.font : sheet._render._getDefaultFont();
                if(sheet._zoomFactor > 1)
                    font = sheet._render._getZoomFont(font);
                $(select).html(innerHtml).css("outline","none").css("position","absolute").css("font",font).width(rect.width).height(length > 8 ? 140 : "auto").css("top",rect.y + rect.height + offset.top).css("left",rect.x + offset.left).attr("gcUIElement","gcValidationSelect").appendTo(document.body).attr("size",length > 2 ? length : 2).hide().val(sheet.getValue(row,col)).bind("click",function()
                {
                    var newValue = $(select).val();
                    if(newValue)
                    {
                        var cellEditInfo = {
                                row: row,
                                col: col,
                                newValue: newValue,
                                autoFormat: true
                            };
                        var undoAction = new UI.UndoRedo.CellEditUndoAction(sheet,cellEditInfo);
                        sheet._doCommand(undoAction)
                    }
                    $(select).hide()
                }).bind("keydown",function(event)
                {
                    if(event.keyCode === UI.Key.enter && !event.ctrlKey && !event.shiftKey && !event.altKey)
                    {
                        var newValue = $(select).val();
                        if(newValue)
                        {
                            var cellEditInfo = {
                                    row: row,
                                    col: col,
                                    newValue: newValue,
                                    autoFormat: true
                                };
                            var undoAction = new UI.UndoRedo.CellEditUndoAction(sheet,cellEditInfo);
                            sheet._doCommand(undoAction)
                        }
                        $(select).hide()
                    }
                    else if(event.keyCode === UI.Key.esc && !event.ctrlKey && !event.shiftKey && !event.altKey)
                        $(select).hide()
                });
                sheet._validationSelect = select;
                var spans = sheet.getSpans(new UI.Range(row,col,1,1));
                var colSpan = 1;
                if(spans && spans.length > 0)
                    if(spans[0])
                        colSpan = spans[0].colCount;
                var isLastColumn = col + colSpan - 1 === sheet.getColumnCount() - 1;
                var button = document.createElement("input");
                button.type = "image";
                button.src = UI._GcFilterDialog.getImageSrc(UI._FilterButtonState.noSortFilter);
                button.alt = "v";
                var size = sheet.getRowHeight(row) * sheet._zoomFactor;
                if(size > 15)
                    size = 15;
                $(button).css("position","absolute").width(size).height(size).css("top",rect.y + rect.height + offset.top - (size + 3)).css("left",rect.x + rect.width + offset.left - (isLastColumn ? size : 0)).css("background-color","white").css("border","1px solid gray").attr("gcUIElement","gcValidationButton").appendTo(document.body).click(function()
                {
                    if(sheet.isEditing())
                        sheet.endEdit();
                    $(sheet._validationSelect).toggle().focus()
                });
                sheet._validationButton = button
            }
        },
        startSelecting: function(e, x, y, target)
        {
            var colIndex = 0,
                rowIndex = 0,
                rowCount = 0,
                colCount = 0;
            var sheet = this._sheet;
            this._getHScrollTimer().start();
            this._getVScrollTimer().start();
            if(!this.ctrl && !this.shift)
                sheet._clearSelectionImp();
            this.beginHitTest = target;
            var firstRow,
                args,
                cellRange,
                activeRowChanged;
            if(target.hitTestType === GrapeCity.UI.SheetArea.corner)
            {
                if(!this.shift)
                {
                    firstRow = sheet.frozenRowCount ? sheet._getFirstVisualRow() : sheet._scrollTopRow;
                    var leftCol = sheet.frozenColCount ? sheet._getFirstVisualColumn() : sheet._scrollLeftCol;
                    args = {
                        sheet: sheet,
                        sheetName: sheet._name,
                        row: sheet._activeRowIndex,
                        col: sheet._activeColIndex,
                        cancel: false
                    };
                    sheet._trigger(GrapeCity.UI.Events.LeaveCell,args);
                    if(args && args.cancel === true)
                        return;
                    sheet._setActiveCellImp(firstRow,leftCol,1,1);
                    sheet._trigger(GrapeCity.UI.Events.EnterCell,{
                        sheet: sheet,
                        sheetName: sheet._name,
                        row: firstRow,
                        col: leftCol
                    });
                    this._updateValidationUI(firstRow,leftCol)
                }
                if(!this.ctrl)
                    sheet._clearSelectionImp();
                sheet._setSelectedRange(-1,-1,sheet.getRowCount(),sheet.getColumnCount(),true);
                this.isWorking = true
            }
            else if(target.hitTestType === GrapeCity.UI.SheetArea.colHeader || target.hitTestType === GrapeCity.UI.SheetArea.colFooter)
            {
                if(!this.shift)
                {
                    firstRow = sheet.frozenRowCount ? sheet._getFirstVisualRow() : sheet._scrollTopRow;
                    cellRange = sheet._getAvailableActiveCell(firstRow,target.col,false);
                    args = {
                        sheet: sheet,
                        sheetName: sheet._name,
                        row: sheet._activeRowIndex,
                        col: sheet._activeColIndex,
                        cancel: false
                    };
                    sheet._trigger(GrapeCity.UI.Events.LeaveCell,args);
                    if(args && args.cancel === true)
                        return;
                    sheet._setActiveCellImp(cellRange.row,cellRange.col,1,target.colViewportIndex);
                    sheet._trigger(GrapeCity.UI.Events.EnterCell,{
                        sheet: sheet,
                        sheetName: sheet._name,
                        row: cellRange.row,
                        col: cellRange.col
                    });
                    this._updateValidationUI(cellRange.row,cellRange.col)
                }
                if(this.shift)
                {
                    colIndex = Math.min(sheet._activeColIndex,target.col);
                    colCount = Math.abs(sheet._activeColIndex - target.col) + 1;
                    sheet._replaceActiveSelectedRange(-1,colIndex,sheet.getRowCount(),colCount,true)
                }
                else
                    sheet._setSelectedRange(-1,sheet._activeColIndex,sheet.getRowCount(),1,true);
                this.isWorking = true
            }
            else if(target.hitTestType === GrapeCity.UI.SheetArea.rowHeader)
            {
                if(!this.shift)
                {
                    var firstCol = sheet.frozenColCount ? sheet._getFirstVisualColumn() : sheet._scrollLeftCol;
                    cellRange = sheet._getAvailableActiveCell(target.row,firstCol,true);
                    args = {
                        sheet: sheet,
                        sheetName: sheet._name,
                        row: sheet._activeRowIndex,
                        col: sheet._activeColIndex,
                        cancel: false
                    };
                    sheet._trigger(GrapeCity.UI.Events.LeaveCell,args);
                    if(args && args.cancel === true)
                        return;
                    activeRowChanged = sheet._activeRowIndex !== cellRange.row;
                    sheet._setActiveCellImp(cellRange.row,cellRange.col,target.rowViewportIndex,1);
                    if(activeRowChanged)
                        sheet.doDataItemChanged();
                    sheet._trigger(GrapeCity.UI.Events.EnterCell,{
                        sheet: sheet,
                        sheetName: sheet._name,
                        row: cellRange.row,
                        col: cellRange.col
                    });
                    this._updateValidationUI(cellRange.row,cellRange.col)
                }
                if(this.shift)
                {
                    rowIndex = Math.min(sheet._activeRowIndex,target.row);
                    rowCount = Math.abs(sheet._activeRowIndex - target.row) + 1;
                    sheet._replaceActiveSelectedRange(rowIndex,-1,rowCount,sheet.getColumnCount(),true)
                }
                else
                    sheet._setSelectedRange(sheet._activeRowIndex,-1,1,sheet.getColumnCount(),true);
                this.isWorking = true
            }
            else if(target.hitTestType === GrapeCity.UI.SheetArea.viewport)
                if(!isNaN(target.row) && !isNaN(target.col))
                {
                    if(!this.shift)
                        if(sheet._activeRowIndex !== target.row || sheet._activeColIndex !== target.col)
                        {
                            args = {
                                sheet: sheet,
                                sheetName: sheet._name,
                                row: sheet._activeRowIndex,
                                col: sheet._activeColIndex,
                                cancel: false
                            };
                            sheet._trigger(GrapeCity.UI.Events.LeaveCell,args);
                            if(args && args.cancel === true)
                                return;
                            activeRowChanged = sheet._activeRowIndex !== target.row;
                            sheet._setActiveCellImp(target.row,target.col,target.rowViewportIndex,target.colViewportIndex);
                            if(activeRowChanged)
                                sheet.doDataItemChanged();
                            sheet._trigger(GrapeCity.UI.Events.EnterCell,{
                                sheet: sheet,
                                sheetName: sheet._name,
                                row: target.row,
                                col: target.col
                            });
                            this._updateValidationUI(target.row,target.col)
                        }
                    if(this.shift)
                        sheet._extendSelectedRange(target.row,target.col,true);
                    else
                    {
                        var span = sheet._spanModel.find(target.row,target.col);
                        if(span)
                            sheet._setSelectedRange(span.row,span.col,span.rowCount,span.colCount,true);
                        else
                            sheet._setSelectedRange(target.row,target.col,1,1,true)
                    }
                    this.isWorking = true
                }
                else
                {
                    if(sheet.isEditing())
                    {
                        var rect = sheet.getCellRect(sheet._activeRowIndex,sheet._activeColIndex,sheet.activeRowViewportIndex,sheet.activeColViewportIndex);
                        sheet._render.update(rect.x,rect.y,rect.width,rect.height)
                    }
                    this.beginHitTest = null
                }
        },
        continueSelecting: function(e, x, y, target)
        {
            if(!this.beginHitTest)
                return;
            if(this._forceCancelSelectiong === true)
                return;
            this.isWorking = true;
            var sheet = this._sheet;
            if(!this.beginHitTest.freezeInfo)
            {
                var freezeInfo = {
                        freezeTop: false,
                        freezeLeft: false
                    };
                if(this.beginHitTest.col < sheet._getFirstPageLeftColumn())
                    freezeInfo.freezeLeft = true;
                if(this.beginHitTest.row < sheet._getFirstPageTopRow())
                    freezeInfo.freezeTop = true;
                this.beginHitTest.freezeInfo = freezeInfo
            }
            var hitTestType = this.beginHitTest.hitTestType;
            if(hitTestType === GrapeCity.UI.SheetArea.viewport)
                this.continueCellSelecting(e,x,y,target);
            else if(hitTestType === GrapeCity.UI.SheetArea.rowHeader)
                this.continueRowSelecting(e,x,y,target);
            else if(hitTestType === GrapeCity.UI.SheetArea.colHeader || hitTestType === GrapeCity.UI.SheetArea.colFooter)
                this.continueColumnSelecting(e,x,y,target)
        },
        continueCellSelecting: function(e, x, y, target)
        {
            var sheet = this._sheet;
            var self = this;
            var freezeLeft = this.beginHitTest.freezeInfo.freezeLeft;
            var freezeTop = this.beginHitTest.freezeInfo.freezeTop;
            var mousePosition = this.getMousePosition(e,x,y,GrapeCity.UI.SheetArea.viewport);
            if(mousePosition.left)
            {
                if(!freezeLeft)
                    this._getHScrollTimer().setAction(function()
                    {
                        var firstPageLeftCol = sheet._getFirstPageLeftColumn();
                        var newLeftCol = sheet._getPrevVisualColumn(isNaN(sheet.frozenColCount) ? sheet._scrollLeftCol : Math.max(sheet._scrollLeftCol,sheet.frozenColCount));
                        if(newLeftCol >= firstPageLeftCol)
                        {
                            sheet._setLeftColumn(newLeftCol);
                            self.continueCellSelecting(e,x,y,target)
                        }
                    })
            }
            else if(mousePosition.right)
                if(freezeLeft)
                {
                    sheet._setLeftColumn(sheet._getFirstPageLeftColumn());
                    this.beginHitTest.freezeInfo.freezeLeft = false
                }
                else
                    this._getHScrollTimer().setAction(function()
                    {
                        var lastPageLeftCol = sheet._getLastPageLeftColumn();
                        var newLeftCol = sheet._getNextVisualColumn(isNaN(sheet.frozenColCount) ? sheet._scrollLeftCol : Math.max(sheet._scrollLeftCol,sheet.frozenColCount));
                        if(newLeftCol <= lastPageLeftCol)
                        {
                            sheet._setLeftColumn(newLeftCol);
                            self.continueCellSelecting(e,x,y,target)
                        }
                    });
            if(mousePosition.top)
            {
                if(!freezeTop)
                    this._getVScrollTimer().setAction(function()
                    {
                        var firstPageTopRow = sheet._getFirstPageTopRow();
                        var newTopRow = sheet._getPrevVisualRow(isNaN(sheet.frozenRowCount) ? sheet._scrollTopRow : Math.max(sheet._scrollTopRow,sheet.frozenRowCount));
                        if(self.getIntervalFromDistance(mousePosition.distanceX) <= 50)
                            newTopRow = sheet._getPrevVisualRow(newTopRow);
                        if(newTopRow >= firstPageTopRow)
                        {
                            sheet._setTopRow(newTopRow);
                            self.continueCellSelecting(e,x,y,target)
                        }
                    })
            }
            else if(mousePosition.bottom)
                if(freezeTop)
                {
                    sheet._setTopRow(sheet._getFirstPageTopRow());
                    this.beginHitTest.freezeInfo.freezeTop = false
                }
                else
                    this._getVScrollTimer().setAction(function()
                    {
                        var lastPageTopRow = sheet._getLastPageTopRow();
                        var newTopRow = sheet._getNextVisualRow(isNaN(sheet.frozenRowCount) ? sheet._scrollTopRow : Math.max(sheet._scrollTopRow,sheet.frozenRowCount));
                        if(self.getIntervalFromDistance(mousePosition.distanceX) <= 50)
                            newTopRow = sheet._getNextVisualRow(newTopRow);
                        if(newTopRow <= lastPageTopRow)
                        {
                            sheet._setTopRow(newTopRow);
                            self.continueCellSelecting(e,x,y,target)
                        }
                    });
            this._getHScrollTimer().setInterval(this.getIntervalFromDistance(mousePosition.distanceX));
            this._getVScrollTimer().setInterval(this.getIntervalFromDistance(mousePosition.distanceY));
            target = sheet.hitTest(mousePosition.nearMouseX,mousePosition.nearMouseY);
            var oldSelections = sheet._selectionModel.toArray();
            sheet._extendSelectedRange(target.row,target.col,true);
            var newSelections = sheet._selectionModel.toArray();
            if(this._notEqualSelecions(oldSelections,newSelections))
                sheet._trigger(GrapeCity.UI.Events.SelectionChanging,{
                    sheet: sheet,
                    sheetName: sheet._name,
                    oldSelections: oldSelections,
                    newSelections: newSelections
                })
        },
        continueRowSelecting: function(e, x, y, target)
        {
            var sheet = this._sheet;
            var self = this;
            var freezeTop = this.beginHitTest.freezeInfo.freezeTop;
            var mousePosition = this.getMousePosition(e,x,y,GrapeCity.UI.SheetArea.rowHeader);
            if(mousePosition.top)
            {
                if(!freezeTop)
                    this._getVScrollTimer().setAction(function()
                    {
                        var firstPageTopRow = sheet._getFirstPageTopRow();
                        var newTopRow = sheet._getPrevVisualRow(sheet._scrollTopRow);
                        if(newTopRow >= firstPageTopRow)
                        {
                            sheet._setTopRow(newTopRow);
                            self.continueRowSelecting(e,x,y,target)
                        }
                    })
            }
            else if(mousePosition.bottom)
                if(freezeTop)
                {
                    sheet._setTopRow(sheet._getFirstPageTopRow());
                    this.beginHitTest.freezeInfo.freezeTop = false
                }
                else
                    this._getVScrollTimer().setAction(function()
                    {
                        var lastPageTopRow = sheet._getLastPageTopRow();
                        var newTopRow = sheet._getNextVisualRow(sheet._scrollTopRow);
                        if(newTopRow <= lastPageTopRow)
                        {
                            sheet._setTopRow(newTopRow);
                            self.continueRowSelecting(e,x,y,target)
                        }
                    });
            this._getVScrollTimer().setInterval(this.getIntervalFromDistance(mousePosition.distanceY));
            target = sheet.hitTest(mousePosition.nearMouseX,mousePosition.nearMouseY);
            var r = Math.min(sheet._activeRowIndex,target.row);
            var rc = Math.max(sheet._activeRowIndex,target.row) - r + 1;
            var c = -1;
            var cc = sheet.getColumnCount();
            var selectionPolicy = sheet.selectionPolicy();
            if(selectionPolicy === UI.SelectionPolicy.Single)
                return;
            var selectionUnit = sheet.selectionUnit();
            if(selectionUnit === UI.SelectionUnit.Column)
            {
                r = -1;
                rc = -1
            }
            var oldSelections = sheet._selectionModel.toArray();
            sheet._replaceActiveSelectedRange(r,c,rc,cc,true);
            var newSelections = sheet._selectionModel.toArray();
            if(this._notEqualSelecions(oldSelections,newSelections))
                sheet._trigger(GrapeCity.UI.Events.SelectionChanging,{
                    sheet: sheet,
                    sheetName: sheet._name,
                    oldSelections: oldSelections,
                    newSelections: newSelections
                })
        },
        continueColumnSelecting: function(e, x, y, target)
        {
            var sheet = this._sheet;
            var self = this;
            var freezeLeft = this.beginHitTest.freezeInfo.freezeLeft;
            var mousePosition = this.getMousePosition(e,x,y,GrapeCity.UI.SheetArea.colHeader);
            if(mousePosition.left)
            {
                if(!freezeLeft)
                    this._getHScrollTimer().setAction(function()
                    {
                        var firstPageLeftCol = sheet._getFirstPageLeftColumn();
                        var newLeftCol = sheet._getPrevVisualColumn(sheet._scrollLeftCol);
                        if(newLeftCol >= firstPageLeftCol)
                        {
                            sheet._setLeftColumn(newLeftCol);
                            self.continueColumnSelecting(e,x,y,target)
                        }
                    })
            }
            else if(mousePosition.right)
                if(freezeLeft)
                {
                    sheet._setLeftColumn(sheet._getFirstPageLeftColumn());
                    this.beginHitTest.freezeInfo.freezeLeft = false
                }
                else
                    this._getHScrollTimer().setAction(function()
                    {
                        var lastPageLeftCol = sheet._getLastPageLeftColumn();
                        var newLeftCol = sheet._getNextVisualColumn(sheet._scrollLeftCol);
                        if(newLeftCol <= lastPageLeftCol)
                        {
                            sheet._setLeftColumn(newLeftCol);
                            self.continueColumnSelecting(e,x,y,target)
                        }
                    });
            this._getHScrollTimer().setInterval(this.getIntervalFromDistance(mousePosition.distanceX));
            target = sheet.hitTest(mousePosition.nearMouseX,mousePosition.nearMouseY);
            var c = Math.min(sheet._activeColIndex,target.col);
            var cc = Math.max(sheet._activeColIndex,target.col) - c + 1;
            var r = -1;
            var rc = sheet.getRowCount();
            var selectionPolicy = sheet.selectionPolicy();
            if(selectionPolicy === UI.SelectionPolicy.Single)
                return;
            var selectionUnit = sheet.selectionUnit();
            if(selectionUnit === UI.SelectionUnit.Row)
            {
                c = -1;
                cc = -1
            }
            var oldSelections = sheet._selectionModel.toArray();
            sheet._replaceActiveSelectedRange(r,c,rc,cc,true);
            var newSelections = sheet._selectionModel.toArray();
            if(this._notEqualSelecions(oldSelections,newSelections))
                sheet._trigger(GrapeCity.UI.Events.SelectionChanging,{
                    sheet: sheet,
                    sheetName: sheet._name,
                    oldSelections: oldSelections,
                    newSelections: newSelections
                })
        },
        stopSelecting: function()
        {
            this.beginHitTest = null;
            this._getHScrollTimer().stop();
            this._getVScrollTimer().stop();
            this._forceCancelSelectiong = null;
            if(this.isWorking === false)
                return;
            else
                this.isWorking = false;
            var sheet = this._sheet;
            var needRaiseChangedEvent = !this._lastSelections;
            if(needRaiseChangedEvent)
                this._lastSelections = sheet._selectionModel.toArray();
            else
            {
                var currentSelections = sheet._selectionModel.toArray();
                needRaiseChangedEvent = this._notEqualSelecions(this._lastSelections,currentSelections);
                if(needRaiseChangedEvent)
                    this._lastSelections = currentSelections
            }
            if(needRaiseChangedEvent)
                sheet._trigger(GrapeCity.UI.Events.SelectionChanged,{
                    sheet: this._sheet,
                    sheetName: this._sheet._name
                })
        },
        startDragDropping: function(e, x, y, beginTarget)
        {
            var sheet = this._sheet;
            if(this.isDragDropping)
                return;
            var fromRange = null;
            if(sheet._selectionModel.length === 1)
                fromRange = sheet._selectionModel[0];
            else if(sheet._selectionModel.length < 1)
            {
                var spanActiveCell = sheet._spanModel.find(sheet._activeRowIndex,sheet._activeColIndex);
                if(spanActiveCell)
                    fromRange = spanActiveCell;
                else
                    fromRange = new GrapeCity.UI.Range(sheet._activeRowIndex,sheet._activeColIndex,1,1)
            }
            if(fromRange)
            {
                this.isDragDropping = true;
                this.isWorking = true;
                this._dragDropFromRange = fromRange;
                var rg = null;
                var activeSelRange = sheet._getActiveSelectedRange();
                if(activeSelRange && (activeSelRange.rowCount > 0 || activeSelRange.colCount > 0))
                    rg = {
                        r: activeSelRange.row,
                        c: activeSelRange.col,
                        rc: activeSelRange.rowCount,
                        cc: activeSelRange.colCount
                    };
                else
                    rg = {
                        r: sheet._activeRowIndex,
                        c: sheet._activeColIndex,
                        rc: 1,
                        cc: 1
                    };
                this.beginHitTest = beginTarget;
                var target = sheet._currentTarget;
                var rect = sheet._getActiveSelectionRect(target.rowViewportIndex,target.colViewportIndex);
                var dragRect = sheet._dragRect;
                dragRect.x = rect.x;
                dragRect.y = rect.y;
                dragRect.width = rect.width - 1;
                dragRect.height = rect.height - 1;
                dragRect.row = rg.r;
                dragRect.col = rg.c;
                dragRect.rowCount = rg.rc;
                dragRect.colCount = rg.cc;
                dragRect.beginHitRow = target.row;
                dragRect.beginHitCol = target.col;
                dragRect.hitRow = target.row;
                dragRect.hitCol = target.col;
                if(this.beginHitTest)
                {
                    var dragInfo = this.beginHitTest.dragInfo;
                    if(dragInfo.outside === true)
                        switch(dragInfo.side)
                        {
                            case"left":
                                dragRect.col--;
                                break;
                            case"right":
                                dragRect.col++;
                                break;
                            case"top":
                                dragRect.row--;
                                break;
                            case"bottom":
                                dragRect.row++;
                                break
                        }
                    dragRect.beginRow = dragRect.row;
                    dragRect.beginCol = dragRect.col
                }
                this._getHScrollTimer().start();
                this._getVScrollTimer().start()
            }
        },
        continueDragDropping: function(e, x, y)
        {
            var sheet = this._sheet;
            if(!this.beginHitTest)
                return;
            var target = sheet.hitTest(x,y,true);
            if(!this.isDragDropping || !this._dragDropFromRange)
                return;
            this.isWorking = true;
            var dragRect = sheet._dragRect;
            dragRect.hitTarget = target;
            if(!this.beginHitTest.freezeInfo)
            {
                var freezeInfo = {
                        freezeTop: false,
                        freezeLeft: false
                    };
                if(this.beginHitTest.col < sheet._getFirstPageLeftColumn())
                    freezeInfo.freezeLeft = true;
                if(this.beginHitTest.row < sheet._getFirstPageTopRow())
                    freezeInfo.freezeTop = true;
                this.beginHitTest.freezeInfo = freezeInfo
            }
            this.continueCellDragDropping(e,x,y)
        },
        continueCellDragDropping: function(e, x, y)
        {
            var sheet = this._sheet;
            var self = this;
            var mousePosition = this.getMousePosition(e,x,y,GrapeCity.UI.SheetArea.viewport);
            if(!mousePosition)
                return;
            var freezeLeft = this.beginHitTest.freezeInfo.freezeLeft;
            var freezeTop = this.beginHitTest.freezeInfo.freezeTop;
            if(mousePosition.left)
            {
                if(!freezeLeft)
                    this._getHScrollTimer().setAction(function()
                    {
                        var firstPageLeftCol = sheet._getFirstPageLeftColumn();
                        var newLeftCol = sheet._getPrevVisualColumn(isNaN(sheet.frozenColCount) ? sheet._scrollLeftCol : Math.max(sheet._scrollLeftCol,sheet.frozenColCount));
                        if(newLeftCol >= firstPageLeftCol)
                        {
                            sheet._setLeftColumn(newLeftCol);
                            self.continueCellDragDropping(e,x,y)
                        }
                    })
            }
            else if(mousePosition.right)
                if(freezeLeft)
                {
                    sheet._setLeftColumn(sheet._getFirstPageLeftColumn());
                    this.beginHitTest.freezeInfo.freezeLeft = false
                }
                else
                    this._getHScrollTimer().setAction(function()
                    {
                        var lastPageLeftCol = sheet._getLastPageLeftColumn();
                        var newLeftCol = sheet._getNextVisualColumn(isNaN(sheet.frozenColCount) ? sheet._scrollLeftCol : Math.max(sheet._scrollLeftCol,sheet.frozenColCount));
                        if(newLeftCol <= lastPageLeftCol)
                        {
                            sheet._setLeftColumn(newLeftCol);
                            self.continueCellDragDropping(e,x,y)
                        }
                    });
            if(mousePosition.top)
            {
                if(!freezeTop)
                    this._getVScrollTimer().setAction(function()
                    {
                        var firstPageTopRow = sheet._getFirstPageTopRow();
                        var newTopRow = sheet._getPrevVisualRow(isNaN(sheet.frozenRowCount) ? sheet._scrollTopRow : Math.max(sheet._scrollTopRow,sheet.frozenRowCount));
                        if(newTopRow >= firstPageTopRow)
                        {
                            sheet._setTopRow(newTopRow);
                            self.continueCellDragDropping(e,x,y)
                        }
                    })
            }
            else if(mousePosition.bottom)
                if(freezeTop)
                {
                    sheet._setTopRow(sheet._getFirstPageTopRow());
                    this.beginHitTest.freezeInfo.freezeTop = false
                }
                else
                    this._getVScrollTimer().setAction(function()
                    {
                        var lastPageTopRow = sheet._getLastPageTopRow();
                        var newTopRow = sheet._getNextVisualRow(isNaN(sheet.frozenRowCount) ? sheet._scrollTopRow : Math.max(sheet._scrollTopRow,sheet.frozenRowCount));
                        if(newTopRow <= lastPageTopRow)
                        {
                            sheet._setTopRow(newTopRow);
                            self.continueCellDragDropping(e,x,y)
                        }
                    });
            this._getHScrollTimer().setInterval(this.getIntervalFromDistance(mousePosition.distanceX));
            this._getVScrollTimer().setInterval(this.getIntervalFromDistance(mousePosition.distanceY));
            this.updateDragDropRect(mousePosition.nearMouseX,mousePosition.nearMouseY)
        },
        updateDragDropRect: function(x, y)
        {
            var sheet = this._sheet;
            if(!this.beginHitTest)
                return;
            var target = sheet.hitTest(x,y,true);
            if(target.row === undefined || target.row === null || target.col === undefined || target.col === null)
                return;
            if(!this.isDragDropping || !this._dragDropFromRange)
                return;
            var dragRect = sheet._dragRect;
            var activeSelRange = sheet._getActiveSelectedRange();
            if(activeSelRange.row === -1 && activeSelRange.col !== -1)
            {
                dragRect.row = -1;
                dragRect.col = target.col
            }
            else if(activeSelRange.row !== -1 && activeSelRange.col === -1)
            {
                dragRect.row = target.row;
                dragRect.col = -1
            }
            else
            {
                dragRect.row = dragRect.beginRow + (target.row - dragRect.beginHitRow);
                dragRect.col = dragRect.beginCol + (target.col - dragRect.beginHitCol);
                if(dragRect.row < 0)
                    dragRect.row = 0;
                if(dragRect.col < 0)
                    dragRect.col = 0
            }
            dragRect.hitRow = target.row;
            dragRect.hitCol = target.col;
            var sheetRowCount = sheet.getRowCount();
            var sheetColCount = sheet.getColumnCount();
            if(dragRect.row + dragRect.rowCount > sheetRowCount)
                dragRect.row = sheetRowCount - dragRect.rowCount;
            if(dragRect.col + dragRect.colCount > sheetColCount)
                dragRect.col = sheetColCount - dragRect.colCount;
            var row = dragRect.row < 0 ? 0 : dragRect.row;
            var col = dragRect.col < 0 ? 0 : dragRect.col;
            var dragRange = new GrapeCity.UI.Range(row,col,dragRect.rowCount,dragRect.colCount);
            var oldDragRange = sheet._oldDragRange;
            if(oldDragRange)
                if(dragRange.row === oldDragRange.row && dragRange.col === oldDragRange.col && dragRange.rowCount === oldDragRange.rowCount && dragRange.colCount === oldDragRange.colCount && dragRange.row > sheet._getFirstVisualRow() && dragRange.col > sheet._getFirstVisualColumn() && dragRange.row + dragRange.rowCount - 1 < sheet._getLastVisualRow() && dragRange.col + dragRange.colCount - 1 < sheet._getLastVisualColumn())
                    return;
            sheet._actualDragRange = dragRange;
            sheet._render.refreshDragDropIndicator()
        },
        stopDragDrop: function(e)
        {
            var isInvalid = false;
            var invalidMessage = "";
            var doCommand = false;
            var sheet = this._sheet;
            var rg = null;
            this.beginHitTest = null;
            this._getHScrollTimer().stop();
            this._getVScrollTimer().stop();
            var _selectedRange = sheet._getActiveSelectedRange();
            if(_selectedRange && (_selectedRange.rowCount > 0 || _selectedRange.colCount > 0))
                rg = {
                    r: _selectedRange.row,
                    c: _selectedRange.col,
                    rc: _selectedRange.rowCount,
                    cc: _selectedRange.colCount
                };
            else
                rg = {
                    r: sheet._activeRowIndex,
                    c: sheet._activeColIndex,
                    rc: 1,
                    cc: 1
                };
            if(this.isDragDropping === true && this.isWorking === true)
            {
                var fromRow = rg.r;
                var fromColumn = rg.c;
                var rowCount = rg.rc;
                var columnCount = rg.cc;
                var toRow = sheet._dragRect.row;
                var toColumn = sheet._dragRect.col;
                var action,
                    cancel,
                    dragMoveExtent;
                if(this._isDragInsert && (fromRow === -1 || fromColumn === -1))
                {
                    if(fromColumn >= 0 && fromRow < 0)
                    {
                        if(this._isDragCopy && (toColumn <= fromColumn || toColumn >= fromColumn + columnCount) || !this._isDragCopy && (toColumn < fromColumn || toColumn > fromColumn + columnCount))
                        {
                            if(sheet._hasPartSpans(-1,fromColumn,-1,columnCount) || sheet._hasPartSpans(-1,toColumn,-1,0))
                            {
                                isInvalid = true;
                                invalidMessage = "Cannot change part of a merged cell."
                            }
                            if(!isInvalid)
                                if(sheet._hasPartArrayFormulas(-1,fromColumn,-1,columnCount) || sheet._hasPartArrayFormulas(-1,toColumn,-1,0))
                                {
                                    isInvalid = true;
                                    invalidMessage = "Cannot change part of an array."
                                }
                            if(!isInvalid && sheet.isProtected)
                            {
                                isInvalid = true;
                                invalidMessage = "The column you are trying to change is protected and therefore read-only."
                            }
                            if(!isInvalid)
                            {
                                dragMoveExtent = new GrapeCity.UI.UndoRedo.DragDropExtent(-1,fromColumn,-1,toColumn,-1,columnCount);
                                cancel = sheet._raiseDragDropBlock(dragMoveExtent.fromRow,dragMoveExtent.fromColumn,dragMoveExtent.toRow,dragMoveExtent.toColumn,dragMoveExtent.rowCount,dragMoveExtent.columnCount,this._isDragCopy,true,GrapeCity.UI.CopyToOption.All);
                                if(!cancel)
                                {
                                    action = new GrapeCity.UI.UndoRedo.DragDropUndoAction(sheet,dragMoveExtent,this._isDragCopy,true,GrapeCity.UI.CopyToOption.All);
                                    sheet.doCommand(action);
                                    sheet._raiseDragDropBlockCompleted(dragMoveExtent.fromRow,dragMoveExtent.fromColumn,dragMoveExtent.toRow,dragMoveExtent.toColumn,dragMoveExtent.rowCount,dragMoveExtent.columnCount,this._isDragCopy,true,GrapeCity.UI.CopyToOption.All);
                                    doCommand = true
                                }
                            }
                        }
                    }
                    else if(fromRow >= 0 && fromColumn < 0)
                        if(this._isDragCopy && (toRow <= fromRow || toRow >= fromRow + rowCount) || !this._isDragCopy && (toRow < fromRow || toRow > fromRow + rowCount))
                        {
                            if(sheet._hasPartSpans(fromRow,-1,rowCount,-1) || sheet._hasPartSpans(toRow,-1,0,-1))
                            {
                                isInvalid = true;
                                invalidMessage = "Cannot change part of a merged cell."
                            }
                            if(!isInvalid)
                                if(sheet._hasPartArrayFormulas(fromRow,-1,rowCount,-1) || sheet._hasPartArrayFormulas(toRow,-1,0,-1))
                                {
                                    isInvalid = true;
                                    invalidMessage = "Cannot change part of an array."
                                }
                            if(!isInvalid && sheet.isProtected)
                            {
                                isInvalid = true;
                                invalidMessage = "The row you are trying to change is protected and therefore read-only."
                            }
                            if(!isInvalid)
                            {
                                dragMoveExtent = new GrapeCity.UI.UndoRedo.DragDropExtent(fromRow,-1,toRow,-1,rowCount,-1);
                                cancel = sheet._raiseDragDropBlock(dragMoveExtent.fromRow,dragMoveExtent.fromColumn,dragMoveExtent.toRow,dragMoveExtent.toColumn,dragMoveExtent.rowCount,dragMoveExtent.columnCount,this._isDragCopy,true,GrapeCity.UI.CopyToOption.All);
                                if(!cancel)
                                {
                                    action = new GrapeCity.UI.UndoRedo.DragDropUndoAction(sheet,dragMoveExtent,this._isDragCopy,true,GrapeCity.UI.CopyToOption.All);
                                    sheet.doCommand(action);
                                    sheet._raiseDragDropBlockCompleted(dragMoveExtent.fromRow,dragMoveExtent.fromColumn,dragMoveExtent.toRow,dragMoveExtent.toColumn,dragMoveExtent.rowCount,dragMoveExtent.columnCount,this._isDragCopy,true,GrapeCity.UI.CopyToOption.All);
                                    doCommand = true
                                }
                            }
                        }
                }
                else if(toRow !== fromRow || toColumn !== fromColumn)
                {
                    if(sheet._hasPartSpans(fromRow,fromColumn,rowCount,columnCount) || sheet._hasPartSpans(toRow,toColumn,rowCount,columnCount))
                    {
                        isInvalid = true;
                        invalidMessage = "Cannot change part of a merged cell."
                    }
                    if(!isInvalid)
                        if(sheet._hasPartArrayFormulas(fromRow,fromColumn,rowCount,columnCount) || sheet._hasPartArrayFormulas(toRow,toColumn,rowCount,columnCount))
                        {
                            isInvalid = true;
                            invalidMessage = "Cannot change part of an array."
                        }
                    if(!isInvalid && sheet.isProtected)
                        if(!this._isDragCopy && sheet._isAnyCellInRangeLocked(new GrapeCity.UI.Range(fromRow,fromColumn,rowCount,columnCount)) || sheet._isAnyCellInRangeLocked(new GrapeCity.UI.Range(toRow,toColumn,rowCount,columnCount)))
                        {
                            isInvalid = true;
                            invalidMessage = "The cells you are trying to change is protected and therefore read-only."
                        }
                    if(!isInvalid)
                    {
                        dragMoveExtent = new GrapeCity.UI.UndoRedo.DragDropExtent(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount);
                        cancel = sheet._raiseDragDropBlock(dragMoveExtent.fromRow,dragMoveExtent.fromColumn,dragMoveExtent.toRow,dragMoveExtent.toColumn,dragMoveExtent.rowCount,dragMoveExtent.columnCount,this._isDragCopy,false,GrapeCity.UI.CopyToOption.All);
                        if(!cancel)
                        {
                            action = new GrapeCity.UI.UndoRedo.DragDropUndoAction(sheet,dragMoveExtent,this._isDragCopy,false,GrapeCity.UI.CopyToOption.All);
                            sheet.doCommand(action);
                            sheet._raiseDragDropBlockCompleted(dragMoveExtent.fromRow,dragMoveExtent.fromColumn,dragMoveExtent.toRow,dragMoveExtent.toColumn,dragMoveExtent.rowCount,dragMoveExtent.columnCount,this._isDragCopy,false,GrapeCity.UI.CopyToOption.All);
                            doCommand = true
                        }
                    }
                }
            }
            this.isWorking = false;
            this.isDragDropping = false;
            this._dragDropFromRange = null;
            this._isDragInsert = false;
            this._isDragCopy = false;
            if(doCommand === false)
            {
                sheet._dragRect = {};
                sheet.repaint()
            }
            if(isInvalid)
                sheet._raiseInvalidOperation(invalidMessage)
        },
        startDragFill: function(e, x, y, target)
        {
            if(this.isDraggingFill === true || this.isWorking === true)
                return;
            this.beginHitTest = target;
            this.updateDragFillStartRange();
            if(!this._dragFillStartRange)
                return;
            this.isWorking = true;
            this._dragStartRowViewport = target.rowViewportIndex;
            this._dragStartColumnViewport = target.colViewportIndex;
            this._dragToRowViewport = target.rowViewportIndex;
            this._dragToColumnViewport = target.colViewportIndex;
            this.updateDragStartRangeViewports();
            this._getHScrollTimer().start();
            this._getVScrollTimer().start()
        },
        updateDragStartRangeViewports: function()
        {
            var dragFillStartTopRow = this.dragFillStartTopRow();
            if(dragFillStartTopRow >= 0 && dragFillStartTopRow < this._sheet.frozenRowCount)
                this._dragFillStartTopRowViewport = 0;
            else if(dragFillStartTopRow >= this._sheet.frozenRowCount && dragFillStartTopRow <= this._sheet.getRowCount())
                this._dragFillStartTopRowViewport = 1;
            if(this.isDragFillWholeColumns())
                this._dragFillStartBottomRowViewport = 1;
            else
                this._dragFillStartBottomRowViewport = this._dragStartRowViewport;
            var dragFillStartLeftColumn = this.dragFillStartLeftColumn();
            if(dragFillStartLeftColumn >= 0 && dragFillStartLeftColumn < this._sheet.frozenColCount)
                this._dragFillStartLeftColumnViewport = 0;
            else if(dragFillStartLeftColumn >= this._sheet.frozenColCount && dragFillStartLeftColumn <= this._sheet.getColumnCount())
                this._dragFillStartLeftColumnViewport = 1;
            if(this.isDragFillWholeRows())
                this._dragFillStartRightColumnViewport = 1;
            else
                this._dragFillStartRightColumnViewport = this._dragStartColumnViewport
        },
        updateDragToViewports: function(e, x, y)
        {
            this.updateDragToRowViewport(e,x,y);
            this.updateDragToColumnViewport(e,x,y)
        },
        updateDragToRowViewport: function(e, x, y)
        {
            this._dragToRowViewport = this._dragStartRowViewport;
            var row = this._sheet._getRowIndex(y,this._dragToRowViewport);
            if(row === undefined || row === null || this._sheet._getRowLayout(this._dragToRowViewport).findRow(row) === null)
            {
                var startHitTestY = this._sheet._currentTarget.y;
                if(y < startHitTestY)
                {
                    if(this._dragToRow <= this._sheet.frozenRowCount)
                        this._dragToRowViewport = 0;
                    else if(this._dragToRow <= this._sheet.getRowCount())
                        this._dragToRowViewport = 1
                }
                else if(this._dragStartRowViewport === 0 && this._dragToRow >= this._sheet.frozenRowCount)
                    this._dragToRowViewport = 1
            }
        },
        updateDragToColumnViewport: function(e, x, y)
        {
            this._dragToColumnViewport = this._dragStartColumnViewport;
            var col = this._sheet._getColumnIndex(x,this._dragToColumnViewport);
            if(col === undefined || col === null || this._sheet._getColumnLayout(this._dragToColumnViewport).findCol(col) === null)
            {
                var startHitTestX = this._sheet._currentTarget.x;
                if(x < startHitTestX)
                {
                    if(this._dragToColumn <= this._sheet.frozenColCount)
                        this._dragToColumnViewport = 0;
                    else if(this._dragToColumn <= this._sheet.getColumnCount())
                        this._dragToColumnViewport = 1
                }
                else if(this._dragStartColumnViewport === 0 && this._dragToColumn >= this._sheet.frozenColCount)
                    this._dragToColumnViewport = 1
            }
        },
        continueDragFill: function(e, x, y)
        {
            var sheet = this._sheet;
            if(!this.beginHitTest)
                return;
            if(!this.isWorking || !this._dragFillStartRange)
                return;
            this.isDraggingFill = true;
            if(!this.beginHitTest.freezeInfo)
            {
                var freezeInfo = {
                        freezeTop: false,
                        freezeLeft: false
                    };
                if(this.beginHitTest.col < sheet._getFirstPageLeftColumn())
                    freezeInfo.freezeLeft = true;
                if(this.beginHitTest.row < sheet._getFirstPageTopRow())
                    freezeInfo.freezeTop = true;
                this.beginHitTest.freezeInfo = freezeInfo
            }
            this.continueCellDragFill(e,x,y)
        },
        continueCellDragFill: function(e, x, y)
        {
            var sheet = this._sheet;
            var self = this;
            var mousePosition = this.getMousePosition(e,x,y,GrapeCity.UI.SheetArea.viewport);
            if(!mousePosition)
                return;
            var freezeLeft = this.beginHitTest.freezeInfo.freezeLeft;
            var freezeTop = this.beginHitTest.freezeInfo.freezeTop;
            if(mousePosition.left)
            {
                if(!freezeLeft)
                    this._getHScrollTimer().setAction(function()
                    {
                        var firstPageLeftCol = sheet._getFirstPageLeftColumn();
                        var newLeftCol = sheet._getPrevVisualColumn(isNaN(sheet.frozenColCount) ? sheet._scrollLeftCol : Math.max(sheet._scrollLeftCol,sheet.frozenColCount));
                        if(newLeftCol >= firstPageLeftCol)
                        {
                            sheet._setLeftColumn(newLeftCol);
                            self.continueCellDragFill(e,x,y)
                        }
                    })
            }
            else if(mousePosition.right)
                if(freezeLeft)
                {
                    sheet._setLeftColumn(sheet._getFirstPageLeftColumn());
                    this.beginHitTest.freezeInfo.freezeLeft = false
                }
                else
                    this._getHScrollTimer().setAction(function()
                    {
                        var lastPageLeftCol = sheet._getLastPageLeftColumn();
                        var newLeftCol = sheet._getNextVisualColumn(isNaN(sheet.frozenColCount) ? sheet._scrollLeftCol : Math.max(sheet._scrollLeftCol,sheet.frozenColCount));
                        if(newLeftCol <= lastPageLeftCol)
                        {
                            sheet._setLeftColumn(newLeftCol);
                            self.continueCellDragFill(e,x,y)
                        }
                    });
            if(mousePosition.top)
            {
                if(!freezeTop)
                    this._getVScrollTimer().setAction(function()
                    {
                        var firstPageTopRow = sheet._getFirstPageTopRow();
                        var newTopRow = sheet._getPrevVisualRow(isNaN(sheet.frozenRowCount) ? sheet._scrollTopRow : Math.max(sheet._scrollTopRow,sheet.frozenRowCount));
                        if(newTopRow >= firstPageTopRow)
                        {
                            sheet._setTopRow(newTopRow);
                            self.continueCellDragFill(e,x,y)
                        }
                    })
            }
            else if(mousePosition.bottom)
                if(freezeTop)
                {
                    sheet._setTopRow(sheet._getFirstPageTopRow());
                    this.beginHitTest.freezeInfo.freezeTop = false
                }
                else
                    this._getVScrollTimer().setAction(function()
                    {
                        var lastPageTopRow = sheet._getLastPageTopRow();
                        var newTopRow = sheet._getNextVisualRow(isNaN(sheet.frozenRowCount) ? sheet._scrollTopRow : Math.max(sheet._scrollTopRow,sheet.frozenRowCount));
                        if(newTopRow <= lastPageTopRow)
                        {
                            sheet._setTopRow(newTopRow);
                            self.continueCellDragFill(e,x,y)
                        }
                    });
            this._getHScrollTimer().setInterval(this.getIntervalFromDistance(mousePosition.distanceX));
            this._getVScrollTimer().setInterval(this.getIntervalFromDistance(mousePosition.distanceY));
            this.updateDragFillRect(e,mousePosition.nearMouseX,mousePosition.nearMouseY)
        },
        updateDragFillRect: function(e, x, y)
        {
            this.updateDragToViewports(e,x,y);
            this.updateDragToCoordinates(e,x,y);
            if(this._dragToRow < 0 && this._dragToColumn < 0)
                return;
            this.updateCurrentFillSettings(e,x,y);
            this.updateCurrentFillRange();
            this.refreshDragFill()
        },
        refreshDragFill: function()
        {
            if($.browser.chrome || $.browser.safari)
            {
                var sheet = this._sheet;
                var ctx = sheet._render._getCtx();
                var bufferCtx = sheet._render._getBufferCtx();
                var bufferCtx2 = sheet._render._getBufferCtx2();
                var rg = this.getDragFillFrameRange();
                if(this._oldDragFillFrameRange)
                {
                    var rect = null;
                    if(rg.containsRange(this._oldDragFillFrameRange))
                        rect = sheet._getRangeRect(1,1,rg);
                    else
                        rect = sheet._getRangeRect(1,1,this._oldDragFillFrameRange);
                    rect.x -= 2;
                    rect.y -= 2;
                    rect.width += 4;
                    rect.height += 4;
                    sheet._render._copyDoubleBufferRect(rect,null,null,null,bufferCtx,bufferCtx2)
                }
                if(rg)
                {
                    var rect = sheet._getRangeRect(1,1,rg);
                    this.refreshSelectionBorder(bufferCtx2);
                    sheet._render.paintDragRectangle(bufferCtx2,rect);
                    if(!this._oldDragFillFrameRange || rg.containsRange(this._oldDragFillFrameRange))
                        rect = sheet._getRangeRect(1,1,rg);
                    else
                        rect = sheet._getRangeRect(1,1,this._oldDragFillFrameRange);
                    rect.x -= 2;
                    rect.y -= 2;
                    rect.width += 4;
                    rect.height += 4;
                    sheet._render._copyDoubleBufferRect(rect,null,null,null,bufferCtx2,ctx)
                }
                this._oldDragFillFrameRange = rg
            }
            else
            {
                this.clearDragFill();
                this.refreshSelectionBorder();
                this.paintDragFill();
                this._oldDragFillFrameRange = this.getDragFillFrameRange()
            }
        },
        clearDragFill: function()
        {
            var sheet = this._sheet;
            if(this._oldDragFillFrameRange)
            {
                var oldDragFillRect = sheet._getRangeRect(1,1,this._oldDragFillFrameRange);
                oldDragFillRect.x -= 2;
                oldDragFillRect.y -= 2;
                oldDragFillRect.width += 4;
                oldDragFillRect.height += 4;
                sheet._render._copyDoubleBufferRect(oldDragFillRect)
            }
        },
        refreshSelectionBorder: function(ctx)
        {
            var sheet = this._sheet;
            sheet._render.repaintSelection(this._dragFillStartRange,null,ctx)
        },
        paintDragFill: function()
        {
            var sheet = this._sheet;
            var render = sheet._render;
            var dragFillFrameRange = this.getDragFillFrameRange();
            if(!dragFillFrameRange)
                return;
            var ctx = render._getCtx();
            var layout = sheet._getSheetLayout();
            for(var r = 0; r < 2; r++)
                for(var c = 0; c < 2; c++)
                {
                    var dragFillRect = sheet._getRangeRect(r,c,dragFillFrameRange);
                    var clipRect = layout.viewportRect(r,c);
                    ctx.save();
                    ctx.rect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
                    ctx.clip();
                    ctx.beginPath();
                    render.paintDragRectangle(ctx,dragFillRect);
                    ctx.beginPath();
                    ctx.restore()
                }
        },
        updateCurrentFillRange: function()
        {
            this._currentFillRange = this.getCurrentFillRange()
        },
        isDragFillWholeRows: function()
        {
            return this._dragFillStartRange.col === -1 && this._dragFillStartRange.row !== -1
        },
        isDragFillWholeColumns: function()
        {
            return this._dragFillStartRange.row === -1 && this._dragFillStartRange.col !== -1
        },
        isDragClear: function()
        {
            return this._currentFillDirection === GrapeCity.UI.DragFillDirection.LeftClear || this._currentFillDirection === GrapeCity.UI.DragFillDirection.UpClear
        },
        getCurrentFillRange: function()
        {
            var row = -1,
                column = -1,
                rowCount = -1,
                columnCount = -1;
            switch(this._currentFillDirection)
            {
                case GrapeCity.UI.DragFillDirection.Left:
                    if(this.isDragFillWholeColumns())
                    {
                        row = -1;
                        rowCount = -1
                    }
                    else
                    {
                        row = this.dragFillStartTopRow();
                        rowCount = this._dragFillStartRange.rowCount
                    }
                    column = this._dragToColumn;
                    columnCount = this.dragFillStartLeftColumn() - column;
                    break;
                case GrapeCity.UI.DragFillDirection.Right:
                    if(this.isDragFillWholeColumns())
                    {
                        row = -1;
                        rowCount = -1
                    }
                    else
                    {
                        row = this.dragFillStartTopRow();
                        rowCount = this._dragFillStartRange.rowCount
                    }
                    column = this.dragFillStartRightColumn() + 1;
                    columnCount = this._dragToColumn - column + 1;
                    break;
                case GrapeCity.UI.DragFillDirection.Up:
                    row = this._dragToRow;
                    rowCount = this.dragFillStartTopRow() - row;
                    if(this.isDragFillWholeRows())
                    {
                        column = -1;
                        columnCount = -1
                    }
                    else
                    {
                        column = this.dragFillStartLeftColumn();
                        columnCount = this._dragFillStartRange.colCount
                    }
                    break;
                case GrapeCity.UI.DragFillDirection.Down:
                    row = this.dragFillStartBottomRow() + 1;
                    rowCount = this._dragToRow - row + 1;
                    if(this.isDragFillWholeRows())
                    {
                        column = -1;
                        columnCount = -1
                    }
                    else
                    {
                        column = this.dragFillStartLeftColumn();
                        columnCount = this._dragFillStartRange.colCount
                    }
                    break;
                case GrapeCity.UI.DragFillDirection.UpClear:
                    row = this._dragToRow;
                    rowCount = this.dragFillStartBottomRow() - row + 1;
                    if(this.isDragFillWholeRows())
                    {
                        column = -1;
                        columnCount = -1
                    }
                    else
                    {
                        column = this.dragFillStartLeftColumn();
                        columnCount = this._dragFillStartRange.colCount
                    }
                    break;
                case GrapeCity.UI.DragFillDirection.LeftClear:
                    if(this.isDragFillWholeColumns())
                    {
                        row = -1;
                        rowCount = -1
                    }
                    else
                    {
                        row = this._dragFillStartRange.row;
                        rowCount = this._dragFillStartRange.rowCount
                    }
                    column = this._dragToColumn;
                    columnCount = this.dragFillStartRightColumn() - column + 1;
                    break;
                default:
                    break
            }
            return new GrapeCity.UI.Range(row,column,rowCount,columnCount)
        },
        updateDragToCoordinates: function(e, x, y)
        {
            var sheet = this._sheet;
            var target = sheet.hitTest(x,y,true);
            if(target.row === null || target.col === null)
                return;
            var lastFullyVisibleRow = sheet._getLastFullyVisibleRow();
            var lastFullyVisibleColumn = sheet._getLastFullyVisibleColumn();
            var lastVisualRow = sheet._getLastVisualRow();
            var lastVisualColumn = sheet._getLastVisualColumn();
            if(target.row > lastFullyVisibleRow && lastFullyVisibleRow < lastVisualRow)
                this._dragToRow = sheet._getPrevVisualRow(target.row);
            else
                this._dragToRow = target.row;
            if(target.col > lastFullyVisibleColumn && lastFullyVisibleColumn < lastVisualColumn)
                this._dragToColumn = sheet._getPrevVisualColumn(target.col);
            else
                this._dragToColumn = target.col
        },
        dragFillStartBottomRowLayout: function()
        {
            var dragStartBottomRow = this.dragFillStartBottomRow();
            if(dragStartBottomRow !== -1)
                return this._sheet._getRowLayout(this._dragFillStartBottomRowViewport).findRow(dragStartBottomRow);
            return null
        },
        dragFillToViewportBottomRowLayout: function()
        {
            return this._sheet._getRowLayout(this._dragToRowViewport).findRow(this.dragFillToViewportBottomRow())
        },
        dragFillToViewportBottomRow: function()
        {
            return this._sheet.getViewportBottomRow(this._dragToRowViewport)
        },
        dragFillStartRightColumnLayout: function()
        {
            var dragStartRightColumn = this.dragFillStartRightColumn();
            if(dragStartRightColumn !== -1)
                return this._sheet._getColumnLayout(this._dragFillStartRightColumnViewport).findCol(dragStartRightColumn);
            return null
        },
        dragFillToViewportRightColumnLayout: function()
        {
            return this._sheet._getColumnLayout(this._dragToColumnViewport).findCol(this.dragFillToViewportRightColumn())
        },
        dragFillToViewportRightColumn: function()
        {
            return this._sheet.getViewportRightColumn(this._dragToColumnViewport)
        },
        updateCurrentFillSettings: function(e, x, y)
        {
            var isWholeRows = this.isDragFillWholeRows(),
                isWholeColumns = this.isDragFillWholeColumns();
            var t = $(this._sheet._getCanvas()).offset();
            var actualX = e.pageX - t.left,
                actualY = e.pageY - t.top,
                notClear = false;
            var hOffset,
                vOffset;
            if(!isWholeRows && !isWholeColumns)
            {
                if(this._dragToRow >= this.dragFillStartTopRow() && this._dragToRow <= this.dragFillStartBottomRow())
                {
                    if(this._dragToColumn >= this.dragFillStartLeftColumn() && this._dragToColumn <= this.dragFillStartRightColumn())
                    {
                        hOffset = Math.abs(this._dragToColumn - this.dragFillStartRightColumn());
                        vOffset = Math.abs(this._dragToRow - this.dragFillStartBottomRow());
                        if(vOffset > hOffset)
                            this._currentFillDirection = GrapeCity.UI.DragFillDirection.UpClear;
                        else if(vOffset < hOffset)
                            this._currentFillDirection = GrapeCity.UI.DragFillDirection.LeftClear;
                        else
                        {
                            var dragFillStartBottomRowLayout = this.dragFillStartBottomRowLayout();
                            if(!dragFillStartBottomRowLayout)
                                dragFillStartBottomRowLayout = this.dragFillToViewportBottomRowLayout();
                            if(y > dragFillStartBottomRowLayout.y + dragFillStartBottomRowLayout.height)
                                this._currentFillDirection = GrapeCity.UI.DragFillDirection.Down;
                            else
                            {
                                var dragFillStartRightColumnLayout = this.dragFillStartRightColumnLayout();
                                if(!dragFillStartRightColumnLayout)
                                    dragFillStartRightColumnLayout = this.dragFillToViewportRightColumnLayout();
                                var hDistance = dragFillStartRightColumnLayout.x + dragFillStartRightColumnLayout.width - x;
                                var vDistance = dragFillStartBottomRowLayout.y + dragFillStartBottomRowLayout.height - y;
                                if(actualX >= dragFillStartRightColumnLayout.x && actualX <= dragFillStartRightColumnLayout.x + dragFillStartRightColumnLayout.width && actualY >= dragFillStartBottomRowLayout.y && actualY <= dragFillStartBottomRowLayout.y + dragFillStartBottomRowLayout.height)
                                    if(hDistance >= vDistance)
                                        this._currentFillDirection = GrapeCity.UI.DragFillDirection.LeftClear;
                                    else
                                        this._currentFillDirection = GrapeCity.UI.DragFillDirection.UpClear;
                                else
                                    notClear = true
                            }
                        }
                    }
                    else if(this._dragToColumn < this.dragFillStartLeftColumn())
                        this._currentFillDirection = GrapeCity.UI.DragFillDirection.Left;
                    else if(this._dragToColumn > this.dragFillStartRightColumn())
                        this._currentFillDirection = GrapeCity.UI.DragFillDirection.Right
                }
                else if(this._dragToRow < this.dragFillStartTopRow())
                {
                    if(this._dragToColumn >= this.dragFillStartLeftColumn() && this._dragToColumn <= this.dragFillStartRightColumn())
                        this._currentFillDirection = GrapeCity.UI.DragFillDirection.Up;
                    else if(this._dragToColumn < this.dragFillStartLeftColumn())
                    {
                        hOffset = Math.abs(this._dragToColumn - this.dragFillStartLeftColumn());
                        vOffset = Math.abs(this._dragToRow - this.dragFillStartTopRow());
                        if(vOffset >= hOffset)
                            this._currentFillDirection = GrapeCity.UI.DragFillDirection.Up;
                        else
                            this._currentFillDirection = GrapeCity.UI.DragFillDirection.Left
                    }
                    else if(this._dragToColumn > this.dragFillStartRightColumn())
                    {
                        hOffset = Math.abs(this._dragToColumn - this.dragFillStartRightColumn());
                        vOffset = Math.abs(this._dragToRow - this.dragFillStartTopRow());
                        if(vOffset >= hOffset)
                            this._currentFillDirection = GrapeCity.UI.DragFillDirection.Up;
                        else
                            this._currentFillDirection = GrapeCity.UI.DragFillDirection.Right
                    }
                }
                else if(this._dragToRow > this.dragFillStartBottomRow())
                    if(this._dragToColumn >= this.dragFillStartLeftColumn() && this._dragToColumn <= this.dragFillStartRightColumn())
                        this._currentFillDirection = GrapeCity.UI.DragFillDirection.Down;
                    else if(this._dragToColumn < this.dragFillStartLeftColumn())
                    {
                        hOffset = Math.abs(this._dragToColumn - this.dragFillStartLeftColumn());
                        vOffset = Math.abs(this._dragToRow - this.dragFillStartBottomRow());
                        if(vOffset >= hOffset)
                            this._currentFillDirection = GrapeCity.UI.DragFillDirection.Down;
                        else
                            this._currentFillDirection = GrapeCity.UI.DragFillDirection.Left
                    }
                    else if(this._dragToColumn > this.dragFillStartRightColumn())
                    {
                        hOffset = Math.abs(this._dragToColumn - this.dragFillStartRightColumn());
                        vOffset = Math.abs(this._dragToRow - this.dragFillStartBottomRow());
                        if(vOffset >= hOffset)
                            this._currentFillDirection = GrapeCity.UI.DragFillDirection.Down;
                        else
                            this._currentFillDirection = GrapeCity.UI.DragFillDirection.Right
                    }
            }
            else if(isWholeColumns)
            {
                if(this._dragToColumn >= this.dragFillStartLeftColumn() && this._dragToColumn <= this.dragFillStartRightColumn())
                    this._currentFillDirection = GrapeCity.UI.DragFillDirection.LeftClear;
                else if(this._dragToColumn < this.dragFillStartLeftColumn())
                    this._currentFillDirection = GrapeCity.UI.DragFillDirection.Left;
                else if(this._dragToColumn > this.dragFillStartRightColumn())
                    this._currentFillDirection = GrapeCity.UI.DragFillDirection.Right
            }
            else if(isWholeRows)
                if(this._dragToRow >= this.dragFillStartTopRow() && this._dragToRow <= this.dragFillStartBottomRow())
                    this._currentFillDirection = GrapeCity.UI.DragFillDirection.UpClear;
                else if(this._dragToRow < this.dragFillStartTopRow())
                    this._currentFillDirection = GrapeCity.UI.DragFillDirection.Up;
                else if(this._dragToRow > this.dragFillStartBottomRow())
                    this._currentFillDirection = GrapeCity.UI.DragFillDirection.Down;
            var rect = this._sheet._dragFillIndicatorRect;
            if(rect)
            {
                var currentRow = this.dragFillStartBottomRow(),
                    currentColumn = this.dragFillStartRightColumn(),
                    nextRow = currentRow + 1,
                    nextColumn = currentColumn + 1;
                var currentRowHieght = this._sheet.getRowHeight(currentRow,GrapeCity.UI.SheetArea.viewport),
                    currentColumnWidth = this._sheet.getColumnWidth(currentColumn,GrapeCity.UI.SheetArea.viewport),
                    nextRowHeight = this._sheet.getRowHeight(nextRow,GrapeCity.UI.SheetArea.viewport),
                    nextColumnWidth = this._sheet.getColumnWidth(nextColumn,GrapeCity.UI.SheetArea.viewport);
                var minX = rect.x + rect.width / 2 - Math.min(10,currentColumnWidth / 2),
                    maxX = rect.x + rect.width / 2 + Math.min(10,nextColumnWidth / 2),
                    minY = rect.y + rect.height / 2 - Math.min(10,currentRowHieght / 2),
                    maxY = rect.y + rect.height / 2 + Math.min(10,nextRowHeight / 2);
                var isAroundIndicator = false;
                if(!isWholeRows && !isWholeColumns)
                    isAroundIndicator = minX <= actualX && actualX <= maxX && minY <= actualY && actualY <= maxY;
                else if(isWholeColumns)
                    isAroundIndicator = minX <= actualX && actualX <= maxX;
                else if(isWholeRows)
                    isAroundIndicator = minY <= actualY && actualY <= maxY;
                if(isAroundIndicator || notClear)
                {
                    this._isDragAroundIndicator = true;
                    this._currentFillDirection = GrapeCity.UI.DragFillDirection.LeftClear
                }
                else
                    this._isDragAroundIndicator = false
            }
        },
        dragFillStartTopRow: function()
        {
            if(!this._dragFillStartRange)
                return-1;
            else if(this._dragFillStartRange.row === -1)
                return 0;
            else
                return this._dragFillStartRange.row
        },
        dragFillStartBottomRow: function()
        {
            if(!this._dragFillStartRange)
                return-1;
            else if(this._dragFillStartRange.row === -1)
                return this._sheet.getRowCount() - 1;
            else
                return this._dragFillStartRange.row + this._dragFillStartRange.rowCount - 1
        },
        dragFillStartLeftColumn: function()
        {
            if(!this._dragFillStartRange)
                return-1;
            else if(this._dragFillStartRange.col === -1)
                return 0;
            else
                return this._dragFillStartRange.col
        },
        dragFillStartRightColumn: function()
        {
            if(!this._dragFillStartRange)
                return-1;
            else if(this._dragFillStartRange.col === -1)
                return this._sheet.getColumnCount() - 1;
            else
                return this._dragFillStartRange.col + this._dragFillStartRange.colCount - 1
        },
        endDragFill: function(e)
        {
            this.beginHitTest = null;
            this._getHScrollTimer().stop();
            this._getVScrollTimer().stop();
            if(!this.isDraggingFill || !this.isWorking)
            {
                this.resetDragFill();
                return
            }
            this.isDraggingFill = false;
            this.isWorking = false;
            var wholeFillRange = this.getDragFillFrameRange();
            if(!wholeFillRange)
                return;
            var canDragFill = this.validateFillRange(wholeFillRange);
            if(!canDragFill || this._isDragAroundIndicator)
            {
                this.resetDragFill();
                this.refreshSelection(wholeFillRange)
            }
            else
            {
                var autoFillType = this.getDragAutoFillType();
                var cancel = this.executeDragFillAction(this._currentFillRange,autoFillType);
                if(!cancel && this.isDragFill())
                    this.showDragFillSmartTag(autoFillType);
                else
                    this.refreshSelection(wholeFillRange);
                this.resetDragFill()
            }
        },
        showDragFillSmartTag: function(autoFillType)
        {
            var sheet = this._sheet;
            var rect = sheet._dragFillIndicatorRect;
            var fillInfo = {
                    x: rect.x + rect.width,
                    y: rect.y + rect.height,
                    fillType: autoFillType
                };
            sheet._smartTag = new GrapeCity.UI._GcFillDialog(sheet,fillInfo);
            sheet._smartTag.open()
        },
        updateDragFillStartRange: function()
        {
            var sheet = this._sheet;
            if(sheet._selectionModel.length === 1)
                this._dragFillStartRange = sheet._selectionModel[0];
            else if(sheet._activeRowIndex >= 0 && sheet._activeColIndex >= 0)
                this._dragFillStartRange = new GrapeCity.UI.Range(sheet._activeRowIndex,sheet._activeColIndex,1,1)
        },
        resetDragFill: function()
        {
            this.isWorking = false;
            this.isDraggingFill = false
        },
        refreshSelection: function(range)
        {
            this._sheet._render.repaintSelection(range)
        },
        getDragAutoFillType: function()
        {
            if(this.isDragClear())
                return GrapeCity.UI.AutoFillType.ClearValues;
            var isDragSingleCell = this._dragFillStartRange.rowCount === 1 && this._dragFillStartRange.colCount === 1 && !this.isDragFillWholeColumns() && !this.isDragFillWholeRows();
            if(isDragSingleCell)
                if(this.isControlPressed)
                    return GrapeCity.UI.AutoFillType.FillSeries;
                else
                    return GrapeCity.UI.AutoFillType.CopyCells;
            else if(this.isControlPressed)
                return GrapeCity.UI.AutoFillType.CopyCells;
            else
                return GrapeCity.UI.AutoFillType.FillSeries
        },
        getDragFillFrameRange: function()
        {
            if(!this._dragFillStartRange)
                return null;
            if(this.isDragClear())
                return this._dragFillStartRange;
            if(!this._currentFillRange)
                return null;
            var row = 0,
                rowCount = 0,
                column = 0,
                columnCount = 0;
            if(this.isVerticalDragFill())
            {
                row = this._currentFillDirection === GrapeCity.UI.DragFillDirection.Up ? this._currentFillRange.row : this._dragFillStartRange.row;
                rowCount = this._dragFillStartRange.rowCount + this._currentFillRange.rowCount;
                column = this._dragFillStartRange.col;
                columnCount = this._dragFillStartRange.colCount
            }
            else
            {
                row = this._dragFillStartRange.row;
                rowCount = this._dragFillStartRange.rowCount;
                column = this._currentFillDirection === GrapeCity.UI.DragFillDirection.Left ? this._currentFillRange.col : this._dragFillStartRange.col;
                columnCount = this._dragFillStartRange.colCount + this._currentFillRange.colCount
            }
            return new GrapeCity.UI.Range(row,column,rowCount,columnCount)
        },
        validateFillRange: function(fillRange)
        {
            var sheet = this._sheet;
            var canDragFill = true;
            var invalidMessage = "";
            if(sheet._hasSpans(fillRange.row,fillRange.col,fillRange.rowCount,fillRange.colCount))
            {
                canDragFill = false;
                invalidMessage = "Can not fill range that contains merged cell."
            }
            if(canDragFill && sheet.isProtected)
                if(sheet._isAnyCellInRangeLocked(fillRange))
                {
                    canDragFill = false;
                    invalidMessage = "The cells you are trying to fill is protected and therefore read-only."
                }
            if(!canDragFill)
                sheet._raiseInvalidOperation(invalidMessage);
            return canDragFill
        },
        executeDragFillAction: function(fillRange, autoFillType)
        {
            var sheet = this._sheet;
            var savedRange = sheet._getActualRange(fillRange);
            this._preFillCellsInfo = new GrapeCity.UI.CopyMoveCellsInfo(savedRange.rowCount,savedRange.colCount);
            GrapeCity.UI.CopyMoveHelper.saveViewportInfo(sheet,this._preFillCellsInfo,savedRange.row,savedRange.col,GrapeCity.UI.CopyToOption.All);
            var dragFillExtent = {
                    startRange: this._dragFillStartRange,
                    fillRange: fillRange,
                    autoFillType: autoFillType,
                    fillDirection: this.getCurrentFillDirection()
                };
            var cancel = sheet._raiseDragFillBlock(dragFillExtent.fillRange,dragFillExtent.fillDirection,dragFillExtent.autoFillType);
            if(!cancel)
            {
                var dragFillUndoAction = new GrapeCity.UI.UndoRedo.DragFillUndoAction(sheet,dragFillExtent);
                sheet.doCommand(dragFillUndoAction);
                sheet._raiseDragFillBlockCompleted(dragFillExtent.fillRange,dragFillExtent.fillDirection,dragFillExtent.autoFillType);
                return false
            }
            return true
        },
        isDragFill: function()
        {
            return this.isIncreaseFill() || this.isDecreaseFill()
        },
        isIncreaseFill: function()
        {
            return this._currentFillDirection === GrapeCity.UI.DragFillDirection.Down || this._currentFillDirection === GrapeCity.UI.DragFillDirection.Right
        },
        isDecreaseFill: function()
        {
            return this._currentFillDirection === GrapeCity.UI.DragFillDirection.Left || this._currentFillDirection === GrapeCity.UI.DragFillDirection.Up
        },
        isVerticalDragFill: function()
        {
            return this._currentFillDirection === GrapeCity.UI.DragFillDirection.Up || this._currentFillDirection === GrapeCity.UI.DragFillDirection.Down || this._currentFillDirection === GrapeCity.UI.DragFillDirection.UpClear
        },
        isDragToRowInView: function()
        {
            return this.isRowInViewport(this._dragToRowViewport,this._dragToRow)
        },
        isRowInViewport: function(rowViewport, row)
        {
            var viewportTopRow = this._sheet.getViewportTopRow(rowViewport);
            var viewportBottomRow = this._sheet.getViewportBottomRow(rowViewport);
            if(row >= viewportTopRow && row <= viewportBottomRow)
                return true;
            else
                return false
        },
        getCurrentDragToRowLayout: function()
        {
            return this._sheet._getRowLayout(this._dragToRowViewport).findRow(this._dragToRow)
        },
        isDragFillStartBottomRowInView: function()
        {
            return this.isRowInViewport(this._dragFillStartBottomRowViewport,this.dragFillStartBottomRow())
        },
        dragFillStartViewportBottomRowLayout: function()
        {
            return this._sheet._getRowLayout(this._dragStartRowViewport).findRow(this.dragFillStartViewportBottomRow())
        },
        dragFillStartViewportBottomRow: function()
        {
            return this._sheet.getViewportBottomRow(this._dragStartRowViewport)
        },
        getValidVerDragToRowLayout: function()
        {
            var rowLayout;
            if(this.isIncreaseFill())
                if(this.isDragToRowInView())
                    rowLayout = this.getCurrentDragToRowLayout();
                else
                    rowLayout = this.dragFillToViewportBottomRowLayout();
            else if(this.isDragFillStartBottomRowInView)
                rowLayout = this.dragFillStartBottomRowLayout();
            else
                rowLayout = this.dragFillStartViewportBottomRowLayout();
            return rowLayout
        },
        isDragToColumnInView: function()
        {
            return this.isColumnInViewport(this._dragToColumnViewport,this._dragToColumn)
        },
        isColumnInViewport: function(columnViewport, column)
        {
            var viewportLeftColumn = this._sheet.getViewportLeftColumn(columnViewport);
            var viewportRightColumn = this._sheet.getViewportRightColumn(columnViewport);
            if(column >= viewportLeftColumn && column <= viewportRightColumn)
                return true;
            else
                return false
        },
        getCurrentDragToColumnLayout: function()
        {
            return this._sheet._getColumnLayout(this._dragToColumnViewport).findCol(this._dragToColumn)
        },
        isDragFillStartRightColumnInView: function()
        {
            return this.isColumnInViewport(this._dragFillStartRightColumnViewport,this.dragFillStartRightColumn())
        },
        dragFillStartViewportRightColumnLayout: function()
        {
            return this._sheet._getColumnLayout(this._dragStartColumnViewport).findCol(this.dragFillStartViewportRightColumn())
        },
        dragFillStartViewportRightColumn: function()
        {
            return this._sheet.getViewportRightColumn(this._dragStartColumnViewport)
        },
        getValidHorDragToColumnLayout: function()
        {
            var columnLayout;
            if(this.isIncreaseFill())
                if(this.isDragToColumnInView())
                    columnLayout = this.getCurrentDragToColumnLayout();
                else
                    columnLayout = this.dragFillToViewportRightColumnLayout();
            else if(this.isDragFillStartRightColumnInView())
                columnLayout = this.dragFillStartRightColumnLayout();
            else
                columnLayout = this.dragFillStartViewportRightColumnLayout();
            return columnLayout
        },
        dragFillToViewportTopRowLayout: function()
        {
            return this._sheet._getRowLayout(this._dragToRowViewport).findRow(this.dragFillToViewportTopRow())
        },
        dragFillToViewportTopRow: function()
        {
            return this._sheet.getViewportTopRow(this._dragToRowViewport)
        },
        dragFillToViewportLeftColumnLayout: function()
        {
            return this._sheet._getColumnLayout(this._dragToColumnViewport).findCol(this.dragFillToViewportLeftColumn())
        },
        dragFillToViewportLeftColumn: function()
        {
            return this._sheet.getViewportLeftColumn(this._dragToColumnViewport)
        },
        getCurrentFillDirection: function()
        {
            switch(this._currentFillDirection)
            {
                case GrapeCity.UI.DragFillDirection.Left:
                    return GrapeCity.UI.FillDirection.Left;
                case GrapeCity.UI.DragFillDirection.Right:
                    return GrapeCity.UI.FillDirection.Right;
                case GrapeCity.UI.DragFillDirection.Up:
                    return GrapeCity.UI.FillDirection.Up;
                case GrapeCity.UI.DragFillDirection.Down:
                    return GrapeCity.UI.FillDirection.Down;
                case GrapeCity.UI.DragFillDirection.LeftClear:
                    return GrapeCity.UI.FillDirection.Left;
                case GrapeCity.UI.DragFillDirection.UpClear:
                    return GrapeCity.UI.FillDirection.Up;
                default:
                    break
            }
            return GrapeCity.UI.FillDirection.Down
        },
        getMousePosition: function(e, x, y, sheetArea)
        {
            var mousePosition = null;
            if(sheetArea === GrapeCity.UI.SheetArea.viewport)
            {
                var viewPortRect = this.getViewPortRect();
                mousePosition = this.createMousePosition(e,x,y,viewPortRect)
            }
            else if(sheetArea === GrapeCity.UI.SheetArea.rowHeader)
            {
                var rowHeaderRect = this.getRowHeaderRect();
                mousePosition = this.createMousePosition(e,x,y,rowHeaderRect)
            }
            else if(sheetArea === GrapeCity.UI.SheetArea.colHeader)
            {
                var colHeaderRect = this.getColHeaderRect();
                mousePosition = this.createMousePosition(e,x,y,colHeaderRect)
            }
            return mousePosition
        },
        getViewPortRect: function()
        {
            var sheet = this._sheet;
            var sheetLayout = sheet._getSheetLayout();
            var cls = sheet._getColumnLayout(1,GrapeCity.UI.SheetArea.viewport);
            var rls = sheet._getRowLayout(1,GrapeCity.UI.SheetArea.viewport);
            var viewPortRect = null;
            var freezeLeft = this.beginHitTest.freezeInfo.freezeLeft;
            var freezeTop = this.beginHitTest.freezeInfo.freezeTop;
            if(freezeLeft && freezeTop)
                viewPortRect = new GrapeCity.UI.Rect(sheetLayout.x + sheetLayout.rowHeaderWidth,sheetLayout.y + sheetLayout.colHeaderHeight,sheetLayout.frozenWidth,sheetLayout.frozenHeight);
            else if(!freezeLeft && freezeTop)
                viewPortRect = new GrapeCity.UI.Rect(sheetLayout.viewportX,sheetLayout.y + sheetLayout.colHeaderHeight,sheetLayout.viewportWidth,sheetLayout.frozenHeight);
            else if(freezeLeft && !freezeTop)
                viewPortRect = new GrapeCity.UI.Rect(sheetLayout.x + sheetLayout.rowHeaderWidth,sheetLayout.viewportY,sheetLayout.frozenWidth,sheetLayout.viewportHeight);
            else if(!freezeLeft && !freezeTop)
                viewPortRect = new GrapeCity.UI.Rect(sheetLayout.viewportX,sheetLayout.viewportY,sheetLayout.viewportWidth,sheetLayout.viewportHeight);
            if(sheet._scrollLeftCol <= sheet._getFirstPageLeftColumn())
            {
                viewPortRect.x = sheetLayout.x + sheetLayout.rowHeaderWidth;
                viewPortRect.width = sheetLayout.width - viewPortRect.x
            }
            if(sheet._scrollTopRow <= sheet._getFirstPageTopRow())
            {
                viewPortRect.y = sheetLayout.y + sheetLayout.colHeaderHeight;
                viewPortRect.height = sheetLayout.height - viewPortRect.y
            }
            if(sheet._scrollLeftCol >= sheet._getLastPageLeftColumn() && !freezeLeft && cls.length > 0)
                viewPortRect.width = cls[cls.length - 1].x + cls[cls.length - 1].width - viewPortRect.x;
            if(sheet._scrollTopRow >= sheet._getLastPageTopRow() && !freezeTop && rls.length > 0)
                viewPortRect.height = rls[rls.length - 1].y + rls[rls.length - 1].height - viewPortRect.y;
            return viewPortRect
        },
        getRowHeaderRect: function()
        {
            var sheet = this._sheet;
            var sheetLayout = sheet._getSheetLayout();
            var rls = sheet._getRowLayout(1,GrapeCity.UI.SheetArea.viewport);
            var rowHeaderRect = null;
            var freezeTop = this.beginHitTest.freezeInfo.freezeTop;
            if(freezeTop)
                rowHeaderRect = new GrapeCity.UI.Rect(sheetLayout.headerX,sheetLayout.headerY,sheetLayout.rowHeaderWidth,sheetLayout.frozenHeight);
            else
                rowHeaderRect = new GrapeCity.UI.Rect(sheetLayout.headerX,sheetLayout.viewportY,sheetLayout.rowHeaderWidth,sheetLayout.viewportHeight);
            if(sheet._scrollTopRow === sheet._getFirstPageTopRow())
            {
                rowHeaderRect.y = sheetLayout.headerY + sheetLayout.colHeaderHeight;
                rowHeaderRect.height = sheetLayout.height - rowHeaderRect.y
            }
            if(sheet._scrollTopRow >= sheet._getLastPageTopRow() && !freezeTop && rls.length > 0)
                rowHeaderRect.height = rls[rls.length - 1].y + rls[rls.length - 1].height - rowHeaderRect.y;
            return rowHeaderRect
        },
        getColHeaderRect: function()
        {
            var sheet = this._sheet;
            var sheetLayout = sheet._getSheetLayout();
            var cls = sheet._getColumnLayout(1,GrapeCity.UI.SheetArea.viewport);
            var columnHeaderRect = null;
            var freezeLeft = this.beginHitTest.freezeInfo.freezeLeft;
            if(freezeLeft)
                columnHeaderRect = new GrapeCity.UI.Rect(sheetLayout.headerX,sheetLayout.headerY,sheetLayout.frozenWidth,sheetLayout.colHeaderHeight);
            else
                columnHeaderRect = new GrapeCity.UI.Rect(sheetLayout.viewportX,sheetLayout.headerY,sheetLayout.viewportWidth,sheetLayout.colHeaderHeight);
            if(sheet._scrollLeftCol === sheet._getFirstPageLeftColumn())
            {
                columnHeaderRect.x = sheetLayout.headerX + sheetLayout.rowHeaderWidth;
                columnHeaderRect.width = sheetLayout.width - columnHeaderRect.x
            }
            if(sheet._scrollLeftCol >= sheet._getLastPageLeftColumn() && !freezeLeft && cls.length > 0)
                columnHeaderRect.width = cls[cls.length - 1].x + cls[cls.length - 1].width - columnHeaderRect.x;
            return columnHeaderRect
        },
        createMousePosition: function(e, x, y, rect)
        {
            var mousePosition = {
                    top: false,
                    bottom: false,
                    left: false,
                    right: false,
                    distanceX: null,
                    distanceY: null,
                    nearMouseX: x,
                    nearMouseY: y
                };
            if(x <= rect.x)
                mousePosition.left = true;
            else if(x >= rect.x + rect.width)
                mousePosition.right = true;
            if(y <= rect.y)
                mousePosition.top = true;
            else if(y >= rect.y + rect.height)
                mousePosition.bottom = true;
            if(mousePosition.left)
            {
                mousePosition.distanceX = rect.x - x;
                mousePosition.nearMouseX = rect.x + 0.5
            }
            else if(mousePosition.right)
            {
                mousePosition.distanceX = x - rect.x - rect.width;
                mousePosition.nearMouseX = rect.x + rect.width - 0.5
            }
            if(mousePosition.top)
            {
                mousePosition.distanceY = rect.y - y;
                mousePosition.nearMouseY = rect.y + 0.5
            }
            else if(mousePosition.bottom)
            {
                mousePosition.distanceY = y - rect.y - rect.height;
                mousePosition.nearMouseY = rect.y + rect.height - 0.5
            }
            return mousePosition
        },
        getIntervalFromDistance: function(distance)
        {
            var interval = -1;
            if(!isNaN(distance) && distance > 0)
            {
                interval = Math.ceil(500 / distance);
                interval = Math.max(20,interval * 10);
                if(interval > 200)
                    interval = 200
            }
            return interval
        },
        updateRange: function(row, col, rowCount, colCount)
        {
            if(rowCount === 0 || colCount === 0)
                return;
            var sheet = this._sheet;
            var viewport = GrapeCity.UI.SheetArea.viewport;
            var frozRowLayoutModel = sheet._getRowLayout(0,viewport);
            var frozColLayoutModel = sheet._getColumnLayout(0,viewport);
            var rowLayoutModel = sheet._getRowLayout(1,viewport);
            var colLayoutModel = sheet._getColumnLayout(1,viewport);
            var maxCol = 0;
            if(frozColLayoutModel && frozColLayoutModel.length > 0)
                maxCol = frozColLayoutModel[frozColLayoutModel.length - 1].col;
            if(colLayoutModel && colLayoutModel.length > 0)
                maxCol = colLayoutModel[colLayoutModel.length - 1].col;
            var y = this.getYForRow(row,col,rowCount,colCount,true,maxCol,frozRowLayoutModel,rowLayoutModel);
            var y2 = this.getYForRow(row,col,rowCount,colCount,false,maxCol,frozRowLayoutModel,rowLayoutModel);
            var maxRow = 0;
            if(frozRowLayoutModel && frozRowLayoutModel.length > 0)
                maxRow = frozRowLayoutModel[frozRowLayoutModel.length - 1].row;
            if(rowLayoutModel && rowLayoutModel.length > 0)
                maxRow = rowLayoutModel[rowLayoutModel.length - 1].row;
            var x = this.getXForCol(row,col,rowCount,colCount,true,maxRow,frozColLayoutModel,colLayoutModel);
            var x2 = this.getXForCol(row,col,rowCount,colCount,false,maxRow,frozColLayoutModel,colLayoutModel);
            var layout = sheet._getSheetLayout();
            if(x2 > x && y2 > y)
                sheet._render.update(x - layout.headerX - 2,y - layout.headerY - 2,x2 - x + 2 * layout.headerX + 2,y2 - y + 2 * layout.headerY + 2);
            if(y2 > y)
                sheet._render.update(layout.headerX,y,layout.rowHeaderWidth,y2 - y);
            if(x2 > x)
            {
                sheet._render.update(x,layout.headerY,x2 - x,layout.colHeaderHeight);
                sheet._render.update(x,layout.footerY,x2 - x,layout.colHeaderHeight)
            }
        },
        getYForRow: function(row, col, rowCount, colCount, bTop, maxCol, frozRowLayoutModel, rowLayoutModel)
        {
            var sheet = this._sheet;
            var trow = row;
            if(!bTop)
                trow = row + rowCount - 1;
            if(trow < sheet.frozenRowCount)
            {
                if(frozRowLayoutModel.length < 1)
                    return-1
            }
            else
            {
                if(rowLayoutModel.length < 1)
                    return-1;
                if(bTop)
                {
                    if(trow < rowLayoutModel[0].row)
                        trow = rowLayoutModel[0].row
                }
                else if(trow > rowLayoutModel[rowLayoutModel.length - 1].row)
                    trow = rowLayoutModel[rowLayoutModel.length - 1].row
            }
            var y = 0;
            if(bTop)
                y = 90000;
            for(var c = Math.max(sheet._scrollLeftCol,col); c < col + colCount && c <= maxCol; c++)
            {
                var rect = sheet.getCellRect(trow,c);
                if(rect)
                    if(bTop)
                    {
                        if(rect.y < y)
                            y = rect.y
                    }
                    else if(y < rect.y + rect.height)
                        y = rect.y + rect.height
            }
            return y
        },
        getXForCol: function(row, col, rowCount, colCount, bLeft, maxRow, frozColLayoutModel, colLayoutModel)
        {
            var sheet = this._sheet;
            var tcol = col;
            if(!bLeft)
                tcol = col + colCount - 1;
            if(tcol < sheet.frozenColCount)
            {
                if(frozColLayoutModel.length < 1)
                    return
            }
            else
            {
                if(colLayoutModel.length < 1)
                    return;
                if(bLeft)
                {
                    if(tcol < colLayoutModel[0].col)
                        tcol = colLayoutModel[0].col
                }
                else if(tcol > colLayoutModel[colLayoutModel.length - 1].col)
                    tcol = colLayoutModel[colLayoutModel.length - 1].col
            }
            var x = 0;
            if(bLeft)
                x = 90000;
            for(var r = Math.max(sheet._scrollTopRow,row); r < row + rowCount && r <= maxRow; r++)
            {
                var rect = sheet.getCellRect(r,tcol);
                if(rect)
                    if(bLeft)
                    {
                        if(rect.x < x)
                            x = rect.x
                    }
                    else if(x < rect.x + rect.width)
                        x = rect.x + rect.width
            }
            return x
        },
        getResizingArea: function()
        {
            return 4
        },
        _getPrevVisualRowBeforeFindRow: function(row, sheetArea)
        {
            var sheet = this._sheet;
            var tempRow = row;
            if(sheet._render._isCellHiddenByGroup(tempRow,0,sheetArea))
            {
                tempRow = sheet._getPrevVisualRow(tempRow,sheetArea);
                return tempRow !== null ? tempRow : row
            }
            else
                return row
        },
        _getPrevVisualColBeforeFindCol: function(col, sheetArea)
        {
            var sheet = this._sheet;
            var tempCol = col;
            if(sheet._render._isCellHiddenByGroup(0,tempCol,sheetArea))
            {
                tempCol = sheet._getPrevVisualColumn(tempCol,sheetArea);
                return tempCol !== null ? tempCol : col
            }
            else
                return col
        },
        _getResizeRowInfo: function(sheet, target, resizeArea, sheetArea, y)
        {
            var op = null;
            var rowLayout = sheet._getRowLayout(target.rowViewportIndex,sheetArea);
            if(!isNaN(target.row) && !isNaN(target.col))
            {
                target.row = this._getPrevVisualRowBeforeFindRow(target.row,sheetArea);
                var rl = rowLayout.findRow(target.row);
                if(rl)
                    if(rl.y + rl.height - resizeArea < y && y < rl.y + rl.height + resizeArea)
                    {
                        op = {
                            action: "sizeRow",
                            index: target.row,
                            sheetArea: sheetArea
                        };
                        var lastRow = sheet.getRowCount(sheetArea) - 1;
                        if(lastRow >= 0)
                        {
                            var lastVisibleRow = sheet._getLastVisualRow(sheetArea);
                            if(op.index === lastVisibleRow && op.index !== lastRow)
                                if(rl.y + rl.height - resizeArea / 2 < y)
                                    op = {
                                        action: "sizeRow",
                                        index: lastRow,
                                        sheetArea: sheetArea
                                    }
                        }
                    }
                    else if(rl.y - resizeArea < y && y < rl.y + resizeArea)
                        if(rowLayout.indexOf(rl) > 0)
                            op = {
                                action: "sizeRow",
                                index: target.row - 1,
                                sheetArea: sheetArea
                            }
            }
            if(op && !sheet.getRowResizable(op.index,sheetArea))
                op = null;
            return op
        },
        _getResizeColInfo: function(sheet, target, resizeArea, sheetArea, x)
        {
            var op = null;
            var colLayout = sheet._getColumnLayout(target.colViewportIndex,sheetArea);
            if(!isNaN(target.col) && !isNaN(target.row))
            {
                target.col = this._getPrevVisualColBeforeFindCol(target.col,sheetArea);
                var cl = colLayout.findCol(target.col);
                if(cl)
                    if(cl.x + cl.width - resizeArea < x && x < cl.x + cl.width + resizeArea)
                    {
                        op = {
                            action: "sizeCol",
                            index: target.col,
                            sheetArea: sheetArea
                        };
                        var lastCol = sheet.getColumnCount(sheetArea) - 1;
                        if(lastCol >= 0)
                        {
                            var lastVisibleCol = sheet._getLastVisualColumn(sheetArea);
                            if(op.index === lastVisibleCol && op.index !== lastCol)
                                if(cl.x + cl.width - resizeArea / 2 < x)
                                    op = {
                                        action: "sizeCol",
                                        index: lastCol,
                                        sheetArea: sheetArea
                                    }
                        }
                    }
                    else if(cl.x - resizeArea < x && x < cl.x + resizeArea)
                        if(colLayout.indexOf(cl) > 0)
                            op = {
                                action: "sizeCol",
                                index: target.col - 1,
                                sheetArea: sheetArea
                            }
            }
            if(op && !sheet.getColumnResizable(op.index,sheetArea))
                op = null;
            return op
        },
        getResizingRowCol: function(target, x, y)
        {
            var sheet = this._sheet;
            var op = null,
                r,
                c;
            if(!sheet.parent || sheet.parent._allowUserResize)
            {
                var resizeArea = this.getResizingArea();
                var sheetLayout = sheet._getSheetLayout();
                if(target.rowViewportIndex < 0 && target.colViewportIndex >= 0)
                {
                    op = this._getResizeColInfo(sheet,target,resizeArea,GrapeCity.UI.SheetArea.colHeader,x);
                    if(!op)
                        op = this._getResizeRowInfo(sheet,target,resizeArea,GrapeCity.UI.SheetArea.colHeader,y)
                }
                else if(target.rowViewportIndex >= 0 && target.colViewportIndex < 0)
                {
                    op = this._getResizeRowInfo(sheet,target,resizeArea,GrapeCity.UI.SheetArea.rowHeader,y);
                    if(!op)
                        op = this._getResizeColInfo(sheet,target,resizeArea,GrapeCity.UI.SheetArea.rowHeader,x)
                }
                else if(target.rowViewportIndex < 0 && target.colViewportIndex < 0)
                {
                    if(sheet._getLastVisualRow(GrapeCity.UI.SheetArea.rowHeader) === null && Math.abs(y - sheetLayout.colHeaderHeight) <= resizeArea)
                    {
                        r = sheet.getRowCount() - 1;
                        if(r >= 0)
                            op = {
                                action: "sizeRow",
                                index: r,
                                sheetArea: GrapeCity.UI.SheetArea.rowHeader
                            }
                    }
                    else if(sheet._getLastVisualColumn(GrapeCity.UI.SheetArea.colHeader) === null && Math.abs(x - sheetLayout.rowHeaderWidth) <= resizeArea)
                    {
                        c = sheet.getColumnCount() - 1;
                        if(c >= 0)
                            op = {
                                action: "sizeCol",
                                index: c,
                                sheetArea: GrapeCity.UI.SheetArea.colHeader
                            }
                    }
                }
                else if(target.rowViewportIndex >= 0 && target.colViewportIndex >= 0)
                    if(sheetLayout.colHeaderHeight === 0 && y <= resizeArea)
                    {
                        r = sheet.getRowCount(GrapeCity.UI.SheetArea.colHeader) - 1;
                        if(r >= 0)
                            op = {
                                action: "sizeRow",
                                index: r,
                                sheetArea: GrapeCity.UI.SheetArea.colHeader
                            }
                    }
                    else if(sheetLayout.rowHeaderWidth === 0 && x <= resizeArea)
                    {
                        c = sheet.getColumnCount(GrapeCity.UI.SheetArea.rowHeader) - 1;
                        if(c >= 0)
                            op = {
                                action: "sizeCol",
                                index: c,
                                sheetArea: GrapeCity.UI.SheetArea.rowHeader
                            }
                    }
            }
            return op
        },
        getDragInfo: function(target, x, y)
        {
            var op = null;
            var sheet = this._sheet;
            var activeSelRange = sheet._getActiveSelectedRange();
            if(activeSelRange.row === -1 && activeSelRange.col === -1)
                return op;
            if(target.rowViewportIndex >= 0 && target.colViewportIndex >= 0 && sheet._selectionModel.length === 1)
            {
                var r = sheet._getActiveSelectionRect(target.rowViewportIndex,target.colViewportIndex);
                if(r.x - 4 < x && x < r.x + 4 && r.y <= y && y < r.y + r.height)
                    op = {
                        action: "drag",
                        side: "left"
                    };
                if(!op)
                {
                    var rect = this._sheet._dragFillIndicatorRect;
                    if(rect && rect.x <= x && x <= rect.x + rect.width && rect.y <= y && y <= rect.y + rect.height)
                        op = {
                            action: "drag",
                            side: "corner"
                        }
                }
                if(!op)
                    if(r.x + r.width - 4 < x && x < r.x + r.width + 4 && r.y <= y && y < r.y + r.height)
                        op = {
                            action: "drag",
                            side: "right"
                        };
                if(!op)
                    if(r.y - 4 < y && y < r.y + 4 && r.x <= x && x < r.x + r.width)
                        op = {
                            action: "drag",
                            side: "top"
                        };
                if(!op)
                    if(r.y + r.height - 4 < y && y < r.y + r.height + 4 && r.x <= x && x < r.x + r.width)
                        op = {
                            action: "drag",
                            side: "bottom"
                        };
                if(op)
                    if(x < r.x || x > r.x + r.width || y < r.y || y > r.y + r.height)
                        op.outside = true
            }
            if(!this._sheet.canUserDragDrop())
                if(op && op.side !== "corner")
                    op.side = null;
            if(!this._sheet.canUserDragFill())
                if(op && op.side === "corner")
                    op.side = null;
            return op
        },
        doMouseMove: function(e)
        {
            var t = this._getCanvasOffset();
            this.doMouseMoveImp(e,e.pageX - t.left,e.pageY - t.top)
        },
        doMouseOut: function(e)
        {
            var sheet = this._sheet;
            var oldTarget = sheet._currentTarget;
            if(oldTarget && oldTarget.cellTypeHitInfo)
            {
                var row = oldTarget.row,
                    col = oldTarget.col;
                var celltype = sheet.getCellType(row,col,oldTarget.hitTestType);
                celltype.processMouseLeave(oldTarget.cellTypeHitInfo)
            }
            if(this.isMouseDown)
                return;
            var outTarget = {
                    x: -10000,
                    y: -10000,
                    rowViewportIndex: null,
                    colViewportIndex: null,
                    row: -1,
                    col: -1,
                    resizeInfo: null,
                    hitTestType: null
                };
            sheet._setHoverCell(outTarget)
        },
        doMouseMoveImp: function(e, x, y)
        {
            var sheet = this._sheet;
            if(this.isMouseDown)
                if(!window.gcGlobal.activeElement)
                    window.gcGlobal.activeElement = sheet;
            if(this.isMouseDown && this.isWorking)
            {
                var currentTarget = sheet._currentTarget;
                var resizeInfo = currentTarget.resizeInfo;
                if(resizeInfo)
                {
                    if(currentTarget.x === x && currentTarget.y === y)
                        return;
                    var sheetLayout = sheet._getSheetLayout();
                    if(currentTarget.rowViewportIndex === -1 && currentTarget.colViewportIndex === -1)
                    {
                        currentTarget.rowViewportIndex = 1;
                        currentTarget.colViewportIndex = 1
                    }
                    var max;
                    if(resizeInfo.action === "sizeRow")
                    {
                        var rowLayout = sheet._getRowLayout(currentTarget.rowViewportIndex,resizeInfo.sheetArea);
                        resizeInfo.index = this._getPrevVisualRowBeforeFindRow(resizeInfo.index,resizeInfo.sheetArea);
                        var rl = rowLayout.findRow(resizeInfo.index);
                        if(!rl)
                            rl = rowLayout.findRow(currentTarget.row);
                        resizeInfo.startY = rl.y;
                        resizeInfo.movingY = y;
                        if(resizeInfo.movingY < rl.y)
                            resizeInfo.movingY = rl.y;
                        max = sheetLayout.y + sheetLayout.height;
                        if(resizeInfo.movingY > max)
                            resizeInfo.movingY = max;
                        sheet._render.update(sheetLayout.x,resizeInfo.startY,sheetLayout.width,sheetLayout.height - resizeInfo.startY,true)
                    }
                    else
                    {
                        var colLayout = sheet._getColumnLayout(currentTarget.colViewportIndex,resizeInfo.sheetArea);
                        resizeInfo.index = this._getPrevVisualColBeforeFindCol(resizeInfo.index,resizeInfo.sheetArea);
                        var cl = colLayout.findCol(resizeInfo.index);
                        if(!cl)
                            cl = colLayout.findCol(currentTarget.col);
                        resizeInfo.startX = cl.x;
                        resizeInfo.movingX = x;
                        if(resizeInfo.movingX < cl.x)
                            resizeInfo.movingX = cl.x;
                        max = sheetLayout.x + sheetLayout.width;
                        if(resizeInfo.movingX > max)
                            resizeInfo.movingX = max;
                        sheet._render.update(resizeInfo.startX,sheetLayout.y,sheetLayout.width - resizeInfo.startX,sheetLayout.height,true)
                    }
                }
                else if(currentTarget.dragInfo && currentTarget.dragInfo.side !== "corner")
                    this.continueDragDropping(e,x,y);
                else if(currentTarget.dragInfo && currentTarget.dragInfo.side === "corner")
                    this.continueDragFill(e,x,y);
                else
                    this.continueSelecting(e,x,y)
            }
            else
            {
                var target = sheet.hitTest(x,y);
                if(!target)
                    return;
                var canvas = sheet._getCanvas();
                if(!canvas)
                    return;
                var oldTarget = sheet._currentTarget;
                var targetChanged = !oldTarget || target.row !== oldTarget.row || target.col !== oldTarget.col || target.hitTestType !== oldTarget.hitTestType || target.resizeInfo && !oldTarget.resizeInfo || !target.resizeInfo && oldTarget.resizeInfo || target.resizeInfo && target.resizeInfo.action !== oldTarget.resizeInfo.action || target.dragInfo && !oldTarget.dragInfo || !target.dragInfo && oldTarget.dragInfo || target.dragInfo && target.dragInfo.action !== oldTarget.dragInfo.action;
                if(oldTarget && targetChanged)
                    if(oldTarget.cellTypeHitInfo)
                    {
                        var row = oldTarget.row,
                            col = oldTarget.col;
                        var celltype = sheet.getCellType(row,col,oldTarget.hitTestType);
                        celltype.processMouseLeave(oldTarget.cellTypeHitInfo)
                    }
                var op = target.resizeInfo;
                if(op)
                    if(op.action === "sizeCol")
                        canvas.style.cursor = "w-resize";
                    else if(op.action === "sizeRow")
                        canvas.style.cursor = "n-resize";
                    else
                        canvas.style.cursor = "default";
                else
                {
                    op = target.dragInfo;
                    if(op && op.action === "drag")
                    {
                        if(op.side === "corner")
                            canvas.style.cursor = "crosshair";
                        else if(op.side)
                            canvas.style.cursor = "move"
                    }
                    else
                    {
                        if(target.cellTypeHitInfo)
                        {
                            var row = target.row,
                                col = target.col;
                            var celltype = sheet.getCellType(row,col,target.hitTestType);
                            target.cellTypeHitInfo.sheet = sheet;
                            if(targetChanged)
                                celltype.processMouseEnter(target.cellTypeHitInfo);
                            celltype.processMouseMove(target.cellTypeHitInfo)
                        }
                        if(targetChanged || !target.cellTypeHitInfo || !target.cellTypeHitInfo.isReservedLocation)
                            canvas.style.cursor = "default"
                    }
                }
                if(target)
                    sheet._setHoverCell(target)
            }
        },
        doMouseUp: function(e)
        {
            var sheet = this._sheet;
            if(!sheet.inCanvas)
                this.unhandleDocumentMouseMove();
            var t = this._getCanvasOffset();
            this.doMouseUpImp(e,e.pageX - t.left,e.pageY - t.top);
            t = sheet._isMouseDownInSheet;
            return!t
        },
        doMouseUpImp: function(e, x, y)
        {
            var sheet = this._sheet;
            if(sheet._isMouseDownInSheet !== true)
                return;
            else
                sheet._isMouseDownInSheet = false;
            var ae = window.gcGlobal.activeElement;
            this.isMouseDown = false;
            if(ae && ae !== sheet && ae.endEdit)
            {
                ae.endEdit();
                ae.repaint()
            }
            var currentTarget = sheet._currentTarget;
            if(sheet._currentTarget)
            {
                var resizeInfo = currentTarget.resizeInfo;
                var value,
                    i,
                    selectedRange,
                    action;
                if(resizeInfo)
                {
                    if(resizeInfo.action === "sizeRow")
                    {
                        if(resizeInfo.movingY !== undefined && resizeInfo.movingY !== null)
                        {
                            value = Math.max(0,(resizeInfo.movingY - resizeInfo.startY) / sheet._zoomFactor);
                            var rowList = [];
                            if(sheet._isRowSelected(resizeInfo.index))
                                for(i = 0; i < sheet._selectionModel.length; i++)
                                {
                                    selectedRange = sheet._selectionModel[i];
                                    if(selectedRange.col === -1)
                                    {
                                        selectedRange = sheet._getActualRange(selectedRange);
                                        rowList.push({
                                            firstRow: selectedRange.row,
                                            lastRow: selectedRange.row + selectedRange.rowCount - 1
                                        })
                                    }
                                }
                            else
                                rowList.push({
                                    firstRow: resizeInfo.index,
                                    lastRow: resizeInfo.index
                                });
                            var isColHeader = resizeInfo.sheetArea === GrapeCity.UI.SheetArea.colHeader;
                            action = new GrapeCity.UI.UndoRedo.RowResizeUndoAction(sheet,rowList,value,isColHeader);
                            sheet._doCommand(action)
                        }
                    }
                    else if(resizeInfo.movingX !== undefined && resizeInfo.movingX !== null)
                    {
                        value = Math.max(0,(resizeInfo.movingX - resizeInfo.startX) / sheet._zoomFactor);
                        var columnList = [];
                        if(sheet._isColumnSelected(resizeInfo.index))
                            for(i = 0; i < sheet._selectionModel.length; i++)
                            {
                                selectedRange = sheet._selectionModel[i];
                                if(selectedRange.row === -1)
                                {
                                    selectedRange = sheet._getActualRange(selectedRange);
                                    columnList.push({
                                        firstCol: selectedRange.col,
                                        lastCol: selectedRange.col + selectedRange.colCount - 1
                                    })
                                }
                            }
                        else
                            columnList.push({
                                firstCol: resizeInfo.index,
                                lastCol: resizeInfo.index
                            });
                        var isRowHeader = resizeInfo.sheetArea === GrapeCity.UI.SheetArea.rowHeader;
                        action = new GrapeCity.UI.UndoRedo.ColumnResizeUndoAction(sheet,columnList,value,isRowHeader);
                        sheet._doCommand(action)
                    }
                    this.isWorking = false
                }
                else if(sheet._currentTarget.dragInfo && !this.isDraggingFill)
                {
                    this.stopDragDrop(e);
                    sheet._currentTarget = null
                }
                else if(sheet._currentTarget.dragInfo && this.isDraggingFill === true)
                {
                    this.endDragFill(e);
                    sheet._currentTarget = null
                }
                else
                {
                    if(sheet._currentTarget.cellTypeHitInfo)
                    {
                        var r = sheet._currentTarget.row,
                            c = sheet._currentTarget.col;
                        var ct = sheet.getCellType(r,c,sheet._currentTarget.hitTestType);
                        sheet._currentTarget.cellTypeHitInfo.sheet = sheet;
                        ct.processMouseUp(sheet._currentTarget.cellTypeHitInfo)
                    }
                    if(!sheet._currentTarget.cellTypeHitInfo || !sheet._currentTarget.cellTypeHitInfo.isReservedLocation)
                        sheet._trigger(GrapeCity.UI.Events.CellClick,{
                            sheet: sheet,
                            sheetName: sheet._name,
                            sheetArea: sheet._currentTarget.hitTestType,
                            row: sheet._currentTarget.row,
                            col: sheet._currentTarget.col
                        })
                }
            }
            window.gcGlobal.activeElement = sheet;
            this.stopSelecting();
            this.setMetaKeyState(e)
        },
        startEdit: function(e)
        {
            var sheet = this._sheet;
            var canvas = sheet._getCanvas();
            var t = this._getCanvasOffset();
            if(!e.shiftKey && !e.ctrlKey)
                sheet._doStartEdit(canvas,e.pageX - t.left,e.pageY - t.top)
        },
        doKeyDown: function(event)
        {
            var sheet = this._sheet;
            var navKey = !sheet.isEditing() && !event.ctrlKey && (event.keyCode === GrapeCity.UI.Key.pdn || event.keyCode === GrapeCity.UI.Key.pup || event.keyCode === GrapeCity.UI.Key.left || event.keyCode === GrapeCity.UI.Key.right || event.keyCode === GrapeCity.UI.Key.tab || event.keyCode === GrapeCity.UI.Key.up || event.keyCode === GrapeCity.UI.Key.down);
            if(navKey)
            {
                if(this._keyPressed)
                {
                    if(this._keyPressedCount < 5)
                        this._keyPressedCount++;
                    GrapeCity.UI.cancelDefault(event);
                    return
                }
                this._keyPressed = true;
                this._keyPressedCount = 1;
                this._repeatKeyDown(event)
            }
            else
                this._keyDownImp(event)
        },
        _repeatKeyDown: function(event)
        {
            if(!this._keyPressed)
                return;
            var self = this;
            self._keyDownImp(event);
            if(this._keyPressed)
                window.setTimeout(function()
                {
                    self._repeatKeyDown(event)
                },100 / this._keyPressedCount)
        },
        _keyDownImp: function(event)
        {
            var sheet = this._sheet;
            this.setMetaKeyState(event);
            if(event.keyCode === GrapeCity.UI.Key.tab)
                GrapeCity.UI.cancelDefault(event);
            if(!sheet.isEditing() && event.keyCode === GrapeCity.UI.Key.esc && !event.altKey && !event.ctrlKey && !event.shiftKey && sheet._validationInputMessage)
            {
                $(sheet._validationInputMessage).remove();
                sheet._validationInputMessage = null
            }
            if(!sheet.isEditing() && (event.keyCode === GrapeCity.UI.Key.pdn || event.keyCode === GrapeCity.UI.Key.pup || event.keyCode === GrapeCity.UI.Key.end || event.keyCode === GrapeCity.UI.Key.home || event.keyCode === GrapeCity.UI.Key.up || event.keyCode === GrapeCity.UI.Key.down))
                GrapeCity.UI.cancelDefault(event);
            if(!sheet.isEditing() && (event.keyCode === GrapeCity.UI.Key.left || event.keyCode === GrapeCity.UI.Key.right))
                GrapeCity.UI.cancelDefault(event);
            if(!sheet._processKeyMap(event))
                return;
            var c = sheet._getCanvas();
            if(c && this.allowEnterEditing(event))
            {
                sheet._startEditByKeydown = true;
                try
                {
                    var isEditing = sheet.isEditing();
                    sheet._startEditImp(c,sheet._activeRowIndex,sheet._activeColIndex,null,null,true);
                    if(!isEditing && sheet.isEditing())
                    {
                        var ct = sheet.getCellType(sheet._activeRowIndex,sheet._activeColIndex);
                        if(ct._triggerButtonClicked && event.keyCode === UI.Key.space && !event.ctrlKey && !event.shiftKey && !event.altKey)
                            ct._triggerButtonClicked(sheet,sheet._activeRowIndex,sheet._activeColIndex);
                        if(ct._cancelDefaultKeydown)
                            ct._cancelDefaultKeydown(event)
                    }
                }
                finally
                {
                    sheet._startEditByKeydown = false
                }
            }
        },
        doKeyUp: function(event)
        {
            this._keyPressed = false;
            this.setMetaKeyState(event)
        },
        allowEnterEditing: function(e)
        {
            if(e.ctrlKey || e.altKey)
                return false;
            if(e.keyCode >= 65 && e.keyCode <= 90)
                return true;
            else if(e.keyCode >= 48 && e.keyCode <= 57 || e.keyCode >= 96 && e.keyCode <= 105)
                return true;
            else if(e.keyCode >= 186 && e.keyCode <= 192)
                return true;
            else if(e.keyCode >= 220 && e.keyCode <= 222 || e.keyCode === 219)
                return true;
            else if(e.keyCode >= 106 && e.keyCode <= 111)
                return true;
            else if(e.keyCode === 32)
                return true;
            else if(e.keyCode === 61)
                return true;
            else if(e.keyCode === 173)
                return true;
            else if(e.keyCode === 229 || e.keyCode === 0)
                return true;
            return false
        },
        setMetaKeyState: function(e)
        {
            var sheet = this._sheet;
            this.shift = e.shiftKey && !e.ctrlKey;
            this.ctrl = e.ctrlKey && !e.shiftKey;
            sheet._isNavigateInSelection = false;
            if(e.keyCode === GrapeCity.UI.Key.tab)
                if(sheet._selectionModel.length > 1)
                    sheet._isNavigateInSelection = true;
                else
                {
                    var range = sheet._getActiveSelectedRange();
                    if(range && sheet._selectionModel.length > 0)
                        sheet._isNavigateInSelection = !(sheet._activeRowIndex === range.row && sheet._activeColIndex === range.col && sheet._activeRowCount === range.rowCount && sheet._activeColCount === range.colCount)
                }
            if(this.isDragDropping === true)
            {
                var activeSelRange = sheet._getActiveSelectedRange();
                var tempInsert = this._isDragInsert;
                var tempCopy = this._isDragCopy;
                if(activeSelRange.row === -1 || activeSelRange.col === -1)
                    this._isDragInsert = e.shiftKey;
                else
                    this._isDragInsert = false;
                this._isDragCopy = e.ctrlKey;
                if(tempInsert !== this._isDragInsert || tempCopy !== this._isDragCopy)
                    sheet._render.refreshDragDropIndicator()
            }
            this.isShiftPressed = e.shiftKey;
            this.isControlPressed = e.ctrlKey
        },
        doMouseWheel: function(event, detail)
        {
            var sheet = this._sheet;
            if(!sheet.endEdit())
                return;
            if(event.ctrlKey)
            {
                if(!sheet.parent || sheet.parent._allowUserZoom)
                {
                    var oldZoomFactor = sheet._zoomFactor;
                    var newZoomFactor = sheet._zoomFactor - 0.05 * detail;
                    var action = new GrapeCity.UI.UndoRedo.ZoomUndoAction(sheet,newZoomFactor);
                    sheet._doCommand(action);
                    newZoomFactor = sheet._zoomFactor;
                    if(oldZoomFactor !== newZoomFactor)
                        sheet._trigger(GrapeCity.UI.Events.UserZooming,{
                            sheet: sheet,
                            sheetName: sheet._name,
                            oldZoomFactor: oldZoomFactor,
                            newZoomFactor: newZoomFactor
                        });
                    return
                }
            }
            else
            {
                var oldTopRow = sheet._scrollTopRow;
                if(sheet.frozenRowCount > 0)
                    if(sheet._scrollTopRow === 0 && detail > 0)
                        sheet._scrollTopRow = sheet.frozenRowCount;
                    else if(detail < 0 && sheet._scrollTopRow === sheet.frozenRowCount - detail)
                        sheet._scrollTopRow = 0;
                sheet._scrollTopRow = sheet._scrollTopRow + detail;
                if(sheet._scrollTopRow < sheet._getFirstPageTopRow())
                    sheet._scrollTopRow = sheet._getFirstPageTopRow();
                else if(sheet._scrollTopRow > sheet._getLastVisualRow())
                    sheet._scrollTopRow = sheet._getLastVisualRow();
                if(oldTopRow !== sheet._scrollTopRow)
                    sheet._trigger(GrapeCity.UI.Events.TopRowChanged,{
                        sheet: sheet,
                        sheetName: sheet._name,
                        oldTopRow: oldTopRow,
                        newTopRow: sheet._scrollTopRow
                    });
                this.scrolling = true
            }
            sheet.invalidateLayout();
            sheet.repaint();
            sheet._syncVScrollbarPosition()
        },
        waittime: 100,
        scrollTop: 0,
        scrollLeft: 0,
        oldLeft: 0,
        oldTop: 0,
        doVScroll: function(topRow)
        {
            var sheet = this._sheet;
            if(!sheet.endEdit())
                return;
            this.scrolling = true;
            sheet._scrollTopRow = topRow;
            var self = this;
            if(!this.scrollTimer)
                this.scrollTimer = window.setInterval(function()
                {
                    self._scrollView()
                },this.waittime)
        },
        doHScroll: function(leftColumn)
        {
            var sheet = this._sheet;
            if(!sheet.endEdit())
                return;
            this.scrolling = true;
            sheet._scrollLeftCol = leftColumn;
            var self = this;
            if(!this.scrollTimer)
                this.scrollTimer = window.setInterval(function()
                {
                    self._scrollView()
                },this.waittime)
        },
        _scrollView: function()
        {
            if(!this.scrolling)
            {
                if(this.scrollTimer)
                {
                    window.clearInterval(this.scrollTimer);
                    this.scrollTimer = null;
                    this._sheet.repaint();
                    var bLoadData = this.oldTop !== this._sheet._scrollTopRow;
                    this.oldTop = this._sheet._scrollTopRow;
                    this.oldLeft = this._sheet._scrollLeftCol;
                    if(bLoadData)
                    {
                        var topRow = this._sheet.getViewportTopRow(1);
                        var bottomRow = this._sheet.getViewportBottomRow(1);
                        var start = Math.max(0,topRow - 60);
                        if(start < this._sheet.getRowCount())
                            this._loadData(start,2 * bottomRow - topRow)
                    }
                }
                return
            }
            this.scrolling = false;
            this._updateView()
        },
        _getLoadingProgress: function(parentId)
        {
            var progId = parentId + "_loading";
            var prog = $("#" + progId);
            if(!prog || prog.length === 0)
            {
                var progDiv = document.createElement("div");
                prog = $(progDiv);
                prog.attr("id",progId);
                prog.css("textAlign","center");
                prog.css("background-color","#DCDCDC");
                document.body.insertBefore(progDiv,null)
            }
            prog.css("position","absolute");
            var jParent = $("#" + parentId);
            var pos = jParent.position();
            prog.css("left",pos.left);
            prog.css("top",pos.top);
            prog.css("width",jParent.width());
            prog.css("height",jParent.height());
            return{
                    show: function()
                    {
                        prog.fadeIn('fast');
                        prog.append("postback and loading ...")
                    },
                    hide: function()
                    {
                        prog.fadeOut('fast');
                        prog.html("")
                    }
                }
        },
        _loadData: function(startIndex, endIndex)
        {
            if(!this._sheet.parent || !this._sheet.parent._host)
                return;
            if(this._sheet.dataContext && this._sheet.dataContext.read)
            {
                var self = this;
                this._dataContextLoadDelegate = function(event, context)
                {
                    return self._dataContextLoaded(event,context)
                };
                this.lastLoadedRowIndex = startIndex;
                jQuery.ajax({
                    url: this._sheet.dataContext.read,
                    type: "POST",
                    data: JSON.stringify({
                        index: startIndex,
                        count: endIndex - startIndex + 1
                    }),
                    dataType: "json",
                    contentType: "application/json;charset=UTF-8",
                    success: this._dataContextLoadDelegate
                })
            }
            else
            {
                var id = this._sheet.parent._host.id;
                if(id && id.length > 0 && typeof window.WebForm_DoCallback !== "undefined")
                {
                    this.lastLoadedRowIndex = startIndex;
                    this._doLoadData(id,startIndex,endIndex)
                }
            }
        },
        _dataContextLoaded: function(result, context)
        {
            var tmp = result.data;
            if(tmp && tmp.length > 0)
            {
                var sheet = this._sheet;
                var ds = sheet.getDataSource();
                if(ds && this.lastLoadedRowIndex === result.start)
                    for(var i = 0; i < tmp.length; i++)
                        ds[result.start + i] = tmp[i];
                sheet.recalcAll();
                sheet.repaint()
            }
        },
        _doCallBack: function(cmd, context)
        {
            var id = this._sheet.parent._host.id;
            if(id && id.length > 0 && typeof window.WebForm_DoCallback !== "undefined")
            {
                var form = $(document.forms);
                if(!form || form.length === 0)
                    return;
                form = form[0];
                form.__EVENTTARGET.value = id;
                form.__EVENTARGUMENT.value = cmd;
                this._sheet.parent.saveData();
                window.__theFormPostCollection.length = 0;
                window.__theFormPostData = "";
                window.WebForm_InitCallback();
                window.__theFormPostData = "gcallback=true&" + window.__theFormPostData;
                if(form["__EVENTVALIDATION"])
                    window.__theFormPostData += "&__EVENTVALIDATION=" + WebForm_EncodeCallback(form["__EVENTVALIDATION"].value);
                var href = form.action;
                var loadProg = this._getLoadingProgress(id);
                if(loadProg)
                    loadProg.show();
                var self = this;
                this._updateDelegate = function(event, context)
                {
                    loadProg.hide();
                    return self._onUpdateCallback(event,context)
                };
                this._errorDelegate = function(event, contex)
                {
                    loadProg.hide()
                };
                $.ajax({
                    url: href,
                    type: "POST",
                    data: window.__theFormPostData,
                    dataType: "html",
                    contentType: "application/x-www-form-urlencoded, charset=UTF-8",
                    success: this._updateDelegate,
                    error: this._errorDelegate
                })
            }
        },
        _doLoadData: function(id, startIndex, endIndex)
        {
            var context = {
                    start: startIndex,
                    end: endIndex
                };
            var dirty = this._sheet.hasPendingChanges();
            var self = this;
            if(dirty)
            {
                var form = $(document.forms);
                if(!form || form.length === 0)
                    return;
                form = form[0];
                var cmd = "loadData," + startIndex + "," + endIndex;
                form.__EVENTTARGET.value = id;
                form.__EVENTARGUMENT.value = cmd;
                this._sheet.parent.saveData();
                window.__theFormPostCollection.length = 0;
                window.__theFormPostData = "";
                window.WebForm_InitCallback();
                window.__theFormPostData = "gcallback=true&" + window.__theFormPostData;
                if(form["__EVENTVALIDATION"])
                    window.__theFormPostData += "&__EVENTVALIDATION=" + WebForm_EncodeCallback(form["__EVENTVALIDATION"].value);
                var href = form.action;
                var loadProg = this._getLoadingProgress(id);
                if(loadProg)
                    loadProg.show();
                this._updateDelegate = function(event, context)
                {
                    loadProg.hide();
                    return self._onUpdateCallback(event,context)
                };
                this._errorDelegate = function(event, contex)
                {
                    loadProg.hide()
                };
                $.ajax({
                    url: href,
                    type: "POST",
                    data: window.__theFormPostData,
                    dataType: "html",
                    contentType: "application/x-www-form-urlencoded, charset=UTF-8",
                    success: this._updateDelegate,
                    error: this._errorDelegate
                })
            }
            else
            {
                this._loadDataDelegate = function(event, context)
                {
                    return self._onLoadDataCallback(event,context)
                };
                this._sheet.parent.saveData();
                window.__theFormPostCollection.length = 0;
                window.__theFormPostData = "";
                window.WebForm_InitCallback();
                window.WebForm_DoCallback(id,"loadData," + startIndex + "," + endIndex,this._loadDataDelegate,context,null,true)
            }
        },
        _onUpdateCallback: function(result, context)
        {
            this._sheet.clearPendingChanges();
            var doc = document;
            try
            {
                doc.selection.empty()
            }
            catch(e){}
            var buff = doc.createElement("div");
            if(!buff)
                return;
            buff.innerHTML = result;
            var form = $(document.forms);
            if(!form || form.length === 0)
                return;
            form = form[0];
            form.__EVENTTARGET.value = "";
            form.__EVENTARGUMENT.value = "";
            var old = doc.getElementById("__VIEWSTATE");
            var t = this.getElementById(buff,"__VIEWSTATE");
            if(old && t)
                old.value = t.value;
            old = doc.getElementById("__EVENTVALIDATION");
            t = this.getElementById(buff,"__EVENTVALIDATION");
            if(old && t)
                old.value = t.value;
            var id = this._sheet.parent._host.id;
            old = document.getElementById(id + "_data");
            t = this.getElementById(buff,id + "_data");
            if(old && t)
                old.value = t.value;
            buff.innerHTML = "";
            this._sheet.parent._loadData(true);
            this._sheet.parent.invalidateLayout();
            this._sheet.parent.repaint()
        },
        getElementById: function(parent, id)
        {
            if(!parent)
                return null;
            var t = parent.firstChild;
            while(t)
            {
                if(t.id === id || t.name === id)
                    return t;
                var t1 = this.getElementById(t,id);
                if(t1)
                    return t1;
                t = t.nextSibling
            }
            return null
        },
        _onLoadDataCallback: function(result, context)
        {
            if(!result)
                return;
            var sheet = this._sheet;
            var data = JSON.parse(result);
            if(!data)
                return;
            var tmp = data.dataSource,
                i;
            if(tmp && tmp.length > 0)
            {
                var ds = sheet.getDataSource();
                if(ds && this.lastLoadedRowIndex === context.start)
                {
                    var dataSource = JSON.parse(tmp);
                    for(i = 0; i < dataSource.length; i++)
                        ds[context.start + i] = dataSource[i]
                }
            }
            var sheetData = data.data;
            if(sheetData)
                if(this.lastLoadedRowIndex === context.start)
                {
                    for(i in sheetData)
                        if(typeof i !== "function")
                            sheet._dataModel.dataTable[i] = sheetData[i];
                    if(data.rows)
                        for(i in data.rows)
                            if(typeof i !== "function")
                                sheet._rowInfos[i] = data.rows[i];
                    sheet.suspendCalcService();
                    sheet.suspendEvent();
                    sheet.isPaintSuspended(true);
                    var columnCount = sheet.getColumnCount();
                    for(i in sheetData)
                        if(typeof i !== "function")
                        {
                            var rowData = sheetData[i];
                            if(rowData)
                                for(var c = 0; c < columnCount; c++)
                                {
                                    var formula = null;
                                    if(rowData[c])
                                        formula = rowData[c].formula;
                                    if(formula)
                                    {
                                        rowData[c].formula = null;
                                        sheet.setFormula(parseInt(i,10),c,formula)
                                    }
                                }
                        }
                    sheet.resumeCalcService();
                    sheet.resumeEvent();
                    sheet.isPaintSuspended(false)
                }
            sheet.suspendEvent();
            sheet.isPaintSuspended(true);
            sheet.recalcAll();
            sheet.resumeEvent();
            sheet.isPaintSuspended(false);
            sheet.clearPendingChanges();
            sheet.repaint()
        },
        _updateView: function()
        {
            var sheet = this._sheet;
            if(this.painting)
                return;
            this.painting = true;
            var painted = false;
            if(this.oldTop !== sheet._scrollTopRow && this.oldLeft === sheet._scrollLeftCol)
                painted = this._vScrollView(sheet);
            else if(this.oldTop === sheet._scrollTopRow && this.oldLeft !== sheet._scrollLeftCol)
                painted = this._hScrollView(sheet);
            if(!painted)
            {
                sheet.invalidateLayout();
                sheet.repaint()
            }
            this.painting = false
        },
        _vScrollView: function(sheet)
        {
            var painted = false,
                oldTop = this.oldTop,
                sl;
            if(sheet._scrollTopRow > oldTop)
            {
                var rls = sheet._getRowLayout(1,GrapeCity.UI.SheetArea.viewport);
                var rl = rls.findRow(sheet._scrollTopRow);
                if(rl)
                {
                    painted = true;
                    sl = sheet._getSheetLayout();
                    sheet._render.translateScreen(sl.x,rl.y,sl.rowHeaderWidth + sl.frozenWidth + sl.viewportWidth,sl.viewportHeight - rl.y,sl.x,sl.viewportY);
                    sheet.invalidateLayout();
                    sheet._render.update(sl.x,sl.viewportY + sl.viewportHeight - rl.y,sl.rowHeaderWidth + sl.frozenWidth + sl.viewportWidth,rl.y,false)
                }
            }
            else
            {
                sl = sheet._getSheetLayout();
                var h = 0;
                for(var i = sheet._scrollTopRow; i < oldTop && h < sl.viewportHeight; i++)
                    h += sheet._getZoomRowHeight(i);
                if(h < sl.viewportHeight)
                {
                    painted = true;
                    sheet._render.translateScreen(sl.x,sl.viewportY,sl.rowHeaderWidth + sl.frozenWidth + sl.viewportWidth,sl.viewportHeight - h,sl.x,sl.viewportY + h);
                    sheet.invalidateLayout();
                    sheet._render.update(sl.x,sl.viewportY,sl.rowHeaderWidth + sl.frozenWidth + sl.viewportWidth,h,false)
                }
            }
            return painted
        },
        _hScrollView: function(sheet)
        {
            var painted = false,
                oldLeft = this.oldLeft,
                sl;
            if(sheet._scrollLeftCol > oldLeft)
            {
                var cls = sheet._getColumnLayout(1,GrapeCity.UI.SheetArea.viewport);
                var cl = cls.findCol(sheet._scrollLeftCol);
                if(cl)
                {
                    painted = true;
                    sl = sheet._getSheetLayout();
                    sheet._render.translateScreen(cl.x,sl.y,sl.viewportWidth - cl.x,sl.colHeaderHeight + sl.frozenHeight + sl.viewportHeight,sl.viewportX,sl.y);
                    sheet.invalidateLayout();
                    sheet._render.update(sl.viewportX + sl.viewportWidth - cl.x,sl.y,cl.x,sl.colHeaderHeight + sl.frozenHeight + sl.viewportHeight,false)
                }
            }
            else
            {
                sl = sheet._getSheetLayout();
                var w = 0;
                for(var i = sheet._scrollLeftCol; i < oldLeft && w < sl.viewportWidth; i++)
                    w += sheet._getZoomColumnWidth(i);
                if(w < sl.viewportWidth)
                {
                    painted = true;
                    sheet._render.translateScreen(sl.viewportX,sl.y,sl.viewportWidth - w,sl.colHeaderHeight + sl.frozenHeight + sl.viewportHeight,sl.viewportX + w,sl.y);
                    sheet.invalidateLayout();
                    sheet._render.update(sl.viewportX,sl.y,w,sl.colHeaderHeight + sl.frozenHeight + sl.viewportHeight,false)
                }
            }
            return painted
        },
        _notEqualSelecions: function(oldSelections, newSelections)
        {
            var notEqual = true;
            if(oldSelections.length === newSelections.length)
                for(var i = 0; i < oldSelections.length; i++)
                {
                    var oldItem = oldSelections[i];
                    var newItem = newSelections[i];
                    if(oldItem.row !== newItem.row || oldItem.col !== newItem.col || oldItem.rowCount !== newItem.rowCount || oldItem.colCount !== newItem.colCount)
                    {
                        notEqual = true;
                        break
                    }
                    else
                        notEqual = false
                }
            return notEqual
        },
        _setFocus: function()
        {
            if(!this._focusHolder)
            {
                var focusHolder = document.createElement("div");
                focusHolder.style.position = "relative";
                focusHolder.style.overflow = "hidden";
                focusHolder.style.width = "0px";
                focusHolder.style.height = "0px";
                var h = null;
                var c = this._sheet._getCanvas();
                if(c)
                    h = c.parentNode;
                if(h)
                    h.insertBefore(focusHolder,c);
                this._focusHolder = focusHolder
            }
            if(!this._focusElem)
            {
                var focusElem = document.createElement("input");
                focusElem.type = "text";
                focusElem.style.position = "absolute";
                focusElem.className = "gcSheetFocusInputStyle";
                focusElem.setAttribute("gcUIElement","gcSheetFocusInput");
                this._focusHolder.insertBefore(focusElem,null);
                this._focusElem = focusElem
            }
            var ar,
                ac,
                arvi,
                acvi;
            if(this._hitTestResult)
            {
                ar = this._hitTestResult.row;
                ac = this._hitTestResult.col;
                arvi = this._hitTestResult.rowViewportIndex;
                acvi = this._hitTestResult.colViewportIndex
            }
            else
            {
                ar = this._sheet.getActiveRowIndex();
                ac = this._sheet.getActiveColumnIndex();
                arvi = this._sheet.activeRowViewportIndex;
                acvi = this._sheet.activeColViewportIndex
            }
            var activeCellRect = this._sheet._getRangeRect(arvi,acvi,{
                    row: ar,
                    col: ac,
                    rowCount: 1,
                    colCount: 1
                });
            this._focusHolder.style.left = "" + (activeCellRect.x + 1) + "px";
            this._focusHolder.style.top = "" + (activeCellRect.y + 1) + "px";
            document.body.focus();
            this._focusElem.focus();
            if(!window.gcGlobal.activeElement)
            {
                window.gcGlobal.activeElement = this._sheet;
                if(this._sheet._eventHandler)
                    this._sheet._eventHandler._updateValidationUI(this._sheet._activeRowIndex,this._sheet._activeColIndex)
            }
        },
        bind: function(type, data, fn)
        {
            $(this._getElem()).bind(type,data,fn)
        },
        unbind: function(type, fn)
        {
            $(this._getElem()).unbind(type,fn)
        },
        trigger: function(type, data)
        {
            if(this._eventSuspended === 0)
                $(this._getElem()).trigger(type,data)
        }
    };
    UI.Events = {
        ValidationError: "ValidationError",
        CellClick: "CellClick",
        CellDoubleClick: "CellDoubleClick",
        EnterCell: "EnterCell",
        LeaveCell: "LeaveCell",
        ValueChanged: "ValueChanged",
        TopRowChanged: "TopRowChanged",
        LeftColumnChanged: "LeftColumnChanged",
        InvalidOperation: "InvalidOperation",
        RangeFiltering: "RangeFiltering",
        RangeFiltered: "RangeFiltered",
        RangeSorting: "RangeSorting",
        RangeSorted: "RangeSorted",
        ClipboardChanging: "ClipboardChanging",
        ClipboardChanged: "ClipboardChanged",
        ClipboardPasting: "ClipboardPasting",
        ClipboardPasted: "ClipboardPasted",
        ColumnWidthChanging: "ColumnWidthChanging",
        ColumnWidthChanged: "ColumnWidthChanged",
        RowHeightChanging: "RowHeightChanging",
        RowHeightChanged: "RowHeightChanged",
        DragDropBlock: "DragDropBlock",
        DragDropBlockCompleted: "DragDropBlockCompleted",
        DragFillBlock: "DragFillBlock",
        DragFillBlockCompleted: "DragFillBlockCompleted",
        EditStarting: "EditStarting",
        EditChange: "EditChange",
        EditEnd: "EditEnd",
        RangeGroupStateChanging: "RangeGroupStateChanging",
        RangeGroupStateChanged: "RangeGroupStateChanged",
        SelectionChanging: "SelectionChanging",
        SelectionChanged: "SelectionChanged",
        SheetTabClick: "SheetTabClick",
        SheetTabDoubleClick: "SheetTabDoubleClick",
        UserZooming: "UserZooming",
        UserFormulaEntered: "UserFormulaEntered",
        CellChanged: "CellChanged",
        ColumnChanged: "ColumnChanged",
        RowChanged: "RowChanged",
        ActiveSheetChanging: "ActiveSheetChanging",
        ActiveSheetChanged: "ActiveSheetChanged",
        SparklineChanged: "SparklineChanged",
        RangeChanged: "RangeChanged",
        ButtonClicked: "ButtonClicked",
        EditorStatusChanged: "EditorStatusChanged"
    }
})(window,jQuery);
(function(window)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    var const_undefined = "undefined";
    if(typeof GrapeCity === const_undefined)
        GrapeCity = {};
    if(typeof GrapeCity.UI === const_undefined)
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    UI.Range = function(r, c, rc, cc)
    {
        this.row = r;
        this.rowCount = rc;
        this.col = c;
        this.colCount = cc
    };
    UI.Range.prototype = {
        intersect: function(row, col, rowCount, colCount)
        {
            return(row === -1 || this.row === -1 || this.row < row + rowCount && row < this.row + this.rowCount) && (col === -1 || this.col === -1 || this.col < col + colCount && col < this.col + this.colCount)
        },
        contains: function(row, col, rowCount, colCount)
        {
            if(arguments.length === 2)
                return(this.row === -1 || this.row <= row && row < this.row + this.rowCount) && (this.col === -1 || this.col <= col && col < this.col + this.colCount);
            else if(arguments.length === 4)
                return this.containsRange(new UI.Range(row,col,rowCount,colCount))
        },
        containsRange: function(range)
        {
            return(this.row === -1 || this.row <= range.row && range.row + range.rowCount <= this.row + this.rowCount) && (this.col === -1 || this.col <= range.col && range.col + range.colCount <= this.col + this.colCount)
        },
        offset: function(x, y)
        {
            var column = this.col;
            var row = this.row;
            if(this.col !== -1)
                column += x;
            if(this.row !== -1)
                row += y;
            return new UI.Range(row,column,this.rowCount,this.colCount)
        },
        union: function(range)
        {
            var ltr = Math.min(this.row,range.row);
            var ltc = Math.min(this.col,range.col);
            var rbr = Math.max(this.row + this.rowCount - 1,range.row + range.rowCount - 1);
            var rbc = Math.max(this.col + this.colCount - 1,range.col + range.colCount - 1);
            return new UI.Range(ltr,ltc,rbr - ltr + 1,rbc - ltc + 1)
        },
        equals: function(range)
        {
            if(range instanceof UI.Range)
                return this.row === range.row && this.col === range.col && this.rowCount === range.rowCount && this.colCount === range.colCount;
            return false
        }
    };
    UI.SheetArea = {
        corner: 0,
        colHeader: 1,
        rowHeader: 2,
        viewport: 3
    };
    UI.CopyToOption = {
        Value: 0x01,
        Formula: 0x02,
        RangeGroup: 0x08,
        Sparkline: 0x10,
        Span: 0x20,
        Style: 0x40,
        Tag: 0x80,
        BindingPath: 0x100
    };
    var CopyToOption = UI.CopyToOption;
    CopyToOption.All = CopyToOption.Value | CopyToOption.Formula | CopyToOption.RangeGroup | CopyToOption.Sparkline | CopyToOption.Span | CopyToOption.Style | CopyToOption.Tag | CopyToOption.BindingPath;
    UI.SelectionPolicy = {
        Single: 0,
        Range: 1,
        MultiRange: 2
    };
    UI.SelectionUnit = {
        Cell: 0,
        Row: 1,
        Column: 2
    };
    var mergeSpanErrorStr = "This operation will cause overlapping spans.";
    function findImp(obj, condition)
    {
        for(var i = 0; i < obj.length; i++)
        {
            var t = obj[i];
            if(condition(t))
                return t
        }
        return null
    }
    UI._SpanModel = function(){};
    UI._SpanModel.prototype = [];
    var spanModelPrototype = UI._SpanModel.prototype;
    spanModelPrototype.find = function(row, col)
    {
        return findImp(this,function(t)
            {
                return t.contains(row,col)
            })
    };
    spanModelPrototype.remove = function(ele)
    {
        for(var i = 0; i < this.length; i++)
            if(this[i] === ele)
            {
                this.splice(i,1);
                return
            }
    };
    spanModelPrototype.copy = function(fromRow, fromColumn, toRow, toColumn, rowCount, columnCount)
    {
        var changed = false;
        var cellRangeCount = this.length;
        var cs;
        var cr,
            i;
        var temp;
        if(fromRow === -1)
        {
            temp = [];
            for(i = 0; i < cellRangeCount; i++)
            {
                cs = this[i];
                if(fromColumn <= cs.col && cs.col < fromColumn + columnCount)
                {
                    temp.push(new UI.Range(cs.row,toColumn + cs.col - fromColumn,cs.rowCount,cs.colCount));
                    changed = true
                }
                else if(toColumn <= cs.col && cs.col < toColumn + columnCount)
                {
                    this.splice(i,1);
                    i--;
                    cellRangeCount--;
                    changed = true
                }
            }
            for(i = 0; i < temp.length; i++)
            {
                cr = temp[i];
                if(!this.isValid(this,0,this.length,cr))
                    throw new Error(mergeSpanErrorStr);
                this.push(cr)
            }
        }
        else if(fromColumn === -1)
        {
            temp = [];
            for(i = 0; i < cellRangeCount; i++)
            {
                cs = this[i];
                if(fromRow <= cs.row && cs.row < fromRow + rowCount)
                {
                    temp.push(new UI.Range(toRow + cs.row - fromRow,cs.col,cs.rowCount,cs.colCount));
                    changed = true
                }
                else if(toRow <= cs.row && cs.row < toRow + rowCount)
                {
                    this.splice(i,1);
                    i--;
                    cellRangeCount--;
                    changed = true
                }
            }
            for(i = 0; i < temp.length; i++)
            {
                cr = temp[i];
                if(!this.isValid(this,0,this.length,cr))
                    throw new Error(mergeSpanErrorStr);
                this.push(cr)
            }
        }
        else
        {
            temp = [];
            for(i = 0; i < cellRangeCount; i++)
            {
                cs = this[i];
                if(fromRow <= cs.row && cs.row < fromRow + rowCount && fromColumn <= cs.col && cs.col < fromColumn + columnCount)
                {
                    temp.push(new UI.Range(toRow + cs.row - fromRow,toColumn + cs.col - fromColumn,cs.rowCount,cs.colCount));
                    changed = true
                }
                else if(toRow <= cs.row && cs.row < toRow + rowCount && toColumn <= cs.col && cs.col < toColumn + columnCount)
                {
                    this.splice(i,1);
                    i--;
                    cellRangeCount--;
                    changed = true
                }
            }
            for(i = 0; i < temp.length; i++)
            {
                cr = temp[i];
                if(!this.isValid(this,0,this.length,cr))
                    throw new Error(mergeSpanErrorStr);
                this.push(cr)
            }
        }
    };
    spanModelPrototype.isValid = function(list, start, end, cellRange)
    {
        for(var i = start; i < end && i < list.length; i++)
        {
            var cr = list[i];
            if(cr.intersect(cellRange.row,cellRange.col,cellRange.rowCount,cellRange.colCount))
                return false
        }
        return true
    };
    spanModelPrototype.getEnumerator = function(row, col, rowCount, colCount)
    {
        return new UI._SpanEnumerator(this,row,col,rowCount,colCount)
    };
    spanModelPrototype.clear = function(row, col, rowCount, colCount)
    {
        for(var i = 0; i < this.length; i++)
        {
            var span = this[i];
            if((row === -1 || row <= span.row && span.row < row + rowCount) && (col === -1 || col <= span.col && span.col < col + colCount))
                this.splice(i--,1)
        }
    };
    spanModelPrototype.move = function(fromRow, fromCol, toRow, toCol, rowCount, colCount)
    {
        var changed = false;
        var tmpList = new UI._SpanModel;
        var changeList = [];
        var cellRangeCount = this.length;
        var cs;
        var Range = UI.Range;
        var i,
            cr;
        if(fromRow === -1)
            for(i = 0; i < cellRangeCount; i++)
            {
                cs = this[i];
                if(fromCol <= cs.col && cs.col < fromCol + colCount)
                {
                    cr = new Range(cs.row,toCol + cs.col - fromCol,cs.rowCount,cs.colCount);
                    changeList.push(cr);
                    changed = true
                }
                else if(toCol < cs.col && cs.col < toCol + colCount)
                    changed = true;
                else
                    tmpList.push(cs)
            }
        else if(fromCol === -1)
            for(i = 0; i < cellRangeCount; i++)
            {
                cs = this[i];
                if(fromRow <= cs.row && cs.row < fromRow + rowCount)
                {
                    cr = new Range(toRow + cs.row - fromRow,cs.col,cs.rowCount,cs.colCount);
                    changeList.push(cr);
                    changed = true
                }
                else if(toRow <= cs.row && cs.row < toRow + rowCount)
                    changed = true;
                else
                    tmpList.push(cs)
            }
        else
            for(i = 0; i < cellRangeCount; i++)
            {
                cs = this[i];
                if(fromRow <= cs.row && cs.row < fromRow + rowCount && fromCol <= cs.col && cs.col < fromCol + colCount)
                {
                    cr = new Range(toRow + cs.row - fromRow,toCol + cs.col - fromCol,cs.rowCount,cs.colCount);
                    changeList.push(cr);
                    changed = true
                }
                else if(toRow <= cs.row && cs.row < toRow + rowCount && toCol <= cs.col && cs.col < toCol + colCount)
                    changed = true;
                else
                    tmpList.push(cs)
            }
        if(changed)
        {
            if(changeList.length > 0)
                for(i = 0; i < changeList.length; i++)
                {
                    cr = changeList[i];
                    if(!this.isValid(tmpList,0,tmpList.length,cr))
                        throw new Error(mergeSpanErrorStr);
                    tmpList.push(cr)
                }
            this.length = 0;
            for(i = 0; i < tmpList.length; i++)
                this.push(tmpList[i])
        }
    };
    spanModelPrototype.isEmpty = function()
    {
        if(this.length <= 0)
            return true;
        return false
    };
    spanModelPrototype.addRows = function(row, count)
    {
        var changed = false;
        var countOld = this.length;
        var Range = UI.Range;
        for(var i = 0; i < countOld; i++)
        {
            var cs = this[i];
            if(cs.row >= row)
            {
                this[i] = new Range(cs.row + count,cs.col,cs.rowCount,cs.colCount);
                changed = true
            }
            else if(cs.row < row && row < cs.row + cs.rowCount)
            {
                this[i] = new Range(cs.row,cs.col,cs.rowCount + count,cs.colCount);
                changed = true
            }
        }
    };
    spanModelPrototype.addColumns = function(column, count)
    {
        var changed = false;
        var countOld = this.length;
        var Range = UI.Range;
        for(var i = 0; i < countOld; i++)
        {
            var cs = this[i];
            if(cs.col >= column)
            {
                this[i] = new Range(cs.row,cs.col + count,cs.rowCount,cs.colCount);
                changed = true
            }
            else if(cs.col < column && column < cs.col + cs.colCount)
            {
                this[i] = new Range(cs.row,cs.col,cs.rowCount,cs.colCount + count);
                changed = true
            }
        }
    };
    spanModelPrototype.removeRows = function(row, count)
    {
        var changed = false;
        var delList = [];
        var countOld = this.length;
        var Range = UI.Range;
        var i;
        for(i = 0; i < countOld; i++)
        {
            var cs = this[i];
            if(cs.row >= row)
                if(cs.row < row + count)
                {
                    delList.push(i);
                    changed = true
                }
                else
                {
                    this[i] = new Range(cs.row - count,cs.col,cs.rowCount,cs.colCount);
                    changed = true
                }
            else if(cs.row < row && row < cs.row + cs.rowCount)
            {
                this[i] = new Range(cs.row,cs.col,cs.rowCount - Math.min(cs.row + cs.rowCount - row,count),cs.colCount);
                if(this[i].rowCount === 1 && this[i].colCount === 1)
                    delList.push(i);
                changed = true
            }
        }
        for(i = 0; i < delList.length; i++)
        {
            var index = delList[i];
            this.splice(index,1)
        }
    };
    spanModelPrototype.removeColumns = function(column, count)
    {
        var changed = false;
        var delList = [];
        var countOld = this.length;
        var Range = UI.Range;
        var i;
        for(i = 0; i < countOld; i++)
        {
            var cs = this[i];
            if(cs.col >= column)
                if(cs.col < column + count)
                {
                    delList.push(i);
                    changed = true
                }
                else
                {
                    this[i] = new Range(cs.row,cs.col - count,cs.rowCount,cs.colCount);
                    changed = true
                }
            else if(cs.col < column && column < cs.col + cs.colCount)
            {
                this[i] = new Range(cs.row,cs.col,cs.rowCount,cs.colCount - Math.min(cs.col + cs.colCount - column,count));
                if(this[i].rowCount === 1 && this[i].colCount === 1)
                    delList.push(i);
                changed = true
            }
        }
        for(i = 0; i < delList.length; i++)
        {
            var index = delList[i];
            this.splice(index,1)
        }
    };
    spanModelPrototype.toJSON = function()
    {
        return this
    };
    spanModelPrototype.fromJSON = function(setting)
    {
        if(!setting)
            return;
        var spans = setting;
        for(var i = 0; i < spans.length; i++)
        {
            var cr = spans[i];
            this.push(new UI.Range(cr.row,cr.col,cr.rowCount,cr.colCount))
        }
    };
    UI._SpanEnumerator = function(model, row, col, rowCount, colCount)
    {
        this.model = model;
        this.row = row;
        this.column = col;
        this.rowCount = rowCount;
        this.columnCount = colCount;
        this.currentIndex = -1;
        if(row === -1 && col === -1)
            this.all = true
    };
    UI._SpanEnumerator.prototype = {
        current: function()
        {
            if(0 <= this.currentIndex && this.currentIndex < this.model.length)
                return this.model[this.currentIndex];
            else
                return null
        },
        moveNext: function()
        {
            this.currentIndex++;
            if(!this.all)
                while(this.currentIndex < this.model.length && !this.model[this.currentIndex].intersect(this.row,this.column,this.rowCount,this.columnCount))
                    this.currentIndex++;
            return this.currentIndex < this.model.length
        },
        reset: function()
        {
            this.currentIndex = -1
        }
    };
    UI._CellOverflowLayoutModel = function(){};
    UI._CellOverflowLayoutModel.prototype = [];
    var cellOverflowLayoutModelPrototype = UI._CellOverflowLayoutModel.prototype;
    cellOverflowLayoutModelPrototype.find = function(col)
    {
        return findImp(this,function(t)
            {
                return t.contains(col)
            })
    };
    UI._CellOverflowLayout = function(column, startCol, endCol, valueWidth, columnWidth, hAlign, columnPrevWidth)
    {
        this.column = column;
        this.startCol = startCol;
        this.endCol = endCol;
        this.columnWidth = columnWidth;
        this.hAlign = hAlign;
        this.columnPrevWidth = columnPrevWidth
    };
    UI._CellOverflowLayout.prototype.contains = function(col)
    {
        return col >= this.startCol && col <= this.endCol
    };
    UI._CellOverflowValueLayoutModel = function(){};
    UI._CellOverflowValueLayoutModel.prototype = [];
    UI._CellOverflowValueLayoutModel.prototype.find = function(col)
    {
        return findImp(this,function(t)
            {
                return t.contains(col)
            })
    };
    UI._CellOverflowValueLayout = function(data, row, col, x, y, width, height, sheetArea, cellOverflowLayout)
    {
        this.data = data;
        this.row = row;
        this.col = col;
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        this.sheetArea = sheetArea;
        this.cellOverflowLayout = cellOverflowLayout
    };
    UI._CellOverflowValueLayout.prototype.contains = function(col)
    {
        return col === this.cellOverflowLayout.column
    };
    UI._SelectionModel = function()
    {
        this.selectionPolicy = UI.SelectionPolicy.MultiRange;
        this.selectionUnit = UI.SelectionUnit.Cell
    };
    UI._SelectionModel.prototype = [];
    var SelectionModelPrototype = UI._SelectionModel.prototype;
    SelectionModelPrototype.find = function(row, col)
    {
        return findImp(this,function(t)
            {
                return t.contains(row,col)
            })
    };
    SelectionModelPrototype.clear = function()
    {
        this.splice(0,this.length);
        this.activeSelectedRangeIndex = -1
    };
    SelectionModelPrototype.add = function(row, col, rowCount, colCount)
    {
        if(this.selectionPolicy === UI.SelectionPolicy.Single)
        {
            rowCount = Math.min(rowCount,1);
            colCount = Math.min(colCount,1);
            this.clear()
        }
        else if(this.selectionPolicy === UI.SelectionPolicy.Range)
            this.clear();
        if(this.selectionUnit === UI.SelectionUnit.Row)
        {
            col = -1;
            colCount = -1
        }
        else if(this.selectionUnit === UI.SelectionUnit.Column)
        {
            row = -1;
            rowCount = -1
        }
        var r = new UI.Range(row,col,rowCount,colCount);
        this.push(r);
        this.activeSelectedRangeIndex = this.length - 1
    };
    SelectionModelPrototype.toArray = function()
    {
        var newArray = [];
        for(var i = 0; i < this.length; i++)
            newArray.push(this[i]);
        return newArray
    };
    SelectionModelPrototype.toJSON = function()
    {
        return this
    };
    SelectionModelPrototype.fromJSON = function(setting)
    {
        if(!setting)
            return;
        var selections = setting,
            count = setting.length;
        for(var i = 0; i < count; i++)
        {
            var cr = selections[i];
            this.push(new UI.Range(cr.row,cr.col,cr.rowCount,cr.colCount))
        }
        if(setting.activeSelectedRangeIndex !== undefined && setting.activeSelectedRangeIndex !== null)
            this.activeSelectedRangeIndex = setting.activeSelectedRangeIndex
    };
    UI._LayoutModel = function(){};
    UI._LayoutModel.prototype = [];
    var LayoutModelPrototype = UI._LayoutModel.prototype;
    LayoutModelPrototype.findCell = function(row, col)
    {
        return findImp(this,function(layout)
            {
                return layout.contains(row,col)
            })
    };
    LayoutModelPrototype.findRow = function(row)
    {
        return findImp(this,function(layout)
            {
                return layout.row === row
            })
    };
    LayoutModelPrototype.findCol = function(col)
    {
        return findImp(this,function(layout)
            {
                return layout.col === col
            })
    };
    UI._Layout = function(row, col, x, y, width, height, rowCount, colCount)
    {
        this.rowCount = rowCount;
        this.colCount = colCount;
        this.row = row;
        this.col = col;
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height
    };
    UI._Layout.prototype.contains = function(row, col)
    {
        return row < this.row + this.rowCount && this.row <= row && col < this.col + this.colCount && this.col <= col
    };
    UI._Layout.prototype.intersect = function(rect)
    {
        return(this.x < 0 || rect.x < this.x + this.width && this.x < rect.x + rect.width) && (this.y < 0 || rect.y < this.y + this.height && this.y < rect.y + rect.height)
    };
    UI._GcSheetModel = function(rowCount, colCount, name)
    {
        this.rowCount = rowCount !== null && rowCount !== undefined ? rowCount : 200;
        this.colCount = colCount !== null && colCount !== undefined ? colCount : 20;
        this.name = name;
        this.dataTable = {};
        this._rowDataArray = [];
        this._columnDataArray = [];
        this._defaultDataNode = null;
        this.dirtyNodes = {}
    };
    UI._GcSheetModel.prototype = {
        getRowCount: function()
        {
            return this.rowCount
        },
        getColumnCount: function()
        {
            return this.colCount
        },
        _updateDirty: function(row, col)
        {
            if(row >= 0 && col >= 0)
            {
                var dr = this.dataTable[row];
                if(dr.rs !== "n")
                    dr.rs = "e";
                this.dirtyNodes[row] = dr
            }
        },
        getValue: function(row, col)
        {
            var node = this.getNode(row,col);
            if(node && node.value !== undefined && node.value !== null)
                return node.value;
            return null
        },
        setValue: function(row, col, value)
        {
            var node = this.getNode(row,col,true);
            if(node.value !== value)
            {
                node.value = value;
                this._updateDirty(row,col)
            }
        },
        getBindingPath: function(row, col)
        {
            var node = this.getNode(row,col);
            if(node && node.bindingPath !== undefined && node.bindingPath !== null)
                return node.bindingPath;
            return null
        },
        setBindingPath: function(row, col, value)
        {
            var node = this.getNode(row,col,true);
            if(node.bindingPath !== value)
            {
                node.bindingPath = value;
                this._updateDirty(row,col)
            }
        },
        getSparkline: function(row, col)
        {
            var node = this.getNode(row,col);
            if(node && node.sparkline)
                return node.sparkline;
            return null
        },
        setSparkline: function(row, col, sparkline)
        {
            var node = this.getNode(row,col,true);
            if(node.sparkline !== sparkline)
            {
                node.sparkline = sparkline;
                this._updateDirty(row,col)
            }
        },
        setFormula: function(row, col, value)
        {
            var node = this.getNode(row,col,true);
            if(node.formula !== value)
            {
                node.formula = value;
                this._updateDirty(row,col)
            }
        },
        getFormula: function(row, col)
        {
            var node = this.getNode(row,col);
            if(node)
                return node.formula
        },
        getStyle: function(row, col)
        {
            var node = this.getNode(row,col);
            if(node)
                return node.style
        },
        setStyle: function(row, col, value)
        {
            var node = this.getNode(row,col,true);
            if(node && node.style !== value)
            {
                node.style = value;
                this._updateDirty(row,col)
            }
        },
        getVisualState: function(row, col)
        {
            var node = this.getNode(row,col);
            if(node)
                return node.visualState
        },
        setVisualState: function(row, col, value)
        {
            var node = this.getNode(row,col,true);
            if(node.visualState !== value)
            {
                node.visualState = value;
                this._updateDirty(row,col)
            }
        },
        addRows: function(row, count)
        {
            if(row < 0 || row > this.rowCount || count < 0)
                return;
            this.addElements(this.dataTable,this.rowCount,row,count);
            this.addElements(this._rowDataArray,this.rowCount,row,count);
            this.addElements(this.dirtyNodes,this.rowCount,row,count);
            this.rowCount += count;
            for(var i = 0; i < count; i++)
            {
                this.dataTable[row + i] = {rs: "n"};
                this.dirtyNodes[row + i] = this.dataTable[row + i]
            }
        },
        deleteRows: function(row, count)
        {
            var n = this.rowCount;
            if(row < 0 || row >= n || count <= 0)
                return;
            if(row + count > n)
                count = n - row;
            this.deleteElements(this.dataTable,n,row,count);
            this.deleteElements(this._rowDataArray,n,row,count);
            this.deleteElements(this.dirtyNodes,n,row,count);
            this.rowCount -= count
        },
        addElements: function(a, aCount, index, count)
        {
            if(0 <= index && index < aCount)
            {
                var rows = [];
                for(var x in a)
                    if(a.hasOwnProperty(x) && !isNaN(x) && x >= index)
                        rows.push(x);
                rows.sort(function(a, b)
                {
                    return a - b
                });
                for(var i = 0; i < rows.length; i++)
                {
                    var k = rows[rows.length - i - 1];
                    var value = a[k];
                    a[k] = null;
                    a[parseInt(k,10) + count] = value
                }
            }
        },
        deleteElements: function(a, aCount, index, count)
        {
            if(0 <= index && index < aCount)
            {
                var rows = [];
                for(var x in a)
                    if(a.hasOwnProperty(x) && !isNaN(x))
                        if(index <= x && x < index + count)
                            a[x] = null;
                        else if(x >= index + count)
                            rows.push(x);
                rows.sort(function(c, d)
                {
                    return c - d
                });
                for(var i = 0; i < rows.length; i++)
                {
                    var k = rows[i];
                    var value = a[k];
                    a[k] = null;
                    a[parseInt(k,10) - count] = value
                }
            }
        },
        addColumns: function(col, count)
        {
            if(col < 0 || col > this.colCount || count < 0)
                return;
            for(var i = 0; i < this.rowCount; i++)
            {
                var tr = this.dataTable[i];
                if(tr && col < this.colCount)
                    this.addElements(tr,this.colCount,col,count)
            }
            this.addElements(this._columnDataArray,this.colCount,col,count);
            this.colCount += count
        },
        deleteColumns: function(col, count)
        {
            var n = this.colCount;
            if(col < 0 || col >= n || count < 0)
                return;
            for(var i = 0; i < this.rowCount; i++)
            {
                var tr = this.dataTable[i];
                if(tr && col < this.colCount)
                    this.deleteElements(tr,this.colCount,col,count)
            }
            this.deleteElements(this._columnDataArray,this.colCount,col,count);
            if(col + count > n)
                count = n - col;
            this.colCount -= count
        },
        getNode: function(row, col, create)
        {
            if(row >= this.rowCount || col >= this.colCount)
                return null;
            var node = null;
            if(row >= 0 && col >= 0)
            {
                var dr = this.dataTable[row];
                if(!dr && create)
                    dr = this.dataTable[row] = {};
                if(dr)
                {
                    node = dr[col];
                    if(!node && create)
                        node = dr[col] = {}
                }
            }
            else if(row === -1 && col >= 0)
            {
                node = this._columnDataArray[col];
                if(!node && create)
                    node = this._columnDataArray[col] = {}
            }
            else if(row >= 0 && col === -1)
            {
                node = this._rowDataArray[row];
                if(!node && create)
                    node = this._rowDataArray[row] = {}
            }
            else if(row === -1 && col === -1)
            {
                node = this._defaultDataNode;
                if(!node && create)
                    node = this._defaultDataNode = {}
            }
            return node
        },
        _setNode4Swap: function(or, oc, row, col, node)
        {
            if(row >= 0 && col >= 0)
            {
                var dr = this.dataTable[row];
                if(!dr)
                    dr = this.dataTable[row] = {};
                dr[col] = node;
                if(node && node.cellCalc)
                    delete node.cellCalc
            }
            else if(row >= 0 && col === -1 && or >= 0 && oc === -1)
                this._rowDataArray[row] = node;
            else if(col >= 0 && row === -1 && oc >= 0 && or === -1)
                this._columnDataArray[col] = node
        },
        swapNode: function(row, col, row2, col2)
        {
            var node = this.getNode(row,col);
            var node2 = this.getNode(row2,col2);
            if(node)
                this._setNode4Swap(row,col,row2,col2,node);
            else if(node2)
                this._setNode4Swap(row,col,row2,col2,null);
            if(node2)
                this._setNode4Swap(row2,col2,row,col,node2);
            else if(node)
                this._setNode4Swap(row2,col2,row,col,null)
        },
        getDataRowIndex: function(row)
        {
            return row
        },
        nextNonNullRow: function(row)
        {
            row++;
            while(row >= 0 && row < this.rowCount)
            {
                if(this.dataTable.hasOwnProperty(row) && this.dataTable[row])
                    break;
                row++
            }
            if(row < this.rowCount)
                return row;
            return-1
        },
        nextNonNullColumn: function(row, col)
        {
            var dr = null;
            if(row >= 0 && row < this.rowCount)
                if(this.dataTable.hasOwnProperty(row))
                    dr = this.dataTable[row];
            if(!dr)
                return-1;
            col++;
            while(col >= 0 && col < this.colCount)
            {
                if(dr.hasOwnProperty(col) && dr[col])
                    break;
                col++
            }
            if(col < this.colCount)
                return col;
            return-1
        },
        clear: function(row, column, rowCount, columnCount, type)
        {
            var nodes = [];
            var node,
                c,
                r;
            if(row >= 0 && column >= 0)
            {
                if(row + rowCount > this.rowCount)
                    rowCount = this.rowCount - row;
                if(rowCount <= 0)
                    return;
                if(column + columnCount > this.columnCount)
                    columnCount = this.columnCount - column;
                if(columnCount <= 0)
                    return;
                for(r = row; r < row + rowCount; r++)
                    for(c = column; c < column + columnCount; c++)
                    {
                        node = this.getNode(r,c);
                        if(node)
                            nodes.push(node)
                    }
            }
            else if(row >= 0 && column === -1)
            {
                if(row + rowCount > this.rowCount)
                    rowCount = this.rowCount - row;
                if(rowCount <= 0)
                    return;
                for(r = row; r < row + rowCount; r++)
                {
                    node = this.getNode(r,-1);
                    if(node)
                        nodes.push(node)
                }
            }
            else if(row === -1 && column >= 0)
            {
                if(column + columnCount > this.columnCount)
                    columnCount = this.columnCount - column;
                if(columnCount <= 0)
                    return;
                for(c = column; c < column + columnCount; c++)
                {
                    node = this.getNode(-1,c);
                    if(node)
                        nodes.push(node)
                }
            }
            else if(row === -1 && column === -1)
            {
                node = this._defaultDataNode;
                if(node)
                    nodes.push(node)
            }
            for(var i = 0; i < nodes.length; i++)
                if(nodes[i])
                {
                    if((type & UI.StorageType.Style) > 0)
                        nodes[i].style = null;
                    if((type & UI.StorageType.Data) > 0)
                    {
                        nodes[i].value = null;
                        nodes[i].formula = null
                    }
                    if((type & UI.StorageType.Sparkline) > 0)
                        nodes[i].sparkline = null;
                    if((type & UI.StorageType.BindingPath) > 0)
                        nodes[i].bindingPath = null
                }
        },
        toJSON: function()
        {
            return{
                    name: this.name,
                    rowCount: this.rowCount,
                    colCount: this.colCount,
                    dataTable: this.dataTable,
                    _rowDataArray: this._rowDataArray,
                    _columnDataArray: this._columnDataArray,
                    _defaultDataNode: this._defaultDataNode
                }
        },
        fromJSON: function(setting)
        {
            if(!setting)
                return;
            var dt = setting;
            this.name = dt.name;
            this.rowCount = dt.rowCount;
            this.colCount = dt.colCount;
            var data = dt.dataTable;
            if(data)
            {
                var dm = new GrapeCity.UI._GcSheetModel(this.rowCount,this.colCount);
                dm.dataTable = data;
                var r = dm.nextNonNullRow(-1);
                while(r >= 0)
                {
                    var c = dm.nextNonNullColumn(r,-1);
                    while(c >= 0)
                    {
                        var node = dm.getNode(r,c);
                        if(node)
                            this._copyJSONNode(r,c,node);
                        c = dm.nextNonNullColumn(r,c)
                    }
                    r = dm.nextNonNullRow(r)
                }
            }
            var rowData = dt._rowDataArray;
            if(rowData)
                for(var r = 0; r < this.rowCount; r++)
                    if(rowData.hasOwnProperty(r) && rowData[r])
                        this._copyJSONNode(r,-1,rowData[r]);
            var colData = dt._columnDataArray;
            if(colData)
                for(var c = 0; c < this.colCount; c++)
                    if(colData.hasOwnProperty(c) && colData[c])
                        this._copyJSONNode(-1,c,colData[c]);
            var defDataNode = dt._defaultDataNode;
            if(defDataNode)
                this._copyJSONNode(-1,-1,defDataNode)
        },
        _getCellTypeKinds: function()
        {
            if(!this._dict)
            {
                var dict = {};
                dict[UI.CellTypeKind.BaseCellType] = UI.BaseCellType;
                dict[UI.CellTypeKind.TextCellType] = UI.TextCellType;
                dict[UI.CellTypeKind.ColumnHeaderCellType] = UI.ColumnHeaderCellType;
                dict[UI.CellTypeKind.RowHeaderCellType] = UI.RowHeaderCellType;
                dict[UI.CellTypeKind.CornerCellType] = UI.CornerCellType;
                dict[UI.CellTypeKind.CheckBoxCellType] = UI.CheckBoxCellType;
                dict[UI.CellTypeKind.ButtonCellType] = UI.ButtonCellType;
                dict[UI.CellTypeKind.ComboBoxCellType] = UI.ComboBoxCellType;
                dict[UI.CellTypeKind.HyperLinkCellType] = UI.HyperLinkCellType;
                this._dict = dict
            }
            return this._dict
        },
        _copyJSONNode: function(row, col, node)
        {
            if(!node)
                return;
            var r = row,
                c = col;
            if(typeof node.value !== const_undefined)
                this.setValue(r,c,node.value);
            if(typeof node.formula !== const_undefined)
                this.setFormula(r,c,node.formula);
            if(typeof node.style !== const_undefined)
            {
                var style = null;
                if(node.style)
                {
                    style = new UI.Style;
                    style.copyFrom(node.style);
                    if(typeof node.style.validator !== const_undefined)
                    {
                        var dv = new UI.DefaultDataValidator;
                        dv.fromJSON(node.style.validator);
                        style.validator = dv
                    }
                    if(typeof node.style.cellType !== const_undefined)
                    {
                        var ct = null;
                        var dict = this._getCellTypeKinds();
                        var cellTypeClass = dict[node.style.cellType.type];
                        if(cellTypeClass)
                        {
                            ct = new cellTypeClass;
                            ct.fromJSON(node.style.cellType)
                        }
                        style.cellType = ct
                    }
                }
                this.setStyle(r,c,style)
            }
            if(typeof node.visualState !== const_undefined)
                this.setVisualState(r,c,node.visualState);
            if(typeof node.bindingPath !== const_undefined)
                this.setBindingPath(r,c,node.bindingPath)
        }
    };
    UI._UndoManager = function(context, maxLength, allowUndo)
    {
        this._context = context;
        if(parseInt(maxLength,10) < 0)
            maxLength = 2147483647;
        this._maxLength = maxLength;
        this._allowUndo = allowUndo;
        this._undoStack = [];
        this._redoStack = []
    };
    UI._UndoManager.prototype = {
        changed: null,
        context: function()
        {
            return this._context
        },
        canUndo: function()
        {
            return this._undoStack.length > 0
        },
        canRedo: function()
        {
            return this._redoStack.length > 0
        },
        _shrinkUndoStack: function(count)
        {
            for(var i = 0; i < count; i++)
                this._undoStack.shift()
        },
        _raiseChanged: function()
        {
            if(this.changed && typeof this.changed === 'function')
                this.changed(this)
        },
        doAction: function(action)
        {
            if(!action || !action.execute)
                return false;
            var success = true;
            try
            {
                action.execute(this._context)
            }
            catch(e)
            {
                success = false
            }
            if(this._allowUndo)
                if(success && action.canUndo() && action.undo)
                {
                    if(this._maxLength > 0 && this._undoStack.length >= this._maxLength)
                        this._shrinkUndoStack(this._undoStack.length - this._maxLength + 1);
                    this._undoStack.push(action);
                    this._redoStack = [];
                    this._raiseChanged()
                }
            return success
        },
        undo: function()
        {
            var result = true;
            if(this._allowUndo && this.canUndo())
            {
                var undoAction = this._undoStack[this._undoStack.length - 1];
                try
                {
                    if(undoAction && undoAction.canUndo && undoAction.canUndo() && undoAction.undo)
                        result = undoAction.undo(this._context)
                }
                catch(e)
                {
                    result = false
                }
                if(result)
                {
                    this._redoStack.push(this._undoStack.pop());
                    this._raiseChanged()
                }
            }
            return result
        },
        redo: function()
        {
            var result = true;
            if(this._allowUndo && this.canRedo())
            {
                var redoAction = this._redoStack[this._redoStack.length - 1];
                try
                {
                    if(redoAction && redoAction.execute)
                        redoAction.execute(this._context)
                }
                catch(e)
                {
                    result = false
                }
                if(result)
                {
                    this._undoStack.push(this._redoStack.pop());
                    this._raiseChanged()
                }
            }
            return result
        },
        getUndoList: function()
        {
            return this._undoStack.slice(0)
        },
        getRedoList: function()
        {
            return this._redoStack.slice(0)
        }
    }
})(window);
(function(window)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var document = window.document;
    GrapeCity.UI._SheetRender = function(sheet)
    {
        this._init(sheet)
    };
    GrapeCity.UI._SheetRender.prototype = {
        _init: function(sheet)
        {
            this._sheet = sheet
        },
        _getCtx: function()
        {
            this.ctx = null;
            var sheet = this._sheet;
            var c = sheet._getCanvas();
            if(c)
            {
                if(!c.getContext && GrapeCity.UI._isSilverlightCanvas())
                    if(!c.getContext)
                        return;
                if(!c.getContext && typeof window.FlashCanvas !== "undefined")
                    window.FlashCanvas.initElement(c);
                if(c.getContext)
                    this.ctx = c.getContext('2d')
            }
            if(this.ctx)
                this.ctx.name = "userContext";
            return this.ctx
        },
        _getBufferCtx: function()
        {
            var sheet = this._sheet;
            var rect = sheet._bounds;
            var buffer = this.buffer;
            if(!buffer || buffer.width !== rect.width || buffer.height !== rect.height)
            {
                this.buffer = buffer = document.createElement("canvas");
                buffer.width = rect.width;
                buffer.height = rect.height
            }
            if(buffer.getContext)
                this.bufferCtx = buffer.getContext('2d');
            else if(GrapeCity.UI._isSilverlightCanvas())
                if(!buffer.getContext)
                    return;
                else
                    this.bufferCtx = buffer.getContext('2d');
            else if(typeof window.FlashCanvas !== "undefined")
            {
                window.FlashCanvas.initElement(c);
                if(!buffer.getContext)
                    return;
                this.bufferCtx = buffer.getContext('2d')
            }
            if(this.bufferCtx)
            {
                this.bufferCtx.beginPath();
                this.bufferCtx.font = this._getZoomFont(this._getDefaultFont());
                this.bufferCtx.name = "bufferContext"
            }
            return this.bufferCtx
        },
        _getBufferCtx2: function()
        {
            var sheet = this._sheet;
            var rect = sheet._bounds;
            var buffer = this.buffer2;
            if(!buffer || buffer.width !== rect.width || buffer.height !== rect.height)
            {
                this.buffer2 = buffer = document.createElement("canvas");
                buffer.width = rect.width;
                buffer.height = rect.height
            }
            if(buffer.getContext)
                this.bufferCtx2 = buffer.getContext('2d');
            else if(GrapeCity.UI._isSilverlightCanvas())
                if(!buffer.getContext)
                    return;
                else
                    this.bufferCtx2 = buffer.getContext('2d');
            else if(typeof window.FlashCanvas !== "undefined")
            {
                window.FlashCanvas.initElement(c);
                if(!buffer.getContext)
                    return;
                this.bufferCtx2 = buffer.getContext('2d')
            }
            if(this.bufferCtx2)
            {
                this.bufferCtx2.beginPath();
                this.bufferCtx2.font = this._getZoomFont(this._getDefaultFont());
                this.bufferCtx2.name = "bufferContext"
            }
            return this.bufferCtx2
        },
        _getDefaultFont: function(sheetArea)
        {
            if(!sheetArea || sheetArea === GrapeCity.UI.SheetArea.viewport)
                return"10pt Arial";
            else
                return"11pt " + this._sheet.currentTheme().bodyFont()
        },
        _getDefaultBackground: function()
        {
            return"white"
        },
        _scaleFont: function(font, factor, minFont)
        {
            if(factor === 1)
                return font;
            var sheet = this._sheet;
            var span = sheet._getEditingSpan();
            span.style.font = font;
            var style = span.currentStyle;
            if(document.defaultView && document.defaultView.getComputedStyle)
                style = document.defaultView.getComputedStyle(span,'');
            var fontSize = style.fontSize;
            var type = "px";
            if(fontSize.indexOf(type) !== -1)
            {
                var size = Math.max(1,Math.floor(parseFloat(fontSize.replace(type,"")) * factor));
                if(minFont && size === 1)
                    minFont.value = true;
                fontSize = "" + size + type
            }
            return fontSize + " " + style.fontFamily
        },
        _getZoomFont: function(font)
        {
            var sheet = this._sheet;
            return this._scaleFont(font,sheet._zoomFactor)
        },
        _copyDoubleBuffer: function(srcX, srcY, srcW, srcH, destX, destY, clipRect, srcCtx, targetCtx)
        {
            if(!GrapeCity.UI._useDoubleBuffer())
                return;
            if(srcX !== undefined && srcX !== null && srcY !== undefined && srcY !== null && srcW > 0 && srcH > 0)
            {
                var bufferCtx = srcCtx ? srcCtx : this._getBufferCtx();
                var ctx = targetCtx ? targetCtx : this._getCtx();
                var imgData = null;
                if(this._sheet._canvas)
                    if(clipRect)
                    {
                        var rect = new GrapeCity.UI.Rect(srcX,srcY,srcW,srcH);
                        var intersectRect = rect.getIntersectRect(clipRect);
                        if(intersectRect)
                        {
                            srcX = intersectRect.x;
                            srcY = intersectRect.y;
                            imgData = bufferCtx.getImageData(intersectRect.x,intersectRect.y,intersectRect.width,intersectRect.height)
                        }
                        else
                            return
                    }
                    else
                        imgData = bufferCtx.getImageData(srcX,srcY,srcW,srcH);
                else
                    imgData = bufferCtx.getImageData(0,0,srcW,srcH);
                if(destX === undefined || destX === null)
                    destX = srcX;
                if(destY === undefined || destY === null)
                    destY = srcY;
                ctx.putImageData(imgData,destX,destY)
            }
        },
        _copyDoubleBufferRect: function(rect, destX, destY, clipRect, srcCtx, targetCtx)
        {
            if(rect)
                this._copyDoubleBuffer(rect.x,rect.y,rect.width,rect.height,destX,destY,clipRect,srcCtx,targetCtx)
        },
        translateScreen: function(srcX, srcY, srcW, srcH, destX, destY, destW, destH)
        {
            try
            {
                var ctx = this._getCtx();
                var imgData = ctx.getImageData(srcX,srcY,srcW,srcH);
                ctx.putImageData(imgData,destX,destY)
            }
            catch(e){}
        },
        repaintSelection: function(range, clipRect, ctx)
        {
            if(!ctx)
                ctx = this._getCtx();
            var ret = false;
            var sheet = this._sheet;
            var newRange = new GrapeCity.UI.Range(-1,-1,-1,-1);
            if(range)
            {
                if(sheet._allowCellOverflow)
                    newRange = new GrapeCity.UI.Range(range.row,0,range.rowCount,sheet.getColumnCount());
                else
                    newRange = new GrapeCity.UI.Range(range.row,range.col,range.rowCount,range.colCount);
                if(!sheet._isNavigateInSelection)
                {
                    if(newRange.col >= 0)
                    {
                        var startCol = newRange.col,
                            endCol = newRange.col + newRange.colCount - 1,
                            rCount = sheet.getRowCount(GrapeCity.UI.SheetArea.colHeader);
                        for(var r = 0; r < rCount; r++)
                        {
                            var cr = sheet._colHeaderSpanModel.find(r,newRange.col);
                            if(cr)
                                startCol = Math.min(cr.col,startCol);
                            var cr = sheet._colHeaderSpanModel.find(r,newRange.col + newRange.colCount - 1);
                            if(cr)
                                endCol = Math.max(cr.col + cr.colCount - 1,endCol)
                        }
                        newRange.col = startCol;
                        newRange.colCount = endCol - startCol + 1
                    }
                    if(newRange.row >= 0)
                    {
                        var startRow = newRange.row,
                            endRow = newRange.row + newRange.rowCount - 1,
                            cCount = sheet.getColumnCount(GrapeCity.UI.SheetArea.rowHeader);
                        for(var c = 0; c < cCount; c++)
                        {
                            var cr = sheet._rowHeaderSpanModel.find(newRange.row,c);
                            if(cr)
                                startRow = Math.min(cr.row,startRow);
                            var cr = sheet._rowHeaderSpanModel.find(newRange.row + newRange.rowCount - 1,c);
                            if(cr)
                                endRow = Math.max(cr.row + cr.rowCount - 1,endRow)
                        }
                        newRange.row = startRow;
                        newRange.rowCount = endRow - startRow + 1
                    }
                }
            }
            var viewport = this._getInViewportIndex(newRange);
            var rect = sheet._getRangeRect(viewport.rowViewportIndex,viewport.colViewportIndex,newRange);
            if(rect.width >= 0 && rect.height >= 0)
            {
                rect.x -= 2;
                rect.y -= 2;
                rect.width += 4;
                rect.height += 4;
                if(clipRect)
                {
                    var actualRect = rect.getIntersectRect(clipRect);
                    if(actualRect)
                        rect = actualRect;
                    else
                        return
                }
                var useDoubleBuffer = GrapeCity.UI._useDoubleBuffer();
                if(!useDoubleBuffer)
                    sheet._dirty = true;
                if(rect.width != 4 && rect.height != 4)
                    this.paint(ctx,rect);
                var layout = sheet._getSheetLayout();
                if(rect.height > 0 && !sheet._isNavigateInSelection)
                {
                    sheet._dirty = true;
                    this.paint(ctx,new GrapeCity.UI.Rect(layout.x,rect.y,layout.rowHeaderWidth,rect.height))
                }
                if(rect.width > 0 && !sheet._isNavigateInSelection)
                {
                    sheet._dirty = true;
                    this.paint(ctx,new GrapeCity.UI.Rect(rect.x,layout.y,rect.width,layout.colHeaderHeight))
                }
                ret = true
            }
            return ret
        },
        _getInViewportIndex: function(range)
        {
            var sheet = this._sheet;
            var obj = {
                    rowViewportIndex: 1,
                    colViewportIndex: 1
                };
            var frozenColCount = isNaN(sheet.frozenColCount) ? 0 : Math.min(sheet.frozenColCount,sheet.getColumnCount());
            var frozenRowCount = isNaN(sheet.frozenRowCount) ? 0 : Math.min(sheet.frozenRowCount,sheet.getRowCount());
            if(range.row < frozenRowCount)
                obj.rowViewportIndex = 0;
            if(range.col < frozenColCount)
                obj.colViewportIndex = 0;
            return obj
        },
        update: function(x, y, width, height, ignoreDirtyFlag)
        {
            var sheet = this._sheet;
            if(sheet._layoutSuspended > 0)
                return;
            var ctx = this._getCtx();
            if(ctx)
            {
                if(!ignoreDirtyFlag)
                    sheet._dirty = true;
                this.paint(ctx,new GrapeCity.UI.Rect(x,y,width,height))
            }
        },
        repaint: function(clipRect)
        {
            var sheet = this._sheet;
            if(sheet._layoutSuspended > 0)
                return;
            var ctx = this._getCtx();
            if(ctx)
            {
                sheet._dirty = true;
                this.paint(ctx,clipRect)
            }
        },
        paint: function(ctx, clipRect)
        {
            if(!ctx)
                return;
            var sheet = this._sheet;
            var rect = sheet._bounds;
            if(!clipRect)
                clipRect = rect;
            var useDoubleBuffer = GrapeCity.UI._useDoubleBuffer();
            var bufferCtx = useDoubleBuffer ? this._getBufferCtx() : ctx;
            if(!bufferCtx)
            {
                useDoubleBuffer = false;
                bufferCtx = ctx
            }
            if(sheet._dirty)
            {
                sheet._dirty = false;
                if(useDoubleBuffer)
                {
                    bufferCtx.clearRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
                    bufferCtx.translate(-rect.x,-rect.y)
                }
                this.paintSheet(bufferCtx,clipRect);
                if(useDoubleBuffer)
                    bufferCtx.translate(rect.x,rect.y)
            }
            if(useDoubleBuffer)
            {
                var bufferCtx2 = ctx;
                if($.browser.chrome || $.browser.safari)
                    bufferCtx2 = this._getBufferCtx2();
                this._copyDoubleBufferRect(clipRect,null,null,null,bufferCtx,bufferCtx2);
                if(!sheet._hoverCell && sheet._canvas)
                {
                    var layout = sheet._getSheetLayout();
                    for(var r = 0; r < 2; r++)
                        for(var c = 0; c < 2; c++)
                        {
                            rect = layout.viewportRect(r,c);
                            if(!clipRect || rect.intersectRect(clipRect))
                            {
                                bufferCtx2.save();
                                bufferCtx2.rect(rect.x,rect.y,rect.width,rect.height);
                                bufferCtx2.clip();
                                bufferCtx2.beginPath();
                                this.paintSelection(bufferCtx2,r,c,clipRect);
                                bufferCtx2.beginPath();
                                bufferCtx2.restore()
                            }
                        }
                    this.paintResizeLine(bufferCtx2,clipRect)
                }
                if(bufferCtx2 !== ctx)
                    this._copyDoubleBufferRect(clipRect,null,null,null,bufferCtx2,ctx)
            }
        },
        paintSheet: function(ctx, clipRect)
        {
            var sheet = this._sheet;
            var r,
                c;
            if(!ctx || sheet._layoutSuspended > 0)
                return;
            var rect = sheet._bounds;
            ctx.save();
            if(!clipRect)
                ctx.rect(rect.x,rect.y,rect.width,rect.height);
            else
                ctx.rect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
            ctx.clip();
            ctx.beginPath();
            ctx.fillStyle = this._getThemeContentBackgroundColor("gray");
            ctx.fillRect(rect.x,rect.y,rect.width,rect.height);
            var layout = sheet._getSheetLayout();
            this.paintGroup(ctx,clipRect);
            for(c = 0; c < 2; c++)
                if(!clipRect || layout.colHeaderRect(c).intersectRect(clipRect))
                    this.paintColHeader(ctx,c,clipRect);
            for(r = 0; r < 2; r++)
            {
                if(!clipRect || layout.rowHeaderRect(r).intersectRect(clipRect))
                    this.paintRowHeader(ctx,r,clipRect);
                for(c = 0; c < 2; c++)
                    if(!clipRect || layout.viewportRect(r,c).intersectRect(clipRect))
                        this.paintViewport(ctx,r,c,clipRect)
            }
            if(!clipRect || layout.headerCornerRect().intersectRect(clipRect))
                this.paintColHeaderCorner(ctx,clipRect);
            if(sheet._colFooterVisible)
            {
                if(!clipRect || layout.footerCornerRect().intersectRect(clipRect))
                    this.paintColFooterCorner(ctx,clipRect);
                for(c = 0; c < 2; c++)
                    if(!clipRect || layout.footerRect(c).intersectRect(clipRect))
                        this.paintColFooter(ctx,c,clipRect)
            }
            this.paintFrozenLine(ctx,clipRect);
            if(sheet.borderWidth > 0)
            {
                ctx.strokeStyle = sheet.borderColor;
                ctx.lineWidth = sheet.borderWidth;
                ctx.strokeRect(rect.x + ctx.lineWidth / 2,rect.y + ctx.lineWidth / 2,Math.max(0,rect.width - ctx.lineWidth),Math.max(0,rect.height - ctx.lineWidth))
            }
            ctx.beginPath();
            ctx.restore()
        },
        _getThemeContentBackgroundColor: function(defaultColor)
        {
            var sheet = this._sheet;
            var useWijmoTheme = sheet.parent ? sheet.parent.useWijmoTheme : sheet.useWijmoTheme;
            if(useWijmoTheme === true)
            {
                var t = GrapeCity.UI.Global.prototype._dummyContent;
                var themeStyle = t.currentStyle;
                if(document.defaultView && document.defaultView.getComputedStyle)
                    themeStyle = document.defaultView.getComputedStyle(t,'');
                if(themeStyle && themeStyle.backgroundColor && themeStyle.backgroundColor !== "")
                    defaultColor = themeStyle.backgroundColor
            }
            return defaultColor
        },
        refreshDragDropIndicator: function()
        {
            var sheet = this._sheet;
            var target = sheet._currentTarget;
            if(!target || !target.dragInfo)
                return;
            var ctx = this._getCtx();
            var layout = sheet._getSheetLayout();
            var oldDragRect = sheet._dragOldRect;
            var dragRange = sheet._actualDragRange;
            var dragRect = sheet._dragRect;
            for(var r = 0; r < 2; r++)
                for(var c = 0; c < 2; c++)
                {
                    var viewportRect = layout.viewportRect(r,c);
                    var rect = sheet._getRangeRect(r,c,dragRange);
                    if(rect && viewportRect.intersectRect(new GrapeCity.UI.Rect(rect.x - 2,rect.y - 2,rect.width + 4,rect.height + 4)) || oldDragRect && viewportRect.intersectRect(new GrapeCity.UI.Rect(oldDragRect.x - 2,oldDragRect.y - 2,oldDragRect.width + 4,oldDragRect.height + 4)))
                    {
                        dragRect.x = rect.x;
                        dragRect.y = rect.y;
                        dragRect.width = rect.width - 1;
                        dragRect.height = rect.height - 1;
                        this.paintDragDropIndicator(ctx,viewportRect)
                    }
                }
            sheet._oldDragRange = dragRange;
            var isDragInsert = sheet._eventHandler._isDragInsert;
            if(isDragInsert === true)
                sheet._dragOldRect = sheet._insertDragRect;
            else
                sheet._dragOldRect = new GrapeCity.UI.Rect(dragRect.x,dragRect.y,dragRect.width,dragRect.height)
        },
        paintDragDropIndicator: function(ctx, viewportClipRect)
        {
            var sheet = this._sheet;
            var dragRect = sheet._dragRect;
            var isDragInsert = sheet._eventHandler._isDragInsert;
            var hi = dragRect.hitTarget;
            var oldRect,
                clipRect;
            if(!sheet || !dragRect || !hi)
                return;
            ctx.save();
            if(isDragInsert === true && (dragRect.row === -1 || dragRect.col === -1))
            {
                if(dragRect.row === -1 && dragRect.col !== -1)
                {
                    var cl = sheet._getColumnLayout(0).findCol(dragRect.hitCol);
                    if(!cl)
                        cl = sheet._getColumnLayout(1).findCol(dragRect.hitCol);
                    if(cl)
                    {
                        var x = cl.x;
                        var width = cl.width;
                        dragRect.col = dragRect.hitCol;
                        if(hi.x > x + width / 2 && (dragRect.col < sheet._getLastFullyVisibleColumn() || dragRect.col === sheet._getLastVisualColumn()))
                        {
                            x = cl.x + cl.width;
                            dragRect.col++
                        }
                        oldRect = sheet._dragOldRect;
                        if(!oldRect || oldRect.width !== 0)
                            oldRect = sheet._dragRect;
                        if(oldRect)
                        {
                            clipRect = new GrapeCity.UI.Rect(oldRect.x - 2,oldRect.y - 2,oldRect.width + 4,oldRect.height + 4);
                            this._copyDoubleBufferRect(clipRect,null,null,viewportClipRect);
                            this.repaintSelection(sheet._getActiveSelectedRange(),viewportClipRect)
                        }
                        sheet._insertDragRect = new GrapeCity.UI.Rect(x,dragRect.y,0,dragRect.height);
                        ctx.rect(viewportClipRect.x,viewportClipRect.y,viewportClipRect.width,viewportClipRect.height);
                        ctx.clip();
                        ctx.beginPath();
                        this.paintDragLine(ctx,x,dragRect.y,x,dragRect.y + dragRect.height)
                    }
                }
                else if(dragRect.row !== -1 && dragRect.col === -1)
                {
                    var rl = sheet._getRowLayout(0).findRow(dragRect.hitRow);
                    if(!rl)
                        rl = sheet._getRowLayout(1).findRow(dragRect.hitRow);
                    if(rl)
                    {
                        var y = rl.y;
                        var height = rl.height;
                        dragRect.row = dragRect.hitRow;
                        if(hi.y > y + height / 2 && (dragRect.row < sheet._getLastFullyVisibleRow() || dragRect.row === sheet._getLastVisualRow()))
                        {
                            y = rl.y + rl.height;
                            dragRect.row++
                        }
                        oldRect = sheet._dragOldRect;
                        if(!oldRect || oldRect.height !== 0)
                            oldRect = sheet._dragRect;
                        if(oldRect)
                        {
                            clipRect = new GrapeCity.UI.Rect(oldRect.x - 2,oldRect.y - 2,oldRect.width + 4,oldRect.height + 4);
                            this._copyDoubleBufferRect(clipRect,null,null,viewportClipRect);
                            this.repaintSelection(sheet._getActiveSelectedRange(),viewportClipRect)
                        }
                        sheet._insertDragRect = new GrapeCity.UI.Rect(dragRect.x,y,dragRect.width,0);
                        ctx.rect(viewportClipRect.x,viewportClipRect.y,viewportClipRect.width,viewportClipRect.height);
                        ctx.clip();
                        ctx.beginPath();
                        this.paintDragLine(ctx,dragRect.x,y,dragRect.x + dragRect.width,y)
                    }
                }
            }
            else
            {
                oldRect = sheet._dragOldRect;
                if(oldRect)
                {
                    clipRect = new GrapeCity.UI.Rect(oldRect.x - 2,oldRect.y - 2,oldRect.width + 4,oldRect.height + 4);
                    this._copyDoubleBufferRect(clipRect,null,null,viewportClipRect);
                    this.repaintSelection(sheet._getActiveSelectedRange(),viewportClipRect)
                }
                ctx.rect(viewportClipRect.x,viewportClipRect.y,viewportClipRect.width,viewportClipRect.height);
                ctx.clip();
                ctx.beginPath();
                this.paintDragRectangle(ctx,dragRect)
            }
            ctx.beginPath();
            ctx.restore()
        },
        _union: function(rect1, rect2)
        {
            var x = Math.min(rect1.x,rect2.x);
            var y = Math.min(rect1.y,rect2.y);
            var w = Math.max(rect1.x + rect1.width - 1,rect2.x + rect2.width - 1);
            var h = Math.max(rect1.y + rect1.height - 1,rect2.y + rect2.height - 1);
            return new GrapeCity.UI.Rect(x,y,w,h)
        },
        paintDragRectangle: function(ctx, rect)
        {
            this.paintDragLine(ctx,rect.x,rect.y,rect.x + rect.width,rect.y);
            this.paintDragLine(ctx,rect.x,rect.y + rect.height,rect.x + rect.width,rect.y + rect.height);
            this.paintDragLine(ctx,rect.x,rect.y,rect.x,rect.y + rect.height);
            this.paintDragLine(ctx,rect.x + rect.width,rect.y,rect.x + rect.width,rect.y + rect.height)
        },
        paintDragLine: function(ctx, fromX, fromY, toX, toY)
        {
            ctx.save();
            var line1,
                line2,
                line3;
            if(fromX === toX)
            {
                ctx.rect(fromX - 2,fromY,7,Math.abs(toY - fromY));
                ctx.clip();
                ctx.beginPath();
                if(fromY < toY)
                {
                    fromY -= 3;
                    toY += 3
                }
                else
                {
                    fromY += 3;
                    toY -= 3
                }
                line1 = new GrapeCity.UI.Line.Create(fromX - 1,fromY - 1,toX - 1,toY + 1,"black",GrapeCity.UI.LineStyle.hair);
                line2 = new GrapeCity.UI.Line.Create(fromX,fromY,toX,toY,"black",GrapeCity.UI.LineStyle.hair);
                line3 = new GrapeCity.UI.Line.Create(fromX + 1,fromY - 1,toX + 1,toY + 1,"black",GrapeCity.UI.LineStyle.hair)
            }
            else if(fromY === toY)
            {
                ctx.rect(fromX,fromY - 2,Math.abs(toX - fromX),7);
                ctx.clip();
                ctx.beginPath();
                if(fromX < toX)
                {
                    fromX -= 3;
                    toX += 3
                }
                else
                {
                    fromX += 3;
                    toX -= 3
                }
                line1 = new GrapeCity.UI.Line.Create(fromX - 1,fromY - 1,toX + 1,toY - 1,"black",GrapeCity.UI.LineStyle.hair);
                line2 = new GrapeCity.UI.Line.Create(fromX,fromY,toX,toY,"black",GrapeCity.UI.LineStyle.hair);
                line3 = new GrapeCity.UI.Line.Create(fromX - 1,fromY + 1,toX + 1,toY + 1,"black",GrapeCity.UI.LineStyle.hair)
            }
            line1.paintLine(ctx);
            line2.paintLine(ctx);
            line3.paintLine(ctx);
            ctx.stroke();
            ctx.beginPath();
            ctx.restore()
        },
        paintResizeLine: function(ctx, clipRect)
        {
            var x,
                y;
            var sheet = this._sheet;
            if(!sheet._eventHandler.isMouseDown)
                return;
            var currentTarget = sheet._currentTarget;
            if(!currentTarget)
                return;
            var resizeInfo = currentTarget.resizeInfo;
            if(resizeInfo)
            {
                var sheetLayout = sheet._getSheetLayout();
                ctx.save();
                ctx.strokeStyle = "black";
                ctx.lineWidth = 1;
                ctx.beginPath();
                if(resizeInfo.action === "sizeRow")
                    for(x = sheetLayout.x; x < sheetLayout.x + sheetLayout.width; x += 2)
                    {
                        y = Math.max(0,resizeInfo.startY - 0.5);
                        ctx.moveTo(x,y);
                        ctx.lineTo(x + 1,y);
                        ctx.moveTo(x,resizeInfo.movingY - 0.5);
                        ctx.lineTo(x + 1,resizeInfo.movingY - 0.5)
                    }
                else
                    for(y = sheetLayout.y; y < sheetLayout.y + sheetLayout.height; y += 2)
                    {
                        x = Math.max(0,resizeInfo.startX - 0.5);
                        ctx.moveTo(x,y);
                        ctx.lineTo(x,y + 1);
                        ctx.moveTo(resizeInfo.movingX - 0.5,y);
                        ctx.lineTo(resizeInfo.movingX - 0.5,y + 1)
                    }
                ctx.stroke();
                ctx.restore()
            }
        },
        paintColHeaderCorner: function(ctx, clipRect)
        {
            var sheet = this._sheet;
            ctx.beginPath();
            var rect = sheet._getSheetLayout().headerCornerRect();
            var style = sheet.getActualStyle(-1,-1,GrapeCity.UI.SheetArea.corner);
            this.paintCell(ctx,null,-1,-1,rect.x,rect.y,rect.width,rect.height,style,GrapeCity.UI.SheetArea.corner);
            var border = new GrapeCity.UI._GcBorders(sheet,0,0,GrapeCity.UI.SheetArea.corner);
            border.addCellLines(0,0,rect.x,rect.y,rect.width,rect.height);
            border.paint(ctx,clipRect)
        },
        paintFrozenLine: function(ctx, clipRect)
        {
            var sheet = this._sheet;
            if(sheet.frozenRowCount || sheet.frozenColCount)
            {
                var sheetLayout = sheet._getSheetLayout();
                ctx.save();
                ctx.strokeStyle = new GrapeCity.UI._ThemeContext(sheet).getColor(sheet._frozenlineColor);
                ctx.lineWidth = 1;
                ctx.beginPath();
                if(sheet.frozenColCount)
                {
                    ctx.moveTo(sheetLayout.viewportX - 0.5,sheetLayout.y);
                    ctx.lineTo(sheetLayout.viewportX - 0.5,sheetLayout.y + sheetLayout.height)
                }
                if(sheet.frozenRowCount)
                {
                    ctx.moveTo(sheetLayout.x,sheetLayout.viewportY - 0.5);
                    ctx.lineTo(sheetLayout.x + sheetLayout.width,sheetLayout.viewportY - 0.5)
                }
                ctx.stroke();
                ctx.restore()
            }
        },
        paintColHeader: function(ctx, c, clipRect)
        {
            this.paintViewportImp(ctx,-1,c,GrapeCity.UI.SheetArea.colHeader,clipRect)
        },
        paintRowHeader: function(ctx, r, clipRect)
        {
            this.paintViewportImp(ctx,r,-1,GrapeCity.UI.SheetArea.rowHeader,clipRect)
        },
        paintColFooterCorner: function(ctx, clipRect)
        {
            var sheet = this._sheet;
            var layout = sheet._getSheetLayout();
            var x = layout.footerX,
                y = layout.footerY,
                width = layout.footerWidth,
                height = layout.footerHeight;
            var sheetWidth = 0;
            var colLayout = sheet._getColumnLayout(1,GrapeCity.UI.SheetArea.viewport);
            var cl = colLayout.findCol(colLayout[colLayout.length - 1].col,GrapeCity.UI.SheetArea.viewport);
            if(cl)
                sheetWidth = cl.x + cl.width;
            else
                sheetWidth = layout.width;
            ctx.save();
            ctx.strokeStyle = "#9EB6CE";
            ctx.beginPath();
            ctx.moveTo(x,y - 0.5);
            ctx.lineTo(sheetWidth,y - 0.5);
            ctx.stroke();
            ctx.restore();
            ctx.fillStyle = "white";
            ctx.fillRect(x,y,width,height);
            var style = sheet.getActualStyle(-1,-1,GrapeCity.UI.SheetArea.footerCorner);
            this.paintCell(ctx,null,-1,-1,x,y,width,height,style,GrapeCity.UI.SheetArea.footerCorner)
        },
        paintColFooter: function(ctx, c, clipRect)
        {
            var sheet = this._sheet;
            var layout = sheet._getSheetLayout();
            ctx.fillStyle = "white";
            if(c === 0)
                ctx.fillRect(layout.frozenX,layout.footerY,layout.frozenWidth,layout.footerHeight);
            else
                ctx.fillRect(layout.viewportX,layout.footerY,layout.viewportWidth,layout.footerHeight);
            this.paintViewportImp(ctx,-1,c,GrapeCity.UI.SheetArea.colFooter,clipRect)
        },
        paintViewport: function(ctx, rowViewportIndex, colViewportIndex, clipRect)
        {
            this.paintViewportImp(ctx,rowViewportIndex,colViewportIndex,GrapeCity.UI.SheetArea.viewport,clipRect)
        },
        paintSelection: function(ctx, rowViewportIndex, colViewportIndex, clipRect)
        {
            var sheet = this._sheet;
            var selectRect = null;
            ctx.save();
            if(clipRect)
            {
                ctx.rect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
                ctx.clip();
                ctx.beginPath()
            }
            for(var i = 0; i < sheet._selectionModel.length; i++)
            {
                var selectedRange = sheet._selectionModel[i];
                var span = sheet._spanModel.find(selectedRange.row,selectedRange.col);
                if(span && span.containsRange(selectedRange))
                    selectedRange = span;
                selectRect = sheet._getRangeRect(rowViewportIndex,colViewportIndex,selectedRange);
                if(selectRect && selectRect.width > 0 && selectRect.height > 0)
                {
                    ctx.fillStyle = "rgba(180,180,200, 0.2)";
                    ctx.fillRect(selectRect.x,selectRect.y,selectRect.width,selectRect.height)
                }
            }
            var rect = sheet.getCellRect(sheet._activeRowIndex,sheet._activeColIndex,rowViewportIndex,colViewportIndex);
            var layout = sheet._getSheetLayout();
            var viewportRect = layout.viewportRect(rowViewportIndex,colViewportIndex);
            if(viewportRect.intersectRect(rect))
            {
                rect.x = Math.max(rect.x,viewportRect.x);
                rect.y = Math.max(rect.y,viewportRect.y)
            }
            if(!rect || rect.x === null || rect.x === undefined || rect.y === null || rect.y === undefined || rect.width === null || rect.width === undefined || rect.height === null || rect.height === undefined)
                rect = null;
            if(clipRect && rect && rect.intersectRect(clipRect))
            {
                this._copyDoubleBuffer(rect.x + 1,rect.y + 1,rect.width - 2,rect.height - 2,rect.x + 1,rect.y + 1,null,null,ctx);
                if(sheet._selectionModel.length > 1)
                {
                    ctx.strokeStyle = "black";
                    ctx.lineWidth = 1;
                    ctx.strokeRect(rect.x + 1.5,rect.y + 1.5,rect.width - 4,rect.height - 4)
                }
            }
            if(sheet._selectionModel.length === 1)
            {
                if(sheet._eventHandler.isDraggingFill && sheet._eventHandler.isDragClear() && !sheet._eventHandler._isDragAroundIndicator)
                {
                    var fillRange = sheet._eventHandler.getCurrentFillRange();
                    var fillRect = sheet._getRangeRect(rowViewportIndex,colViewportIndex,fillRange);
                    if(fillRect && fillRect.width > 0 && fillRect.height > 0)
                    {
                        ctx.fillStyle = "rgba(110,110,110, 0.5)";
                        ctx.fillRect(fillRect.x,fillRect.y,fillRect.width,fillRect.height)
                    }
                }
                this.paintSelectionBorder(ctx,rowViewportIndex,colViewportIndex,selectRect,clipRect)
            }
            ctx.beginPath();
            ctx.restore()
        },
        paintSelectionBorder: function(ctx, rowViewportIndex, colViewportIndex, selectRect, clipRect)
        {
            var sheet = this._sheet;
            if(selectRect.width >= 0 && selectRect.height >= 0 && (!clipRect || selectRect.intersect(clipRect.x - 1,clipRect.y - 1,clipRect.width + 2,clipRect.height + 2)))
            {
                ctx.save();
                ctx.beginPath();
                ctx.strokeStyle = "black";
                ctx.lineWidth = 2;
                if(selectRect.width > 0 && selectRect.height > 0)
                {
                    if(sheet._eventHandler.isDraggingFill)
                        switch(sheet._eventHandler._currentFillDirection)
                        {
                            case GrapeCity.UI.DragFillDirection.Up:
                                ctx.moveTo(selectRect.x + 0.5,selectRect.y - 0.5);
                                ctx.lineTo(selectRect.x + selectRect.width - 1,selectRect.y - 0.5);
                                break;
                            case GrapeCity.UI.DragFillDirection.Down:
                                ctx.moveTo(selectRect.x + 0.5,selectRect.y + selectRect.height - 0.5);
                                ctx.lineTo(selectRect.x + selectRect.width - 1,selectRect.y + selectRect.height - 0.5);
                                break;
                            case GrapeCity.UI.DragFillDirection.Left:
                                ctx.moveTo(selectRect.x - 0.5,selectRect.y + 0.5);
                                ctx.lineTo(selectRect.x - 0.5,selectRect.y + selectRect.height - 1);
                                break;
                            case GrapeCity.UI.DragFillDirection.Right:
                                ctx.moveTo(selectRect.x + selectRect.width - 0.5,selectRect.y + 0.5);
                                ctx.lineTo(selectRect.x + selectRect.width - 0.5,selectRect.y + selectRect.height - 1);
                                break;
                            case GrapeCity.UI.DragFillDirection.UpClear:
                            case GrapeCity.UI.DragFillDirection.LeftClear:
                                break;
                            default:
                                break
                        }
                    else
                        ctx.rect(selectRect.x - 0.5,selectRect.y - 0.5,selectRect.width,selectRect.height);
                    ctx.stroke();
                    if(sheet.canUserDragFill())
                        this.paintDragFillIndicator(ctx,rowViewportIndex,colViewportIndex,selectRect,clipRect)
                }
                else if(selectRect.width === 0 || selectRect.height === 0)
                    ctx.strokeRect(selectRect.x - 1,selectRect.y - 1,selectRect.width + 1,selectRect.height + 1);
                ctx.beginPath();
                ctx.restore()
            }
        },
        getDragFillIndicatorRect: function(selectRect)
        {
            var sheet = this._sheet;
            var range = sheet._getActiveSelectedRange();
            var layout = sheet._getSheetLayout();
            var rect = new GrapeCity.UI.Rect(-4,-4,4,4);
            if(range.col === -1)
            {
                rect.x = layout.frozenX;
                rect.y = selectRect.y + selectRect.height - 0.5 - 2
            }
            else if(range.row === -1)
            {
                rect.x = selectRect.x + selectRect.width - 0.5 - 2;
                rect.y = layout.frozenY
            }
            else
            {
                rect.x = selectRect.x + selectRect.width - 0.5 - 2;
                rect.y = selectRect.y + selectRect.height - 0.5 - 2
            }
            sheet._dragFillIndicatorRect = rect;
            return rect
        },
        paintDragFillIndicator: function(ctx, rowViewportIndex, colViewportIndex, selectRect, clipRect)
        {
            var sheet = this._sheet;
            var layout = sheet._getSheetLayout();
            var indicatorRect = this.getDragFillIndicatorRect(selectRect);
            var viewportRect = layout.viewportRect(rowViewportIndex,colViewportIndex);
            if(!clipRect || indicatorRect.intersectRect(clipRect))
                if(indicatorRect.intersectRect(viewportRect))
                {
                    var copyRect = new GrapeCity.UI.Rect(indicatorRect.x - 1.5,indicatorRect.y - 1.5,indicatorRect.width + 2,indicatorRect.height + 2);
                    copyRect.x = Math.max(copyRect.x,viewportRect.x);
                    copyRect.y = Math.max(copyRect.y,viewportRect.y);
                    this._copyDoubleBufferRect(copyRect,null,null,null,null,ctx);
                    ctx.fillStyle = "black";
                    ctx.fillRect(indicatorRect.x,indicatorRect.y,indicatorRect.width,indicatorRect.height)
                }
        },
        paintViewportImp: function(ctx, rowViewportIndex, colViewportIndex, sheetArea, clipRect)
        {
            ctx.beginPath();
            var sheet = this._sheet;
            var sheetLayout = sheet._getSheetLayout();
            var rowLayouts = sheet._getRowLayout(rowViewportIndex,sheetArea);
            var colLayouts = sheet._getColumnLayout(colViewportIndex,sheetArea);
            var cellLayouts = sheet._getCellLayout(rowViewportIndex,colViewportIndex,sheetArea);
            var useDoubleBuffer = GrapeCity.UI._useDoubleBuffer();
            var cellsRect = new GrapeCity.UI.Rect;
            var needRestoreContext = false;
            if(useDoubleBuffer && rowLayouts && rowLayouts.length > 0 && colLayouts && colLayouts.length > 0)
            {
                cellsRect.x = colLayouts[0].x;
                cellsRect.y = rowLayouts[0].y;
                cellsRect.width = colLayouts[colLayouts.length - 1].x + colLayouts[colLayouts.length - 1].width - cellsRect.x;
                cellsRect.height = rowLayouts[rowLayouts.length - 1].y + rowLayouts[rowLayouts.length - 1].height - cellsRect.y;
                ctx.save();
                ctx.rect(cellsRect.x,cellsRect.y,cellsRect.width,cellsRect.height);
                ctx.clip();
                ctx.beginPath();
                needRestoreContext = true;
                ctx.fillStyle = this._getDefaultBackground();
                ctx.fillRect(cellsRect.x,cellsRect.y,cellsRect.width,cellsRect.height)
            }
            var rowLayout = null;
            var colLayout = null;
            var cellLayout = null;
            var row,
                col,
                x = 0,
                y = 0,
                width = 0,
                height = 0,
                data,
                style;
            var rowLayoutCount = rowLayouts.length,
                colLayoutCount = colLayouts.length;
            var border = new GrapeCity.UI._GcBorders(sheet,rowViewportIndex,colViewportIndex,sheetArea);
            var paintedSpans = [];
            var spanModel = [];
            var filterHeaderModel = [];
            var cellOverflowValueLayoutModel = new GrapeCity.UI._CellOverflowValueLayoutModel;
            var validationUIModel = [];
            if(rowLayoutCount > 0 && colLayoutCount > 0)
            {
                for(var i = 0; i < rowLayoutCount; i++)
                {
                    rowLayout = rowLayouts[i];
                    var cellOverflowModel = this._buildCellOverflowLayoutModel(rowLayout.row,sheetArea);
                    for(var j = 0; j < colLayoutCount; j++)
                    {
                        colLayout = colLayouts[j];
                        if(clipRect && !colLayout.intersect(clipRect))
                            continue;
                        cellLayout = cellLayouts.findCell(rowLayout.row,colLayout.col);
                        if(cellLayout)
                        {
                            row = cellLayout.row;
                            col = cellLayout.col;
                            x = cellLayout.x;
                            y = cellLayout.y;
                            width = cellLayout.width;
                            height = cellLayout.height
                        }
                        else
                        {
                            row = rowLayout.row;
                            col = colLayout.col;
                            x = colLayout.x;
                            y = rowLayout.y;
                            width = colLayout.width;
                            height = rowLayout.height
                        }
                        if(row >= 0 && col >= 0)
                        {
                            data = sheet.getValue(row,col,sheetArea);
                            style = sheet.getActualStyle(row,col,sheetArea)
                        }
                        var cellOverflowLayout = null;
                        if(sheet._allowCellOverflow && cellOverflowModel)
                            cellOverflowLayout = cellOverflowModel.find(col);
                        if(sheet.rowFilter() && sheet.rowFilter().showFilterButton && sheet.rowFilter().isFilterHeader(rowLayout.row,colLayout.col,sheetArea))
                            if(colLayout.width > 0 && rowLayout.height > 0)
                            {
                                var item = {
                                        row: row,
                                        col: col,
                                        x: x,
                                        y: y,
                                        width: width,
                                        height: height,
                                        sheetArea: sheetArea
                                    };
                                filterHeaderModel.push(item)
                            }
                        if(sheetArea === GrapeCity.UI.SheetArea.viewport)
                            if(sheet.parent && sheet.parent.highlightInvalidData() && !sheet.isValid(row,col,data))
                                validationUIModel.push({
                                    x: x,
                                    y: y,
                                    width: width,
                                    height: height
                                });
                        if(sheetArea !== GrapeCity.UI.SheetArea.viewport || !(cellLayout && paintedSpans.contains(cellLayout) && (cellLayout.row !== rowLayout.row || cellLayout.col !== colLayout.col)))
                        {
                            if(!cellLayout || !paintedSpans.contains(cellLayout))
                            {
                                var bs = style;
                                if(this._isCellHidden(row,col,sheetArea))
                                    bs = {_backColor: style.backColor};
                                border.addCellLines(row,col,x,y,width,height,bs,cellLayout,cellOverflowLayout)
                            }
                            if(width > 0 && height > 0 && !cellLayout)
                            {
                                if(!useDoubleBuffer)
                                {
                                    ctx.fillStyle = this._getDefaultBackground();
                                    ctx.fillRect(x,y,width,height)
                                }
                                this.paintCell(ctx,data,row,col,x,y,width,height,style,sheetArea,cellOverflowLayout)
                            }
                            if(cellLayout)
                            {
                                paintedSpans.push(cellLayout);
                                spanModel.push({
                                    row: row,
                                    col: col,
                                    x: x,
                                    y: y,
                                    width: width,
                                    height: height,
                                    data: data,
                                    style: style,
                                    cellOverflowLayout: cellOverflowLayout
                                })
                            }
                            if(sheet._allowCellOverflow && height > 0)
                                this._buildCellOverflowValueLayoutModel(ctx,data,row,col,x,y,width,height,sheetArea,cellOverflowLayout,cellOverflowValueLayoutModel,colLayouts)
                        }
                    }
                }
                if(spanModel.length > 0)
                {
                    var count = spanModel.length,
                        span;
                    for(var i = 0; i < count; i++)
                    {
                        span = spanModel[i];
                        if(!useDoubleBuffer)
                        {
                            ctx.fillStyle = this._getDefaultBackground();
                            ctx.fillRect(x,y,width,height)
                        }
                        this.paintCell(ctx,span.data,span.row,span.col,span.x,span.y,span.width,span.height,span.style,sheetArea,span.cellOverflowLayout)
                    }
                }
                if(cellOverflowValueLayoutModel.length > 0)
                    this._paintCellOverflowValue(ctx,cellOverflowValueLayoutModel);
                if(filterHeaderModel.length > 0)
                    this._paintFilterButton(ctx,filterHeaderModel,rowViewportIndex,colViewportIndex)
            }
            border.paint(ctx,clipRect);
            this._paintValidationUI(ctx,validationUIModel);
            if(needRestoreContext)
            {
                ctx.restore();
                ctx.beginPath()
            }
        },
        _isHiddenByGroup: function(rangeGroup, index)
        {
            if(!rangeGroup || rangeGroup._getCount() === 0)
                return false;
            return rangeGroup.isCollapsed(index)
        },
        _isCellHiddenByGroup: function(row, col, sheetArea)
        {
            var sheet = this._sheet;
            if(sheetArea === undefined || sheetArea === null || sheetArea === GrapeCity.UI.SheetArea.viewport)
                return this._isHiddenByGroup(sheet.rowRangeGroup,row) || this._isHiddenByGroup(sheet.colRangeGroup,col);
            else if(sheetArea === GrapeCity.UI.SheetArea.colHeader)
                return this._isHiddenByGroup(sheet.colRangeGroup,col);
            else if(sheetArea === GrapeCity.UI.SheetArea.rowHeader)
                return this._isHiddenByGroup(sheet.rowRangeGroup,row)
        },
        _isRowHiddenByFilter: function(row)
        {
            var sheet = this._sheet;
            if(sheet.rowFilter())
                return!sheet.rowFilter().isRowFilteredOut(row)
        },
        _isCellHidden: function(row, col, sheetArea)
        {
            var sheet = this._sheet;
            if(!sheet.getRowVisible(row,sheetArea) || !sheet.getColumnVisible(col,sheetArea))
                return false;
            return this._isCellHiddenByGroup(row,col,sheetArea)
        },
        _getFilterButtonRect: function(cellRect)
        {
            var sheet = this._sheet;
            var filterButtonZoom = sheet._zoomFactor > 1 ? 1 : sheet._zoomFactor;
            var width = parseInt(Math.min(20,sheet.defaults.rowHeight) * filterButtonZoom,10),
                height = width,
                x = cellRect.x + cellRect.width - width,
                y = cellRect.y + cellRect.height - height;
            return{
                    x: x,
                    y: y,
                    width: width,
                    height: height
                }
        },
        _paintFilterButton: function(ctx, filterHeaderModel, rowViewportIndex, colViewportIndex)
        {
            var sheet = this._sheet;
            ctx.save();
            ctx.lineWidth = 1;
            ctx.fillStyle = "#FFFFFF";
            ctx.strokeStyle = "#CCCCCC";
            var ctx2 = this._getCtx();
            for(var i = 0; i < filterHeaderModel.length; i++)
            {
                var cellLayout = filterHeaderModel[i];
                var filterButtonRect = this._getFilterButtonRect(new GrapeCity.UI.Rect(cellLayout.x,cellLayout.y,cellLayout.width,cellLayout.height));
                var width = filterButtonRect.width;
                var height = filterButtonRect.height;
                var x = filterButtonRect.x;
                var y = filterButtonRect.y;
                ctx.save();
                ctx.rect(cellLayout.x,cellLayout.y,cellLayout.width,cellLayout.height);
                ctx.clip();
                ctx.beginPath();
                ctx.fillRect(x + 1,y + 1,width - 3,height - 3);
                ctx.strokeRect(x + 1 - 0.5,y + 2 - 0.5,width - 3,height - 4);
                var filterButtonState = this._getFilterButtonState(cellLayout.col);
                var imgSrc = GrapeCity.UI._GcFilterDialog.getImageSrc(filterButtonState);
                if(!sheet.imageLoader)
                    sheet.imageLoader = new GrapeCity.UI._GcImageLoader(function()
                    {
                        sheet.repaint()
                    });
                try
                {
                    if(sheet.imageLoader.getState(imgSrc))
                        ctx.drawImage(sheet.imageLoader.getImage(imgSrc),x,y + 1,width - 3,height - 3);
                    else
                        sheet.imageLoader.addImage(imgSrc)
                }
                catch(ex){}
                ctx.beginPath();
                ctx.restore()
            }
            ctx.restore()
        },
        _paintValidationUI: function(ctx, validationUIModel)
        {
            if(validationUIModel)
            {
                var length = validationUIModel.length;
                for(var i = 0; i < length; i++)
                {
                    var circle = validationUIModel[i];
                    var x = circle.x - 4,
                        y = circle.y - 4,
                        w = circle.width + 8,
                        h = circle.height + 8;
                    var kappa = 0.5522848,
                        ox = w / 2 * kappa,
                        oy = h / 2 * kappa,
                        xe = x + w,
                        ye = y + h,
                        xm = x + w / 2,
                        ym = y + h / 2;
                    ctx.save();
                    ctx.lineWidth = 2;
                    ctx.strokeStyle = "red";
                    ctx.beginPath();
                    ctx.moveTo(x,ym);
                    ctx.bezierCurveTo(x,ym - oy,xm - ox,y,xm,y);
                    ctx.bezierCurveTo(xm + ox,y,xe,ym - oy,xe,ym);
                    ctx.bezierCurveTo(xe,ym + oy,xm + ox,ye,xm,ye);
                    ctx.bezierCurveTo(xm - ox,ye,x,ym + oy,x,ym);
                    ctx.closePath();
                    ctx.stroke();
                    ctx.restore()
                }
            }
        },
        paintCell: function(ctx, data, row, col, x, y, width, height, style, sheetArea, cellOverflowLayout)
        {
            var sheet = this._sheet;
            var visualState = this.getVisualState(row,col,sheetArea);
            var font = "";
            if(style.font)
            {
                style.font = this._getZoomFont(style.font);
                font = style.font
            }
            else
            {
                style.font = this._getZoomFont(this._getDefaultFont(sheetArea));
                font = style.font
            }
            if(!style.wordWrap && style.shrinkToFit)
            {
                var text = sheet.getText(row,col,sheetArea);
                if(text && text !== "")
                {
                    var minFont = {value: false};
                    for(var i = 0; i < 3 && minFont.value === false; i++)
                    {
                        var w = sheet._getStringWidth(text,font);
                        var space = Math.max(0,width - 4);
                        if(style.hAlign !== GrapeCity.UI.HorizontalAlign.center && style.textIndent)
                            space = Math.max(0,space - style.textIndent * 8);
                        if(space < w)
                        {
                            font = this._scaleFont(font,space / w,minFont);
                            style.font = font
                        }
                        else
                        {
                            style.font = font;
                            break
                        }
                    }
                }
            }
            if(width > 0 && height > 0)
            {
                var ct = sheet.getCellType(row,col,sheetArea);
                x--;
                y--;
                width++;
                height++;
                var options = {
                        visualState: visualState,
                        cellOverflowLayout: cellOverflowLayout,
                        useWijmoTheme: sheet.parent ? sheet.parent.useWijmoTheme : sheet.useWijmoTheme
                    };
                if(sheetArea === GrapeCity.UI.SheetArea.viewport)
                {
                    var sparkline = sheet.getSparkline(row,col);
                    if(sparkline)
                        options.sparkline = sparkline
                }
                if(style.wordWrap)
                    options.lineHeight = sheet._getFontHeight(font);
                if(!sheet.imageLoader)
                    sheet.imageLoader = new GrapeCity.UI._GcImageLoader(function()
                    {
                        sheet.repaint()
                    });
                options.imageLoader = sheet.imageLoader;
                options.sheet = this._sheet, options.row = row, options.col = col;
                ct.paint(ctx,data,x,y,width,height,style,options)
            }
        },
        getVisualState: function(row, col, sheetArea)
        {
            var sheet = this._sheet;
            var m = sheet._getModel(sheetArea);
            var visualState = null;
            if(m)
                visualState = m.getVisualState(row,col);
            if(visualState === undefined || visualState === null)
                visualState = GrapeCity.UI.VisualState.Normal;
            if(sheetArea === undefined || sheetArea === null || sheetArea === GrapeCity.UI.SheetArea.viewport)
            {
                if(!sheet.isEditing() && sheet._isActiveCell(row,col))
                    visualState = GrapeCity.UI.VisualState.Active;
                else if(sheet._isSelected(row,col,sheetArea))
                    visualState = GrapeCity.UI.VisualState.Selected
            }
            else
            {
                if(sheet._isSelected(row,col,sheetArea))
                {
                    visualState = GrapeCity.UI.VisualState.Highlight;
                    if(sheet._isAllSelected(row,col,sheetArea))
                        visualState = GrapeCity.UI.VisualState.Selected
                }
                if(sheet._isHover(row,col,sheetArea))
                    visualState = GrapeCity.UI.VisualState.Hover
            }
            return visualState
        },
        paintLine: function(ctx, line, x1, y1, x2, y2)
        {
            ctx.lineWidth = line.width();
            ctx.strokeStyle = line.color;
            ctx.moveTo(x1,y1);
            ctx.lineTo(x2,y2)
        },
        paintGroup: function(ctx, clipRect)
        {
            var sheet = this._sheet;
            var groupLayout = sheet._getGroupLayout();
            if(groupLayout.width > 0 || groupLayout.height > 0)
            {
                ctx.save();
                this.paintGroupHeader(ctx);
                this._rowGroups = [new GrapeCity.UI._RangeGroupPresenter(sheet,true,0),new GrapeCity.UI._RangeGroupPresenter(sheet,true,1)];
                var i;
                for(i = 0; i < 2; i++)
                {
                    this._rowGroups[i].createGroupInfo();
                    this._rowGroups[i].paintGroups(ctx)
                }
                this._columnGroups = [new GrapeCity.UI._RangeGroupPresenter(sheet,false,0),new GrapeCity.UI._RangeGroupPresenter(sheet,false,1)];
                for(i = 0; i < 2; i++)
                {
                    this._columnGroups[i].createGroupInfo();
                    this._columnGroups[i].paintGroups(ctx)
                }
                ctx.beginPath();
                ctx.restore()
            }
        },
        paintGroupHeader: function(ctx)
        {
            var sheet = this._sheet;
            var colGroupHeader = new GrapeCity.UI._RangeGroupHeaderPresenter(sheet,false);
            colGroupHeader.paintGroupHeader(ctx);
            var rowGroupHeader = new GrapeCity.UI._RangeGroupHeaderPresenter(sheet,true);
            rowGroupHeader.paintGroupHeader(ctx)
        },
        groupHitTest: function(x, y)
        {
            var sheet = this._sheet;
            var groupLayout = sheet._getGroupLayout();
            if(!groupLayout)
                return null;
            var gRect = new GrapeCity.UI.Rect(groupLayout.x,groupLayout.y,groupLayout.width,sheet._bounds.height);
            if(!gRect.contains(x,y))
            {
                gRect = new GrapeCity.UI.Rect(groupLayout.x,groupLayout.y,sheet._bounds.width,groupLayout.height);
                if(!gRect.contains(x,y))
                    return null
            }
            var t = {
                    what: "empty",
                    info: null
                };
            var rowGroupHeader = new GrapeCity.UI._RangeGroupHeaderPresenter(sheet,true);
            var k = rowGroupHeader.getGroupButton(x,y);
            if(k)
                t = {
                    what: "rgh",
                    info: k
                };
            if(t.what === "empty")
            {
                var colGroupHeader = new GrapeCity.UI._RangeGroupHeaderPresenter(sheet,false);
                k = colGroupHeader.getGroupButton(x,y);
                if(k)
                    t = {
                        what: "cgh",
                        info: k
                    }
            }
            var i;
            if(t.what === "empty")
            {
                var rowGroups = [new GrapeCity.UI._RangeGroupPresenter(sheet,true,0),new GrapeCity.UI._RangeGroupPresenter(sheet,true,1)];
                for(i = 0; i < 2; i++)
                {
                    rowGroups[i].createGroupInfo();
                    k = rowGroups[i].getRowGroupButton(x,y);
                    if(k)
                    {
                        t = {
                            what: "rg",
                            info: k
                        };
                        break
                    }
                }
            }
            if(t.what === "empty")
            {
                var colGroups = [new GrapeCity.UI._RangeGroupPresenter(sheet,false,0),new GrapeCity.UI._RangeGroupPresenter(sheet,false,1)];
                for(i = 0; i < 2; i++)
                {
                    colGroups[i].createGroupInfo();
                    k = colGroups[i].getColGroupButton(x,y);
                    if(k)
                    {
                        t = {
                            what: "cg",
                            info: k
                        };
                        break
                    }
                }
            }
            return t
        },
        _paintCellOverflowValue: function(ctx, cellOverflowValueLayoutModel)
        {
            for(var flowIndex = 0; flowIndex < cellOverflowValueLayoutModel.length; flowIndex++)
            {
                var overflow = cellOverflowValueLayoutModel[flowIndex];
                this.paintCellOverflowValue(ctx,overflow.data,overflow.row,overflow.col,overflow.x,overflow.y,overflow.width,overflow.height,overflow.sheetArea,overflow.cellOverflowLayout)
            }
        },
        _buildCellOverflowValueLayoutModel: function(ctx, data, row, col, x, y, width, height, sheetArea, cellOverflowLayout, cellOverflowValueLayoutModel, colLayouts)
        {
            var sheet = this._sheet,
                newX,
                textX,
                cellOverflowValueLayout,
                colIndex,
                newWidth;
            var frozenColCount = isNaN(sheet.frozenColCount) ? 0 : sheet.frozenColCount;
            if(cellOverflowLayout && cellOverflowValueLayoutModel)
            {
                var isFrozenLeft = false;
                if(col < frozenColCount)
                    isFrozenLeft = true;
                if(cellOverflowLayout.column === col)
                {
                    newX = x;
                    textX = newX;
                    if(cellOverflowLayout.hAlign === GrapeCity.UI.HorizontalAlign.right)
                        textX = x + width - cellOverflowLayout.columnWidth;
                    else if(cellOverflowLayout.hAlign === GrapeCity.UI.HorizontalAlign.center)
                        textX = x + width / 2 - cellOverflowLayout.columnPrevWidth;
                    cellOverflowLayout.layout = {
                        x: textX,
                        y: y,
                        width: cellOverflowLayout.columnWidth,
                        height: height
                    };
                    cellOverflowValueLayout = new GrapeCity.UI._CellOverflowValueLayout(data,row,cellOverflowLayout.column,newX,y,width,height,sheetArea,cellOverflowLayout);
                    cellOverflowValueLayoutModel.push(cellOverflowValueLayout)
                }
                else if(!isFrozenLeft && cellOverflowLayout.column < sheet._scrollLeftCol)
                {
                    newX = colLayouts[0].x;
                    newWidth = 0;
                    for(colIndex = sheet._scrollLeftCol - 1; colIndex >= cellOverflowLayout.column; colIndex--)
                    {
                        newWidth = sheet._getZoomColumnWidth(colIndex,sheetArea);
                        newX -= newWidth
                    }
                    textX = newX;
                    if(cellOverflowLayout.hAlign === GrapeCity.UI.HorizontalAlign.right)
                        textX = newX + newWidth - cellOverflowLayout.columnWidth;
                    else if(cellOverflowLayout.hAlign === GrapeCity.UI.HorizontalAlign.center)
                        textX = newX + newWidth / 2 - cellOverflowLayout.columnPrevWidth;
                    cellOverflowLayout.layout = {
                        x: textX,
                        y: y,
                        width: cellOverflowLayout.columnWidth,
                        height: height
                    };
                    data = sheet.getValue(row,cellOverflowLayout.column,sheetArea);
                    cellOverflowValueLayout = new GrapeCity.UI._CellOverflowValueLayout(data,row,cellOverflowLayout.column,newX,y,newWidth,height,sheetArea,cellOverflowLayout);
                    cellOverflowValueLayoutModel.push(cellOverflowValueLayout)
                }
                else if(!isFrozenLeft && cellOverflowLayout.column > sheet._getPageRightColumn() || isFrozenLeft && cellOverflowLayout.column >= frozenColCount)
                {
                    newX = colLayouts[colLayouts.length - 1].x + colLayouts[colLayouts.length - 1].width;
                    newWidth = 0;
                    if(isFrozenLeft)
                    {
                        for(colIndex = frozenColCount; colIndex < cellOverflowLayout.column; colIndex++)
                        {
                            newWidth = sheet._getZoomColumnWidth(colIndex,sheetArea);
                            newX += newWidth
                        }
                        newWidth = sheet._getZoomColumnWidth(cellOverflowLayout.column,sheetArea)
                    }
                    else
                    {
                        for(colIndex = sheet._getPageRightColumn() + 1; colIndex < cellOverflowLayout.column; colIndex++)
                        {
                            newWidth = sheet._getZoomColumnWidth(colIndex,sheetArea);
                            newX += newWidth
                        }
                        newWidth = sheet._getZoomColumnWidth(cellOverflowLayout.column,sheetArea)
                    }
                    textX = newX;
                    if(cellOverflowLayout.hAlign === GrapeCity.UI.HorizontalAlign.right)
                        textX = newX + newWidth - cellOverflowLayout.columnWidth;
                    else if(cellOverflowLayout.hAlign === GrapeCity.UI.HorizontalAlign.center)
                        textX = newX + newWidth / 2 - cellOverflowLayout.columnPrevWidth;
                    cellOverflowLayout.layout = {
                        x: textX,
                        y: y,
                        width: cellOverflowLayout.columnWidth,
                        height: height
                    };
                    data = sheet.getValue(row,cellOverflowLayout.column,sheetArea);
                    cellOverflowValueLayout = new GrapeCity.UI._CellOverflowValueLayout(data,row,cellOverflowLayout.column,newX,y,newWidth,height,sheetArea,cellOverflowLayout);
                    cellOverflowValueLayoutModel.push(cellOverflowValueLayout)
                }
            }
        },
        _buildCellOverflowLayoutModel: function(rowIndex, sheetArea)
        {
            if(sheetArea !== undefined && sheetArea !== null && sheetArea !== GrapeCity.UI.SheetArea.viewport)
                return null;
            var sheet = this._sheet;
            if(!sheet._allowCellOverflow)
                return null;
            if(sheet.maxCellOverflowDistance === 1)
                return null;
            var cellOverflowLayoutModel = null;
            if(sheet._cellOverflowModelCache)
            {
                cellOverflowLayoutModel = sheet._cellOverflowModelCache[rowIndex];
                if(cellOverflowLayoutModel)
                    return cellOverflowLayoutModel
            }
            else
                sheet._cellOverflowModelCache = {};
            cellOverflowLayoutModel = new GrapeCity.UI._CellOverflowLayoutModel;
            for(var colIndex = 0; colIndex < sheet.getColumnCount(sheetArea); colIndex++)
            {
                var visible = sheet.getColumnVisible(colIndex,sheetArea);
                var width = sheet.getColumnWidth(colIndex,sheetArea);
                if(visible === false || width <= 0)
                    continue;
                var cellStyle = sheet.getActualStyle(rowIndex,colIndex,GrapeCity.UI.SheetArea.viewport);
                if(cellStyle && (cellStyle.shrinkToFit || cellStyle.wordWrap))
                    continue;
                var hAlign = GrapeCity.UI._SheetRender.getHAlignByValueType(cellStyle.hAlign,sheet.getValue(rowIndex,colIndex));
                var cellOverflow;
                if(hAlign === undefined || hAlign === null || hAlign === GrapeCity.UI.HorizontalAlign.left)
                {
                    cellOverflow = this._buildCellOverflowLayoutModelForLeft(rowIndex,colIndex,sheetArea,cellStyle);
                    if(cellOverflow)
                    {
                        cellOverflowLayoutModel.push(cellOverflow);
                        colIndex = cellOverflow.endCol
                    }
                }
                else if(hAlign === GrapeCity.UI.HorizontalAlign.right)
                {
                    cellOverflow = this._buildCellOverflowLayoutModelForRight(rowIndex,colIndex,sheetArea,cellStyle);
                    if(cellOverflow)
                    {
                        cellOverflowLayoutModel.push(cellOverflow);
                        colIndex = cellOverflow.endCol
                    }
                }
                else if(hAlign === GrapeCity.UI.HorizontalAlign.center)
                {
                    cellOverflow = this._buildCellOverflowLayoutModelForCenter(rowIndex,colIndex,sheetArea,cellStyle);
                    if(cellOverflow)
                    {
                        cellOverflowLayoutModel.push(cellOverflow);
                        colIndex = cellOverflow.endCol
                    }
                }
            }
            sheet._cellOverflowModelCache[rowIndex] = cellOverflowLayoutModel;
            return cellOverflowLayoutModel
        },
        _buildCellOverflowLayoutModelForLeft: function(row, col, sheetArea, style)
        {
            var sheet = this._sheet;
            var text = sheet.getText(row,col,sheetArea);
            if(!text || text === "" || sheet._spanModel.find(row,col) !== null)
                return null;
            if(sheet.getValue(row,col + 1,sheetArea) !== null || sheet._spanModel.find(row,col + 1) !== null)
                return null;
            var columnWidth = sheet._getZoomColumnWidth(col,sheetArea);
            var font = !style || !style.font ? this._getZoomFont(this._getDefaultFont(sheetArea)) : this._getZoomFont(style.font);
            var valueWidth = sheet._getStringWidth(text,font) + (style.textIndent ? style.textIndent * 8 : 0);
            if(valueWidth <= columnWidth)
                return null;
            var endCol = col;
            var count = sheet.getColumnCount(sheetArea);
            if(sheet.maxCellOverflowDistance)
            {
                var maxCellIndex = parseInt(sheet.maxCellOverflowDistance,10) + col;
                if(maxCellIndex < count)
                    count = maxCellIndex
            }
            for(var c = col + 1; c < count; c++)
            {
                var data = sheet.getValue(row,c,sheetArea);
                var spanCell = sheet._spanModel.find(row,c);
                if(data !== undefined && data !== null || spanCell || valueWidth < columnWidth)
                    break;
                columnWidth += sheet._getZoomColumnWidth(c,sheetArea);
                endCol = c
            }
            if(endCol === col)
                return null;
            return new GrapeCity.UI._CellOverflowLayout(col,col,endCol,valueWidth,columnWidth - 1,GrapeCity.UI.HorizontalAlign.left,null)
        },
        _buildCellOverflowLayoutModelForRight: function(row, col, sheetArea, style)
        {
            var sheet = this._sheet;
            var text = sheet.getText(row,col,sheetArea);
            if(!text || text === "" || sheet._spanModel.find(row,col) !== null)
                return null;
            if(sheet.getValue(row,col - 1,sheetArea) !== null || sheet._spanModel.find(row,col - 1) !== null)
                return null;
            var columnWidth = sheet._getZoomColumnWidth(col,sheetArea);
            var font = !style || !style.font ? this._getZoomFont(this._getDefaultFont(sheetArea)) : this._getZoomFont(style.font);
            var valueWidth = sheet._getStringWidth(text,font);
            if(valueWidth <= columnWidth)
                return null;
            var startCol = col;
            var count = -1;
            if(sheet.maxCellOverflowDistance)
            {
                var minCellIndex = col - parseInt(sheet.maxCellOverflowDistance,10);
                if(minCellIndex > count)
                    count = minCellIndex
            }
            for(var c = col - 1; c > count; c--)
            {
                var data = sheet.getValue(row,c,sheetArea);
                var spanCell = sheet._spanModel.find(row,c);
                if(data !== undefined && data !== null || spanCell || valueWidth < columnWidth)
                    break;
                columnWidth += sheet._getZoomColumnWidth(c,sheetArea);
                startCol = c
            }
            if(startCol === col)
                return null;
            return new GrapeCity.UI._CellOverflowLayout(col,startCol,col,valueWidth,columnWidth - 1,GrapeCity.UI.HorizontalAlign.right,null)
        },
        _buildCellOverflowLayoutModelForCenter: function(row, col, sheetArea, style)
        {
            var sheet = this._sheet;
            var text = sheet.getText(row,col,sheetArea);
            if(!text || text === "" || sheet._spanModel.find(row,col) !== null)
                return null;
            var columnWidth = sheet._getZoomColumnWidth(col,sheetArea);
            var font = !style || !style.font ? this._getZoomFont(this._getDefaultFont(sheetArea)) : this._getZoomFont(style.font);
            var valueWidth = sheet._getStringWidth(text,font);
            if(valueWidth <= columnWidth)
                return null;
            var columnPrevWidth = columnWidth / 2;
            var startCol = col;
            var preMaxCellOverflowDistance = 0;
            var count = -1;
            if(sheet.maxCellOverflowDistance)
            {
                preMaxCellOverflowDistance = parseInt(parseInt(sheet.maxCellOverflowDistance,10) / 2 + 0.5,10);
                var minCellIndex = col - preMaxCellOverflowDistance;
                if(minCellIndex > count)
                    count = minCellIndex
            }
            var c,
                data,
                spanCell;
            for(c = col - 1; c > count; c--)
            {
                data = sheet.getValue(row,c,sheetArea);
                spanCell = sheet._spanModel.find(row,c);
                if(data !== undefined && data !== null || spanCell || valueWidth / 2 < columnPrevWidth)
                    break;
                columnPrevWidth += sheet._getZoomColumnWidth(c,sheetArea);
                startCol = c
            }
            var columnNextWidth = columnWidth / 2;
            var endCol = col;
            count = sheet.getColumnCount(sheetArea);
            if(sheet.maxCellOverflowDistance)
            {
                var maxCellIndex = parseInt(sheet.maxCellOverflowDistance,10) - preMaxCellOverflowDistance + 1 + col;
                if(maxCellIndex < count)
                    count = maxCellIndex
            }
            for(c = col + 1; c < count; c++)
            {
                data = sheet.getValue(row,c,sheetArea);
                spanCell = sheet._spanModel.find(row,c);
                if(data !== undefined && data !== null || spanCell || valueWidth / 2 < columnNextWidth)
                    break;
                columnNextWidth += sheet._getZoomColumnWidth(c,sheetArea);
                endCol = c
            }
            if(startCol === endCol)
                return null;
            return new GrapeCity.UI._CellOverflowLayout(col,startCol,endCol,valueWidth,columnPrevWidth + columnNextWidth - 1,GrapeCity.UI.HorizontalAlign.center,columnPrevWidth)
        },
        _getFilterButtonState: function(col)
        {
            var filterButtonState = GrapeCity.UI._FilterButtonState.noSortFilter;
            var drf = this._sheet.rowFilter();
            if(!drf)
                return filterButtonState;
            var state = drf.getSortState(col);
            if(drf.isColumnFiltered(col))
            {
                if(state === GrapeCity.UI.SortState.None)
                    filterButtonState = GrapeCity.UI._FilterButtonState.filter;
                else if(state === GrapeCity.UI.SortState.Ascending)
                    filterButtonState = GrapeCity.UI._FilterButtonState.filterAscend;
                else if(state === GrapeCity.UI.SortState.Descending)
                    filterButtonState = GrapeCity.UI._FilterButtonState.filterDescend
            }
            else if(state === GrapeCity.UI.SortState.None)
                filterButtonState = GrapeCity.UI._FilterButtonState.noSortFilter;
            else if(state === GrapeCity.UI.SortState.Ascending)
                filterButtonState = GrapeCity.UI._FilterButtonState.ascend;
            else if(state === GrapeCity.UI.SortState.Descending)
                filterButtonState = GrapeCity.UI._FilterButtonState.descend;
            return filterButtonState
        },
        paintCellOverflowValue: function(ctx, data, row, col, x, y, width, height, sheetArea, cellOverflowLayout)
        {
            var sheet = this._sheet;
            var style = sheet.getActualStyle(row,col,sheetArea);
            if(style.font)
                style.font = this._getZoomFont(style.font);
            else
                style.font = this._getZoomFont(this._getDefaultFont(sheetArea));
            var visualState = this.getVisualState(row,col,sheetArea);
            var options = {
                    visualState: visualState,
                    cellOverflowLayout: cellOverflowLayout,
                    useWijmoTheme: sheet.parent ? sheet.parent.useWijmoTheme : sheet.useWijmoTheme,
                    sheet: sheet,
                    row: row,
                    col: col
                };
            if(width > 0 && height > 0)
            {
                var ct = sheet.getCellType(row,col,sheetArea);
                if(!(sheet.isEditing() && row === sheet._activeRowIndex && col === sheet._activeColIndex))
                {
                    x--;
                    y--;
                    width++;
                    height++;
                    if(ct.paintValue)
                        ct.paintValue(ctx,data,x,y,width,height,style,options)
                }
            }
        }
    };
    GrapeCity.UI._SheetRender.getHAlignByValueType = function(hAlign, value)
    {
        if(hAlign === GrapeCity.UI.HorizontalAlign.general)
        {
            var type = (typeof value === "object" ? Object.prototype.toString.call(value).slice(8,-1) : typeof value).toLowerCase();
            if(type === "boolean")
                hAlign = GrapeCity.UI.HorizontalAlign.center;
            else if(type === "number" || type === "date")
                hAlign = GrapeCity.UI.HorizontalAlign.right;
            else
                hAlign = GrapeCity.UI.HorizontalAlign.left
        }
        return hAlign
    }
})(window);
(function(window, $, undefined)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    GrapeCity.UI._ArrayHelper = function()
    {
        function __insert(array, index, item)
        {
            array.splice(index,0,item)
        }
        function __add(array, item)
        {
            array[array.length] = item
        }
        function __indexOf(array, item, start)
        {
            if(typeof item === "undefined")
                return-1;
            var length = array.length;
            if(length !== 0)
            {
                start = start - 0;
                if(isNaN(start))
                    start = 0;
                else
                {
                    if(isFinite(start))
                        start = start - start % 1;
                    if(start < 0)
                        start = Math.max(0,length + start)
                }
                for(var i = start; i < length; i++)
                    if(typeof array[i] !== "undefined" && array[i] === item)
                        return i
            }
            return-1
        }
        function ArrayHelper(array)
        {
            this._array = array
        }
        ArrayHelper.prototype = {
            add: function(item)
            {
                __add(this._array,item)
            },
            insert: function(index, item)
            {
                __insert(this._array,index,item)
            },
            indexOf: function(item, start)
            {
                return __indexOf(this._array,item,start)
            },
            array: function(value)
            {
                if(arguments.length === 0)
                    return this._array;
                this._array = value;
                return this
            }
        };
        return ArrayHelper
    }();
    function StringBuilder(initialText)
    {
        this._init(initialText)
    }
    StringBuilder.prototype = {
        _init: function(initialText)
        {
            this._parts = typeof initialText !== 'undefined' && initialText !== null && initialText !== '' ? [initialText.toString()] : [];
            this._value = {};
            this._len = 0
        },
        _insert: function(text)
        {
            this._parts.splice(0,0,text)
        },
        insert: function(text, position)
        {
            if(position === undefined || position === null)
                position = 0;
            if(position === 0)
            {
                this._insert(text);
                return
            }
            var content = this.toString();
            if(position >= content.length)
            {
                this.append(text);
                return
            }
            var first = content.substring(0,position);
            var left = content.substr(position);
            this._init(first + text + left)
        },
        append: function(text)
        {
            this._parts[this._parts.length] = text
        },
        appendLine: function(text)
        {
            this._parts[this._parts.length] = typeof text === 'undefined' || text === null || text === '' ? '\r\n' : text + '\r\n'
        },
        clear: function()
        {
            this._init()
        },
        isEmpty: function()
        {
            if(this._parts.length === 0)
                return true;
            return this.toString() === ''
        },
        toString: function(separator)
        {
            separator = separator || '';
            var parts = this._parts;
            if(this._len !== parts.length)
            {
                this._value = {};
                this._len = parts.length
            }
            var val = this._value;
            if(typeof val[separator] === 'undefined')
            {
                if(separator !== '')
                    for(var i = 0; i < parts.length; )
                        if(typeof parts[i] === 'undefined' || parts[i] === '' || parts[i] === null)
                            parts.splice(i,1);
                        else
                            i++;
                val[separator] = this._parts.join(separator)
            }
            return val[separator]
        }
    };
    GrapeCity.UI._StringBuilder = StringBuilder;
    function __toUpper(value)
    {
        return value.split("\u00A0").join(' ').toUpperCase()
    }
    function __toUpperArray(arr)
    {
        var result = [];
        for(var i = 0, il = arr.length; i < il; i++)
            result[i] = __toUpper(arr[i]);
        return result
    }
    function __getIndex(value, a1, a2)
    {
        var upper = __toUpper(value),
            i = new GrapeCity.UI._ArrayHelper(a1).indexOf(upper);
        if(i === -1)
            i = new GrapeCity.UI._ArrayHelper(a2).indexOf(upper);
        return i
    }
    function CultureInfo(name, numberFormat, dateTimeFormat)
    {
        this.name = name;
        this.numberFormat = numberFormat;
        this.dateTimeFormat = dateTimeFormat
    }
    CultureInfo.prototype = {
        _getDateTimeFormats: function()
        {
            if(!this._dateTimeFormats)
                //this._dateTimeFormats = ["yy/MM/dd", "yy/MM/d", "yy/M/dd", "yy/M/d", "yyyy/MM/dd", "yyyy/MM/d", "yyyy/M/dd", "yyyy/M/d", "MM/dd/yyyy", "MM/d/yyyy", "M/dd/yyyy", "M/d/yyyy", "hh:mm:ss", "hh:mm:s", "hh:m:ss", "hh:m:s", "h:mm:ss", "h:mm:s", "h:m:ss", "h:m:s", "hh:mm:ss tt", "hh:mm:s tt", "hh:m:ss tt", "hh:m:s tt", "h:mm:ss tt", "h:mm:s tt", "h:m:ss tt", "h:m:s tt", "hh:mm", "hh:m", "h:mm", "h:m", "yy/MM/dd hh:mm:ss", "yy/MM/dd hh:mm:s", "yy/MM/dd hh:m:ss", "yy/MM/dd hh:m:s", "yy/MM/dd h:mm:ss", "yy/MM/dd h:mm:s", "yy/MM/dd h:m:ss", "yy/MM/dd h:m:s", "yy/MM/dd hh:mm:ss tt", "yy/MM/dd hh:mm:s tt", "yy/MM/dd hh:m:ss tt", "yy/MM/dd hh:m:s tt", "yy/MM/dd h:mm:ss tt", "yy/MM/dd h:mm:s tt", "yy/MM/dd h:m:ss tt", "yy/MM/dd h:m:s tt", "yy/MM/d hh:mm:ss", "yy/MM/d hh:mm:s", "yy/MM/d hh:m:ss", "yy/MM/d hh:m:s", "yy/MM/d hh:mm:ss", "yy/MM/d h:mm:s", "yy/MM/d h:m:ss", "yy/MM/d h:m:s", "yy/MM/d hh:mm:ss tt", "yy/MM/d hh:mm:s tt", "yy/MM/d hh:m:ss tt", "yy/MM/d hh:m:s tt", "yy/MM/d h:mm:ss tt", "yy/MM/d h:mm:s tt", "yy/MM/d h:m:ss tt", "yy/MM/d h:m:s tt", "yy/M/dd hh:mm:ss", "yy/M/dd hh:mm:s", "yy/M/dd hh:m:ss", "yy/M/dd hh:m:s", "yy/M/dd hh:mm:ss", "yy/M/dd h:mm:s", "yy/M/dd h:m:ss", "yy/M/dd h:m:s", "yy/M/dd hh:mm:ss tt", "yy/M/dd hh:mm:s tt", "yy/M/dd hh:m:ss tt", "yy/M/dd hh:m:s tt", "yy/M/dd h:mm:ss tt", "yy/M/dd h:mm:s tt", "yy/M/dd h:m:ss tt", "yy/M/dd h:m:s tt", "yy/M/d hh:mm:ss", "yy/M/d hh:mm:s", "yy/M/d hh:m:ss", "yy/M/d hh:m:s", "yy/M/d hh:mm:ss", "yy/M/d h:mm:s", "yy/M/d h:m:ss", "yy/M/d h:m:s", "yy/M/d hh:mm:ss tt", "yy/M/d hh:mm:s tt", "yy/M/d hh:m:ss tt", "yy/M/d hh:m:s tt", "yy/M/d h:mm:ss tt", "yy/M/d h:mm:s tt", "yy/M/d h:m:ss tt", "yy/M/d h:m:s tt", "yyyy/MM/dd hh:mm:ss", "yyyy/MM/dd hh:mm:s", "yyyy/MM/dd hh:m:ss", "yyyy/MM/dd hh:m:s", "yyyy/MM/dd hh:mm:ss", "yyyy/MM/dd h:mm:s", "yyyy/MM/dd h:m:ss", "yyyy/MM/dd h:m:s", "yyyy/MM/dd hh:mm:ss tt", "yyyy/MM/dd hh:mm:s tt", "yyyy/MM/dd hh:m:ss tt", "yyyy/MM/dd hh:m:s tt", "yyyy/MM/dd h:mm:ss tt", "yyyy/MM/dd h:mm:s tt", "yyyy/MM/dd h:m:ss tt", "yyyy/MM/dd h:m:s tt", "yyyy/MM/d hh:mm:ss", "yyyy/MM/d hh:mm:s", "yyyy/MM/d hh:m:ss", "yyyy/MM/d hh:m:s", "yyyy/MM/d hh:mm:ss", "yyyy/MM/d h:mm:s", "yyyy/MM/d h:m:ss", "yyyy/MM/d h:m:s", "yyyy/MM/d hh:mm:ss tt", "yyyy/MM/d hh:mm:s tt", "yyyy/MM/d hh:m:ss tt", "yyyy/MM/d hh:m:s tt", "yyyy/MM/d h:mm:ss tt", "yyyy/MM/d h:mm:s tt", "yyyy/MM/d h:m:ss tt", "yyyy/MM/d h:m:s tt", "yyyy/M/dd hh:mm:ss", "yyyy/M/dd hh:mm:s", "yyyy/M/dd hh:m:ss", "yyyy/M/dd hh:m:s", "yyyy/M/dd hh:mm:ss", "yyyy/M/dd h:mm:s", "yyyy/M/dd h:m:ss", "yyyy/M/dd h:m:s", "yyyy/M/dd hh:mm:ss tt", "yyyy/M/dd hh:mm:s tt", "yyyy/M/dd hh:m:ss tt", "yyyy/M/dd hh:m:s tt", "yyyy/M/dd h:mm:ss tt", "yyyy/M/dd h:mm:s tt", "yyyy/M/dd h:m:ss tt", "yyyy/M/dd h:m:s tt", "yyyy/M/d hh:mm:ss", "yyyy/M/d hh:mm:s", "yyyy/M/d hh:m:ss", "yyyy/M/d hh:m:s", "yyyy/M/d hh:mm:ss", "yyyy/M/d h:mm:s", "yyyy/M/d h:m:ss", "yyyy/M/d h:m:s", "yyyy/M/d hh:mm:ss tt", "yyyy/M/d hh:mm:s tt", "yyyy/M/d hh:m:ss tt", "yyyy/M/d hh:m:s tt", "yyyy/M/d h:mm:ss tt", "yyyy/M/d h:mm:s tt", "yyyy/M/d h:m:ss tt", "yyyy/M/d h:m:s tt", "MM/dd/yyyy hh:mm:ss", "MM/dd/yyyy hh:mm:s", "MM/dd/yyyy hh:m:ss", "MM/dd/yyyy hh:m:s", "MM/dd/yyyy hh:mm:ss", "MM/dd/yyyy h:mm:s", "MM/dd/yyyy h:m:ss", "MM/dd/yyyy h:m:s", "MM/dd/yyyy hh:mm:ss tt", "MM/dd/yyyy hh:mm:s tt", "MM/dd/yyyy hh:m:ss tt", "MM/dd/yyyy hh:m:s tt", "MM/dd/yyyy h:mm:ss tt", "MM/dd/yyyy h:mm:s tt", "MM/dd/yyyy h:m:ss tt", "MM/dd/yyyy h:m:s tt", "MM/d/yyyy hh:mm:ss", "MM/d/yyyy hh:mm:s", "MM/d/yyyy hh:m:ss", "MM/d/yyyy hh:m:s", "MM/d/yyyy hh:mm:ss", "MM/d/yyyy h:mm:s", "MM/d/yyyy h:m:ss", "MM/d/yyyy h:m:s", "MM/d/yyyy hh:mm:ss tt", "MM/d/yyyy hh:mm:s tt", "MM/d/yyyy hh:m:ss tt", "MM/d/yyyy hh:m:s tt", "MM/d/yyyy h:mm:ss tt", "MM/d/yyyy h:mm:s tt", "MM/d/yyyy h:m:ss tt", "MM/d/yyyy h:m:s tt", "M/dd/yyyy hh:mm:ss", "M/dd/yyyy hh:mm:s", "M/dd/yyyy hh:m:ss", "M/dd/yyyy hh:m:s", "M/dd/yyyy hh:mm:ss", "M/dd/yyyy h:mm:s", "M/dd/yyyy h:m:ss", "M/dd/yyyy h:m:s", "M/dd/yyyy hh:mm:ss tt", "M/dd/yyyy hh:mm:s tt", "M/dd/yyyy hh:m:ss tt", "M/dd/yyyy hh:m:s tt", "M/dd/yyyy h:mm:ss tt", "M/dd/yyyy h:mm:s tt", "M/dd/yyyy h:m:ss tt", "M/dd/yyyy h:m:s tt", "M/d/yyyy hh:mm:ss", "M/d/yyyy hh:mm:s", "M/d/yyyy hh:m:ss", "M/d/yyyy hh:m:s", "M/d/yyyy hh:mm:ss", "M/d/yyyy h:mm:s", "M/d/yyyy h:m:ss", "M/d/yyyy h:m:s", "M/d/yyyy hh:mm:ss tt", "M/d/yyyy hh:mm:s tt", "M/d/yyyy hh:m:ss tt", "M/d/yyyy hh:m:s tt", "M/d/yyyy h:mm:ss tt", "M/d/yyyy h:mm:s tt", "M/d/yyyy h:m:ss tt", "M/d/yyyy h:m:s tt"];
                this._dateTimeFormats = ["dd/MM/yy", "d/MM/yy", "dd/M/yy", "d/M/yy", "dd/MM/yyyy", "d/MM/yyyy", "dd/M/yyyy", "d/M/yyyy", "dd/MM/yyyy", "d/MM/yyyy", "dd/M/yyyy", "d/M/yyyy", "hh:mm:ss", "hh:mm:s", "hh:m:ss", "hh:m:s", "h:mm:ss", "h:mm:s", "h:m:ss", "h:m:s", "hh:mm:ss tt", "hh:mm:s tt", "hh:m:ss tt", "hh:m:s tt", "h:mm:ss tt", "h:mm:s tt", "h:m:ss tt", "h:m:s tt", "hh:mm", "hh:m", "h:mm", "h:m", "dd/MM/yy hh:mm:ss", "dd/MM/yy hh:mm:s", "dd/MM/yy hh:m:ss", "dd/MM/yy hh:m:s", "dd/MM/yy h:mm:ss", "dd/MM/yy h:mm:s", "dd/MM/yy h:m:ss", "dd/MM/yy h:m:s", "dd/MM/yy hh:mm:ss tt", "dd/MM/yy hh:mm:s tt", "dd/MM/yy hh:m:ss tt", "dd/MM/yy hh:m:s tt", "dd/MM/yy h:mm:ss tt", "dd/MM/yy h:mm:s tt", "dd/MM/yy h:m:ss tt", "dd/MM/yy h:m:s tt", "d/MM/yy hh:mm:ss", "d/MM/yy hh:mm:s", "d/MM/yy hh:m:ss", "d/MM/yy hh:m:s", "d/MM/yy hh:mm:ss", "d/MM/yy h:mm:s", "d/MM/yy h:m:ss", "d/MM/yy h:m:s", "d/MM/yy hh:mm:ss tt", "d/MM/yy hh:mm:s tt", "d/MM/yy hh:m:ss tt", "d/MM/yy hh:m:s tt", "d/MM/yy h:mm:ss tt", "d/MM/yy h:mm:s tt", "d/MM/yy h:m:ss tt", "d/MM/yy h:m:s tt", "dd/M/yy hh:mm:ss", "dd/M/yy hh:mm:s", "dd/M/yy hh:m:ss", "dd/M/yy hh:m:s", "dd/M/yy hh:mm:ss", "dd/M/yy h:mm:s", "dd/M/yy h:m:ss", "dd/M/yy h:m:s", "dd/M/yy hh:mm:ss tt", "dd/M/yy hh:mm:s tt", "dd/M/yy hh:m:ss tt", "dd/M/yy hh:m:s tt", "dd/M/yy h:mm:ss tt", "dd/M/yy h:mm:s tt", "dd/M/yy h:m:ss tt", "dd/M/yy h:m:s tt", "dd/M/yy hh:mm:ss", "dd/M/yy hh:mm:s", "dd/M/yy hh:m:ss", "dd/M/yy hh:m:s", "d/M/yy hh:mm:ss", "d/M/yy h:mm:s", "d/M/yy h:m:ss", "d/M/yy h:m:s", "d/M/yy hh:mm:ss tt", "d/M/yy hh:mm:s tt", "d/M/yy hh:m:ss tt", "d/M/yy hh:m:s tt", "d/M/yy h:mm:ss tt", "d/M/yy h:mm:s tt", "d/M/yy h:m:ss tt", "d/M/yy h:m:s tt", "dd/MM/yyyy hh:mm:ss", "dd/MM/yyyy hh:mm:s", "dd/MM/yyyy hh:m:ss", "dd/MM/yyyy hh:m:s", "dd/MM/yyyy hh:mm:ss", "dd/MM/yyyy h:mm:s", "dd/MM/yyyy h:m:ss", "dd/MM/yyyy h:m:s", "dd/MM/yyyy hh:mm:ss tt", "dd/MM/yyyy hh:mm:s tt", "dd/MM/yyyy hh:m:ss tt", "dd/MM/yyyy hh:m:s tt", "dd/MM/yyyy h:mm:ss tt", "dd/MM/yyyy h:mm:s tt", "dd/MM/yyyy h:m:ss tt", "dd/MM/yyyy h:m:s tt", "d/MM/yyyy hh:mm:ss", "d/MM/yyyy hh:mm:s", "d/MM/yyyy hh:m:ss", "d/MM/yyyy hh:m:s", "d/MM/yyyy hh:mm:ss", "d/MM/yyyy h:mm:s", "d/MM/yyyy h:m:ss", "d/MM/yyyy h:m:s", "d/MM/yyyy hh:mm:ss tt", "d/MM/yyyy hh:mm:s tt", "d/MM/yyyy hh:m:ss tt", "d/MM/yyyy hh:m:s tt", "d/MM/yyyy h:mm:ss tt", "d/MM/yyyy h:mm:s tt", "d/MM/yyyy h:m:ss tt", "d/MM/yyyy h:m:s tt", "dd/M/yyyy hh:mm:ss", "dd/M/yyyy hh:mm:s", "dd/M/yyyy hh:m:ss", "dd/M/yyyy hh:m:s", "dd/M/yyyy hh:mm:ss", "dd/M/yyyy h:mm:s", "dd/M/yyyy h:m:ss", "dd/M/yyyy h:m:s", "dd/M/yyyy hh:mm:ss tt", "dd/M/yyyy hh:mm:s tt", "dd/M/yyyy hh:m:ss tt", "dd/M/yyyy hh:m:s tt", "dd/M/yyyy h:mm:ss tt", "dd/M/yyyy h:mm:s tt", "dd/M/yyyy h:m:ss tt", "dd/M/yyyy h:m:s tt", "d/M/yyyy hh:mm:ss", "d/M/yyyy hh:mm:s", "d/M/yyyy hh:m:ss", "d/M/yyyy hh:m:s", "d/M/yyyy hh:mm:ss", "d/M/yyyy h:mm:s", "d/M/yyyy h:m:ss", "d/M/yyyy h:m:s", "d/M/yyyy hh:mm:ss tt", "d/M/yyyy hh:mm:s tt", "d/M/yyyy hh:m:ss tt", "d/M/yyyy hh:m:s tt", "d/M/yyyy h:mm:ss tt", "d/M/yyyy h:mm:s tt", "d/M/yyyy h:m:ss tt", "d/M/yyyy h:m:s tt", "dd/MM/yyyy hh:mm:ss", "dd/MM/yyyy hh:mm:s", "dd/MM/yyyy hh:m:ss", "dd/MM/yyyy hh:m:s", "dd/MM/yyyy hh:mm:ss", "dd/MM/yyyy h:mm:s", "dd/MM/yyyy h:m:ss", "dd/MM/yyyy h:m:s", "dd/MM/yyyy hh:mm:ss tt", "dd/MM/yyyy hh:mm:s tt", "dd/MM/yyyy hh:m:ss tt", "dd/MM/yyyy hh:m:s tt", "dd/MM/yyyy h:mm:ss tt", "dd/MM/yyyy h:mm:s tt", "dd/MM/yyyy h:m:ss tt", "dd/MM/yyyy h:m:s tt", "d/MM/yyyy hh:mm:ss", "d/MM/yyyy hh:mm:s", "d/MM/yyyy hh:m:ss", "d/MM/yyyy hh:m:s", "d/MM/yyyy hh:mm:ss", "d/MM/yyyy h:mm:s", "d/MM/yyyy h:m:ss", "d/MM/yyyy h:m:s", "d/MM/yyyy hh:mm:ss tt", "d/MM/yyyy hh:mm:s tt", "d/MM/yyyy hh:m:ss tt", "d/MM/yyyy hh:m:s tt", "d/MM/yyyy h:mm:ss tt", "d/MM/yyyy h:mm:s tt", "d/MM/yyyy h:m:ss tt", "d/MM/yyyy h:m:s tt", "dd/M/yyyy hh:mm:ss", "dd/M/yyyy hh:mm:s", "dd/M/yyyy hh:m:ss", "dd/M/yyyy hh:m:s", "dd/M/yyyy hh:mm:ss", "dd/M/yyyy h:mm:s", "dd/M/yyyy h:m:ss", "dd/M/yyyy h:m:s", "dd/M/yyyy hh:mm:ss tt", "dd/M/yyyy hh:mm:s tt", "dd/M/yyyy hh:m:ss tt", "dd/M/yyyy hh:m:s tt", "dd/M/yyyy h:mm:ss tt", "dd/M/yyyy h:mm:s tt", "dd/M/yyyy h:m:ss tt", "dd/M/yyyy h:m:s tt", "d/M/yyyy hh:mm:ss", "d/M/yyyy hh:mm:s", "d/M/yyyy hh:m:ss", "d/M/yyyy hh:m:s", "d/M/yyyy hh:mm:ss", "d/M/yyyy h:mm:s", "d/M/yyyy h:m:ss", "d/M/yyyy h:m:s", "d/M/yyyy hh:mm:ss tt", "d/M/yyyy hh:mm:s tt", "d/M/yyyy hh:m:ss tt", "d/M/yyyy hh:m:s tt", "d/M/yyyy h:mm:ss tt", "d/M/yyyy h:mm:s tt", "d/M/yyyy h:m:ss tt", "d/M/yyyy h:m:s tt"];
            return this._dateTimeFormats
        },
        _getMonthIndex: function(value)
        {
            if(!this._upperMonths)
            {
                this._upperMonths = __toUpperArray(this.dateTimeFormat.MonthNames);
                this._upperMonthsGenitive = __toUpperArray(this.dateTimeFormat.MonthGenitiveNames)
            }
            return __getIndex(value,this._upperMonths,this._upperMonthsGenitive)
        },
        _getAbbrMonthIndex: function(value)
        {
            if(!this._upperAbbrMonths)
            {
                this._upperAbbrMonths = __toUpperArray(this.dateTimeFormat.AbbreviatedMonthNames);
                this._upperAbbrMonthsGenitive = __toUpperArray(this.dateTimeFormat.AbbreviatedMonthGenitiveNames)
            }
            return __getIndex(value,this._upperAbbrMonths,this._upperAbbrMonthsGenitive)
        },
        _getDayIndex: function(value)
        {
            if(!this._upperDays)
                this._upperDays = __toUpperArray(this.dateTimeFormat.DayNames);
            return new GrapeCity.UI._ArrayHelper(this._upperDays).indexOf(__toUpper(value))
        },
        _getAbbrDayIndex: function(value)
        {
            if(!this._upperAbbrDays)
                this._upperAbbrDays = __toUpperArray(this.dateTimeFormat.AbbreviatedDayNames);
            return new GrapeCity.UI._ArrayHelper(this._upperAbbrDays).indexOf(__toUpper(value))
        }
    };
    function _parseCulture(value)
    {
        var dtf = value.dateTimeFormat;
        if(dtf && !dtf.eras)
            dtf.eras = value.eras;
        return new CultureInfo(value.name,value.numberFormat,dtf)
    }
    CultureInfo._parse = _parseCulture;
    CultureInfo.invariantCulture = _parseCulture({
        name: "",
        numberFormat: {
            CurrencyDecimalDigits: 2,
            CurrencyDecimalSeparator: ".",
            IsReadOnly: true,
            CurrencyGroupSizes: [3],
            NumberGroupSizes: [3],
            PercentGroupSizes: [3],
            CurrencyGroupSeparator: ",",
            CurrencySymbol: "\u00A4",
            NaNSymbol: "NaN",
            CurrencyNegativePattern: 0,
            NumberNegativePattern: 1,
            PercentPositivePattern: 0,
            PercentNegativePattern: 0,
            NegativeInfinitySymbol: "-Infinity",
            NegativeSign: "-",
            NumberDecimalDigits: 2,
            NumberDecimalSeparator: ".",
            NumberGroupSeparator: ",",
            CurrencyPositivePattern: 0,
            PositiveInfinitySymbol: "Infinity",
            PositiveSign: "+",
            PercentDecimalDigits: 2,
            PercentDecimalSeparator: ".",
            PercentGroupSeparator: ",",
            PercentSymbol: "%",
            PerMilleSymbol: "\u2030",
            NativeDigits: ["0","1","2","3","4","5","6","7","8","9"],
            DigitSubstitution: 1
        },
        dateTimeFormat: {
            AMDesignator: "AM",
            Calendar: {
                MinSupportedDateTime: "@-62135568000000@",
                MaxSupportedDateTime: "@253402300799999@",
                AlgorithmType: 1,
                CalendarType: 1,
                Eras: [1],
                TwoDigitYearMax: 2029,
                IsReadOnly: true
            },
            DateSeparator: "/",
            FirstDayOfWeek: 0,
            CalendarWeekRule: 0,
            FullDateTimePattern: "dddd, dd MMMM yyyy HH:mm:ss",
            LongDatePattern: "dddd, dd MMMM yyyy",
            LongTimePattern: "HH:mm:ss",
            MonthDayPattern: "MMMM dd",
            PMDesignator: "PM",
            RFC1123Pattern: "ddd, dd MMM yyyy HH\':\'mm\':\'ss \'GMT\'",
            ShortDatePattern: "MM/dd/yyyy",
            ShortTimePattern: "HH:mm",
            SortableDateTimePattern: "yyyy\'-\'MM\'-\'dd\'T\'HH\':\'mm\':\'ss",
            TimeSeparator: ":",
            UniversalSortableDateTimePattern: "yyyy\'-\'MM\'-\'dd HH\':\'mm\':\'ss\'Z\'",
            YearMonthPattern: "yyyy MMMM",
            AbbreviatedDayNames: ["Sun","Mon","Tue","Wed","Thu","Παρ","Sat"],
            ShortestDayNames: ["Su","Mo","Tu","We","Th","Fr","Sa"],
            DayNames: ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
            AbbreviatedMonthNames: ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""],
            MonthNames: ["January","February","March","April","May","June","July","August","September","October","November","December",""],
            IsReadOnly: true,
            NativeCalendarName: "Gregorian Calendar",
            AbbreviatedMonthGenitiveNames: ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""],
            MonthGenitiveNames: ["January","February","March","April","May","June","July","August","September","October","November","December",""]
        },
        eras: [1,"A.D.",null,0]
    });
    CultureInfo.currentCulture = _parseCulture({
        name: "el-GR",
        numberFormat: {
            CurrencyDecimalDigits: 2,
            CurrencyDecimalSeparator: ",",
            IsReadOnly: false,
            CurrencyGroupSizes: [3],
            NumberGroupSizes: [3],
            PercentGroupSizes: [3],
            CurrencyGroupSeparator: ".",
            CurrencySymbol: "€",
            NaNSymbol: "μη αριθμός",
            CurrencyNegativePattern: 0,
            NumberNegativePattern: 1,
            PercentPositivePattern: 0,
            PercentNegativePattern: 0,
            NegativeInfinitySymbol: "-Άπειρο",
            NegativeSign: "-",
            NumberDecimalDigits: 2,
            NumberDecimalSeparator: ",",
            NumberGroupSeparator: ".",
            CurrencyPositivePattern: 0,
            PositiveInfinitySymbol: "Άπειρο",
            PositiveSign: "+",
            PercentDecimalDigits: 2,
            PercentDecimalSeparator: ",",
            PercentGroupSeparator: ".",
            PercentSymbol: "%",
            PerMilleSymbol: "\u2030",
            NativeDigits: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
            DigitSubstitution: 1
        },
        dateTimeFormat: {
            AMDesignator: "πμ",
            Calendar: {
                MinSupportedDateTime: "@-62135568000000@",
                MaxSupportedDateTime: "@253402300799999@",
                AlgorithmType: 1,
                CalendarType: 1,
                Eras: [1],
                TwoDigitYearMax: 2029,
                IsReadOnly: false
            },
            DateSeparator: "/",
            FirstDayOfWeek: 1,
            CalendarWeekRule: 0,
            FullDateTimePattern: "dddd, d MMMM yyyy h:mm:ss tt",
            LongDatePattern: "dddd, d MMMM yyyy",
            LongTimePattern: "h:mm:ss tt",
            MonthDayPattern: "dd MMMM",
            PMDesignator: "μμ",
            RFC1123Pattern: "ddd, dd MMM yyyy HH\':\'mm\':\'ss \'GMT\'",
            ShortDatePattern: "dd/MM/yyyy",
            ShortTimePattern: "h:mm tt",
            SortableDateTimePattern: "yyyy\'-\'MM\'-\'dd\'T\'HH\':\'mm\':\'ss",
            TimeSeparator: ":",
            UniversalSortableDateTimePattern: "yyyy\'-\'MM\'-\'dd HH\':\'mm\':\'ss\'Z\'",
            YearMonthPattern: "MMMM yyyy",
            AbbreviatedDayNames: ["Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ"],
            ShortestDayNames: ["Κυ", "Δε", "Τρ", "Τε", "Πε", "Πα", "Σά"],
            DayNames: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο"],
            AbbreviatedMonthNames: ["Ιαν", "Φεβ", "Μαρ", "Απρ", "Μαϊ", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ", ""],
            MonthNames: ["Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος", ""],
            IsReadOnly: false,
            NativeCalendarName: "Γρηγοριανό Ημερολόγιο",
            AbbreviatedMonthGenitiveNames: ["Ιαν", "Φεβ", "Μαρ", "Απρ", "Μαϊ", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ", ""],
            MonthGenitiveNames: ["Ιανουαρίου", "Φεβρουαρίου", "Μαρτίου", "Απριλίου", "Μαΐου", "Ιουνίου", "Ιουλίου", "Αυγούστου", "Σεπτεμβρίου", "Οκτωβρίου", "Νοεμβρίου", "Δεκεμβρίου", ""]
        },
        eras: [1, "μ.Χ.", null, 0]


        //name: "en-US",
        //numberFormat: {
        //    CurrencyDecimalDigits: 2,
        //    CurrencyDecimalSeparator: ".",
        //    IsReadOnly: false,
        //    CurrencyGroupSizes: [3],
        //    NumberGroupSizes: [3],
        //    PercentGroupSizes: [3],
        //    CurrencyGroupSeparator: ",",
        //    CurrencySymbol: "$",
        //    NaNSymbol: "NaN",
        //    CurrencyNegativePattern: 0,
        //    NumberNegativePattern: 1,
        //    PercentPositivePattern: 0,
        //    PercentNegativePattern: 0,
        //    NegativeInfinitySymbol: "-Infinity",
        //    NegativeSign: "-",
        //    NumberDecimalDigits: 2,
        //    NumberDecimalSeparator: ".",
        //    NumberGroupSeparator: ",",
        //    CurrencyPositivePattern: 0,
        //    PositiveInfinitySymbol: "Infinity",
        //    PositiveSign: "+",
        //    PercentDecimalDigits: 2,
        //    PercentDecimalSeparator: ".",
        //    PercentGroupSeparator: ",",
        //    PercentSymbol: "%",
        //    PerMilleSymbol: "\u2030",
        //    NativeDigits: ["0","1","2","3","4","5","6","7","8","9"],
        //    DigitSubstitution: 1
        //},
        //dateTimeFormat: {
        //    AMDesignator: "AM",
        //    Calendar: {
        //        MinSupportedDateTime: "@-62135568000000@",
        //        MaxSupportedDateTime: "@253402300799999@",
        //        AlgorithmType: 1,
        //        CalendarType: 1,
        //        Eras: [1],
        //        TwoDigitYearMax: 2029,
        //        IsReadOnly: false
        //    },
        //    DateSeparator: "/",
        //    FirstDayOfWeek: 0,
        //    CalendarWeekRule: 0,
        //    FullDateTimePattern: "dddd, MMMM dd, yyyy h:mm:ss tt",
        //    LongDatePattern: "dddd, MMMM dd, yyyy",
        //    LongTimePattern: "h:mm:ss tt",
        //    MonthDayPattern: "MMMM dd",
        //    PMDesignator: "PM",
        //    RFC1123Pattern: "ddd, dd MMM yyyy HH\':\'mm\':\'ss \'GMT\'",
        //    ShortDatePattern: "M/d/yyyy",
        //    ShortTimePattern: "h:mm tt",
        //    SortableDateTimePattern: "yyyy\'-\'MM\'-\'dd\'T\'HH\':\'mm\':\'ss",
        //    TimeSeparator: ":",
        //    UniversalSortableDateTimePattern: "yyyy\'-\'MM\'-\'dd HH\':\'mm\':\'ss\'Z\'",
        //    YearMonthPattern: "MMMM, yyyy",
        //    AbbreviatedDayNames: ["Sun","Mon","Tue","Wed","Thu","Παρ","Sat"],
        //    ShortestDayNames: ["Su","Mo","Tu","We","Th","Fr","Sa"],
        //    DayNames: ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
        //    AbbreviatedMonthNames: ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""],
        //    MonthNames: ["January","February","March","April","May","June","July","August","September","October","November","December",""],
        //    IsReadOnly: false,
        //    NativeCalendarName: "Gregorian Calendar",
        //    AbbreviatedMonthGenitiveNames: ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""],
        //    MonthGenitiveNames: ["January","February","March","April","May","June","July","August","September","October","November","December",""]
        //},
        //eras: [1,"A.D.",null,0]
    });
    GrapeCity.UI._CultureInfo = CultureInfo;
    var StringHelper = GrapeCity.UI._StringHelper = function()
        {
            function __toFormattedString(useLocale, args)
            {
                var result = '';
                var format = args[0];
                for(var i = 0; ; )
                {
                    var open = format.indexOf('{',i);
                    var close = format.indexOf('}',i);
                    if(open < 0 && close < 0)
                    {
                        result += format.slice(i);
                        break
                    }
                    if(close > 0 && (close < open || open < 0))
                    {
                        if(format.charAt(close + 1) !== '}')
                            throw new Error('format: brace mis-match');
                        result += format.slice(i,close + 1);
                        i = close + 2;
                        continue
                    }
                    result += format.slice(i,open);
                    i = open + 1;
                    if(format.charAt(i) === '{')
                    {
                        result += '{';
                        i++;
                        continue
                    }
                    if(close < 0)
                        throw new Error('format: brace mis-match');
                    var brace = format.substring(i,close);
                    var colonIndex = brace.indexOf(':');
                    var argNumber = parseInt(colonIndex < 0 ? brace : brace.substring(0,colonIndex),10) + 1;
                    if(isNaN(argNumber))
                        throw new Error('invalid format');
                    var argFormat = colonIndex < 0 ? '' : brace.substring(colonIndex + 1);
                    var arg = args[argNumber];
                    if(typeof arg === "undefined" || arg === null)
                        arg = '';
                    if(arg.toFormattedString)
                        result += arg.toFormattedString(argFormat);
                    else if(useLocale && arg.localeFormat)
                        result += arg.localeFormat(argFormat);
                    else if(arg.format)
                        result += arg.format(argFormat);
                    else
                        result += arg.toString();
                    i = close + 1
                }
                return result
            }
            function StringHelper(str)
            {
                this._str = str
            }
            StringHelper.prototype = {
                startsWith: function(prefix)
                {
                    return this._str.substr(0,prefix.length) === prefix
                },
                endsWith: function(suffix)
                {
                    return this._str.indexOf(suffix,this._str.length - suffix.length) !== -1
                },
                trim: function()
                {
                    return this._str.replace(/^\s+|\s+$/g,'')
                },
                trimEnd: function()
                {
                    return this._str.replace(/\s+$/,'')
                },
                trimStart: function()
                {
                    return this._str.replace(/^\s+/,'')
                }
            };
            StringHelper.format = function(format, args)
            {
                return __toFormattedString(false,arguments)
            };
            return StringHelper
        }();
    GrapeCity.UI._DateTimeHelper = function()
    {
        function __appendPreOrPostMatch(preMatch, strBuilder)
        {
            var quoteCount = 0;
            var escaped = false;
            for(var i = 0, il = preMatch.length; i < il; i++)
            {
                var c = preMatch.charAt(i);
                switch(c)
                {
                    case'\'':
                    case'\"':
                        if(escaped)
                            strBuilder.append("'");
                        else
                            quoteCount++;
                        escaped = false;
                        break;
                    case'\\':
                        if(escaped)
                            strBuilder.append("\\");
                        escaped = !escaped;
                        break;
                    default:
                        strBuilder.append(c);
                        escaped = false;
                        break
                }
            }
            return quoteCount
        }
        function __expandFormat(dtf, format)
        {
            if(!format)
                format = "F";
            var len = format.length;
            if(len === 1)
                switch(format)
                {
                    case"d":
                        return dtf.ShortDatePattern;
                    case"D":
                        return dtf.LongDatePattern;
                    case"t":
                        return dtf.ShortTimePattern;
                    case"T":
                        return dtf.LongTimePattern;
                    case"f":
                        return dtf.LongDatePattern + " " + dtf.ShortTimePattern;
                    case"F":
                        return dtf.FullDateTimePattern;
                    case"M":
                    case"m":
                        return dtf.MonthDayPattern;
                    case"s":
                        return dtf.SortableDateTimePattern;
                    case"Y":
                    case"y":
                        return dtf.YearMonthPattern;
                    case"g":
                        return dtf.ShortDatePattern + " " + dtf.ShortTimePattern;
                    case"G":
                        return dtf.ShortDatePattern + " " + dtf.LongTimePattern;
                    case"R":
                    case"r":
                        return dtf.RFC1123Pattern;
                    case"u":
                        return dtf.UniversalSortableDateTimePattern;
                    case"U":
                        return dtf.FullDateTimePattern;
                    case"o":
                    case"O":
                        return"yyyy\'-\'MM\'-\'dd\'T\'HH\':\'mm\':\'ss\'.\'fffffff";
                    default:
                        throw new Error("InvalidString");
                }
            else if(len === 2 && format.charAt(0) === "%")
                format = format.charAt(1);
            return format
        }
        function __getEra(date, eras)
        {
            if(!eras)
                return 0;
            var start,
                ticks = date.getTime();
            for(var i = 0, l = eras.length; i < l; i += 4)
            {
                start = eras[i + 2];
                if(start === null || ticks >= start)
                    return i
            }
            return 0
        }
        function __getEraYear(date, dtf, era, sortable)
        {
            var year = date.getFullYear();
            if(!sortable && dtf.eras)
                year -= dtf.eras[era + 3];
            return year
        }
        function __expandYear(dtf, year)
        {
            var now = new Date,
                era = __getEra(now);
            if(year < 100)
            {
                var curr = __getEraYear(now,dtf,era);
                year += curr - curr % 100;
                if(year > dtf.Calendar.TwoDigitYearMax)
                    year -= 100
            }
            return year
        }
        function __getTokenRegExp()
        {
            return/\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g|\[h\]|\[mm\]|\[ss\]/g
        }
        function __getParseRegExp(dtf, format)
        {
            if(!dtf._parseRegExp)
                dtf._parseRegExp = {};
            else if(dtf._parseRegExp[format])
                return dtf._parseRegExp[format];
            var expFormat = __expandFormat(dtf,format);
            expFormat = expFormat.replace(/([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g,"\\\\$1");
            var regexp = new StringBuilder("^");
            var groups = new GrapeCity.UI._ArrayHelper([]);
            var index = 0;
            var quoteCount = 0;
            var tokenRegExp = __getTokenRegExp();
            var match;
            while((match = tokenRegExp.exec(expFormat)) !== null)
            {
                var preMatch = expFormat.slice(index,match.index);
                index = tokenRegExp.lastIndex;
                quoteCount += __appendPreOrPostMatch(preMatch,regexp);
                if(quoteCount % 2 === 1)
                {
                    regexp.append(match[0]);
                    continue
                }
                switch(match[0])
                {
                    case'dddd':
                    case'ddd':
                    case'MMMM':
                    case'MMM':
                    case'gg':
                    case'g':
                        regexp.append("(\\D+)");
                        break;
                    case'tt':
                    case't':
                        regexp.append("(\\D*)");
                        break;
                    case'yyyy':
                        regexp.append("(\\d{4})");
                        break;
                    case'fff':
                        regexp.append("(\\d{3})");
                        break;
                    case'ff':
                        regexp.append("(\\d{2})");
                        break;
                    case'f':
                        regexp.append("(\\d)");
                        break;
                    case'dd':
                    case'd':
                    case'MM':
                    case'M':
                    case'yy':
                    case'y':
                    case'HH':
                    case'H':
                    case'hh':
                    case'h':
                    case'mm':
                    case'm':
                    case'ss':
                    case's':
                        regexp.append("(\\d\\d?)");
                        break;
                    case'zzz':
                        regexp.append("([+-]?\\d\\d?:\\d{2})");
                        break;
                    case'zz':
                    case'z':
                        regexp.append("([+-]?\\d\\d?)");
                        break;
                    case'/':
                        regexp.append("(\\" + dtf.DateSeparator + ")");
                        break;
                    default:
                        throw new Error("Invalid date format pattern");
                }
                groups.add(match[0])
            }
            __appendPreOrPostMatch(expFormat.slice(index),regexp);
            regexp.append("$");
            var regexpStr = regexp.toString().replace(/\s+/g,"\\s+");
            var parseRegExp = {
                    regExp: regexpStr,
                    groups: groups.array()
                };
            dtf._parseRegExp[format] = parseRegExp;
            return parseRegExp
        }
        function _parseDateExact(value, format, cultureInfo)
        {
            value = $.trim(value);
            var dtf = cultureInfo.dateTimeFormat,
                parseInfo = __getParseRegExp(dtf,format),
                match = new RegExp(parseInfo.regExp).exec(value);
            if(match === null)
                return null;
            var groups = parseInfo.groups,
                era = null,
                year = null,
                month = null,
                date = null,
                weekDay = null,
                hour = 0,
                hourOffset,
                min = 0,
                sec = 0,
                msec = 0,
                tzMinOffset = null,
                pmHour = false;
            var isTimeSpan = true;
            for(var j = 0, jl = groups.length; j < jl; j++)
            {
                var matchGroup = match[j + 1];
                if(matchGroup)
                    switch(groups[j])
                    {
                        case'dd':
                        case'd':
                            isTimeSpan = false;
                            date = parseInt(matchGroup,10);
                            if(date < 1 || date > 31)
                                return null;
                            break;
                        case'MMMM':
                            isTimeSpan = false;
                            month = cultureInfo._getMonthIndex(matchGroup);
                            if(month < 0 || month > 11)
                                return null;
                            break;
                        case'MMM':
                            isTimeSpan = false;
                            month = cultureInfo._getAbbrMonthIndex(matchGroup);
                            if(month < 0 || month > 11)
                                return null;
                            break;
                        case'M':
                        case'MM':
                            isTimeSpan = false;
                            month = parseInt(matchGroup,10) - 1;
                            if(month < 0 || month > 11)
                                return null;
                            break;
                        case'y':
                        case'yy':
                            isTimeSpan = false;
                            year = __expandYear(dtf,parseInt(matchGroup,10));
                            if(year < 0 || year > 9999)
                                return null;
                            break;
                        case'yyyy':
                            isTimeSpan = false;
                            year = parseInt(matchGroup,10);
                            if(year < 0 || year > 9999)
                                return null;
                            break;
                        case'h':
                        case'hh':
                        case'H':
                        case'HH':
                            hour = parseInt(matchGroup,10);
                            if(hour < 0 || hour > 23)
                                return null;
                            break;
                        case'm':
                        case'mm':
                            min = parseInt(matchGroup,10);
                            if(min < 0 || min > 59)
                                return null;
                            break;
                        case's':
                        case'ss':
                            sec = parseInt(matchGroup,10);
                            if(sec < 0 || sec > 59)
                                return null;
                            break;
                        case'tt':
                        case't':
                            var upperToken = matchGroup.toUpperCase();
                            pmHour = upperToken === dtf.PMDesignator.toUpperCase();
                            if(!pmHour && upperToken !== dtf.AMDesignator.toUpperCase())
                                return null;
                            break;
                        case'f':
                            msec = parseInt(matchGroup,10) * 100;
                            if(msec < 0 || msec > 999)
                                return null;
                            break;
                        case'ff':
                            msec = parseInt(matchGroup,10) * 10;
                            if(msec < 0 || msec > 999)
                                return null;
                            break;
                        case'fff':
                            msec = parseInt(matchGroup,10);
                            if(msec < 0 || msec > 999)
                                return null;
                            break;
                        case'dddd':
                            isTimeSpan = false;
                            weekDay = cultureInfo._getDayIndex(matchGroup);
                            if(weekDay < 0 || weekDay > 6)
                                return null;
                            break;
                        case'ddd':
                            isTimeSpan = false;
                            weekDay = cultureInfo._getAbbrDayIndex(matchGroup);
                            if(weekDay < 0 || weekDay > 6)
                                return null;
                            break;
                        case'zzz':
                            var offsets = matchGroup.split(/:/);
                            if(offsets.length !== 2)
                                return null;
                            hourOffset = parseInt(offsets[0],10);
                            if(hourOffset < -12 || hourOffset > 13)
                                return null;
                            var minOffset = parseInt(offsets[1],10);
                            if(minOffset < 0 || minOffset > 59)
                                return null;
                            tzMinOffset = hourOffset * 60 + (new StringHelper(matchGroup).startsWith('-') ? -minOffset : minOffset);
                            break;
                        case'z':
                        case'zz':
                            hourOffset = parseInt(matchGroup,10);
                            if(hourOffset < -12 || hourOffset > 13)
                                return null;
                            tzMinOffset = hourOffset * 60;
                            break;
                        case'g':
                        case'gg':
                            var eraName = matchGroup;
                            if(!eraName || !dtf.eras)
                                return null;
                            eraName = eraName.toLowerCase().trim();
                            for(var i = 0, l = dtf.eras.length; i < l; i += 4)
                                if(eraName === dtf.eras[i + 1].toLowerCase())
                                {
                                    era = i;
                                    break
                                }
                            if(era === null)
                                return null;
                            break
                    }
            }
            var result = new Date,
                defaults,
                convert = dtf.Calendar.convert;
            if(convert)
                defaults = convert.fromGregorian(result);
            if(!convert)
                defaults = [result.getFullYear(),result.getMonth(),result.getDate()];
            if(year === null)
                year = defaults[0];
            else if(dtf.eras)
                year += dtf.eras[(era || 0) + 3];
            if(month === null)
                month = defaults[1];
            if(date === null)
                date = defaults[2];
            if(convert)
            {
                result = convert.toGregorian(year,month,date);
                if(result === null)
                    return null
            }
            else
            {
                result.setFullYear(year,month,date);
                if(result.getDate() !== date)
                    return null;
                if(weekDay !== null && result.getDay() !== weekDay)
                    return null
            }
            if(pmHour && hour < 12)
                hour += 12;
            result.setHours(hour,min,sec,msec);
            if(tzMinOffset !== null)
            {
                var adjustedMin = result.getMinutes() - (tzMinOffset + result.getTimezoneOffset());
                result.setHours(result.getHours() + parseInt(adjustedMin / 60,10),adjustedMin % 60)
            }
            result._classNames = [isTimeSpan ? "TimeSpan" : "DateTime"];
            return result
        }
        function ___addLeadingZero(num)
        {
            return num < 10 ? '0' + num : num.toString()
        }
        function ___addLeadingZeros(num)
        {
            return num < 10 ? '00' + num : num < 100 ? '0' + num : num.toString()
        }
        function ___padYear(year)
        {
            return year < 10 ? '000' + year : year < 100 ? '00' + year : year < 1000 ? '0' + year : year.toString()
        }
        function _parseDate(value, cultureInfo, args)
        {
            var i,
                l,
                date,
                format,
                formats,
                custom = false;
            for(i = 1, l = args.length; i < l; i++)
            {
                format = args[i];
                if(format)
                {
                    custom = true;
                    date = _parseDateExact(value,format,cultureInfo);
                    if(date)
                        return date
                }
            }
            if(!custom)
            {
                formats = cultureInfo._getDateTimeFormats();
                for(i = 0, l = formats.length; i < l; i++)
                {
                    date = _parseDateExact(value,formats[i],cultureInfo);
                    if(date)
                        return date
                }
            }
            return null
        }
        var ___oaZeroDate = new Date(1899,11,30);
        var ___oneDayMillSeconds = 24 * 60 * 60 * 1000;
        function ___toOADate(date)
        {
            if(date === undefined || date === null)
                return 0;
            return(date - ___oaZeroDate) / ___oneDayMillSeconds
        }
        function _fromOADate(oadate)
        {
            if(oadate < 0)
                throw new Error("invalid OADate");
            //var val = new Date(___oaZeroDate);
            var val = new Date(___oaZeroDate.toUTCString());
            val.setMilliseconds(oadate * ___oneDayMillSeconds);
            return val
        }
        function DateExtend(date)
        {
            this._date = date
        }
        DateExtend.prototype = {
            Hour: function()
            {
                return this._date.getHours()
            },
            Minute: function()
            {
                return this._date.getMinutes()
            },
            Second: function()
            {
                return this._date.getSeconds()
            },
            Millisecond: function()
            {
                return this._date.getMilliseconds()
            },
            TotalDays: function()
            {
                return Math.floor(this.toOADate())
            },
            toOADate: function()
            {
                return ___toOADate(this._date)
            },
            format: function(format)
            {
                return this._toFormattedString(format,CultureInfo.invariantCulture)
            },
            localeFormat: function(format)
            {
                return this._toFormattedString(format,CultureInfo.currentCulture)
            },
            _toFormattedString: function(format, cultureInfo)
            {
                var dtf = cultureInfo.dateTimeFormat,
                    convert = dtf.Calendar.convert;
                if(!format || !format.length || format === 'i')
                    if(cultureInfo && cultureInfo.name.length)
                        if(convert)
                            return this._toFormattedString(dtf.FullDateTimePattern,cultureInfo);
                        else
                        {
                            var eraDate = new Date(this._date.getTime());
                            var era = __getEra(this._date,dtf.eras);
                            eraDate.setFullYear(__getEraYear(this._date,dtf,era));
                            return eraDate.toLocaleString()
                        }
                    else
                        return this._date.toString();
                var eras = dtf.eras,
                    sortable = format === "s";
                format = __expandFormat(dtf,format);
                var ret = new StringBuilder;
                var hour;
                var foundDay,
                    checkedDay,
                    dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g;
                function hasDay()
                {
                    if(foundDay || checkedDay)
                        return foundDay;
                    foundDay = dayPartRegExp.test(format);
                    checkedDay = true;
                    return foundDay
                }
                var quoteCount = 0,
                    tokenRegExp = __getTokenRegExp(),
                    converted;
                if(!sortable && convert)
                    converted = convert.fromGregorian(this._date);
                function getPart(date, part)
                {
                    if(converted)
                        return converted[part];
                    switch(part)
                    {
                        case 0:
                            return date.getFullYear();
                        case 1:
                            return date.getMonth();
                        case 2:
                            return date.getDate()
                    }
                }
                for(; ; )
                {
                    var index = tokenRegExp.lastIndex;
                    var ar = tokenRegExp.exec(format);
                    var preMatch = format.slice(index,ar ? ar.index : format.length);
                    quoteCount += __appendPreOrPostMatch(preMatch,ret);
                    if(!ar)
                        break;
                    if(quoteCount % 2 === 1)
                    {
                        ret.append(ar[0]);
                        continue
                    }
                    switch(ar[0])
                    {
                        case"dddd":
                            ret.append(dtf.DayNames[this._date.getDay()]);
                            break;
                        case"ddd":
                            ret.append(dtf.AbbreviatedDayNames[this._date.getDay()]);
                            break;
                        case"dd":
                            foundDay = true;
                            ret.append(___addLeadingZero(getPart(this._date,2)));
                            break;
                        case"d":
                            foundDay = true;
                            ret.append(getPart(this._date,2));
                            break;
                        case"MMMM":
                            ret.append(dtf.MonthGenitiveNames && hasDay() ? dtf.MonthGenitiveNames[getPart(this._date,1)] : dtf.MonthNames[getPart(this._date,1)]);
                            break;
                        case"MMM":
                            ret.append(dtf.AbbreviatedMonthGenitiveNames && hasDay() ? dtf.AbbreviatedMonthGenitiveNames[getPart(this._date,1)] : dtf.AbbreviatedMonthNames[getPart(this._date,1)]);
                            break;
                        case"MM":
                            ret.append(___addLeadingZero(getPart(this._date,1) + 1));
                            break;
                        case"M":
                            ret.append(getPart(this._date,1) + 1);
                            break;
                        case"yyyy":
                        case"yyy":
                            ret.append(___padYear(converted ? converted[0] : __getEraYear(this._date,dtf,__getEra(this._date,eras),sortable)));
                            break;
                        case"yy":
                            ret.append(___addLeadingZero((converted ? converted[0] : __getEraYear(this._date,dtf,__getEra(this._date,eras),sortable)) % 100));
                            break;
                        case"y":
                            ret.append((converted ? converted[0] : __getEraYear(this._date,dtf,__getEra(this._date,eras),sortable)) % 100);
                            break;
                        case"hh":
                            hour = this._date.getHours() % 12;
                            if(hour === 0)
                                hour = 12;
                            ret.append(___addLeadingZero(hour));
                            break;
                        case"h":
                            hour = this._date.getHours() % 12;
                            if(hour === 0)
                                hour = 12;
                            ret.append(hour);
                            break;
                        case"HH":
                            ret.append(___addLeadingZero(this._date.getHours()));
                            break;
                        case"H":
                            ret.append(this._date.getHours());
                            break;
                        case"mm":
                            ret.append(___addLeadingZero(this._date.getMinutes()));
                            break;
                        case"m":
                            ret.append(this._date.getMinutes());
                            break;
                        case"ss":
                            ret.append(___addLeadingZero(this._date.getSeconds()));
                            break;
                        case"s":
                            ret.append(this._date.getSeconds());
                            break;
                        case"tt":
                            ret.append(this._date.getHours() < 12 ? dtf.AMDesignator : dtf.PMDesignator);
                            break;
                        case"t":
                            ret.append((this._date.getHours() < 12 ? dtf.AMDesignator : dtf.PMDesignator).charAt(0));
                            break;
                        case"f":
                            ret.append(___addLeadingZeros(this._date.getMilliseconds()).charAt(0));
                            break;
                        case"ff":
                            ret.append(___addLeadingZeros(this._date.getMilliseconds()).substr(0,2));
                            break;
                        case"fff":
                            ret.append(___addLeadingZeros(this._date.getMilliseconds()));
                            break;
                        case"z":
                            hour = this._date.getTimezoneOffset() / 60;
                            ret.append((hour <= 0 ? '+' : '-') + Math.floor(Math.abs(hour)));
                            break;
                        case"zz":
                            hour = this._date.getTimezoneOffset() / 60;
                            ret.append((hour <= 0 ? '+' : '-') + ___addLeadingZero(Math.floor(Math.abs(hour))));
                            break;
                        case"zzz":
                            hour = this._date.getTimezoneOffset() / 60;
                            ret.append((hour <= 0 ? '+' : '-') + ___addLeadingZero(Math.floor(Math.abs(hour))) + ":" + ___addLeadingZero(Math.abs(this._date.getTimezoneOffset() % 60)));
                            break;
                        case"g":
                        case"gg":
                            if(dtf.eras)
                                ret.append(dtf.eras[__getEra(this._date,eras) + 1]);
                            break;
                        case"/":
                            ret.append(dtf.DateSeparator);
                            break;
                        case"[h]":
                            ret.append("[h]");
                            break;
                        case"[mm]":
                            ret.append("[mm]");
                            break;
                        case"[ss]":
                            ret.append("[ss]");
                            break;
                        default:
                            throw new Error("Invalid date format pattern");
                    }
                }
                return ret.toString()
            }
        };
        DateExtend.parseLocale = function(value, formats)
        {
            return _parseDate(value,CultureInfo.currentCulture,arguments)
        };
        DateExtend.parseInvariant = function(value, formats)
        {
            return _parseDate(value,CultureInfo.InvariantCulture,arguments)
        };
        DateExtend.parseExact = _parseDateExact;
        DateExtend.fromOADate = _fromOADate;
        return DateExtend
    }();
    GrapeCity.UI._NumberHelper = function()
    {
        function __getStandardTokenRegExp()
        {
            return/^(C|c|D|d|E|e|F|f|G|g|N|n|P|p|R|r|X|x)(\d*)$/g
        }
        function ___getDigitLength(value, separator)
        {
            var ip = Math.floor(Math.abs(value));
            var digit = {
                    integer: 1,
                    decimal: 0
                };
            while(ip >= 10)
            {
                ip = ip / 10;
                digit.integer++
            }
            var valueStr = value.toString();
            valueStr = valueStr.replace(".", separator);
            var exponentIndex = valueStr.search(/e/ig);
            var pointIndex = valueStr.indexOf(separator);
            var length;
            if(exponentIndex !== -1)
            {
                var numPart = valueStr.substr(0,exponentIndex);
                var expPart = valueStr.substr(exponentIndex + 1);
                var decimalPartLength = 0;
                if(pointIndex !== -1)
                    decimalPartLength = numPart.substr(pointIndex + 1).length;
                var expValue = parseFloat(expPart);
                length = decimalPartLength - expValue;
                if(length < 0)
                    length = 0;
                digit.decimal = length
            }
            else
            {
                length = 0;
                if(pointIndex !== -1)
                    length = valueStr.substr(pointIndex + 1).length;
                digit.decimal = length
            }
            return digit
        }
        function ___parseExponentFormat(format)
        {
            var exponent = {
                    symbol: format.charAt(0),
                    sign: 0
                };
            var ss = '';
            for(var si = 1; si < format.length; si++)
            {
                ss = format.charAt(si);
                if(ss === '+')
                    exponent.sign = 1;
                else if(ss === '-')
                    exponent.sign = -1;
                else if(ss === '0')
                {
                    exponent.exp = format.length - si;
                    break
                }
                else
                    throw new Error("invalid exponent format");
            }
            return exponent
        }
        function ___parseCustomNumberFormatter(format)
        {
            var partNormal = null,
                partNegative = null,
                partZero = null;
            var partCurr = {
                    intPart: null,
                    decPart: null,
                    group: false,
                    scale: 0,
                    percent: 0,
                    permile: 0,
                    exponent: null
                };
            var strBuf = '',
                insqStr = false,
                indqStr = false,
                inESC = false,
                inSci = false,
                decPointFound = false,
                groupSepFound = false,
                sciFound = false,
                intPlaceHoldFound = false;
            var curChar = null,
                prevChar = null,
                curPart = [];
            for(var i = 0; i < format.length; i++)
            {
                curChar = format.charAt(i);
                if(insqStr)
                {
                    if(curChar !== '\'')
                        strBuf += curChar;
                    else
                    {
                        curPart.push(strBuf);
                        strBuf = '';
                        insqStr = false
                    }
                    prevChar = curChar;
                    continue
                }
                else if(indqStr)
                {
                    if(curChar !== '"')
                        strBuf += curChar;
                    else
                    {
                        curPart.push(strBuf);
                        strBuf = '';
                        indqStr = false
                    }
                    prevChar = curChar;
                    continue
                }
                else if(inESC)
                {
                    curPart.push(strBuf + curChar);
                    strBuf = '';
                    prevChar = curChar;
                    continue
                }
                else if(inSci)
                {
                    if(prevChar === 'E' || prevChar === 'e')
                        if(curChar === '+' || curChar === '-' || curChar === '0')
                        {
                            strBuf += curChar;
                            continue
                        }
                        else
                            inSci = false;
                    else if(prevChar === '+' || prevChar === '-')
                        if(curChar === '0')
                        {
                            strBuf += curChar;
                            continue
                        }
                        else
                        {
                            inSci = false;
                            curPart.push(strBuf);
                            strBuf = ''
                        }
                    else if(prevChar === '0')
                        if(curChar === '0')
                        {
                            strBuf += curChar;
                            continue
                        }
                        else
                        {
                            inSci = false;
                            if(!sciFound)
                            {
                                sciFound = true;
                                partCurr.exponent = ___parseExponentFormat(strBuf)
                            }
                            curPart.push(strBuf);
                            strBuf = ''
                        }
                }
                else if(curChar === '0' || curChar === '#')
                {
                    intPlaceHoldFound = true;
                    if(prevChar === '0' || prevChar === '#')
                    {
                        strBuf += curChar;
                        prevChar = curChar;
                        continue
                    }
                    else if(strBuf !== '')
                    {
                        curPart.push(strBuf);
                        strBuf = ''
                    }
                }
                else if((prevChar === '0' || prevChar === '#') && curChar !== '0' && curChar !== '#')
                {
                    curPart.push(strBuf);
                    strBuf = ''
                }
                if(curChar === ';')
                {
                    if(strBuf !== '')
                    {
                        if(inSci && !sciFound)
                            partCurr.exponent = ___parseExponentFormat(strBuf);
                        curPart.push(strBuf);
                        strBuf = ''
                    }
                    if(!decPointFound)
                        partCurr.intPart = curPart;
                    else
                        partCurr.decPart = curChar;
                    curPart = [];
                    if(partNormal === undefined || partNormal === null)
                        partNormal = partCurr;
                    else if(partNegative === undefined || partNegative === null)
                        partNegative = partCurr;
                    else if(partZero === undefined || partZero === null)
                        partZero = partCurr;
                    else
                        throw new Error("invalid format: too much semicolons");
                    decPointFound = false;
                    intPlaceHoldFound = false;
                    if(groupSepFound)
                    {
                        partCurr.group = true;
                        groupSepFound = false
                    }
                    partCurr = {
                        intPart: null,
                        decPart: null,
                        group: false,
                        scale: 0,
                        percent: 0,
                        permile: 0,
                        exponent: null
                    }
                }
                else if(curChar === ',')
                {
                    if(strBuf !== '')
                    {
                        curPart.push(strBuf);
                        strBuf = ''
                    }
                    partCurr.intPart = curPart;
                    curPart = [];
                    decPointFound = true;
                    intPlaceHoldFound = false;
                    if(groupSepFound)
                    {
                        partCurr.group = true;
                        groupSepFound = false
                    }
                }
                else if(curChar === '\'')
                    insqStr = true;
                else if(curChar === '"')
                    indqStr = true;
                else if(curChar === '%')
                {
                    partCurr.percent++;
                    curPart.push(curChar)
                }
                else if(curChar === '\u2030')
                {
                    partCurr.permile++;
                    curPart.push(curChar)
                }
                else if(curChar === '0' || curChar === '#')
                    strBuf += curChar;
                else if(curChar === '.')
                {
                    if(!decPointFound)
                    {
                        if(strBuf !== '')
                        {
                            curPart.push(strBuf);
                            strBuf = ''
                        }
                        if(!intPlaceHoldFound)
                            continue;
                        var isScale = true,
                            strQuote = '';
                        for(var j = i + 1; j < format.length; j++)
                        {
                            var nextChar = format.charAt(j);
                            if(strQuote !== '')
                            {
                                if(nextChar === '\'' || nextChar === '"')
                                    strQuote = '';
                                continue
                            }
                            if(nextChar === '\'' || nextChar === '"')
                                strQuote = nextChar;
                            else if(nextChar === '0' || nextChar === '#')
                            {
                                isScale = false;
                                break
                            }
                            else if(nextChar === '.' || nextChar === ';')
                                break
                        }
                        if(isScale)
                            partCurr.scale++;
                        else
                            groupSepFound = true
                    }
                    else if(strBuf !== '')
                    {
                        curPart.push(strBuf);
                        strBuf = ''
                    }
                }
                else if(curChar === 'E' || curChar === 'e')
                {
                    inSci = true;
                    if(strBuf !== '')
                        curPart.push(strBuf);
                    strBuf = curChar
                }
                else
                    strBuf += curChar;
                prevChar = curChar
            }
            if(strBuf !== '')
            {
                if(inSci && !sciFound)
                    partCurr.exponent = ___parseExponentFormat(strBuf);
                curPart.push(strBuf)
            }
            if(groupSepFound)
                partCurr.group = true;
            if(!decPointFound)
                partCurr.intPart = curPart;
            else
                partCurr.decPart = curPart;
            if(partNormal === undefined || partNormal === null)
                partNormal = partCurr;
            else if(partNegative === undefined || partNegative === null)
                partNegative = partCurr;
            else if(partZero === undefined || partZero === null)
                partZero = partCurr;
            return{
                    normal: partNormal,
                    negative: partNegative,
                    zero: partZero
                }
        }
        var ___signs = {
                '1': '+',
                '0': '',
                '-1': '-'
            };
        function ___zeroPad(str, count, left)
        {
            for(var l = str.length; l < count; l++)
                str = left ? '0' + str : str + '0';
            return str
        }
        function __insertGroupSeparator(numberString, groupSizes, sep, negativeSign)
        {
            var curSize = groupSizes[0];
            var curGroupIndex = 1;
            var stringIndex = numberString.length - 1;
            var stringBuilder = new StringBuilder("");
            var numberCount = 0;
            var needSep = false;
            while(stringIndex >= 0)
            {
                if(curSize < 1 || curSize > 9)
                    throw new Error("NumberGroupSize must be between 1 and 9.");
                if(/\d/ig.test(numberString[stringIndex]))
                {
                    if(needSep)
                    {
                        stringBuilder.insert(sep,0);
                        needSep = false
                    }
                    numberCount++
                }
                stringBuilder.insert(numberString[stringIndex],0);
                if(numberCount === curSize)
                {
                    needSep = true;
                    numberCount = 0;
                    if(curGroupIndex < groupSizes.length)
                    {
                        curSize = groupSizes[curGroupIndex];
                        curGroupIndex++
                    }
                }
                stringIndex--
            }
            return stringBuilder.toString()
        }
        function ___expandNumber(number, precision, groupSizes, sep, decimalChar, negativeSign, noGroupSep)
        {
            var factor = Math.pow(10,precision);
            var rounded = Math.round(number * factor) / factor;
            if(!isFinite(rounded))
                rounded = number;
            number = rounded;
            var numberString = number.toString();
            var right;
            var exponent;
            var split = numberString.split(/e/i);
            numberString = split[0];
            exponent = split.length > 1 ? parseInt(split[1],10) : 0;
            split = numberString.split('.');
            numberString = split[0];
            right = split.length > 1 ? split[1] : "";
            if(exponent > 0)
            {
                right = ___zeroPad(right,exponent,false);
                numberString += right.slice(0,exponent);
                right = right.substr(exponent)
            }
            else if(exponent < 0)
            {
                exponent = -exponent;
                if(number < 0)
                    numberString = negativeSign + ___zeroPad(numberString.replace(negativeSign,""),exponent + 1,true);
                else
                    numberString = ___zeroPad(numberString,exponent + 1,true);
                right = numberString.slice(-exponent,numberString.length) + right;
                numberString = numberString.slice(0,-exponent)
            }
            if(precision > 0)
            {
                if(right.length > precision)
                    right = right.slice(0,precision);
                else
                    right = ___zeroPad(right,precision,false);
                right = decimalChar + right
            }
            else
                right = "";
            if(noGroupSep === true)
                return numberString + right;
            else
                return __insertGroupSeparator(numberString,groupSizes,sep,negativeSign) + right
        }
        function ___formatNumber(value, formatter, nf)
        {
            var resultBuilder = new StringBuilder('');
            value = value * Math.pow(100,formatter.percent);
            value = value * Math.pow(1000,formatter.permile);
            value = value / Math.pow(10,formatter.scale * 3);
            var intPart = formatter.intPart,
                decPart = formatter.decPart;
            if(!intPart && !decPart)
                return'';
            var partIntFormatter = null,
                partDecFormatter = null;
            var i,
                ip,
                d,
                dp,
                exp;
            if(intPart)
            {
                partIntFormatter = '';
                for(i = 0; i < intPart.length; i++)
                {
                    ip = intPart[i];
                    if(/^(0|#)+/g.test(ip))
                        partIntFormatter += ip
                }
            }
            if(decPart)
            {
                partDecFormatter = '';
                for(d = 0; d < decPart.length; d++)
                {
                    dp = decPart[d];
                    if(/^(0|#)+/g.test(dp))
                        partDecFormatter += dp
                }
            }
            if(!partIntFormatter && !partDecFormatter)
                return(intPart ? intPart.join('') : '') + (decPart ? decPart.join('') : '');
            else if(!partDecFormatter)
                partDecFormatter = '';
            var exponentValue = 0;
            var dl = ___getDigitLength(value,nf.NumberDecimalSeparator);
            if(formatter.exponent)
            {
                var aValue = Math.abs(value);
                var intLen = !partIntFormatter ? 1 : partIntFormatter.length;
                if(aValue >= 1)
                {
                    if(dl.integer > intLen)
                    {
                        dl.integer -= intLen;
                        dl.decimal += intLen;
                        value = value / Math.pow(10,dl.integer);
                        exponentValue = dl.integer
                    }
                    else if(dl.integer < intLen)
                        exponentValue = 0;
                    else
                        exponentValue = 0;
                    if(formatter.exponent.sign === -1)
                        formatter.exponent.sign = 0
                }
                else if(aValue < 1 && aValue > 0)
                {
                    formatter.exponent.sign = -1;
                    dl.integer = intLen;
                    dl.decimal -= intLen;
                    var baseVal = Math.pow(10,intLen);
                    while(aValue * 10 < baseVal)
                    {
                        aValue *= 10;
                        exponentValue++
                    }
                    value *= Math.pow(10,exponentValue)
                }
            }
            var zerophIndex = partDecFormatter.lastIndexOf('0');
            var precision = dl.decimal;
            if(zerophIndex >= 0)
                precision = zerophIndex + 1;
            var numbers = ___expandNumber(value,precision,nf.NumberGroupSizes,nf.NumberGroupSeparator,nf.NumberDecimalSeparator,nf.NegativeSign,true);
            if(numbers === '')
                return(intPart ? intPart.join('') : '') + (decPart ? decPart.join('') : '');
            var replaceExponent = false;
            if(intPart)
            {
                var numberIntPart = numbers.split(nf.NumberDecimalSeparator)[0];
                var neg = numberIntPart.substr(0,1);
                if(neg === nf.NegativeSign)
                    numberIntPart = numberIntPart.substr(1);
                var procDigitLen = 0,
                    procIntPart = '';
                var firstZerophIndex = partIntFormatter.indexOf('0');
                var intDigLen = firstZerophIndex === -1 ? numberIntPart.length : partIntFormatter.length - firstZerophIndex;
                for(i = intPart.length - 1; i >= 0; i--)
                {
                    ip = intPart[i];
                    if(/^(0|#)+/g.test(ip))
                    {
                        procIntPart = ip + procIntPart;
                        if(procIntPart !== partIntFormatter)
                        {
                            var iplen = ip.length;
                            for(var ipi = numberIntPart.length - procDigitLen - 1; ipi >= 0 && iplen > 0; ipi--)
                            {
                                var nc = numberIntPart.charAt(ipi);
                                resultBuilder._insert(nc);
                                iplen--;
                                procDigitLen++
                            }
                            if(procDigitLen >= numberIntPart.length && procDigitLen < intDigLen && iplen > 0)
                            {
                                resultBuilder._insert(new Array(iplen + 1).join('0'));
                                procDigitLen += iplen
                            }
                        }
                        else
                        {
                            var part = numberIntPart.substr(0,numberIntPart.length - procDigitLen);
                            if(firstZerophIndex >= 0 && firstZerophIndex < partIntFormatter.length - procDigitLen - part.length)
                                part = new Array(partIntFormatter.length - procDigitLen - firstZerophIndex - part.length + 1).join('0') + part;
                            resultBuilder._insert(part)
                        }
                    }
                    else if(formatter.exponent && !replaceExponent && /^((E(\+|-)?|e(\+|-)?)\d+)/g.test(ip))
                    {
                        replaceExponent = true;
                        exp = '';
                        exp += formatter.exponent.symbol;
                        exp += ___signs[formatter.exponent.sign];
                        exp += ___zeroPad(exponentValue.toString(),formatter.exponent.exp,true);
                        resultBuilder._insert(exp)
                    }
                    else
                        resultBuilder._insert(ip)
                }
                if(neg === nf.NegativeSign)
                    resultBuilder._insert(neg);
                if(formatter.group === true)
                    resultBuilder = new StringBuilder(__insertGroupSeparator(resultBuilder.toString(),nf.NumberGroupSizes,nf.NumberGroupSeparator,nf.NegativeSign))
            }
            if(decPart)
            {
                var numberDecPart = '';
                if(precision > 0)
                {
                    var decSepIndex = numbers.indexOf(nf.NumberDecimalSeparator);
                    if(decSepIndex !== -1)
                    {
                        numberDecPart = numbers.substring(decSepIndex + 1);
                        if(partIntFormatter === '')
                            resultBuilder.append(numbers.substr(0,decSepIndex));
                        resultBuilder.append(nf.NumberDecimalSeparator)
                    }
                }
                else if(!/^(#+)$/ig.test(partDecFormatter) || decPart.join('').length !== partDecFormatter.length)
                {
                    resultBuilder.append(nf.NumberDecimalSeparator);
                    if(zerophIndex > 0)
                        numberDecPart = new Array(zerophIndex + 1).join('0')
                }
                var numDecIndex = 0;
                for(d = 0; d < decPart.length; d++)
                {
                    dp = decPart[d];
                    if(/^(0|#)+/g.test(dp))
                    {
                        resultBuilder.append(numberDecPart.substr(numDecIndex,dp.length));
                        numDecIndex += dp.length
                    }
                    else if(formatter.exponent && !replaceExponent && /^((E(\+|-)?|e(\+|-)?)\d+)/g.test(dp))
                    {
                        replaceExponent = true;
                        exp = '';
                        exp += formatter.exponent.symbol;
                        exp += ___signs[formatter.exponent.sign];
                        exp += ___zeroPad(exponentValue.toString(),formatter.exponent.exp,true);
                        resultBuilder.append(exp)
                    }
                    else
                        resultBuilder.append(dp)
                }
            }
            return resultBuilder.toString()
        }
        function __parseNumberNegativePattern(value, numFormat, numberNegativePattern)
        {
            var neg = numFormat.NegativeSign;
            var pos = numFormat.PositiveSign;
            var valueStr = new StringHelper(value);
            if(numberNegativePattern === 4 || numberNegativePattern === 2)
            {
                neg = ' ' + neg;
                pos = ' ' + pos
            }
            if(numberNegativePattern === 4 || numberNegativePattern === 3)
            {
                if(valueStr.endsWith(neg))
                    return['-',value.substr(0,value.length - neg.length)];
                else if(valueStr.endsWith(pos))
                    return['+',value.substr(0,value.length - pos.length)]
            }
            else if(numberNegativePattern === 2 || numberNegativePattern === 1)
            {
                if(valueStr.startsWith(neg))
                    return['-',value.substr(neg.length)];
                else if(valueStr.startsWith(pos))
                    return['+',value.substr(pos.length)]
            }
            else if(numberNegativePattern === 0)
            {
                if(valueStr.startsWith('(') && valueStr.endsWith(')'))
                    return['-',value.substr(1,value.length - 2)]
            }
            else
                throw new Error("");
            return['',value]
        }
        function __parseNumber(value, cultureInfo)
        {
            value = value !== undefined && value !== null ? new GrapeCity.UI._StringHelper(value).trim() : "";
            if(value.match(/^[+-]?infinity$/i))
                return parseFloat(value);
            if(value.match(/^0x[a-f0-9]+$/i))
                return parseInt(value,10);
            var numFormat = cultureInfo.numberFormat;
            var signInfo = __parseNumberNegativePattern(value,numFormat,numFormat.NumberNegativePattern);
            var sign = signInfo[0];
            var num = signInfo[1];
            if(sign === '' && numFormat.NumberNegativePattern !== 1)
            {
                signInfo = __parseNumberNegativePattern(value,numFormat,1);
                sign = signInfo[0];
                num = signInfo[1]
            }
            if(sign === '')
                sign = '+';
            if(num[0] === numFormat.CurrencySymbol)
                num = num.substr(1);
            var exponent;
            var intAndFraction;
            var exponentPos = num.indexOf('e');
            if(exponentPos < 0)
                exponentPos = num.indexOf('E');
            if(exponentPos < 0)
            {
                intAndFraction = num;
                exponent = null
            }
            else
            {
                intAndFraction = num.substr(0,exponentPos);
                exponent = num.substr(exponentPos + 1)
            }
            var integer,
                fraction;
            var decimalPos = intAndFraction.indexOf(numFormat.NumberDecimalSeparator);
            if(decimalPos < 0)
            {
                integer = intAndFraction;
                fraction = null
            }
            else
            {
                integer = intAndFraction.substr(0,decimalPos);
                fraction = intAndFraction.substr(decimalPos + numFormat.NumberDecimalSeparator.length)
            }
            integer = integer.split(numFormat.NumberGroupSeparator).join('');
            var altNumGroupSeparator = numFormat.NumberGroupSeparator.replace(/\u00A0/g," ");
            if(numFormat.NumberGroupSeparator !== altNumGroupSeparator)
                integer = integer.split(altNumGroupSeparator).join('');
            var p = sign + integer;
            if(fraction !== null)
                p += '.' + fraction;
            var lastChar = p[p.length - 1];
            if(lastChar === numFormat.PercentSymbol)
            {
                p = p.substr(0,p.length - 1);
                p = new GrapeCity.UI._StringHelper(p).trimEnd();
                var ndp = p.indexOf(numFormat.NumberDecimalSeparator);
                if(ndp === -1)
                    ndp = p.length;
                var resultBuilder = new StringBuilder('');
                resultBuilder.append(p.substr(0,ndp - 2));
                resultBuilder.append(numFormat.NumberDecimalSeparator);
                resultBuilder.append(p.substr(ndp - 2,2));
                resultBuilder.append(p.substr(ndp + 1));
                p = resultBuilder.toString()
            }
            if(exponent !== null)
            {
                var expSignInfo = __parseNumberNegativePattern(exponent,numFormat,1);
                if(expSignInfo[0] === '')
                    expSignInfo[0] = '+';
                p += 'e' + expSignInfo[0] + expSignInfo[1]
            }
            if(p.match(/^[+-]?\d*\.?\d*(e[+-]?\d+)?$/))
                return parseFloat(p);
            return NaN
        }
        var ___maxInt32 = 4294967295;
        var ___percentPositivePattern = ["n %","n%","%n"];
        var ___percentNegativePattern = ["-n %","-n%","-%n"];
        var ___numberNegativePattern = ["(n)","-n","- n","n-","n -"];
        var ___currencyPositivePattern = ["$n","n$","$ n","n $"];
        var ___currencyNegativePattern = ["($n)","-$n","$-n","$n-","(n$)","-n$","n-$","n$-","-n $","-$ n","n $-","$ n-","$ -n","n- $","($ n)","(n $)"];
        function ___toHexString(num, lowCase, precision)
        {
            if(Math.abs(Math.floor(num) - num) !== 0)
                throw new Error("Bad Format Specifier");
            var number = num >= 0 ? num.toString(16) : (___maxInt32 + num + 1).toString(16);
            number = lowCase ? number.toLowerCase() : number.toUpperCase();
            if(precision !== undefined && precision !== null && number.length < precision)
                return ___zeroPad(number,precision,true);
            return number
        }
        function ___toScientific(num, e, precision, groupSizes, sep, decimalChar, negativeSign)
        {
            var digitLen = 0;
            var digSepMoveRight = num >= 1 || num === 0;
            var checkFn = digSepMoveRight ? function(v, b)
                {
                    return v / b < 10
                } : function(v, b)
                {
                    return v * b >= 1
                };
            while(digitLen < 1000)
            {
                var base = Math.pow(10,digitLen);
                if(checkFn(num,base))
                    break;
                digitLen++
            }
            num = digSepMoveRight ? Math.abs(num) / Math.pow(10,digitLen) : Math.abs(num) * Math.pow(10,digitLen);
            var number = ___expandNumber(num,precision,groupSizes,sep,decimalChar,negativeSign);
            number += e + (digSepMoveRight ? "+" : "-") + ___zeroPad(digitLen.toString(),3,true);
            return number
        }
        function NumberExtend(num)
        {
            this._num = num
        }
        NumberExtend.prototype = {
            format: function(format)
            {
                return this._toFormattedString(format,CultureInfo.invariantCulture)
            },
            localeFormat: function(format)
            {
                return this._toFormattedString(format,CultureInfo.currentCulture)
            },
            _toFormattedString: function(format, cultureInfo)
            {
                if(!format || format.length === 0 || format === 'i')
                    if(cultureInfo && cultureInfo.name.length > 0)
                        return this._num.toLocaleString();
                    else
                        return this._num.toString();
                if(__getStandardTokenRegExp().test(format))
                    return this._toStandardFormattedString(format,cultureInfo.numberFormat);
                else
                    return this._toCustomFormattedString(format,cultureInfo.numberFormat)
            },
            _toStandardFormattedString: function(format, nf)
            {
                var number = Math.abs(this._num);
                if(!format)
                    format = "D";
                var precision = -1;
                if(format.length > 1)
                    precision = parseInt(format.slice(1),10);
                var pattern,
                    resultArray;
                switch(format.charAt(0))
                {
                    case"d":
                    case"D":
                        pattern = 'n';
                        if(precision !== -1)
                            number = ___zeroPad("" + number,precision,true);
                        if(this._num < 0)
                            number = "-" + number;
                        break;
                    case"c":
                    case"C":
                        if(this._num < 0)
                            pattern = ___currencyNegativePattern[nf.CurrencyNegativePattern];
                        else
                            pattern = ___currencyPositivePattern[nf.CurrencyPositivePattern];
                        if(precision === -1)
                            precision = nf.CurrencyDecimalDigits;
                        number = ___expandNumber(Math.abs(this._num),precision,nf.CurrencyGroupSizes,nf.CurrencyGroupSeparator,nf.CurrencyDecimalSeparator,nf.NegativeSign);
                        break;
                    case"n":
                    case"N":
                        if(this._num < 0)
                            pattern = ___numberNegativePattern[nf.NumberNegativePattern];
                        else
                            pattern = 'n';
                        if(precision === -1)
                            precision = nf.NumberDecimalDigits;
                        number = ___expandNumber(Math.abs(this._num),precision,nf.NumberGroupSizes,nf.NumberGroupSeparator,nf.NumberDecimalSeparator,nf.NegativeSign);
                        break;
                    case"p":
                    case"P":
                        if(this._num < 0)
                            pattern = ___percentNegativePattern[nf.PercentNegativePattern];
                        else
                            pattern = ___percentPositivePattern[nf.PercentPositivePattern];
                        if(precision === -1)
                            precision = nf.PercentDecimalDigits;
                        number = ___expandNumber(Math.abs(this._num) * 100,precision,nf.PercentGroupSizes,nf.PercentGroupSeparator,nf.PercentDecimalSeparator,nf.NegativeSign);
                        break;
                    case"F":
                    case"f":
                        resultArray = this._toFixedPoint(number,pattern,format,precision,nf);
                        number = resultArray[0];
                        pattern = resultArray[1];
                        break;
                    case"e":
                    case"E":
                        resultArray = this._toScientificNotation(number,pattern,format,precision,nf);
                        number = resultArray[0];
                        pattern = resultArray[1];
                        break;
                    case"x":
                    case"X":
                        pattern = "n";
                        number = ___toHexString(this._num,format.charAt(0) === 'x',precision === -1 ? 0 : precision);
                        break;
                    case"g":
                    case"G":
                        var numToStr = this._num.toString();
                        resultArray = [];
                        if(numToStr.indexOf("e") === -1 && numToStr.indexOf("E") === -1)
                            resultArray = this._toFixedPoint(number,pattern,format,precision,nf);
                        else
                            resultArray = this._toScientificNotation(number,pattern,format.replace("g","e").replace("G","E"),precision,nf);
                        number = resultArray[0];
                        pattern = resultArray[1];
                        break;
                    default:
                        throw new Error("Bad Format Specifier");
                }
                var regex = /n|\$|-|%/g;
                var ret = "";
                for(; ; )
                {
                    var index = regex.lastIndex;
                    var ar = regex.exec(pattern);
                    ret += pattern.slice(index,ar ? ar.index : pattern.length);
                    if(!ar)
                        break;
                    switch(ar[0])
                    {
                        case"n":
                            ret += number;
                            break;
                        case"$":
                            ret += nf.CurrencySymbol;
                            break;
                        case"-":
                            if(/[1-9]/.test(number))
                                ret += nf.NegativeSign;
                            break;
                        case"%":
                            ret += nf.PercentSymbol;
                            break;
                        default:
                            throw new Error("Invalid number format pattern");
                    }
                }
                return ret
            },
            _toScientificNotation: function(out_number, out_pattern, format, precision, nf)
            {
                out_pattern = "n";
                out_number = ___toScientific(Math.abs(this._num),format.charAt(0),precision === -1 ? 6 : precision,nf.NumberGroupSizes,nf.NumberGroupSeparator,nf.NumberDecimalSeparator,nf.NegativeSign);
                if(this._num < 0)
                    out_number = "-" + out_number;
                return[out_number,out_pattern]
            },
            _toFixedPoint: function(out_number, out_pattern, format, precision, nf)
            {
                if(this._num < 0)
                    out_pattern = ___numberNegativePattern[nf.NumberNegativePattern];
                else
                    out_pattern = 'n';
                if(precision === -1)
                    precision = 2;
                var integer = Math.floor(out_number);
                var dec = out_number - integer;
                out_number = ___expandNumber(dec,precision,nf.NumberGroupSizes,nf.NumberGroupSeparator,nf.NumberDecimalSeparator,nf.NegativeSign);
                out_number = "" + integer + out_number.substring(1);
                return[out_number,out_pattern]
            },
            _toCustomFormattedString: function(format, nf)
            {
                var parsedFormat = ___parseCustomNumberFormatter(format);
                var formatter = null;
                if(this._num === 0)
                    formatter = parsedFormat.zero;
                else if(this._num < 0)
                    formatter = parsedFormat.negative;
                if(!formatter)
                    formatter = parsedFormat.normal;
                var result = ___formatNumber(this._num,formatter,nf) + "";
                if(result.indexOf(nf.NegativeSign) === 1 && result.indexOf(nf.CurrencySymbol) === 0)
                    result = result[1] + result[0] + result.substring(2);
                return result
            }
        };
        NumberExtend.parseLocale = function(value)
        {
            return __parseNumber(value,CultureInfo.currentCulture)
        };
        NumberExtend.parseInvariant = function(value)
        {
            return __parseNumber(value,CultureInfo.invariantCulture)
        };
        return NumberExtend
    }()
})(this,jQuery);
(function(window)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var StringComparison = {
            CurrentCulture: 0,
            CurrentCultureIgnoreCase: 1,
            InvariantCulture: 2,
            InvariantCultureIgnoreCase: 3,
            Ordinal: 4,
            OrdinalIgnoreCase: 5
        };
    var string = {
            Empty: "",
            Format: function()
            {
                if(arguments.length === 0)
                    return null;
                var str = arguments[0];
                for(var i = 1; i < arguments.length; i++)
                {
                    var re = new RegExp('\\{' + (i - 1) + '\\}','gm');
                    str = str.replace(re,arguments[i])
                }
                return str
            },
            IsNullOrEmpty: function(obj)
            {
                return!obj || obj === string.Empty
            }
        };
    var StringHelper = {
        specials : [".", "?", "*", "+", "^", "$", "\\"],
        Contains: function(str, value)
            {
                return value === "" || str.indexOf(value) >= 0
            },
            IndexOf: function(str, value, comparisonType)
            {
                if(!comparisonType)
                    return str.indexOf(value);
                else if(comparisonType === StringComparison.CurrentCultureIgnoreCase)
                {
                    var tempStr = str.toLowerCase();
                    var tempValue = value.toLowerCase();
                    return tempStr.indexOf(tempValue)
                }
                else
                    return str.indexOf(value)
            },
            TrimStart: function(str, trimChar)
            {
                if(!trimChar)
                    return str;
                var temp = str;
                while(true)
                {
                    if(temp.substr(0,trimChar.length) !== trimChar)
                        break;
                    temp = temp.substr(trimChar.length)
                }
                return temp
            },
            TrimEnd: function(str, trimChar)
            {
                if(!trimChar)
                    return str;
                var temp = str;
                while(true)
                {
                    if(temp.substr(temp.length - trimChar.length,trimChar.length) !== trimChar)
                        break;
                    temp = temp.substr(0,temp.length - trimChar.length)
                }
                return temp
            },
            Trim: function(str, trimChar)
            {
                var temp = trimChar;
                if(!trimChar)
                    temp = " ";
                str = this.TrimStart(str,temp);
                str = this.TrimEnd(str,temp);
                return str
            },
            Insert: function(str, startIndex, value)
            {
                if(startIndex < 0 || startIndex > str.length || value === null || value === undefined)
                    throw new Error;
                var tempStrStart = str.substr(0,startIndex);
                var tempStrEnd = str.substr(startIndex,str.length - startIndex);
                return tempStrStart + value + tempStrEnd
            },
            Remove: function(str, startIndex, count)
            {
                if(count === undefined || count === null)
                    count = str.length - startIndex;
                if(startIndex < 0 || count < 0 || startIndex + count > str.length)
                    throw new Error;
                var valueStart = str.substr(0,startIndex);
                var valueEnd = str.substr(startIndex + count,str.length - startIndex - count);
                return valueStart + valueEnd
            },
            StartsWith: function(str, value, comparisonType)
            {
                if(!value)
                    throw new Error;
                if(value === "")
                    return true;
                if(value.length > str.length)
                    return false;
                var thisStr = str;
                var valueStr = value;
                if(comparisonType === StringComparison.CurrentCultureIgnoreCase)
                {
                    thisStr = thisStr.toLowerCase();
                    valueStr = valueStr.toLowerCase()
                }
                if (this.specials.indexOf(valueStr) != -1)
                    valueStr = "\\" + valueStr;
                return thisStr.search(new RegExp("^" + valueStr)) > -1
            },
            EndsWith: function(str, value, comparisonType)
            {

                if(!value)
                    throw new Error;
                if(value === "")
                    return true;
                if(value.length > str.length)
                    return false;
                var thisStr = str;
                var valueStr = value;
                if(comparisonType === StringComparison.CurrentCultureIgnoreCase)
                {
                    thisStr = thisStr.toLowerCase();
                    valueStr = valueStr.toLowerCase()
                }
                if (this.specials.indexOf(valueStr) != -1)
                    valueStr = "\\" + valueStr;
                return thisStr.search(new RegExp(valueStr + "$")) > -1
            },
            Replace: function(str, oldValue, newValue)
            {
                if(!oldValue || oldValue === "")
                    throw new Error;
                if (this.specials.indexOf(oldValue) != -1)
                    oldValue = "\\" + oldValue;
                var re = new RegExp(oldValue, "g");
                return str.replace(re,newValue)
            }
        };
    GrapeCity.UI.StringHelper = StringHelper;
    function StringBuilder()
    {
        this.strValue = ""
    }
    StringBuilder.prototype = {
        Append: function(obj)
        {
            if(obj)
                this.strValue += obj.toString()
        },
        toString: function()
        {
            return this.strValue.toString()
        },
        Length: function()
        {
            return this.strValue.length
        },
        ToString: function(startIndex, length)
        {
            var str = this.toString();
            if(arguments.length === 0)
                return str;
            var subStr = str.substr(startIndex,length);
            return subStr
        },
        Remove: function(startIndex, length)
        {
            var str = this.toString();
            this.strValue = StringHelper.Remove(str,startIndex,length)
        }
    };
    GrapeCity.UI.StringBuilder = StringBuilder;
    function isType(obj, type)
    {
        if(obj === undefined || obj === null)
            return type === "null";
        if(!type)
            return false;
        var _baseTypes = {
                undefined: 'undefined',
                number: 'number',
                boolean: 'boolean',
                string: 'string'
            };
        if(_baseTypes[typeof obj] === type)
            return true;
        if(type === "function" && /^\s*\bfunction\b/.test("" + obj))
            return true;
        if(Object.prototype.toString.call(obj).slice(8,-1).toLowerCase() === type.toLowerCase())
            return true;
        if(obj && obj._classNames)
        {
            for(var index = 0; index < obj._classNames.length; index++)
            {
                var className = obj._classNames[index];
                if(className === type)
                    return true
            }
            return false
        }
        else
        {
            if(obj === undefined || obj === null)
                return false;
            if(type === "DateTime" || type === "TimeSpan")
                return obj instanceof Date
        }
        if(typeof type === "string")
            if(_baseTypes[type])
                return false;
        return obj instanceof type
    }
    function asType(obj, typeName)
    {
        if(isType(obj,typeName))
            return obj;
        else
            return null
    }
    var DateTimeStyles = {
            None: 0,
            AllowLeadingWhite: 1,
            AllowTrailingWhite: 2,
            AllowInnerWhite: 4,
            AllowWhiteSpaces: 7,
            NoCurrentDateDefault: 8,
            AdjustToUniversal: 16,
            AssumeLocal: 32,
            AssumeUniversal: 64,
            RoundtripKind: 128
        };
    function OutPara(obj)
    {
        this.value = obj
    }
    var char = {
            IsDigit: function(c)
            {
                var cc = c.charCodeAt(0);
                return cc >= 0x30 && cc <= 0x39
            },
            IsWhiteSpace: function(c)
            {
                var cc = c.charCodeAt(0);
                return cc >= 0x0009 && cc <= 0x000D || cc === 0x0020 || cc === 0x0085 || cc === 0x00A0
            }
        };
    var FormatMode = {
            CustomMode: 0,
            StandardDateTimeMode: 1,
            StandardNumericMode: 2
        };
    GrapeCity.UI.FormatMode = FormatMode;
    var NumberFormatType = {
            General: 0,
            Number: 1,
            DateTime: 2,
            Text: 3
        };
    GrapeCity.UI.NumberFormatType = NumberFormatType;
    var TimePart = {
            Hour: 0,
            Minute: 1,
            Second: 2
        };
    var DefaultTokens = {
            DoubleQuote: '\"',
            SingleQuote: '\'',
            Tab: '\t',
            LeftSquareBracket: '[',
            RightSquareBracket: ']',
            LessThanSign: '<',
            GreaterThanSign: '>',
            EqualsThanSign: '=',
            PlusSign: '+',
            HyphenMinus: '-',
            UnderLine: '_',
            LeftParenthesis: '(',
            RightParenthesis: ')',
            Dollar: '$',
            Comma: ',',
            Space: ' ',
            SolidusSign: '/',
            ReverseSolidusSign: '\\',
            Zero: '0',
            QuestionMark: '?',
            Colon: ':',
            Semicolon: ';',
            Sharp: '#',
            CommercialAt: '@',
            NumberSign: '#',
            Asterisk: '*',
            Exponential1: "E+",
            Exponential2: "E-",
            DecimalSeparator: ",",
            NumberGroupSeparator: ".",
            PercentSymbol: "%",
            NaNSymbol: "NaN",
            FormatSeparator: ";",
            NegativeSign: "-",
            ReplacePlaceholder: "@",
            ExponentialSymbol: "E",
            DateTimeFormatInfo: GrapeCity.UI._CultureInfo.currentCulture.dateTimeFormat,
            NumberFormatInfo: {
                CurrencySymbol: "€",
                NegativeSign: "-",
                NaNSymbol: "NaN",
                CurrencyDecimalSeparator: ",",
                CurrencyGroupSeparator: ".",
                NegativeInfinitySymbol: "-Infinity",
                NumberDecimalSeparator: ",",
                NumberGroupSeparator: ".",
                PercentDecimalSeparator: ",",
                PercentGroupSeparator: ".",
                PercentSymbol: "%",
                PerMilleSymbol: "???",
                PositiveInfinitySymbol: "Infinity",
                PositiveSign: "+"
            },
            Filter: function(s, bracketsStart, bracketsEnd)
            {
                if(s === undefined || s === null || s === "")
                    return s;
                var sb = "";
                var refCount = 0;
                for(var n = 0; n < s.length; n++)
                {
                    var c = s[n];
                    if(c === bracketsStart)
                        refCount++;
                    else if(c === bracketsEnd)
                    {
                        refCount--;
                        if(refCount < 0)
                            refCount = 0
                    }
                    else if(refCount === 0)
                        sb += c
                }
                return sb.toString()
            },
            TrimSquareBracket: function(token)
            {
                if(!token || token === string.Empty)
                    return token;
                if(token[0] === DefaultTokens.LeftSquareBracket)
                    token = StringHelper.TrimStart(token,DefaultTokens.LeftSquareBracket);
                if(token[token.length - 1] === DefaultTokens.RightSquareBracket)
                    token = StringHelper.TrimEnd(token,DefaultTokens.RightSquareBracket);
                return token
            },
            IsOperator: function(c)
            {
                return c === DefaultTokens.LessThanSign || c === DefaultTokens.GreaterThanSign || c === DefaultTokens.EqualsThanSign
            },
            TrimEscape: function(token)
            {
                var len = token.length;
                var inEscape = false;
                var sb = new StringBuilder;
                for(var n = 0; n < len; n++)
                {
                    var c = token.charAt(n);
                    if(c === DefaultTokens.ReverseSolidusSign)
                    {
                        inEscape = !inEscape;
                        if(!inEscape)
                            sb.Append(c)
                    }
                    else
                    {
                        inEscape = false;
                        sb.Append(c)
                    }
                }
                return sb.toString()
            },
            AddSquareBracket: function(token)
            {
                if(!token)
                    throw new Error("token");
                if(token.length === 0 || token[0] !== DefaultTokens.LeftSquareBracket)
                    token = StringHelper.Insert(token,0,DefaultTokens.LeftSquareBracket.toString());
                if(token.length === 0 || token[token.length - 1] !== DefaultTokens.RightSquareBracket)
                    token = StringHelper.Insert(token,token.length,DefaultTokens.RightSquareBracket.toString());
                return token
            },
            IsEquals: function(a, b, isIgnoreCase)
            {
                if(!a && !b)
                    return true;
                else if(!a || !b)
                    return false;
                else if(isIgnoreCase)
                    return a.toLowerCase() === b.toLowerCase();
                else
                    return a === b
            },
            ReplaceKeyword: function(s, oldString, newString)
            {
                if(!s || s === string.Empty || this.IsEquals(oldString,newString,true))
                    return s;
                var strTemp = s;
                var start = 0;
                while(true)
                {
                    var index = StringHelper.IndexOf(strTemp,oldString,StringComparison.CurrentCultureIgnoreCase);
                    if(index > -1)
                    {
                        strTemp = StringHelper.Remove(strTemp,index,oldString.length);
                        strTemp = StringHelper.Insert(strTemp,index,newString);
                        start = index + newString.length
                    }
                    else
                        break
                }
                return strTemp
            },
            IsDecimal: function(s, numberFormatInfo)
            {
                var decimalSeparator = DefaultTokens.DecimalSeparator;
                if(numberFormatInfo)
                    decimalSeparator = numberFormatInfo.NumberDecimalSeparator;
                return s.indexOf(decimalSeparator) > -1
            }
        };
    var GeneralCompareType = {
            EqualsTo: 0,
            NotEqualsTo: 1,
            GreaterThan: 2,
            GreaterThanOrEqualsTo: 3,
            LessThan: 4,
            LessThanOrEqualsTo: 5
        };
    var FormatConverter = {
            IsNumber: function(value)
            {
                return isType(value,"number") || isType(value,"DateTime") || isType(value,"TimeSpan") || value && !isType(value,"boolean") && !isNaN(value)
            },
            ToDouble: function(value)
            {
                if(!value)
                    return 0.0;
                else if(isType(value,"number"))
                    return value;
                else if(isType(value,"string") && !isNaN(value))
                    return GrapeCity.UI._NumberHelper.parseLocale(value);
                else if(isType(value,"DateTime"))
                    return new GrapeCity.UI._DateTimeHelper(value).toOADate();
                else if(isType(value,"TimeSpan"))
                    return new GrapeCity.UI._DateTimeHelper(value).TotalDays();
                else
                    return parseFloat(value)
            }
        };
    GrapeCity.UI.FormatConverter = FormatConverter;
    function NumberFormatBase(partLocaleID, dbNumberFormatPart)
    {
        this._classNames = ["NumberFormatBase","IFormatter","IFormatProviderSupport"];
        this._initFileds();
        this.partLocaleID = partLocaleID;
        this.partDbNumberFormat = dbNumberFormatPart
    }
    NumberFormatBase.prototype = {
        _initFileds: function()
        {
            this.numberStringConverter = null;
            this.numberFormatInfo = null;
            this.dateTimeFormatInfo = null;
            this.partLocaleID = null;
            this.partDbNumberFormat = null
        },
        NumberStringConverter: function(value)
        {
            if(arguments.length === 0)
            {
                if(this.numberStringConverter)
                    return this.numberStringConverter;
                return null
            }
            else
                this.numberStringConverter = value
        },
        PartLocaleID: function()
        {
            return asType(this.partLocaleID,"LocaleIDFormatPart")
        },
        PartDBNumberFormat: function()
        {
            return asType(this.partDbNumberFormat,"DBNumberFormatPart")
        },
        DateTimeFormatInfo: function(value)
        {
            if(arguments.length === 0)
                return this.dateTimeFormatInfo;
            else
                this.dateTimeFormatInfo = value
        },
        NumberFormatInfo: function(value)
        {
            if(arguments.length === 0)
                return this.numberFormatInfo;
            else
                this.numberFormatInfo = value
        },
        NumberGroupSeparator: function()
        {
            if(this.NumberFormatInfo())
                return this.NumberFormatInfo().NumberGroupSeparator();
            return DefaultTokens.NumberGroupSeparator
        },
        PercentSymbol: function()
        {
            if(this.NumberFormatInfo())
                return this.NumberFormatInfo().PercentSymbol();
            return DefaultTokens.PercentSymbol
        },
        PositiveSign: function()
        {
            if(this.NumberFormatInfo())
                return this.NumberFormatInfo().PositiveSign();
            return DefaultTokens.PositiveSign
        },
        NegativeSign: function()
        {
            if(this.NumberFormatInfo())
                return this.NumberFormatInfo().NegativeSign();
            return DefaultTokens.NegativeSign
        },
        DecimalSeparator: function()
        {
            if(this.NumberFormatInfo())
                return this.NumberFormatInfo().NumberDecimalSeparator();
            return DefaultTokens.DecimalSeparator
        },
        NaNSymbol: function()
        {
            if(this.NumberFormatInfo())
                return this.NumberFormatInfo().NaNSymbol;
            return DefaultTokens.NaNSymbol
        }
    };
    NumberFormatBase.General = "General";
    NumberFormatBase.TrimNotSupportSymbol = function(format, isSupportFraction)
    {
        if(!isSupportFraction)
            isSupportFraction = true;
        var inComments = false;
        var sb = new StringBuilder;
        for(var n = 0; n < format.length; n++)
        {
            var c = format[n];
            var append = true;
            if(c === '\"')
                inComments = !inComments;
            else if(!inComments)
            {
                if(!isSupportFraction)
                {
                    if(c === '?')
                        if(!NumberFormatBase.IsTransform(format,n))
                            append = false;
                    if(c === '/')
                        if(!NumberFormatBase.IsTransform(format,n))
                            append = false
                }
                if(c === '_')
                {
                    if(!NumberFormatBase.IsTransform(format,n))
                    {
                        append = false;
                        n++
                    }
                }
                else if(c === '*')
                    if(!NumberFormatBase.IsTransform(format,n))
                        append = false
            }
            if(append)
                sb.Append(c)
        }
        return sb.toString()
    };
    NumberFormatBase.IsTransform = function(str, currentpos)
    {
        if(str[currentpos] === '\\')
            throw new Error("the '\\' can't be evaluated");
        if(currentpos - 1 > 0 && currentpos - 1 < str.length)
            if(str[currentpos - 1] === '\\')
                if(currentpos - 2 < 0)
                    return true;
                else if(currentpos - 2 > 0 && currentpos - 2 < str.length)
                    return str[currentpos - 2] !== '\\';
        return false
    };
    NumberFormatBase.ContainsKeywords = function(format, keywords)
    {
        if(!format || format === string.Empty)
            return false;
        var stringOnlyKeywords = new StringBuilder;
        var inComments = false;
        var last = null;
        for(var n = 0; n < format.length; n++)
        {
            var c = format[n];
            if(c === '\"')
                inComments = !inComments;
            else if(!inComments)
                if(c !== DefaultTokens.UnderLine && last !== DefaultTokens.UnderLine)
                    stringOnlyKeywords.Append(c);
            last = c
        }
        var formatTemp = stringOnlyKeywords.toString().toLowerCase();
        for(var index = 0; index < keywords.length; index++)
        {
            var keyword = keywords[index];
            if(StringHelper.Contains(formatTemp,keyword))
                return true
        }
        return false
    };
    function StandardDateTimeFormatter(format)
    {
        this._classNames = ["StandardDateTimeFormatter","IFormatter"];
        this._formatString = format
    }
    StandardDateTimeFormatter.prototype = {
        ShortDatePattern: "d",
        LongDatePattern: "D",
        FullDatePatternShortTime: "f",
        FullDatePatternLongTime: "F",
        GeneralDatePatternLongTimeShortTime: "g",
        GeneralDatePatternLongTimeLongTime: "G",
        MonthDayPattern1: "m",
        MonthDayPattern2: "M",
        RoundTripDatePattern1: "o",
        RoundTripDatePattern2: "O",
        RFC1123Pattern1: "r",
        RFC1123Pattern2: "R",
        SortableDatePattern: "s",
        ShortTimePattern: "t",
        LongTimePattern: "T",
        UniversalSortableDatePattern: "u",
        UniversalFullDatePattern: "U",
        YearMonthPattern1: "y",
        YearMonthPattern2: "Y",
        EvaluateFormat: function(format)
        {
            if(format === this.ShortDatePattern || format === this.LongDatePattern || format === this.FullDatePatternShortTime || format === this.FullDatePatternLongTime || format === this.GeneralDatePatternLongTimeShortTime || format === this.GeneralDatePatternLongTimeLongTime || format === this.MonthDayPattern1 || format === this.MonthDayPattern2 || format === this.RoundTripDatePattern1 || format === this.RoundTripDatePattern2 || format === this.RFC1123Pattern1 || format === this.RFC1123Pattern2 || format === this.SortableDatePattern || format === this.ShortTimePattern || format === this.LongTimePattern || format === this.UniversalSortableDatePattern || format === this.UniversalFullDatePattern || format === this.YearMonthPattern1 || format === this.YearMonthPattern2)
                return true;
            return false
        },
        Format: function(obj)
        {
            try
            {
                if(obj === undefined || obj === null || obj === "")
                    return"";
                return new GrapeCity.UI._DateTimeHelper(obj).localeFormat(this._formatString)
            }
            catch(err)
            {
                return obj.toString()
            }
        },
        Parse: function(str)
        {
            try
            {
                if(!str || str === "")
                    return null;
                return GrapeCity.UI._DateTimeHelper.parseLocale(str,this._formatString)
            }
            catch(err)
            {
                return new Date(str)
            }
        }
    };
    GrapeCity.UI._StandardDateTimeFormatter = StandardDateTimeFormatter;
    function StandardNumberFormatter(format)
    {
        this._classNames = ["StandardNumberFormatter","IFormatter"];
        this._formatString = format
    }
    StandardNumberFormatter.prototype = {
        CurrencyPattern1: "c",
        CurrencyPattern2: "C",
        DecimalPattern1: "d",
        DecimalPattern2: "D",
        ScientificPattern1: "e",
        ScientificPattern2: "E",
        FixedPointPattern1: "f",
        FixedPointPattern2: "F",
        GeneralPattern1: "g",
        GeneralPattern2: "G",
        NumberPattern1: "n",
        NumberPattern2: "N",
        PercentPattern1: "p",
        PercentPattern2: "P",
        RoundTripPattern1: "r",
        RoundTripPattern2: "R",
        HexadecimalPattern1: "x",
        HexadecimalPattern2: "X",
        EvaluateFormat: function(format)
        {
            if(format && format !== string.Empty && format.length > 0)
            {
                var key = format.substr(0,1);
                if(key === this.CurrencyPattern1 || key === this.CurrencyPattern2 || key === this.DecimalPattern1 || key === this.DecimalPattern2 || key === this.ScientificPattern1 || key === this.ScientificPattern2 || key === this.FixedPointPattern1 || key === this.FixedPointPattern2 || key === this.GeneralPattern1 || key === this.GeneralPattern2 || key === this.NumberPattern1 || key === this.NumberPattern2 || key === this.PercentPattern1 || key === this.PercentPattern2 || key === this.RoundTripPattern1 || key === this.RoundTripPattern2 || key === this.HexadecimalPattern1 || key === this.HexadecimalPattern2)
                    return true
            }
            return false
        },
        Format: function(obj)
        {
            try
            {
                if(!obj || obj === "")
                    return"";
                return new GrapeCity.UI._NumberHelper(obj).localeFormat(this._formatString)
            }
            catch(err)
            {
                return obj.toString()
            }
        },
        Parse: function(str)
        {
            try
            {
                if(!str || str === "")
                    return null;
                return GrapeCity.UI._NumberHelper.parseLocale(str)
            }
            catch(err)
            {
                var result = parseFloat(str);
                if(isNaN(result) || !isFinite(result))
                    return null;
                return result
            }
        }
    };
    GrapeCity.UI._StandardNumberFormatter = StandardNumberFormatter;
    function NumberFormatText(format, partLocaleID, dbNumberFormatPart)
    {
        this._classNames = ["NumberFormatText","IFormatter"];
        var formatTemp = NumberFormatBase.TrimNotSupportSymbol(format,false);
        if(partLocaleID)
            formatTemp = DefaultTokens.ReplaceKeyword(formatTemp,this.PartLocaleID().OriginalToken(),this.PartLocaleID().CurrencySymbol());
        formatTemp = DefaultTokens.Filter(formatTemp,DefaultTokens.LeftSquareBracket,DefaultTokens.RightSquareBracket);
        formatTemp = DefaultTokens.TrimEscape(formatTemp);
        this._formatString = formatTemp
    }
    NumberFormatText.prototype = new NumberFormatBase;
    NumberFormatText.prototype.Format = function(obj)
    {
        try
        {
            var result = GrapeCity.Calc.Convert.toString(obj);
            var formatStringTemp = StringHelper.Replace(this._formatString,"\"","");
            if(formatStringTemp)
                result = StringHelper.Replace(formatStringTemp,"@",result);
            return result
        }
        catch(err)
        {
            return""
        }
    };
    NumberFormatText.prototype.Parse = function(str)
    {
        if(!str)
            return"";
        return str
    };
    NumberFormatText.prototype.FormatString = function()
    {
        return this._formatString
    };
    NumberFormatText.EvaluateFormat = function(format)
    {
        return true
    };
    function DefaultDateTimeNumberStringConverter(){}
    GrapeCity.UI.AutoFormatter = function(innerFormatter)
    {
        this._innerFormatter = innerFormatter
    };
    GrapeCity.UI.AutoFormatter.prototype = {
        formatString: function()
        {
            return this._innerFormatter ? this._innerFormatter.formatString() : ''
        },
        innerFormatter: function(formater)
        {
            if(arguments.length === 0)
                return this._innerFormatter;
            this._innerFormatter = formater;
            return this
        },
        Parse: function(text)
        {
            return this._innerFormatter ? this._innerFormatter.Parse(text) : text
        },
        Format: function(obj)
        {
            return this._innerFormatter ? this._innerFormatter.Format(obj) : obj === undefined || obj === null ? "" : obj.toString()
        },
        toJSON: function()
        {
            return null
        }
    };
    function GeneralFormatter(format, formatMode)
    {
        this._classNames = ["GeneralFormatter","IFormatter","INotifyPropertyChanged","IColorFormatter"];
        this._initFileds();
        if(string.IsNullOrEmpty(format))
            format = NumberFormatBase.General;
        if(!formatMode)
            formatMode = FormatMode.CustomMode;
        this.formatCached = format;
        this.formatModeType = formatMode;
        this.isDefault = this.formatCached.toLowerCase() === NumberFormatBase.General.toLowerCase();
        this.isConstructed = false
    }
    var GeneralFormatterHelper = {
            defaultNumberFormatter: null,
            defaultGeneralFormatter: null,
            defaultShortDatePatternFormatter: null,
            defaultLongTimePatternFormatter: null,
            defaultSXDatetimePatternFormatter: null,
            defaultDMMMFormatter: null,
            defaultMMMYYFormatter: null,
            defaultHMMFormatter: null,
            defaultHMMSSFormatter: null,
            defaultHMMSS0Formatter: null,
            defaultShortDatePatternHMMFormatter: null,
            defaultShortDatePatternHMMSSFormatter: null,
            defaultShortDatePatternHMMSS0Formatter: null,
            defaultComboNumberFormatter1: null,
            defaultComboNumberFormatter2: null,
            defaultStandardNumberFormatter: null,
            defaultStandardPercentFormatter1: null,
            defaultStandardPercentFormatter2: null,
            defaultStandardGroupNumberFormatter1: null,
            defaultStandardGroupNumberFormatter2: null,
            DefaultNumberFormatter: function()
            {
                if(!this.defaultNumberFormatter)
                    this.defaultNumberFormatter = new GeneralFormatter("###################0,################");
                return this.defaultNumberFormatter
            },
            DefaultGeneralFormatter: function()
            {
                if(!this.defaultGeneralFormatter)
                    this.defaultGeneralFormatter = new GeneralFormatter;
                return this.defaultGeneralFormatter
            },
            DefaultShortDatePatternFormatter: function()
            {
                if(!this.defaultShortDatePatternFormatter)
                    this.defaultShortDatePatternFormatter = new GeneralFormatter(DefaultTokens.DateTimeFormatInfo.ShortDatePattern);
                return this.defaultShortDatePatternFormatter
            },
            DefaultSXDatetimePatternFormatter: function()
            {
                if(!this.defaultSXDatetimePatternFormatter)
                    this.defaultSXDatetimePatternFormatter = new GeneralFormatter("d/MM/yyyy h:mm:ss tt");
                return this.defaultSXDatetimePatternFormatter
            },
            DefaultLongTimePatternFormatter: function()
            {
                if(!this.defaultLongTimePatternFormatter)
                    this.defaultLongTimePatternFormatter = new GeneralFormatter(DefaultTokens.DateTimeFormatInfo.LongTimePattern);
                return this.defaultLongTimePatternFormatter
            },
            DefaultDMMMFormatter: function()
            {
                if(!this.defaultDMMMFormatter)
                    this.defaultDMMMFormatter = new GeneralFormatter("d-MMM");
                return this.defaultDMMMFormatter
            },
            DefaultMMMYYFormatter: function()
            {
                if(!this.defaultMMMYYFormatter)
                    this.defaultMMMYYFormatter = new GeneralFormatter("MMM-yy");
                return this.defaultMMMYYFormatter
            },
            DefaultHMMFormatter: function()
            {
                if(!this.defaultHMMFormatter)
                    this.defaultHMMFormatter = new GeneralFormatter("h:mm");
                return this.defaultHMMFormatter
            },
            DefaultHMMSSFormatter: function()
            {
                if(!this.defaultHMMSSFormatter)
                    this.defaultHMMSSFormatter = new GeneralFormatter("h:mm:ss");
                return this.defaultHMMSSFormatter
            },
            DefaultHMMSS0Formatter: function()
            {
                if(!this.defaultHMMSS0Formatter)
                    this.defaultHMMSS0Formatter = new GeneralFormatter("h:mm:ss.0");
                return this.defaultHMMSS0Formatter
            },
            DefaultComboNumberFormatter1: function()
            {
                if(!this.defaultComboNumberFormatter1)
                    this.defaultComboNumberFormatter1 = new GeneralFormatter(string.Format("{0}#,##0.00;[Red]({0}#,##0.00)",DefaultTokens.NumberFormatInfo.CurrencySymbol));
                return this.defaultComboNumberFormatter1
            },
            DefaultComboNumberFormatter2: function()
            {
                if(!this.defaultComboNumberFormatter2)
                    this.defaultComboNumberFormatter2 = new GeneralFormatter(string.Format("{0}#,##0;[Red]({0}#,##0)",DefaultTokens.NumberFormatInfo.CurrencySymbol));
                return this.defaultComboNumberFormatter2
            },
            DefaultStandardNumberFormatter: function()
            {
                if(!this.defaultStandardNumberFormatter)
                    this.defaultStandardNumberFormatter = new GeneralFormatter("0.00E+00");
                return this.defaultStandardNumberFormatter
            },
            DefaultStandardPercentFormatter1: function()
            {
                if(!this.defaultStandardPercentFormatter1)
                    this.defaultStandardPercentFormatter1 = new GeneralFormatter("0.00%");
                return this.defaultStandardPercentFormatter1
            },
            DefaultStandardPercentFormatter2: function()
            {
                if(!this.defaultStandardPercentFormatter2)
                    this.defaultStandardPercentFormatter2 = new GeneralFormatter("0%");
                return this.defaultStandardPercentFormatter2
            },
            DefaultStandardGroupNumberFormatter1: function()
            {
                if(!this.defaultStandardGroupNumberFormatter1)
                    this.defaultStandardGroupNumberFormatter1 = new GeneralFormatter("#,##0.00");
                return this.defaultStandardGroupNumberFormatter1
            },
            DefaultStandardGroupNumberFormatter2: function()
            {
                if(!this.defaultStandardGroupNumberFormatter2)
                    this.defaultStandardGroupNumberFormatter2 = new GeneralFormatter("#,##0");
                return this.defaultStandardGroupNumberFormatter2
            }
        };
    function NumberFormatGeneral(format, partLocaleID, dbNumberFormatPart)
    {
        this.digitalFormat = null;
        this.exponentialDigitalFormat = null;
        this.fullFormatString = null;
        NumberFormatBase.call(this,partLocaleID,dbNumberFormatPart);
        this._classNames.push("NumberFormatGeneral");
        if(arguments.length > 0)
            if(NumberFormatGeneral.EvaluateFormat(format))
            {
                if(format.Contains(DefaultTokens.Zero) || format.Contains(DefaultTokens.NumberSign) || format.Contains(DefaultTokens.DecimalSeparator) || format.Contains(DefaultTokens.CommercialAt))
                    throw"format is illegal.";
                this.fullFormatString = format
            }
            else
                throw"format is illegal.";
        else
            this.fullFormatString = NumberFormatBase.General
    }
    function CustomNumberFormat(format)
    {
        this._classNames = ["CustomNumberFormat","IFormatter","IFormatProviderSupport"];
        this._initFileds();
        if(arguments.length === 0)
        {
            this.formatCached = NumberFormatBase.General;
            this.numberFormat = new NumberFormatGeneral
        }
        else
            this.Init(format)
    }
    function findDateTimeGeneralFormatter(s, v, formatter, foundCallback)
    {
        if(formatter && formatter.length > 0)
            for(var k in formatter)
                if(formatter.hasOwnProperty(k))
                {
                    var f = formatter[k];
                    var dt = GrapeCity.UI._DateTimeHelper.parseLocale(s,f);
                    if(dt && dt - v === 0)
                        return foundCallback.call()
                }
        return null
    }
    GeneralFormatter.prototype = {
        _initFileds: function()
        {
            this.formatters = null;
            this.formatModeType = FormatMode.CustomMode;
            this.dateTimeFormatInfo = null;
            this.numberFormatInfo = null;
            this.isSingleFormatterInfo = true;
            this.isDefault = true;
            this.formatCached = null;
            this.isConstructed = false;
            this.PropertyChanged = []
        },
        toJSON: function()
        {
            return{formatters: this.formatters}
        },
        HasFormatedColor: function()
        {
            if(this.isDefault)
                return false;
            if(this.PositiveExpression() && this.PositiveExpression().ColorFormatPart())
                return true;
            if(this.NegativeExpression() && this.NegativeExpression().ColorFormatPart())
                return true;
            if(this.ZeroExpression() && this.ZeroExpression().ColorFormatPart())
                return true;
            if(this.TextExpression() && this.TextExpression().ColorFormatPart())
                return true;
            return false
        },
        IsDefaultFormat: function()
        {
            return this.isDefault
        },
        FormatString: function(value)
        {
            if(arguments.length === 0)
            {
                this.Init();
                var formatStringBuilder = null;
                switch(this.FormatMode())
                {
                    case FormatMode.CustomMode:
                        if(this.formatters)
                            for(var index = 0; index < this.formatters.length; index++)
                            {
                                var formatter = this.formatters[index];
                                if(isType(formatter,"CustomNumberFormat"))
                                {
                                    if(!formatStringBuilder)
                                        formatStringBuilder = new StringBuilder;
                                    else
                                        formatStringBuilder.Append(DefaultTokens.FormatSeparator);
                                    var formatTemp = formatter.FormatString();
                                    formatStringBuilder.Append(formatTemp)
                                }
                            }
                        break;
                    case FormatMode.StandardDateTimeMode:
                        if(isType(this.formatters[0],"StandardDateTimeFormatter"))
                            return this.formatters[0].FormatString();
                        break;
                    case FormatMode.StandardNumericMode:
                        if(isType(this.formatters[0],"StandardNumberFormatter"))
                            return this.formatters[0].FormatString();
                        break
                }
                if(formatStringBuilder)
                    return formatStringBuilder.toString();
                else
                    return string.Empty
            }
            else
            {
                if(!value)
                    throw new Error("value");
                this.formatters = null;
                this.formatCached = value;
                this.isDefault = this.formatCached.toLowerCase() === NumberFormatBase.General.toLowerCase();
                this.isConstructed = false;
                this.Init();
                this.RaisePropertyChanged("FormatString")
            }
        },
        DateTimeFormatInfo: function(value)
        {
            if(arguments.length === 0)
            {
                this.Init();
                if(this.dateTimeFormatInfo)
                    return this.dateTimeFormatInfo;
                return DefaultTokens.DateTimeFormatInfo
            }
            else
            {
                this.Init();
                this.dateTimeFormatInfo = value;
                if(this.formatters)
                    for(var index = 0; index < this.formatters.length; index++)
                    {
                        var formatter = this.formatters[index];
                        if(isType(formatter,"IFormatProviderSupport"))
                            formatter.DateTimeFormatInfo(value)
                    }
                this.RaisePropertyChanged("DateTimeFormatInfo")
            }
        },
        NumberFormatInfo: function(value)
        {
            if(arguments.length === 0)
            {
                this.Init();
                if(this.numberFormatInfo)
                    return this.numberFormatInfo;
                return DefaultTokens.NumberFormatInfo
            }
            else
            {
                this.Init();
                this.numberFormatInfo = value;
                if(this.formatters)
                    for(var index = 0; index < this.formatters.length; index++)
                    {
                        var formatter = this.formatters[index];
                        if(isType(formatter,"IFormatProviderSupport"))
                            formatter.NumberFormatInfo(value)
                    }
                this.RaisePropertyChanged("NumberFormatInfo")
            }
        },
        FormatMode: function(value)
        {
            if(arguments.length === 0)
                return this.formatModeType;
            else
            {
                this.formatModeType = value;
                this.RaisePropertyChanged("FormatMode")
            }
        },
        ExcelCompatibleFormatString: function()
        {
            this.Init();
            var formatStringBuilder = null;
            switch(this.FormatMode())
            {
                case FormatMode.CustomMode:
                    if(this.formatters)
                        for(var index = 0; index < this.formatters.length; index++)
                        {
                            var formatter = this.formatters[index];
                            if(isType(formatter,"CustomNumberFormat"))
                            {
                                if(!formatStringBuilder)
                                    formatStringBuilder = new StringBuilder;
                                else
                                    formatStringBuilder.Append(DefaultTokens.FormatSeparator);
                                var formatTemp = formatter.ExcelCompatibleFormatString();
                                formatStringBuilder.Append(formatTemp)
                            }
                        }
                    break;
                case FormatMode.StandardDateTimeMode:
                    if(isType(this.formatters[0],"StandardDateTimeFormatter"))
                        return this.formatters[0].ExcelCompatibleFormatString();
                    break;
                case FormatMode.StandardNumericMode:
                    if(isType(this.formatters[0],"StandardNumberFormatter"))
                        return this.formatters[0].ExcelCompatibleFormatString();
                    break
            }
            if(formatStringBuilder)
                return formatStringBuilder.toString();
            else
                return string.Empty
        },
        PositiveExpression: function()
        {
            this.Init();
            if(this.formatters && this.formatters.length > 0)
                return asType(this.formatters[0],"CustomNumberFormat");
            return null
        },
        NegativeExpression: function()
        {
            this.Init();
            if(this.formatters && this.formatters.length > 1)
                return asType(this.formatters[1],"CustomNumberFormat");
            return null
        },
        ZeroExpression: function()
        {
            this.Init();
            if(this.formatters && this.formatters.length > 2)
                return asType(this.formatters[2],"CustomNumberFormat");
            return null
        },
        TextExpression: function()
        {
            this.Init();
            if(this.formatters && this.formatters.length > 3)
                return asType(this.formatters[3],"CustomNumberFormat");
            return null
        },
        GetFormatType: function(obj)
        {
            this.Init();
            var formatInfo = this.GetFormatInfo(obj);
            if(isType(formatInfo,"CustomNumberFormat"))
            {
                if(isType(formatInfo.Formatter(),"NumberFormatDigital"))
                    return NumberFormatType.Number;
                else if(isType(formatInfo.Formatter(),"NumberFormatDateTime"))
                    return NumberFormatType.DateTime;
                else if(isType(formatInfo.Formatter(),"NumberFormatText"))
                    return NumberFormatType.Text
            }
            else if(isType(formatInfo,"NumberFormatDigital") || isType(formatInfo,"StandardNumberFormatter"))
                return NumberFormatType.Number;
            else if(isType(formatInfo,"NumberFormatDateTime") || isType(formatInfo,"StandardDateTimeFormatter"))
                return NumberFormatType.DateTime;
            else if(isType(formatInfo,"NumberFormatText"))
                return NumberFormatType.Text;
            return NumberFormatType.General
        },
        GetPreferredEditingFormatter: function(obj)
        {
            this.Init();
            if(isType(obj,"DateTime"))
            {
                var dt = new GrapeCity.UI._DateTimeHelper(obj);
                if(dt.Hour() === 0 && dt.Minute() === 0 && dt.Second() === 0 && dt.Millisecond() === 0)
                    return GeneralFormatterHelper.DefaultShortDatePatternFormatter();
                else
                    return GeneralFormatterHelper.DefaultSXDatetimePatternFormatter()
            }
            else if(isType(obj,"TimeSpan"))
                return GeneralFormatterHelper.DefaultLongTimePatternFormatter();
            else if(FormatConverter.IsNumber(obj))
            {
                var value = FormatConverter.ToDouble(obj);
                if(value > 1E20)
                    return new GeneralFormatter("0.##E+00");
                else
                    return GeneralFormatterHelper.DefaultNumberFormatter()
            }
            else if(isType(obj,"string"))
                return GeneralFormatterHelper.DefaultGeneralFormatter();
            else
                return GeneralFormatterHelper.DefaultGeneralFormatter()
        },
        GetPreferredDisplayFormatter: function(s, valueRef)
        {
            if(!valueRef)
                valueRef = {};
            valueRef.value = null;
            this.Init();
            if(string.IsNullOrEmpty(s))
                return new GeneralFormatter;
            var strTemp = s;
            var v = valueRef.value = this.Parse(strTemp);
            if(isType(v,"DateTime") || isType(v,"TimeSpan"))
            {
                var result;
                if(result = findDateTimeGeneralFormatter(s,v,NumberFormatGeneral.GeneralMonthDay(),function()
                {
                    return GeneralFormatterHelper.DefaultDMMMFormatter()
                }))
                    return result;
                if(result = findDateTimeGeneralFormatter(s,v,NumberFormatGeneral.GeneralYearMonth(),function()
                {
                    return GeneralFormatterHelper.DefaultMMMYYFormatter()
                }))
                    return result;
                if(result = findDateTimeGeneralFormatter(s,v,NumberFormatGeneral.GeneralYearMonthDay(),function()
                {
                    return GeneralFormatterHelper.DefaultShortDatePatternFormatter()
                }))
                    return result;
                if(result = findDateTimeGeneralFormatter(s,v,NumberFormatGeneral.GeneralHourMinute(),function()
                {
                    return GeneralFormatterHelper.DefaultHMMFormatter()
                }))
                    return result;
                if(result = findDateTimeGeneralFormatter(s,v,NumberFormatGeneral.GeneralHourMinuteSecond(),function()
                {
                    return GeneralFormatterHelper.DefaultHMMSSFormatter()
                }))
                    return result;
                if(result = findDateTimeGeneralFormatter(s,v,NumberFormatGeneral.GeneralHourMinuteSecondSubSecond(),function()
                {
                    return GeneralFormatterHelper.DefaultHMMSS0Formatter()
                }))
                    return result;
                var self = this;
                if(result = findDateTimeGeneralFormatter(s,v,NumberFormatGeneral.GeneralHourMinuteWithDate(),function()
                {
                    if(!GeneralFormatterHelper.defaultShortDatePatternHMMFormatter)
                        GeneralFormatterHelper.defaultShortDatePatternHMMFormatter = new GeneralFormatter(self.DateTimeFormatInfo().ShortDatePattern + " " + "h:mm");
                    return GeneralFormatterHelper.defaultShortDatePatternHMMFormatter
                }))
                    return result;
                if(result = findDateTimeGeneralFormatter(s,v,NumberFormatGeneral.GeneralHourMinuteSecondWithDate(),function()
                {
                    if(!GeneralFormatterHelper.defaultShortDatePatternHMMSSFormatter)
                        GeneralFormatterHelper.defaultShortDatePatternHMMSSFormatter = new GeneralFormatter(self.DateTimeFormatInfo().ShortDatePattern + " " + "h:mm:ss");
                    return GeneralFormatterHelper.defaultShortDatePatternHMMSSFormatter
                }))
                    return result;
                if(result = findDateTimeGeneralFormatter(s,v,NumberFormatGeneral.GeneralHourMinuteSecondSubSecondWithDate(),function()
                {
                    if(!GeneralFormatterHelper.defaultShortDatePatternHMMSS0Formatter)
                        GeneralFormatterHelper.defaultShortDatePatternHMMSS0Formatter = new GeneralFormatter(self.DateTimeFormatInfo().ShortDatePattern + " " + "h:mm:ss.0");
                    return GeneralFormatterHelper.defaultShortDatePatternHMMSS0Formatter
                }))
                    return result
            }
            else if(FormatConverter.IsNumber(v))
                if(strTemp[0] === DefaultTokens.NumberFormatInfo.CurrencySymbol[0])
                    if(StringHelper.Contains(strTemp,DefaultTokens.DecimalSeparator))
                        return GeneralFormatterHelper.DefaultComboNumberFormatter1();
                    else
                        return GeneralFormatterHelper.DefaultComboNumberFormatter2();
                else if(StringHelper.IndexOf(strTemp,"e",StringComparison.CurrentCultureIgnoreCase) > -1)
                    return GeneralFormatterHelper.DefaultStandardNumberFormatter();
                else if(strTemp[0].toString() === DefaultTokens.PercentSymbol || strTemp[strTemp.length - 1].toString() === DefaultTokens.PercentSymbol)
                    if(StringHelper.Contains(strTemp,DefaultTokens.DecimalSeparator))
                        return GeneralFormatterHelper.DefaultStandardPercentFormatter1();
                    else
                        return GeneralFormatterHelper.DefaultStandardPercentFormatter2();
                else if(StringHelper.Contains(strTemp,DefaultTokens.NumberGroupSeparator))
                    if(StringHelper.Contains(strTemp,DefaultTokens.DecimalSeparator))
                        return GeneralFormatterHelper.DefaultStandardGroupNumberFormatter1();
                    else
                        return GeneralFormatterHelper.DefaultStandardGroupNumberFormatter2();
            return GeneralFormatterHelper.DefaultGeneralFormatter()
        },
        Format: function(obj, conditionalForeColor)
        {
            if(conditionalForeColor)
                conditionalForeColor.value = null;
            this.Init();
            var formatInfo = this.GetFormatInfo(obj);
            if(isType(formatInfo,"CustomNumberFormat") && formatInfo.ColorFormatPart())
                if(conditionalForeColor)
                    conditionalForeColor.value = formatInfo.ColorFormatPart().ForeColor();
            var value = 0;
            var isNumber = FormatConverter.IsNumber(obj);
            if(isNumber)
                value = FormatConverter.ToDouble(obj);
            if(formatInfo)
            {
                var result = null;
                if(isNumber && formatInfo === this.NegativeExpression())
                {
                    result = formatInfo.Format(Math.abs(value));
                    var cf = asType(formatInfo,"CustomNumberFormat");
                    if(cf && cf.ConditionFormatPart() && cf.ConditionFormatPart().value > 0 && value < 0)
                        result = DefaultTokens.NegativeSign + result
                }
                else
                    try
                    {
                        result = formatInfo.Format(obj)
                    }
                    catch(Exception)
                    {
                        if(isType(obj,"string"))
                            result = obj.toString()
                    }
                if(result)
                    return result;
                else
                    return string.Empty
            }
            else if(isNumber && value < 0)
                return DefaultTokens.HyphenMinus.toString();
            else if(isType(obj,"string"))
                return obj.toString();
            else
                return obj === undefined || obj === null ? string.Empty : obj.toString()
        },
        Parse: function(str)
        {
            this.Init();
            if(this.formatters && this.formatters.length > 0)
                return this.formatters[0].Parse(str);
            return null
        },
        Init: function()
        {
            if(!this.isConstructed)
            {
                this.isConstructed = true;
                switch(this.formatModeType)
                {
                    case FormatMode.CustomMode:
                        this.InitExcelCompatibleMode(this.formatCached);
                        break;
                    case FormatMode.StandardDateTimeMode:
                        this.InitStandardDateTimeMode(this.formatCached);
                        break;
                    case FormatMode.StandardNumericMode:
                        this.InitStandardNumericMode(this.formatCached);
                        break
                }
            }
        },
        InitStandardDateTimeMode: function(format)
        {
            var formatter = new StandardDateTimeFormatter(format);
            if(formatter.EvaluateFormat(format))
            {
                this.formatters = [];
                this.formatters.push(formatter)
            }
            else
                throw new Error("format is illegal.");
        },
        InitStandardNumericMode: function(format)
        {
            var formatter = new StandardNumberFormatter(format);
            if(formatter.EvaluateFormat(format))
            {
                this.formatters = [];
                this.formatters.push(formatter)
            }
            else
                throw new Error("format is illegal.");
        },
        InitExcelCompatibleMode: function(format)
        {
            if(string.IsNullOrEmpty(format))
                throw new Error("format is illegal.");
            this.formatters = [];
            if(this.isDefault)
                this.formatters.push(new CustomNumberFormat);
            else
            {
                this.isSingleFormatterInfo = !StringHelper.Contains(format,DefaultTokens.FormatSeparator.toString());
                var items = format.split(DefaultTokens.FormatSeparator);
                if(!items)
                    throw new Error("format is illegal.");
                if(items.length < 1 || items.length > 5)
                    throw new Error("format is illegal.");
                var count = 0;
                for(var index = 0; index < items.length; index++)
                {
                    count++;
                    if(count > 4)
                        break;
                    var formatItem = new CustomNumberFormat(items[index]);
                    if(formatItem)
                        this.formatters.push(formatItem)
                }
                if(!this.PositiveExpression())
                    throw new Error("format is illegal.");
            }
        },
        GetFormatInfo: function(obj)
        {
            if(this.FormatMode() === FormatMode.CustomMode)
            {
                if(typeof obj === "string" && isNaN(obj))
                    if(this.TextExpression())
                        return this.TextExpression();
                    else
                        return this.PositiveExpression();
                else if(FormatConverter.IsNumber(obj) || isType(obj,"boolean"))
                {
                    var positive = this.PositiveExpression();
                    var negative = this.NegativeExpression();
                    var value = FormatConverter.ToDouble(obj);
                    var positiveHasCondition = positive && positive.ConditionFormatPart();
                    var negativeHasCondition = negative && negative.ConditionFormatPart();
                    var resultFormatter = this.isSingleFormatterInfo ? this.PositiveExpression() : null;
                    if(this.PositiveExpression())
                        if(positiveHasCondition)
                        {
                            if(positive.ConditionFormatPart().IsMeetCondition(value))
                                resultFormatter = positive
                        }
                        else if(value > 0 || value === 0 && !this.ZeroExpression())
                            resultFormatter = positive;
                    if(!resultFormatter && this.NegativeExpression())
                        if(negativeHasCondition)
                        {
                            if(negative.ConditionFormatPart().IsMeetCondition(value))
                                resultFormatter = negative
                        }
                        else if(value < 0)
                            resultFormatter = negative;
                    if(!resultFormatter && this.ZeroExpression())
                        if(value === 0)
                            resultFormatter = this.ZeroExpression();
                    if(!resultFormatter && this.ZeroExpression())
                        resultFormatter = this.ZeroExpression();
                    if(!resultFormatter && this.NegativeExpression())
                        resultFormatter = this.NegativeExpression();
                    return resultFormatter
                }
            }
            else if(this.FormatMode() === FormatMode.StandardDateTimeMode || this.FormatMode() === FormatMode.StandardNumericMode)
                if(this.formatters && this.formatters.length === 1)
                    return this.formatters[0];
            return null
        },
        RaisePropertyChanged: function(propertyName)
        {
            if(this.PropertyChanged)
                for(var index = 0; index < this.PropertyChanged.length; index++)
                {
                    var method = this.PropertyChanged[index];
                    if(typeof method === "function")
                        method(this,propertyName)
                }
        }
    };
    GrapeCity.UI.GeneralFormatter = GeneralFormatter;
    function FormatPartBase(token)
    {
        this._classNames = ["FormatPartBase"];
        this._initFileds();
        this.originalToken = token
    }
    FormatPartBase.prototype = {
        _initFileds: function()
        {
            this.originalToken = null
        },
        OriginalToken: function()
        {
            return this.originalToken
        }
    };
    FormatPartBase.SupportedPartFormat = function()
    {
        if(!FormatPartBase._supportedPartFormat)
            FormatPartBase._supportedPartFormat = ["ConditionFormatPart","ColorFormatPart","LocaleIDFormatPart"];
        return FormatPartBase._supportedPartFormat
    };
    function NumberFormatDateTime(format, absTimeParts, partLocaleID, dbNumberFormatPart)
    {
        NumberFormatBase.call(this,partLocaleID,dbNumberFormatPart);
        this._classNames.push("NumberFormatDateTime");
        this._initFileds();
        this.ExactlyMatch = false;
        this.formatString = this.FixFormat(NumberFormatBase.TrimNotSupportSymbol(format));
        this.absTimeParts = absTimeParts;
        this._init(format,absTimeParts,partLocaleID,dbNumberFormatPart)
    }
    var NumberFormatDateTimeHelper = {
            defaultNumberStringConverter: null,
            YearTwoDigit: "yy",
            YearSingleDigit: "y",
            YearFourDigit: "yyyy",
            MonthSingleDigit: "m",
            MonthTwoDigit: "mm",
            MonthAbbreviation: "mmm",
            MonthUnabbreviated: "mmmm",
            MonthJD: "mmmmm",
            DaySingleDigit: "d",
            DayTwoDigit: "dd",
            DayWeekDayAbbreviation: "aaa",
            DayWeekDayUnabbreviated: "aaaa",
            HoursSingleDigit: "h",
            HoursTwoDigit: "hh",
            MinuteSingleDigit: "m",
            MinuteTwoDigit: "mm",
            SecondSingleDigit: "s",
            SecondTwoDigit: "ss",
            SubSecondSingleDigit: ".0",
            SubSecondTwoDigit: ".00",
            SubSecondThreeDigit: ".000",
            EraYear: "e",
            AMPMTwoDigit: "AM/PM",
            AMPMSingleDigit: "A/P",
            StandardYearSingleDigit: "%y",
            StandardMonthSingleDigit: "%M",
            StandardMonthTwoDigit: "MM",
            StandardMonthAbbreviation: "MMM",
            StandardMonthUnabbreviated: "MMMM",
            StandardAMPMSingleDigit: "tt",
            StandardMinuteSingleDigit: "%m",
            StandardHourSingleDigit: "H",
            StandardHourTwoDigit: "HH",
            StandardSecondSingleDigit: "%s",
            StandardSubSecondSingleDigit: ".f",
            StandardSubSecondTwoDigit: ".ff",
            StandardSubSecondThreeDigit: ".fff",
            StandardDayWeekDayAbbreviation: "ddd",
            StandardDayWeekDayUnabbreviated: "dddd",
            PlaceholderMonthJD: DefaultTokens.ReplacePlaceholder + "mmmmm",
            defaultAbsoluteTime: new Date(1899,11,30,0,0,0,0),
            EvaluateFormat: function(format)
            {
                return NumberFormatBase.ContainsKeywords(format,NumberFormatDateTime.keywords)
            }
        };
    function NumberFormatDigital(format, partLocaleID, dbNumberFormatPart)
    {
        this.numberFormatString = null;
        this.fullFormatString = null;
        this.isGeneralNumber = false;
        this.fractionIntegerFormat = null;
        this.fractionNumeratorFormat = null;
        this.fractionDenominatorFormat = null;
        this.excelFormatString = string.Empty;
        NumberFormatBase.call(this,partLocaleID,dbNumberFormatPart);
        this._classNames.push("NumberFormatDigital");
        var formatTemp = NumberFormatBase.TrimNotSupportSymbol(format);
        this.fullFormatString = DefaultTokens.Filter(format,DefaultTokens.LeftSquareBracket,DefaultTokens.RightSquareBracket);
        this.excelFormatString = formatTemp;
        if(partLocaleID)
        {
            var oldFormat = formatTemp;
            formatTemp = DefaultTokens.ReplaceKeyword(oldFormat,this.PartLocaleID().OriginalToken(),this.PartLocaleID().CurrencySymbol())
        }
        if(this.PartDBNumberFormat())
            this.excelFormatString = DefaultTokens.ReplaceKeyword(this.excelFormatString,this.PartDBNumberFormat().OriginalToken(),this.PartDBNumberFormat().toString());
        formatTemp = DefaultTokens.Filter(formatTemp,DefaultTokens.LeftSquareBracket,DefaultTokens.RightSquareBracket);
        var solidusIndex = formatTemp.indexOf(DefaultTokens.SolidusSign);
        if(solidusIndex > -1)
        {
            formatTemp = StringHelper.Replace(formatTemp,"\\" + DefaultTokens.QuestionMark,DefaultTokens.Zero);
            var sp = formatTemp.split(DefaultTokens.SolidusSign);
            if(sp && sp.length === 2)
            {
                this.fractionDenominatorFormat = sp[1];
                var left = sp[0];
                if(left)
                {
                    var kjIndex = left.lastIndexOf(DefaultTokens.Space);
                    if(kjIndex > -1)
                    {
                        this.fractionIntegerFormat = left.substr(0,kjIndex);
                        this.fractionNumeratorFormat = left.substr(kjIndex + 1,left.length - kjIndex - 1)
                    }
                    else
                        this.fractionNumeratorFormat = left
                }
            }
        }
        this.numberFormatString = formatTemp
    }
    CustomNumberFormat.prototype = {
        _initFileds: function()
        {
            this.conditionFormatPart = null;
            this.colorFormatPart = null;
            this.localeIDFormatPart = null;
            this.dbNumberFormatPart = null;
            this.numberFormat = null;
            this.dateTimeFormatInfo = null;
            this.numberFormatInfo = null;
            this.formatCached = null
        },
        Init: function(format)
        {
            if(format === null || format === undefined)
                throw new Error("format is illegal.");
            this.conditionFormatPart = null;
            this.colorFormatPart = null;
            this.localeIDFormatPart = null;
            this.dbNumberFormatPart = null;
            this.formatCached = format;
            var contentToken = null;
            var token = null;
            var isInFormatPart = false;
            var absTimePart = [];
            for(var index = 0; index < format.length; index++)
            {
                var c = format[index];
                if(c === DefaultTokens.LeftSquareBracket)
                {
                    if(isInFormatPart)
                        throw new Error("format is illegal.");
                    else
                    {
                        if(token)
                        {
                            if(!contentToken)
                                contentToken = new StringBuilder;
                            contentToken.Append(token.toString());
                            token = null
                        }
                        token = new StringBuilder;
                        token.Append(c)
                    }
                    isInFormatPart = true
                }
                else if(c === DefaultTokens.RightSquareBracket)
                {
                    if(isInFormatPart)
                        if(token)
                        {
                            if(!token)
                                token = new StringBuilder;
                            token.Append(c);
                            var part = token.toString();
                            var partObject = asType(FormatPartBase.Create(part),"FormatPartBase");
                            if(partObject && !isType(partObject,"ABSTimeFormatPart"))
                                this.AddPart(partObject);
                            else if(isType(partObject,"ABSTimeFormatPart"))
                            {
                                absTimePart.push(asType(partObject,"ABSTimeFormatPart"));
                                if(!contentToken)
                                    contentToken = new StringBuilder;
                                contentToken.Append(token.toString())
                            }
                            else
                                throw new Error("format is illegal.");
                            token = null
                        }
                        else
                            throw new Error("format is illegal.");
                    else
                        throw new Error("format is illegal.");
                    isInFormatPart = false
                }
                else
                {
                    if(!token)
                        token = new StringBuilder;
                    token.Append(c)
                }
            }
            if(token)
                if(isInFormatPart)
                    throw new Error("format is illegal.");
                else
                {
                    if(!contentToken)
                        contentToken = new StringBuilder;
                    contentToken.Append(token.toString())
                }
            var content = contentToken ? contentToken.toString() : string.Empty;
            if(NumberFormatGeneral.EvaluateFormat(content))
                this.numberFormat = new NumberFormatGeneral(content,this.LocaleIDFormatPart(),this.dbNumberFormatPart);
            else if(NumberFormatDateTimeHelper.EvaluateFormat(content))
            {
                var absPartsArray = absTimePart.length > 0 ? absTimePart : null;
                this.numberFormat = new NumberFormatDateTime(content,absPartsArray,this.LocaleIDFormatPart(),this.dbNumberFormatPart)
            }
            else if(NumberFormatDigital.EvaluateFormat(content))
                this.numberFormat = new NumberFormatDigital(format,this.LocaleIDFormatPart(),this.dbNumberFormatPart);
            else if(NumberFormatText.EvaluateFormat(content))
                this.numberFormat = new NumberFormatText(format,this.LocaleIDFormatPart(),this.dbNumberFormatPart);
            else
                throw new Error("format is illegal.");
        },
        FormatString: function()
        {
            var formatBuilder = new StringBuilder;
            if(this.numberFormat && this.numberFormat.FormatString())
            {
                if(this.ColorFormatPart())
                    formatBuilder.Append(this.ColorFormatPart().toString());
                if(this.ConditionFormatPart())
                    formatBuilder.Append(this.ConditionFormatPart().toString());
                if(this.DBNumberFormatPart())
                    formatBuilder.Append(this.DBNumberFormatPart().toString());
                if(this.LocaleIDFormatPart())
                    formatBuilder.Append(this.LocaleIDFormatPart().toString());
                formatBuilder.Append(this.numberFormat.FormatString())
            }
            return formatBuilder.toString()
        },
        ConditionFormatPart: function()
        {
            return this.conditionFormatPart
        },
        ColorFormatPart: function()
        {
            return this.colorFormatPart
        },
        LocaleIDFormatPart: function()
        {
            return this.localeIDFormatPart
        },
        DBNumberFormatPart: function()
        {
            return this.dbNumberFormatPart
        },
        NumberStringConverter: function(value)
        {
            if(arguments.length === 0)
            {
                if(this.numberFormat)
                    return this.numberFormat.NumberStringConverter();
                return null
            }
            else if(this.numberFormat)
                this.numberFormat.NumberStringConverter(value)
        },
        ExcelCompatibleFormatString: function()
        {
            var formatBuilder = new StringBuilder;
            if(this.numberFormat && this.numberFormat.ExcelCompatibleFormatString())
            {
                if(!isType(this.numberFormat,"NumberFormatDigital"))
                {
                    if(this.DBNumberFormatPart())
                        formatBuilder.Append(this.DBNumberFormatPart().toString());
                    if(this.LocaleIDFormatPart())
                        formatBuilder.Append(this.LocaleIDFormatPart().toString());
                    if(this.ConditionFormatPart())
                        formatBuilder.Append(this.ConditionFormatPart().toString());
                    if(this.ColorFormatPart())
                        formatBuilder.Append(this.ColorFormatPart().toString())
                }
                formatBuilder.Append(this.numberFormat.ExcelCompatibleFormatString())
            }
            return formatBuilder.toString()
        },
        Formatter: function()
        {
            return this.numberFormat
        },
        DateTimeFormatInfo: function(value)
        {
            if(arguments.length === 0)
                return this.Formatter().DateTimeFormatInfo();
            else
            {
                this.Formatter().DateTimeFormatInfo(value);
                this.dateTimeFormatInfo = value
            }
        },
        NumberFormatInfo: function(value)
        {
            if(arguments.length === 0)
                return this.Formatter().NumberFormatInfo();
            else
            {
                this.Formatter().NumberFormatInfo(value);
                this.numberFormatInfo = value
            }
        },
        AddPart: function(part)
        {
            if(!part)
                throw new Error("part");
            if(isType(part,"ConditionFormatPart"))
                if(!this.conditionFormatPart)
                    this.conditionFormatPart = asType(part,"ConditionFormatPart");
                else
                    throw new Error("The type of descriptor was added.");
            else if(isType(part,"ColorFormatPart"))
                if(!this.colorFormatPart)
                    this.colorFormatPart = asType(part,"ColorFormatPart");
                else
                    throw new Error("The type of descriptor was added.");
            else if(isType(part,"LocaleIDFormatPart"))
                if(!this.localeIDFormatPart)
                    this.localeIDFormatPart = asType(part,"LocaleIDFormatPart");
                else
                    throw new Error("The type of descriptor was added.");
            else if(isType(part,"DBNumberFormatPart"))
                if(!this.dbNumberFormatPart)
                    this.dbNumberFormatPart = asType(part,"DBNumberFormatPart");
                else
                    throw new Error("The type of descriptor was added.");
        },
        Format: function(obj)
        {
            return this.numberFormat.Format(obj)
        },
        Parse: function(str)
        {
            return this.numberFormat.Parse(str)
        }
    };
    function ConditionFormatPart(token)
    {
        FormatPartBase.call(this,token);
        this._classNames.push("ConditionFormatPart");
        this._initFileds();
        var content = DefaultTokens.TrimSquareBracket(token);
        if(string.IsNullOrEmpty(content))
            throw new Error("token is illegal.");
        var tokenBuilder = null;
        var index = 0;
        var c = null;
        for(; index < content.length; index++)
        {
            c = content[index];
            if(DefaultTokens.IsOperator(c))
            {
                if(!tokenBuilder)
                    tokenBuilder = new StringBuilder;
                tokenBuilder.Append(c)
            }
            else
                break
        }
        if(!tokenBuilder)
            throw new Error("token is illegal.");
        var strCompareOperator = tokenBuilder.toString();
        tokenBuilder = null;
        switch(strCompareOperator)
        {
            case"<":
                this.compareOperator = GeneralCompareType.LessThan;
                break;
            case"<=":
                this.compareOperator = GeneralCompareType.LessThanOrEqualsTo;
                break;
            case"=":
                this.compareOperator = GeneralCompareType.EqualsTo;
                break;
            case">=":
                this.compareOperator = GeneralCompareType.GreaterThanOrEqualsTo;
                break;
            case">":
                this.compareOperator = GeneralCompareType.GreaterThan;
                break;
            case"<>":
                this.compareOperator = GeneralCompareType.NotEqualsTo;
                break;
            default:
                throw new Error("token is illegal.");
        }
        for(; index < content.length; index++)
        {
            c = content[index];
            if(DefaultTokens.IsOperator(c))
                throw new Error("token is illegal.");
            if(!tokenBuilder)
                tokenBuilder = new StringBuilder;
            tokenBuilder.Append(c)
        }
        if(!tokenBuilder)
            throw new Error("token is illegal.");
        var strValueNumber = tokenBuilder.toString();
        var tempValue = parseFloat(strValueNumber);
        if(!isNaN(tempValue))
            this.value = tempValue;
        else
            throw new Error("token is illegal.");
    }
    ConditionFormatPart.prototype = new FormatPartBase;
    ConditionFormatPart.prototype._initFileds = function()
    {
        this.value = 0.0;
        this.compareOperator = null
    };
    ConditionFormatPart.prototype.CompareOperator = function()
    {
        return this.compareOperator
    };
    ConditionFormatPart.prototype.Value = function()
    {
        return this.value
    };
    ConditionFormatPart.prototype.toString = function()
    {
        var sb = new StringBuilder;
        switch(this.compareOperator)
        {
            case GeneralCompareType.EqualsTo:
                sb.Append("=");
                break;
            case GeneralCompareType.GreaterThan:
                sb.Append(">");
                break;
            case GeneralCompareType.GreaterThanOrEqualsTo:
                sb.Append(">=");
                break;
            case GeneralCompareType.LessThan:
                sb.Append("<");
                break;
            case GeneralCompareType.LessThanOrEqualsTo:
                sb.Append("<=");
                break;
            case GeneralCompareType.NotEqualsTo:
                sb.Append("<>");
                break;
            default:
                throw new Error;
        }
        sb.Append(this.value);
        var result = DefaultTokens.AddSquareBracket(sb.toString());
        return result
    };
    ConditionFormatPart.prototype.IsMeetCondition = function(value)
    {
        switch(this.compareOperator)
        {
            case GeneralCompareType.EqualsTo:
                return value === this.value;
            case GeneralCompareType.GreaterThan:
                return value > this.value;
            case GeneralCompareType.GreaterThanOrEqualsTo:
                return value >= this.value;
            case GeneralCompareType.LessThan:
                return value < this.value;
            case GeneralCompareType.LessThanOrEqualsTo:
                return value <= this.value;
            case GeneralCompareType.NotEqualsTo:
                return value !== this.value
        }
        return false
    };
    ConditionFormatPart.EvaluateFormat = function(token)
    {
        if(!token || token === string.Empty)
            return false;
        var content = DefaultTokens.TrimSquareBracket(token);
        if(!content || content === string.Empty)
            return false;
        return DefaultTokens.IsOperator(content[0])
    };
    function ColorFormatPart(token)
    {
        this.foreColor = "black";
        this.index = -1;
        this.colorName = null;
        FormatPartBase.call(this,token);
        this._classNames.push("ColorFormatPart");
        var content = DefaultTokens.TrimSquareBracket(token);
        if(!content || content === string.Empty)
            throw new Error("token is illegal.");
        try
        {
            this.foreColor = content;
            this.colorName = content;
            return
        }
        catch(ex){}
        if(content.length > "Color".length)
        {
            content = StringHelper.Remove(content,0,"Color".length);
            var index = -1;
            var tempIndex = parseInt(content,10);
            if(!isNaN(tempIndex))
            {
                index = tempIndex;
                if(index >= 1 && index <= 56)
                    return
            }
        }
        throw new Error("token is illegal.");
    }
    ColorFormatPart.prototype = new FormatPartBase;
    ColorFormatPart.prototype.ForeColor = function()
    {
        return this.foreColor
    };
    ColorFormatPart.prototype.toString = function()
    {
        if(this.index > -1)
            return DefaultTokens.AddSquareBracket("Color" + this.index);
        else if(this.colorName)
            return DefaultTokens.AddSquareBracket(this.colorName);
        throw new Error;
    };
    ColorFormatPart.EvaluateFormat = function(token)
    {
        if(!token || token === string.Empty)
            return false;
        var content = DefaultTokens.TrimSquareBracket(token);
        if(!content || content === string.Empty)
            return false;
        if(content.length < 3)
            return false;
        if(!isNaN(token[token.length - 1]))
            return StringHelper.StartsWith(token,"Color",StringComparison.CurrentCultureIgnoreCase);
        else
            return token[0] !== token[1]
    };
    function ABSTimeFormatPart(token)
    {
        FormatPartBase.call(this,token);
        this._classNames.push("ABSTimeFormatPart");
        this._initFileds();
        if(ABSTimeFormatPart.EvaluateFormat(token))
        {
            this.token = token.toLowerCase();
            if(this.token[1] === ABSTimeFormatPart.HoursABSContent)
                this.type = TimePart.Hour;
            else if(this.token[1] === ABSTimeFormatPart.MinuteABSContent)
                this.type = TimePart.Minute;
            else if(this.token[1] === ABSTimeFormatPart.SecondABSContent)
                this.type = TimePart.Second;
            else
                throw new Error("token is illegal.");
        }
        else
            throw new Error("token is illegal.");
        var sb = new StringBuilder;
        for(var n = 0; n < this.token.length - 2; n++)
            sb.Append("0");
        this.formatString = sb.toString()
    }
    ABSTimeFormatPart.prototype = new FormatPartBase;
    ABSTimeFormatPart.prototype._initFileds = function()
    {
        this.token = null;
        this.type = null;
        this.formatString = null
    };
    ABSTimeFormatPart.prototype.FormatString = function()
    {
        return this.formatString
    };
    ABSTimeFormatPart.prototype.TimePartType = function()
    {
        return this.type
    };
    ABSTimeFormatPart.prototype.Token = function()
    {
        return this.token
    };
    ABSTimeFormatPart.HoursABSContent = "h";
    ABSTimeFormatPart.MinuteABSContent = "m";
    ABSTimeFormatPart.SecondABSContent = "s";
    ABSTimeFormatPart.EvaluateFormat = function(token)
    {
        if(!token || token === string.Empty)
            return false;
        var content = DefaultTokens.TrimSquareBracket(token);
        if(!content || content === string.Empty)
            return false;
        content = content.toLowerCase();
        var c = null;
        for(var n = 0; n < content.length; n++)
        {
            if(!c)
                c = content[n];
            var fp = ABSTimeFormatPart;
            if(c !== fp.HoursABSContent && c !== fp.MinuteABSContent && c !== fp.SecondABSContent)
                return false;
            if(c !== content[n])
                return false
        }
        return true
    };
    function DBNumber(units, numbers)
    {
        this._classNames = ["DBNumber"];
        this._initFileds();
        var key = null,
            u = null;
        if(units)
        {
            this.units = [];
            for(key in units)
                if(key)
                {
                    u = units[key];
                    if(u === 0)
                        this.units.push(string.Empty);
                    else
                        this.units.push(u.toString())
                }
        }
        if(numbers)
        {
            this.numbers = [];
            for(key in numbers)
                if(key)
                {
                    u = numbers[key];
                    if(u === 0)
                        this.numbers.push(string.Empty);
                    else
                        this.numbers.push(u.toString())
                }
        }
    }
    DBNumber.prototype = {
        _initFileds: function()
        {
            this.units = null;
            this.numbers = null
        },
        Units: function()
        {
            return this.units
        },
        Numbers: function()
        {
            return this.numbers
        }
    };
    function DBNumberFormatPart(token)
    {
        FormatPartBase.call(this,token);
        this._classNames.push("DBNumberFormatPart");
        this._initFileds();
        if(DBNumberFormatPart.EvaluateFormat(token))
        {
            this.token = token;
            var content = DefaultTokens.TrimSquareBracket(token);
            var strType = StringHelper.Remove(content,0,"dbnum".length);
            this.type = parseInt(strType,10);
            if(this.type < 0 || this.type > 3)
                throw new Error("token is illegal.");
        }
        else
            throw new Error("token is illegal.");
    }
    DBNumberFormatPart.prototype = new FormatPartBase;
    DBNumberFormatPart.prototype._initFileds = function()
    {
        this.token = null;
        this.type = 0
    };
    DBNumberFormatPart.prototype.Token = function()
    {
        if(!this.token)
            return string.Empty;
        return this.token
    };
    DBNumberFormatPart.prototype.Type = function()
    {
        return this.type
    };
    DBNumberFormatPart.prototype.ReplaceNumberString = function(s, dbNumber, isNumber)
    {
        if(!s || s === string.Empty)
            return s;
        var strData = s;
        var str = s;
        var end = -1;
        var start = -1;
        var hasPoint = false;
        var token = null;
        var ret = null;
        var formatedNumber = null;
        for(var n = s.length - 1; n >= 0; n--)
        {
            var c = str[n];
            if(!isNaN(c) || DefaultTokens.IsEquals(c,DefaultTokens.DecimalSeparator,false) && !hasPoint)
            {
                if(DefaultTokens.IsEquals(c,DefaultTokens.DecimalSeparator,false))
                    hasPoint = true;
                if(end === -1)
                    end = n;
                start = n
            }
            else if(start > -1 && end > -1)
            {
                token = str.substr(start,end - start + 1);
                ret = parseFloat(token);
                if(!isNaN(ret))
                {
                    formatedNumber = this.NumberString(token,dbNumber,isNumber);
                    strData = StringHelper.Remove(strData,start,end - start + 1);
                    strData = StringHelper.Insert(strData,start,formatedNumber)
                }
                end = -1;
                start = -1;
                hasPoint = false
            }
        }
        if(start > -1 && end > -1)
        {
            token = str.substr(start,end - start + 1);
            ret = parseFloat(token);
            if(!isNaN(ret))
            {
                formatedNumber = this.NumberString(token,dbNumber,isNumber);
                strData = StringHelper.Remove(strData,start,end - start + 1);
                strData = StringHelper.Insert(strData,start,formatedNumber)
            }
            end = -1;
            start = -1;
            hasPoint = false
        }
        return strData
    };
    DBNumberFormatPart.prototype.NumberString = function(value, dbNumber, isNumber)
    {
        var partValues = value.split('.');
        if(partValues)
            if(partValues.length === 1)
                return DBNumberFormatPart.FormatNumberString(partValues[0],isNumber ? dbNumber.Units() : null,dbNumber.Numbers());
            else if(partValues.length === 2)
            {
                var integerString = DBNumberFormatPart.FormatNumberString(partValues[0],isNumber ? dbNumber.Units() : null,dbNumber.Numbers());
                var decimalString = DBNumberFormatPart.FormatNumberString(partValues[1],dbNumber.Numbers());
                return integerString + "." + decimalString
            }
        throw new Error("value is illegal.");
    };
    DBNumberFormatPart.prototype.toString = function()
    {
        if(this.type > -1)
            return DefaultTokens.AddSquareBracket("DBNum" + this.type);
        throw new Error;
    };
    DBNumberFormatPart.EvaluateFormat = function(token)
    {
        if(!token || token === string.Empty)
            return false;
        var content = DefaultTokens.TrimSquareBracket(token);
        if(!content || content === string.Empty)
            return false;
        if(StringHelper.StartsWith(content,"DBNum",StringComparison.CurrentCultureIgnoreCase))
            return true;
        return false
    };
    DBNumberFormatPart.FormatNumberString = function(value, units, numbers)
    {
        var strValue = value;
        var n = 0;
        var c = null;
        var nC = 0;
        if(arguments.length === 2)
        {
            var sb = new StringBuilder;
            for(n = 0; n < strValue.length; n++)
            {
                c = strValue.substr(n,1);
                nC = parseInt(c,10);
                sb.Append(numbers[nC])
            }
            return sb.toString()
        }
        else if(arguments.length === 3)
        {
            if(!units)
                return DBNumberFormatPart.FormatNumberString(value,numbers);
            var zeroCount = 0;
            var result = null;
            var maxLength = strValue.length;
            var inZero = false;
            var numberLetter = [];
            for(n = 0; n < maxLength; n++)
            {
                var validCharIndex = units.length - 1 - n;
                if(validCharIndex > -1)
                    StringHelper.Insert(numberLetter,0,units[validCharIndex].toString());
                else
                    StringHelper.Insert(numberLetter,0,string.Empty)
            }
            var isZeroAcceptable = false;
            for(var i = 0; i < maxLength; i++)
            {
                c = strValue.substr(i,1);
                nC = parseInt(c,10);
                var ch1 = string.Empty;
                var ch2 = string.Empty;
                if(maxLength - i - 16 > 0)
                {
                    ch1 = numbers[nC];
                    ch2 = "";
                    isZeroAcceptable = true
                }
                else if(i !== maxLength - 1 && i !== maxLength - 5 && i !== maxLength - 9 && i !== maxLength - 13)
                    if(c === "0")
                    {
                        ch1 = "";
                        ch2 = "";
                        zeroCount = zeroCount + 1
                    }
                    else if(c !== "0" && zeroCount !== 0)
                    {
                        ch1 = numbers[0] + numbers[nC];
                        ch2 = numberLetter[i];
                        zeroCount = 0
                    }
                    else
                    {
                        ch1 = numbers[nC];
                        ch2 = numberLetter[i];
                        zeroCount = 0
                    }
                else if(c !== "0" && zeroCount !== 0)
                {
                    ch1 = numbers[0] + numbers[nC];
                    ch2 = numberLetter[i];
                    zeroCount = 0
                }
                else if(c !== "0" && zeroCount === 0 || isZeroAcceptable)
                {
                    ch1 = numbers[nC];
                    ch2 = numberLetter[i];
                    zeroCount = 0;
                    isZeroAcceptable = false
                }
                else if(c === "0" && zeroCount >= 3)
                {
                    ch1 = "";
                    ch2 = "";
                    zeroCount = zeroCount + 1
                }
                else if(maxLength >= 11)
                {
                    ch1 = "";
                    zeroCount = zeroCount + 1
                }
                else
                {
                    ch1 = "";
                    ch2 = numberLetter[i];
                    zeroCount = zeroCount + 1
                }
                var isZero = ch1 + ch2 === string.Empty;
                if(!isZero)
                    inZero = false;
                if(i === maxLength - 13 && !inZero)
                {
                    ch2 = numberLetter[i];
                    inZero = true
                }
                if(i === maxLength - 9 && !inZero)
                {
                    ch2 = numberLetter[i];
                    inZero = true
                }
                if(i === maxLength - 1)
                {
                    ch2 = numberLetter[i];
                    inZero = true
                }
                result = result + ch1 + ch2
            }
            var iValue = parseInt(value,10);
            if(!isNaN(iValue))
                if(iValue === 0)
                    return numbers[0];
            return result
        }
    };
    var NumberHelper = {
            ParseHexString: function(str)
            {
                if(!str || str === string.Empty)
                    throw new Error("string is illegal.");
                return parseInt(str,16)
            },
            FixJapaneseChars: function(s)
            {
                return s
            },
            GetFraction: function(value, denominatorDigits, out_integer, out_numerator, out_denominator)
            {
                out_integer.value = Math.ceil(value) - 1.0;
                out_numerator.value = 0;
                out_denominator.value = 0;
                var decimalValue = value - Math.ceil(value) + 1.0;
                var min = 2;
                var max = 9;
                min = Math.pow(10,denominatorDigits - 1);
                max = Math.pow(10,denominatorDigits) - 1;
                if(min < 2)
                    min = 2;
                if(max < 2)
                    max = 2;
                var isValueSet = false;
                var lastTriedValue = 0;
                for(var n = min; n <= max; n++)
                {
                    var valueTemp = n * decimalValue;
                    var valueIntegerTemp = Math.round(valueTemp);
                    var triedValue = valueIntegerTemp / n;
                    var deviation = Math.abs(triedValue - decimalValue);
                    if(isValueSet ? deviation < Math.abs(lastTriedValue - decimalValue) : true)
                    {
                        isValueSet = true;
                        lastTriedValue = triedValue;
                        out_numerator.value = valueIntegerTemp;
                        out_denominator.value = n
                    }
                }
                return isValueSet
            }
        };
    function LocaleIDFormatPart(token)
    {
        FormatPartBase.call(this,token);
        this._classNames.push("LocaleIDFormatPart");
        this._initFileds();
        if(!token)
            throw new Error("token");
        if(token === string.Empty)
            throw new Error("token is illegal.");
        this.content = DefaultTokens.TrimSquareBracket(token);
        var contentTemp = this.content;
        if(!contentTemp || contentTemp === string.Empty)
            throw new Error("token is illegal.");
        if(DefaultTokens.IsEquals(contentTemp[0],DefaultTokens.Dollar,false))
            contentTemp = StringHelper.Remove(contentTemp,0,1);
        else
            throw new Error("token is illegal.");
        var minus = contentTemp.indexOf(DefaultTokens.HyphenMinus);
        if(minus > -1)
        {
            this.currencySymbol = contentTemp.substr(0,minus);
            contentTemp = StringHelper.Remove(contentTemp,0,minus)
        }
        else
        {
            this.currencySymbol = contentTemp;
            return
        }
        if(DefaultTokens.IsEquals(contentTemp[0],DefaultTokens.HyphenMinus,false))
            contentTemp = StringHelper.Remove(contentTemp,0,1);
        else
            throw new Error("token is illegal.");
        if(contentTemp.length > 0)
            this.locateID = NumberHelper.ParseHexString(contentTemp);
        else
            throw new Error("token is illegal.");
    }
    LocaleIDFormatPart.prototype = new FormatPartBase;
    LocaleIDFormatPart.prototype._initFileds = function()
    {
        this.currencySymbol = null;
        this.locateID = -1;
        this.cultureInfo = null;
        this.content = null
    };
    LocaleIDFormatPart.prototype.CurrencySymbol = function()
    {
        if(this.currencySymbol)
            return this.EncodeSymbol(this.currencySymbol);
        return string.Empty
    };
    LocaleIDFormatPart.prototype.AllowScience = function(){};
    LocaleIDFormatPart.prototype.GetDBNumber = function(type)
    {
        var regionID = this.locateID & 0x00ff;
        return null
    };
    LocaleIDFormatPart.prototype.toString = function()
    {
        if(this.content)
            return DefaultTokens.AddSquareBracket(this.content);
        return string.Empty
    };
    LocaleIDFormatPart.prototype.EncodeSymbol = function(symbol)
    {
        return StringHelper.Replace(symbol,".","'.'")
    };
    LocaleIDFormatPart.EvaluateFormat = function(token)
    {
        if(!token || token === string.Empty)
            return false;
        var content = DefaultTokens.TrimSquareBracket(token);
        if(!content || content === string.Empty)
            return false;
        return DefaultTokens.IsEquals(content[0],DefaultTokens.Dollar,false)
    };
    FormatPartBase.Create = function(token)
    {
        if(ConditionFormatPart.EvaluateFormat(token))
            return new ConditionFormatPart(token);
        else if(DBNumberFormatPart.EvaluateFormat(token))
            return new DBNumberFormatPart(token);
        else if(LocaleIDFormatPart.EvaluateFormat(token))
            return new LocaleIDFormatPart(token);
        else if(ABSTimeFormatPart.EvaluateFormat(token))
            return new ABSTimeFormatPart(token);
        else if(ColorFormatPart.EvaluateFormat(token))
            return new ColorFormatPart(token);
        else
            return null
    };
    NumberFormatDateTime.prototype = new NumberFormatBase;
    NumberFormatDateTime.prototype._initFileds = function()
    {
        this.validDateTimeFormatString = null;
        this.formatString = null;
        this.hasJD = false;
        this.absoluteTime = null;
        this.absTimeParts = null;
        this.hasYearDelay = false
    };
    NumberFormatDateTime.prototype._init = function(format, absTimeParts, partLocaleID, dbNumberFormatPart)
    {
        var formatTemp = new OutPara(this.formatString);
        if(NumberFormatDateTimeHelper.EvaluateFormat(formatTemp.value))
        {
            var hasAMPM = this.ProcessAMPM(formatTemp);
            this.hasJD = this.Replace(formatTemp.value,NumberFormatDateTimeHelper.MonthJD,"\"" + NumberFormatDateTimeHelper.PlaceholderMonthJD + "\"",true,false,formatTemp,false,false);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.MonthUnabbreviated,NumberFormatDateTimeHelper.StandardMonthUnabbreviated,true,false,formatTemp,false,false);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.MonthAbbreviation,NumberFormatDateTimeHelper.StandardMonthAbbreviation,true,false,formatTemp,false,false);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.MonthTwoDigit,NumberFormatDateTimeHelper.StandardMonthTwoDigit,true,false,formatTemp,false,false);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.MonthSingleDigit,NumberFormatDateTimeHelper.StandardMonthSingleDigit,true,false,formatTemp,false,false);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.DayWeekDayAbbreviation,NumberFormatDateTimeHelper.StandardDayWeekDayAbbreviation,true,true,formatTemp,false,true);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.DayWeekDayUnabbreviated,NumberFormatDateTimeHelper.StandardDayWeekDayUnabbreviated,true,true,formatTemp,false,true);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.MinuteSingleDigit,NumberFormatDateTimeHelper.StandardMinuteSingleDigit,false,true,formatTemp,false,false);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.HoursSingleDigit,NumberFormatDateTimeHelper.StandardHourSingleDigit,true,true,formatTemp,false,false);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.HoursTwoDigit,NumberFormatDateTimeHelper.StandardHourTwoDigit,true,true,formatTemp,false,false);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.SecondSingleDigit,NumberFormatDateTimeHelper.StandardSecondSingleDigit,true,true,formatTemp,false,true);
            if(this.PartDBNumberFormat() && this.PartLocaleID())
            {
                this.hasYearDelay |= this.Replace(formatTemp.value,NumberFormatDateTimeHelper.YearFourDigit,"\"" + DefaultTokens.ReplacePlaceholder + NumberFormatDateTimeHelper.YearFourDigit + "\"",true,false,formatTemp,false,true);
                this.hasYearDelay |= this.Replace(formatTemp.value,NumberFormatDateTimeHelper.YearTwoDigit,"\"" + DefaultTokens.ReplacePlaceholder + NumberFormatDateTimeHelper.YearTwoDigit + "\"",true,false,formatTemp,false,true)
            }
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.EraYear,NumberFormatDateTimeHelper.YearFourDigit,true,false,formatTemp,false,true);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.EraYear + NumberFormatDateTimeHelper.EraYear,NumberFormatDateTimeHelper.YearFourDigit,true,false,formatTemp,false,true);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.EraYear + NumberFormatDateTimeHelper.EraYear + NumberFormatDateTimeHelper.EraYear,NumberFormatDateTimeHelper.YearFourDigit,true,false,formatTemp,false,true);
            this.Replace(formatTemp.value,NumberFormatDateTimeHelper.YearSingleDigit,NumberFormatDateTimeHelper.StandardYearSingleDigit,true,false,formatTemp,false,true);
            if(this.absTimeParts)
                for(var key = 0; key < this.absTimeParts.length; key++)
                {
                    var absPart = this.absTimeParts[key];
                    this.Replace(formatTemp.value,absPart.token,DefaultTokens.ReplacePlaceholder + absPart.token,true,true,formatTemp,false,true)
                }
            this.validDateTimeFormatString = formatTemp.value
        }
        else
            throw new Error("format is illegal.");
    };
    NumberFormatDateTime.prototype.baseNumberStringConverter = NumberFormatBase.prototype.NumberStringConverter;
    NumberFormatDateTime.prototype.NumberStringConverter = function(value)
    {
        if(arguments.length === 0)
        {
            if(this.baseNumberStringConverter())
                return this.baseNumberStringConverter();
            return NumberFormatDateTimeHelper.defaultNumberStringConverter
        }
        else
            this.baseNumberStringConverter(value)
    };
    NumberFormatDateTime.prototype.AbsoluteTime = function()
    {
        if(this.absoluteTime)
            return this.absoluteTime;
        return NumberFormatDateTimeHelper.defaultAbsoluteTime
    };
    NumberFormatDateTime.prototype.FormatString = function()
    {
        return this.formatString
    };
    NumberFormatDateTime.prototype.baseDateTimeFormatInfo = NumberFormatBase.prototype.DateTimeFormatInfo;
    NumberFormatDateTime.prototype.DateTimeFormatInfo = function(value)
    {
        if(arguments.length === 0)
        {
            if(this.baseDateTimeFormatInfo())
                return this.baseDateTimeFormatInfo();
            return DefaultTokens.DateTimeFormatInfo
        }
        else
            this.baseDateTimeFormatInfo(value)
    };
    NumberFormatDateTime.prototype.ExactlyMatch = function(value)
    {
        if(arguments.length === 0)
            return this.exactlyMatch;
        else
            this.exactlyMatch = value
    };
    NumberFormatDateTime.prototype.ExcelCompatibleFormatString = function()
    {
        this.formatTemp = this.FormatString;
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardAMPMSingleDigit,this.CurrentAMPM(),true,true,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardMonthUnabbreviated,NumberFormatDateTimeHelper.MonthUnabbreviated,true,false,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardMonthAbbreviation,NumberFormatDateTimeHelper.MonthAbbreviation,true,false,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardMonthTwoDigit,NumberFormatDateTimeHelper.MonthTwoDigit,true,false,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardMonthSingleDigit,NumberFormatDateTimeHelper.MonthSingleDigit,true,false,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardDayWeekDayAbbreviation,NumberFormatDateTimeHelper.DayWeekDayAbbreviation,true,true,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardDayWeekDayUnabbreviated,NumberFormatDateTimeHelper.DayWeekDayUnabbreviated,true,true,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardMinuteSingleDigit,NumberFormatDateTimeHelper.MinuteSingleDigit,false,true,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardHourSingleDigit,NumberFormatDateTimeHelper.HoursSingleDigit,true,true,this.formatTemp,false,false);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardHourTwoDigit,NumberFormatDateTimeHelper.HoursTwoDigit,true,true,this.formatTemp,false,false);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardSecondSingleDigit,NumberFormatDateTimeHelper.SecondSingleDigit,true,true,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardSubSecondThreeDigit,NumberFormatDateTimeHelper.SubSecondThreeDigit,true,true,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardSubSecondTwoDigit,NumberFormatDateTimeHelper.SubSecondTwoDigit,true,true,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardSubSecondSingleDigit,NumberFormatDateTimeHelper.SubSecondSingleDigit,true,true,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.YearFourDigit,NumberFormatDateTimeHelper.EraYear,true,false,this.formatTemp,false,true);
        this.Replace(this.formatTemp,NumberFormatDateTimeHelper.StandardYearSingleDigit,NumberFormatDateTimeHelper.YearSingleDigit,true,true,this.formatTemp,false,true);
        return this.formatTemp
    };
    NumberFormatDateTime.prototype.CurrentAMPM = function()
    {
        var formatInfo = null;
        if(this.DateTimeFormatInfo())
            formatInfo = this.DateTimeFormatInfo();
        else
            formatInfo = DefaultTokens.DateTimeFormatInfo;
        if(formatInfo && formatInfo.AMDesignator && formatInfo.AMDesignator !== string.Empty && formatInfo.PMDesignator && formatInfo.PMDesignator !== string.Empty)
        {
            var ampm = string.Format("{0}/{1}",formatInfo.AMDesignator,formatInfo.PMDesignator);
            return ampm
        }
        return NumberFormatDateTimeHelper.AMPMTwoDigit
    };
    NumberFormatDateTime.prototype.Format = function(obj)
    {
        if(isType(obj,'boolean'))
            return obj.toString().toUpperCase();
        var result = string.Empty;
        var dateTime = new Date;
        try
        {
            try
            {
                dateTime = GrapeCity.Calc.Convert.toDateTime(obj);
                if(!dateTime || isNaN(dateTime))
                    return obj.toString()
            }
            catch(err)
            {
                return obj.toString()
            }
            var validDTF = this.validDateTimeFormatString;
            var fs = this.validDateTimeFormatString.replace(/%/g,"");
            if(fs === "H" || fs === "h" || fs === "m" || fs === "M" || fs === "d" || fs === "s" || fs === "y")
            {
                validDTF = "%" + fs;
                this.validDateTimeFormatString = validDTF
            }
            else
                validDTF = fs;
            result = new GrapeCity.UI._DateTimeHelper(dateTime).localeFormat(validDTF);
            if(this.hasJD)
            {
                var monthName = this._getMonthName(dateTime.getMonth());
                result = StringHelper.Replace(result,NumberFormatDateTimeHelper.PlaceholderMonthJD,monthName.substr(0,1))
            }
            if(this.absTimeParts)
            {
                var span = dateTime - this.AbsoluteTime();
                for(var key = 0; key < this.absTimeParts.length; key++)
                {
                    var absPart = this.absTimeParts[key];
                    var absTimePartString = null;
                    switch(absPart.TimePartType())
                    {
                        case TimePart.Hour:
                            absTimePartString = Math.floor(span / 1000 / 3600);
                            break;
                        case TimePart.Minute:
                            absTimePartString = Math.floor(span / 1000 / 60);
                            break;
                        case TimePart.Second:
                            absTimePartString = Math.floor(span / 1000);
                            break
                    }
                    if(absTimePartString)
                    {
                        var tempAbsPart = absPart.token.replace("[","\\[").replace("]","\\]");
                        result = StringHelper.Replace(result,DefaultTokens.ReplacePlaceholder + tempAbsPart,absTimePartString)
                    }
                }
            }
        }
        catch(ex)
        {
            result = FormatConverter.toString(obj)
        }
        if(this.NumberStringConverter())
            if(this.hasYearDelay)
            {
                result = StringHelper.Replace(result,DefaultTokens.ReplacePlaceholder + NumberFormatDateTimeHelper.YearFourDigit,dateTime.toString(NumberFormatDateTimeHelper.YearFourDigit));
                result = StringHelper.Replace(result,DefaultTokens.ReplacePlaceholder + NumberFormatDateTimeHelper.YearTwoDigit,dateTime.toString(NumberFormatDateTimeHelper.YearTwoDigit))
            }
        return result
    };
    NumberFormatDateTime.prototype._getMonthName = function(monthIndex)
    {
        var array = ["January","February","March","April","May","June","July","August","September","October","November","December"];
        return array[monthIndex]
    };
    NumberFormatDateTime.prototype.Parse = function(s)
    {
        if(!s || s === string.Empty)
            return null;
        var strTemp = NumberHelper.FixJapaneseChars(s);
        var boolResult = strTemp.toLowerCase();
        if(boolResult === "true")
            return true;
        else if(boolResult === "false")
            return false;
        if(this.validDateTimeFormatString)
        {
            var dateTimeResult = GrapeCity.UI._DateTimeHelper.parseExact(strTemp,this.validDateTimeFormatString,GrapeCity.UI._CultureInfo.currentCulture);
            if(dateTimeResult)
                return dateTimeResult
        }
        if(!this.ExactlyMatch)
            try
            {
                var resultDate = GrapeCity.Calc.Convert.toDateTime(strTemp);
                if(resultDate && !isNaN(resultDate))
                    return resultDate;
                else
                    return strTemp
            }
            catch(err)
            {
                return strTemp
            }
        return null
    };
    NumberFormatDateTime.prototype.FixFormat = function(format)
    {
        var formatTemp = format;
        var strBuilder = new StringBuilder;
        var inComments = false;
        var hasTime = StringHelper.IndexOf(formatTemp,NumberFormatDateTimeHelper.HoursSingleDigit[0],StringComparison.CurrentCultureIgnoreCase) > -1 || StringHelper.IndexOf(formatTemp,NumberFormatDateTimeHelper.SecondSingleDigit[0],StringComparison.CurrentCultureIgnoreCase) > -1;
        var hasDate = StringHelper.IndexOf(formatTemp,NumberFormatDateTimeHelper.YearTwoDigit[0],StringComparison.CurrentCultureIgnoreCase) > -1 || StringHelper.IndexOf(formatTemp,NumberFormatDateTimeHelper.DaySingleDigit[0],StringComparison.CurrentCultureIgnoreCase) > -1;
        for(var n = 0; n < formatTemp.length; n++)
        {
            var c = formatTemp[n];
            if(c === '\"')
                inComments = !inComments;
            else if(!inComments)
                if(c === 'Y' || c === 'D' || c === 'S' || c === 'E' || c === 'G')
                    c = c.toLowerCase();
                else if(c === 'M')
                    if(n > 1)
                    {
                        if(!DefaultTokens.IsEquals('A',formatTemp[n - 1],true) && !DefaultTokens.IsEquals('P',formatTemp[n - 1],true))
                            c = c.toLowerCase()
                    }
                    else
                        c = c.toLowerCase();
            strBuilder.Append(c)
        }
        return strBuilder.toString()
    };
    NumberFormatDateTime.prototype.ProcessAMPM = function(format)
    {
        var isHandled = false;
        if(StringHelper.Contains(format.value,NumberFormatDateTimeHelper.AMPMTwoDigit))
        {
            format.value = StringHelper.Replace(format.value,NumberFormatDateTimeHelper.AMPMTwoDigit,NumberFormatDateTimeHelper.StandardAMPMSingleDigit);
            isHandled = true
        }
        if(StringHelper.Contains(format.value,NumberFormatDateTimeHelper.AMPMSingleDigit))
        {
            format.value = StringHelper.Replace(format.value,NumberFormatDateTimeHelper.AMPMSingleDigit,NumberFormatDateTimeHelper.StandardAMPMSingleDigit);
            if(this.DateTimeFormatInfo())
            {
                this.DateTimeFormatInfo().AMDesignator = NumberFormatDateTimeHelper.AMPMSingleDigit.substr(0,1);
                this.DateTimeFormatInfo().PMDesignator = NumberFormatDateTimeHelper.AMPMSingleDigit.substr(2,1)
            }
            isHandled = true
        }
        if(StringHelper.Contains(format.value,NumberFormatDateTimeHelper.CurrentAMPM))
        {
            format.value = StringHelper.Replace(format.value,NumberFormatDateTimeHelper.CurrentAMPM,NumberFormatDateTimeHelper.StandardAMPMSingleDigit);
            var ampm = NumberFormatDateTimeHelper.CurrentAMPM.split('/');
            if(ampm && ampm.length === 2)
            {
                this.DateTimeFormatInfo().AMDesignator = ampm[0];
                this.DateTimeFormatInfo().PMDesignator = ampm[1]
            }
            isHandled = true
        }
        return isHandled
    };
    NumberFormatDateTime.prototype.Replace = function(format, oldToken, newToken, isReplaceInDateFormat, isReplaceInTimeFormat, result, justSearch, isIgnoreCase)
    {
        result.value = format;
        if(isReplaceInDateFormat || isReplaceInTimeFormat)
        {
            var positions = [];
            var isInDate = true;
            var hasTime = StringHelper.IndexOf(result.value,NumberFormatDateTimeHelper.HoursSingleDigit[0],StringComparison.CurrentCultureIgnoreCase) > -1 || StringHelper.IndexOf(result.value,NumberFormatDateTimeHelper.SecondSingleDigit[0],StringComparison.CurrentCultureIgnoreCase) > -1;
            var hasDate = StringHelper.IndexOf(result.value,NumberFormatDateTimeHelper.YearTwoDigit[0],StringComparison.CurrentCultureIgnoreCase) > -1 || StringHelper.IndexOf(result.value,NumberFormatDateTimeHelper.DaySingleDigit[0],StringComparison.CurrentCultureIgnoreCase) > -1;
            if(!hasDate && hasTime)
                isInDate = false;
            var isStartSpecialString = false;
            var tokenCharIndex = 0;
            var index = 0;
            for(; index < result.value.length; index++)
            {
                var c = result.value[index];
                if(DefaultTokens.IsEquals(c,NumberFormatDateTimeHelper.HoursSingleDigit[0],true) || DefaultTokens.IsEquals(c,NumberFormatDateTimeHelper.SecondSingleDigit[0],true))
                    isInDate = false;
                else if(DefaultTokens.IsEquals(c,NumberFormatDateTimeHelper.YearTwoDigit[0],true) || DefaultTokens.IsEquals(c,NumberFormatDateTimeHelper.DaySingleDigit[0],true))
                    isInDate = true;
                if(isReplaceInDateFormat && DefaultTokens.IsEquals(c,oldToken[tokenCharIndex],isIgnoreCase) && isInDate || isReplaceInTimeFormat && DefaultTokens.IsEquals(c,oldToken[tokenCharIndex],isIgnoreCase) && !isInDate)
                {
                    var isMatch = true;
                    for(var x = 0; x < oldToken.length; x++)
                        if(x + index >= format.length || !DefaultTokens.IsEquals(oldToken[x],result.value[x + index],isIgnoreCase))
                        {
                            isMatch = false;
                            break
                        }
                    var indexLastMatch = index + oldToken.length - 1;
                    if(isMatch && indexLastMatch + 1 < result.value.length)
                    {
                        var lastMatchChar = result.value[indexLastMatch];
                        var tail = -1;
                        for(tail = indexLastMatch + 1; tail < result.value.length; tail++)
                            if(!DefaultTokens.IsEquals(lastMatchChar,result.value[tail],isIgnoreCase))
                                break;
                        if(tail > indexLastMatch + 1)
                        {
                            index = tail;
                            isMatch = false
                        }
                    }
                    if(isMatch && !isStartSpecialString)
                        new GrapeCity.UI._ArrayHelper(positions).insert(0,index)
                }
                if(c === '\"')
                    isStartSpecialString = !isStartSpecialString
            }
            if(positions.length > 0)
            {
                if(!justSearch)
                    for(index = 0; index < positions.length; index++)
                    {
                        var position = positions[index];
                        result.value = StringHelper.Remove(result.value,position,oldToken.length);
                        result.value = StringHelper.Insert(result.value,position,newToken)
                    }
                return true
            }
            else
                return false
        }
        return false
    };
    NumberFormatDateTime.keywords = [NumberFormatDateTimeHelper.YearTwoDigit,NumberFormatDateTimeHelper.YearFourDigit,NumberFormatDateTimeHelper.MonthSingleDigit,NumberFormatDateTimeHelper.MonthTwoDigit,NumberFormatDateTimeHelper.MonthAbbreviation,NumberFormatDateTimeHelper.MonthUnabbreviated,NumberFormatDateTimeHelper.MonthJD,NumberFormatDateTimeHelper.DaySingleDigit,NumberFormatDateTimeHelper.DayTwoDigit,NumberFormatDateTimeHelper.DayWeekDayAbbreviation,NumberFormatDateTimeHelper.DayWeekDayUnabbreviated,NumberFormatDateTimeHelper.HoursSingleDigit,NumberFormatDateTimeHelper.HoursTwoDigit,NumberFormatDateTimeHelper.MinuteSingleDigit,NumberFormatDateTimeHelper.MinuteTwoDigit,NumberFormatDateTimeHelper.SecondSingleDigit,NumberFormatDateTimeHelper.SecondTwoDigit];
    NumberFormatDigital.prototype = new NumberFormatBase;
    NumberFormatDigital.prototype.baseNumberStringConverter = NumberFormatBase.prototype.NumberStringConverter;
    NumberFormatDigital.prototype.NumberStringConverter = function(value)
    {
        if(arguments.length === 0)
        {
            if(this.baseNumberStringConverter())
                return this.baseNumberStringConverter();
            return this.defaultNumberStringConverter
        }
        else
            this.baseNumberStringConverter(value)
    };
    NumberFormatDigital.prototype.FormatString = function()
    {
        return this.fullFormatString
    };
    NumberFormatDigital.prototype.baseNumberFormatInfo = NumberFormatBase.prototype.NumberFormatInfo;
    NumberFormatDigital.prototype.NumberFormatInfo = function(value)
    {
        if(arguments.length === 0)
        {
            if(this.baseNumberFormatInfo())
                return this.baseNumberFormatInfo();
            if(this.PartLocaleID() && this.PartLocaleID().CultureInfo())
                return this.PartLocaleID().CultureInfo().NumberFormat();
            return DefaultTokens.NumberFormatInfo
        }
        else
            this.baseNumberFormatInfo(value)
    };
    NumberFormatDigital.prototype.IsGeneralNumber = function(value)
    {
        if(arguments.length === 0)
            return this.isGeneralNumber;
        else
            this.isGeneralNumber = value
    };
    NumberFormatDigital.prototype.ExcelCompatibleFormatString = function()
    {
        return this.excelFormatString
    };
    NumberFormatDigital.prototype.Format = function(obj)
    {
        if(isType(obj,'boolean'))
            return obj.toString().toUpperCase();
        var number = FormatConverter.ToDouble(obj);
        if(isNaN(number) || !isFinite(number) || isNaN(obj))
        {
            if(typeof obj === 'string')
                return obj;
            return null
        }
        var result = this.NaNSymbol();
        var sb = null;
        if(this.fractionNumeratorFormat && this.fractionDenominatorFormat)
        {
            var out_integer = new OutPara(0.0);
            var out_numerator = new OutPara(0.0);
            var out_denominator = new OutPara(0.0);
            var d = this.fractionDenominatorFormat.length;
            if(this.GetFraction(number,d,out_integer,out_numerator,out_denominator))
                if(this.fractionIntegerFormat)
                {
                    sb = new StringBuilder;
                    if(out_integer.value !== 0)
                    {
                        sb.Append(out_integer.value.toString(this.fractionIntegerFormat,this.NumberFormatInfo()));
                        sb.Append(DefaultTokens.Space)
                    }
                    var denominatorFormat = this.fractionDenominatorFormat;
                    var fixedDenominator = parseFloat(denominatorFormat);
                    if(!isNaN(fixedDenominator))
                        if(fixedDenominator > 0)
                        {
                            out_numerator.value *= fixedDenominator / out_denominator.value;
                            denominatorFormat = string.Empty;
                            out_denominator.value = fixedDenominator;
                            var temp = Math.ceil(out_numerator.value) - out_numerator.value;
                            if(temp <= 0.5 && temp > 0)
                                out_numerator.value = parseFloat(parseInt(out_numerator.value,10) + 1);
                            else
                                out_numerator.value = parseFloat(parseInt(out_numerator.value,10))
                        }
                    var numeratorFormat = this.fractionNumeratorFormat;
                    var fixedNumeratorForma = parseFloat(numeratorFormat);
                    if(!isNaN(fixedNumeratorForma))
                        if(fixedNumeratorForma === 0)
                        {
                            var numeratorFormatLength = numeratorFormat.length;
                            var numeratorString = out_numerator.value.toString();
                            var numeratorLength = numeratorString.length;
                            if(numeratorFormatLength > numeratorLength)
                                numeratorFormat = numeratorFormat.substr(0,numeratorFormat.length - (numeratorFormatLength - numeratorLength));
                            else if(numeratorFormatLength < numeratorLength)
                            {
                                numeratorString = numeratorString.substr(0,numeratorLength - (numeratorLength - numeratorFormatLength));
                                out_numerator.value = parseInt(numeratorString,10)
                            }
                        }
                    if(out_numerator.value !== 0)
                    {
                        sb.Append(out_numerator.value.toString(numeratorFormat,this.NumberFormatInfo()));
                        sb.Append(DefaultTokens.SolidusSign);
                        sb.Append(out_denominator.value.toString(denominatorFormat,this.NumberFormatInfo()))
                    }
                    return sb.toString()
                }
                else
                {
                    sb = new StringBuilder;
                    var value = out_integer.value * out_denominator.value + out_numerator.value;
                    sb.Append(value.toString(this.fractionNumeratorFormat,this.NumberFormatInfo()));
                    sb.Append(DefaultTokens.SolidusSign);
                    sb.Append(out_denominator.value.toString(this.fractionDenominatorFormat,this.NumberFormatInfo()));
                    return sb.toString()
                }
            else
                return number.toString(this.NumberFormatInfo())
        }
        else
        {
            result = new GrapeCity.UI._NumberHelper(number).localeFormat(this.EncodeNumberFormat(this.numberFormatString));
            if(this.NumberStringConverter())
                result = this.NumberStringConverter().ConvertTo(result,obj,this.isGeneralNumber,this.PartLocaleID(),this.PartDBNumberFormat())
        }
        return result
    };
    NumberFormatDigital.prototype.Parse = function(s)
    {
        s = this.TrimSpecialSymbol(s);
        s = this.TrimCurrencySymbol(s);
        if(!s || s === string.Empty)
            return null;
        if(StringHelper.EndsWith(s,DefaultTokens.NumberGroupSeparator))
            return s;
        var strTemp = NumberHelper.FixJapaneseChars(s);
        if(s.toLowerCase() === "true")
            return true;
        else if(s.toLowerCase() === "false")
            return false;
        var isDecimal = DefaultTokens.IsDecimal(strTemp,this.NumberFormatInfo());
        var EIndex = StringHelper.IndexOf(strTemp,DefaultTokens.ExponentialSymbol,StringComparison.CurrentCultureIgnoreCase);
        var isE = EIndex > -1;
        if(this.numberFormatString)
        {
            var out_percentSignCount = new OutPara(0);
            s = this.TrimPercentSign(s,out_percentSignCount);
            var str = s;
            if(isE)
                str = s.substr(0,EIndex);
            if(str[0] === DefaultTokens.NumberFormatInfo.PositiveSign)
            {
                str = str.substr(1);
                s = s.substr(1)
            }
            var tempS = StringHelper.Replace(str,DefaultTokens.NumberGroupSeparator,"");
            var index = tempS.indexOf(DefaultTokens.DecimalSeparator);
            if(index === tempS.lastIndexOf(DefaultTokens.DecimalSeparator))
            {
                //var strBuilder = new GrapeCity.UI._StringBuilder("#,##0");
                var strBuilder = new GrapeCity.UI._StringBuilder("#" + DefaultTokens.NumberGroupSeparator + "##0");
                if (index !== -1)
                {
                    //strBuilder.append(".");
                    strBuilder.append(DefaultTokens.DecimalSeparator);
                    var decimalLength = tempS.length - index - 1;
                    for(var i = 0; i < decimalLength; i++)
                        strBuilder.append("0")
                }
                var newS = new GeneralFormatter(strBuilder.toString()).Format(tempS);
                if(newS === str)
                    if(isE)
                        s = tempS + s.substr(EIndex);
                    else
                        s = tempS
            }
            //var value = parseFloat(s);
            var value = parseFloat(s.replace(/,/, '.'));
            var nRegExp = null;
            if(value.toString() !== s)
            {
                var nfi = this.NumberFormatInfo();
                var decimalSeparator = DefaultTokens.DecimalSeparator;
                if(nfi)
                    decimalSeparator = nfi.NumberDecimalSeparator;
                if(!isDecimal && !isE)
                    nRegExp = new RegExp("^((\\+|-)?\\d+)$","ig");
                else if(isDecimal && !isE)
                    nRegExp = new RegExp("^((\\+|-)?\\d*)" + decimalSeparator + "(\\d*)$","ig");
                else if(!isDecimal && isE)
                    nRegExp = new RegExp("^((\\+|-)?\\d+)((E(\\+|-)?|e(\\+|-)?)\\d+)$","ig");
                else if(isDecimal && isE)
                    nRegExp = new RegExp("^((\\+|-)?\\d*)" + decimalSeparator + "(\\d*)((E(\\+|-)?|e(\\+|-)?)\\d+)$","ig")
            }
            if(!isNaN(value) && isFinite(value) && (!nRegExp || nRegExp.test(s)))
            {
                if(out_percentSignCount.value > 0)
                    value = value / parseFloat(100.0 * out_percentSignCount.value);
                if(value !== 0 && Math.abs(value - Math.floor(value)) !== 0)
                    isDecimal = true;
                if(isE)
                    isDecimal = true;
                return this.ToObject(value,isDecimal)
            }
        }
        return null
    };
    NumberFormatDigital.prototype.EncodeNumberFormat = function(format)
    {
        if(format)
        {
            var charArray = format.split("");
            var strBuilder = new GrapeCity.UI._StringBuilder;
            for(var i = 0; i < charArray.length - 1; )
                if(charArray[i] === "\\")
                {
                    strBuilder.append(charArray[i + 1]);
                    i += 2
                }
                else
                {
                    strBuilder.append(charArray[i]);
                    i++
                }
            if(i === charArray.length - 1)
                if(charArray[i] !== "\\")
                    strBuilder.append(charArray[i]);
            format = strBuilder.toString()
        }
        return format
    };
    NumberFormatDigital.prototype.ToObject = function(value, isDecimal)
    {
        if(!isDecimal)
            if(value <= 1E+22)
                return parseInt(value,10);
            else
                return value;
        return value
    };
    NumberFormatDigital.prototype.TrimPercentSign = function(s, out_count)
    {
        out_count.value = 0;
        if(!s || s === string.Empty)
            return s;
        var strTemp = s;
        var percentSymbol = DefaultTokens.PercentSymbol;
        var index = s.indexOf(percentSymbol);
        if(index === s.length - 1 && index === s.lastIndexOf(percentSymbol))
        {
            strTemp = StringHelper.Replace(strTemp,percentSymbol,"");
            out_count.value += (s.length - strTemp.length) / percentSymbol.length
        }
        return strTemp
    };
    NumberFormatDigital.prototype.TrimSpecialSymbol = function(s)
    {
        if(!s || s === string.Empty)
            return s;
        var strTemp = s;
        var firstDigital = -1;
        for(var fd = 0; fd < strTemp.length; fd++)
            if(char.IsDigit(strTemp[fd]))
            {
                firstDigital = fd;
                break
            }
        var lastDigital = -1;
        for(var ld = strTemp.length - 1; ld > -1; ld--)
            if(char.IsDigit(strTemp[ld]))
            {
                lastDigital = ld;
                break
            }
        for(var n = strTemp.length - 1; n > -1; n--)
        {
            var c = strTemp[n];
            if(this.IsSpecialSymbol(c))
                if(char.IsWhiteSpace(c))
                {
                    if(n < firstDigital || lastDigital < n)
                        strTemp = StringHelper.Remove(strTemp,n,1)
                }
                else
                    strTemp = StringHelper.Remove(strTemp,n,1);
            else if(c === '-' || c === '+')
                if(n > 0)
                    if(strTemp[n - 1] !== 'e' && strTemp[n - 1] !== 'E' && strTemp[n - 1] !== '(' && strTemp[n - 1].toString() !== DefaultTokens.NumberFormatInfo.CurrencySymbol)
                        break
        }
        return strTemp
    };
    NumberFormatDigital.prototype.IsStandardNumberSymbol = function(c)
    {
        var formatProvider = this.NumberFormatInfo() ? this.NumberFormatInfo() : DefaultTokens.NumberFormatInfo;
        if(formatProvider)
        {
            var str = c.toString();
            if(str === formatProvider.CurrencyDecimalSeparator || str === formatProvider.CurrencyGroupSeparator || str === formatProvider.CurrencySymbol || str === formatProvider.NaNSymbol || str === formatProvider.NegativeInfinitySymbol || str === formatProvider.NegativeSign || str === formatProvider.NumberDecimalSeparator || str === formatProvider.NumberGroupSeparator || str === formatProvider.PercentDecimalSeparator || str === formatProvider.PercentGroupSeparator || str === formatProvider.PercentSymbol || str === formatProvider.PerMilleSymbol || str === formatProvider.PositiveInfinitySymbol || str === formatProvider.PositiveSign)
                return true
        }
        return false
    };
    NumberFormatDigital.prototype.IsSpecialSymbol = function(c)
    {
        if(this.IsStandardNumberSymbol(c))
            return false;
        if(char.IsWhiteSpace(c))
            return true;
        return false
    };
    NumberFormatDigital.prototype.TrimCurrencySymbol = function(s)
    {
        var formatProvider = this.NumberFormatInfo() ? this.NumberFormatInfo() : DefaultTokens.NumberFormatInfo;
        if(formatProvider)
        {
            var currencySymbol = formatProvider.CurrencySymbol;
            var index = s.indexOf(currencySymbol);
            if(index === 0 && index === s.lastIndexOf(currencySymbol))
                s = s.substr(1)
        }
        return s
    };
    NumberFormatDigital.keywords = [DefaultTokens.Exponential1,DefaultTokens.Exponential2,DefaultTokens.NumberSign,DefaultTokens.DecimalSeparator,DefaultTokens.NumberGroupSeparator,DefaultTokens.PercentSymbol,DefaultTokens.Zero,DefaultTokens.SolidusSign];
    NumberFormatDigital.EvaluateFormat = function(format)
    {
        return NumberFormatBase.ContainsKeywords(format,NumberFormatDigital.keywords)
    };
    NumberFormatDigital.GetFraction = function(value, denominatorDigits, out_integer, out_numerator, out_denominator)
    {
        return NumberHelper.GetFraction(value,denominatorDigits,out_integer,out_numerator,out_denominator)
    };
    NumberFormatGeneral.prototype = new NumberFormatBase;
    NumberFormatGeneral.prototype.DigitalFormat = function()
    {
        if(!this.digitalFormat)
        {
            var nfStringTmp = this.fullFormatString;
            nfStringTmp = DefaultTokens.ReplaceKeyword(nfStringTmp,NumberFormatBase.General,NumberFormatGeneral.GeneralNumber);
            this.digitalFormat = new NumberFormatDigital(nfStringTmp,this.PartLocaleID(),this.PartDBNumberFormat());
            this.digitalFormat.IsGeneralNumber(true)
        }
        return this.digitalFormat
    };
    NumberFormatGeneral.prototype.ExponentialDigitalFormat = function()
    {
        if(!this.exponentialDigitalFormat)
        {
            this.exponentialDigitalFormat = new NumberFormatDigital("0.#####E+00",this.PartLocaleID(),this.PartDBNumberFormat());
            this.exponentialDigitalFormat.IsGeneralNumber(true)
        }
        return this.exponentialDigitalFormat
    };
    NumberFormatGeneral.prototype.FormatString = function()
    {
        return StringHelper.Replace(this.fullFormatString,NumberFormatGeneral.GeneralPlaceholder,NumberFormatBase.General)
    };
    NumberFormatGeneral.prototype.Format = function(obj)
    {
        if(FormatConverter.IsNumber(obj))
        {
            var allowS = !this.partLocaleID ? true : this.partLocaleID.AllowScience();
            var d = FormatConverter.ToDouble(obj);
            if(d !== undefined && d !== null)
                if(Math.abs(d) > 99999999999 && allowS || Math.abs(d) < 1E-11 && d !== 0)
                    return this.ExponentialDigitalFormat().Format(obj);
                else
                    return this.DigitalFormat().Format(obj)
        }
        else if(isType(obj,"string"))
        {
            var formatTmp = StringHelper.Replace(this.FormatString(),'"','');
            formatTmp = DefaultTokens.TrimEscape(formatTmp);
            if(formatTmp)
                return StringHelper.Replace(formatTmp,"General",obj);
            return obj
        }
        else if(isType(obj,"boolean"))
            return obj.toString().toUpperCase();
        return""
    };
    NumberFormatGeneral.prototype.Parse = function(s)
    {
        if(string.IsNullOrEmpty(s))
            return null;
        var hasMin = false;
        var minIndex = StringHelper.IndexOf(s,"-");
        if(minIndex > 0)
            if(!DefaultTokens.IsEquals(s.charAt(minIndex - 1),DefaultTokens.ExponentialSymbol,true))
                hasMin = true;
        if(StringHelper.Contains(s,"/") || hasMin || StringHelper.Contains(s,":"))
        {
            var dt = GrapeCity.UI._DateTimeHelper.parseLocale(s);
            if(dt && !isNaN(dt))
                return dt
        }
        var tmp = s;
        var result = null;
        var hasSignNegative = null;
        if(tmp.charAt(0) === DefaultTokens.NegativeSign)
            hasSignNegative = true;
        else if(tmp.charAt(0) === DefaultTokens.PositiveSign)
            hasSignNegative = false;
        var hasParenthesis = false;
        if(hasSignNegative)
            if(tmp.length > 3)
                if(tmp.charAt(1) === DefaultTokens.LeftParenthesis && tmp.charAt(tmp.length - 1) === DefaultTokens.RightParenthesis)
                    hasParenthesis = true;
        if(hasSignNegative && hasParenthesis)
        {
            result = this.DigitalFormat().Parse(StringHelper.Remove(s,0,1));
            if(result)
            {
                if(isType(result,"number"))
                    return Math.abs(result) * (hasSignNegative ? -1 : 1);
                return result
            }
        }
        else
        {
            result = this.DigitalFormat().Parse(s);
            if(result !== undefined && result !== null)
                return result
        }
        return s
    };
    NumberFormatGeneral.GeneralPlaceholder = "@NumberFormat";
    //NumberFormatGeneral.GeneralNumber = "##################0.################";
    NumberFormatGeneral.GeneralNumber = "##################0,################";
    NumberFormatGeneral.GeneralMonthDay = function ()
    {
        return["d/M","d/MMM","d/MMMM", "d-M","d-MMM","d-MMMM"]
    };
    NumberFormatGeneral.GeneralYearMonth = function()
    {
        return["M/y","MMM/y","M/yyyy","MMM/yyyy","M-y","MMM-y","M-yyyy","MMM-yyyy"]
    };
    NumberFormatGeneral.GeneralYearMonthDay = function()
    {
        //return ["M/d/y", "MMM/d/y", "MMMM/d/y", "M/d/yyyy", "MMM/d/yyyy", "MMMM/d/yyyy", "d/M/y", "d/MMM/y", "d/MMMM/y", "d/M/yyyy", "d/MMM/yyyy", "d/MMMM/yyyy", "yyyy/M/d", "M-d-y", "MMM-d-y", "MMMM-d-y", "M-d-yyyy", "MMM-d-yyyy", "MMMM-d-yyyy", "d-M-y", "d-MMM-y", "d-MMMM-y", "d-M-yyyy", "d-MMM-yyyy", "d-MMMM-yyyy", "yyyy-M-d"]
        return ["d/M/y", "d/MMM/y", "d/MMMM/y", "d/M/yyyy", "d/MMM/yyyy", "d/MMMM/yyyy", "d-M-y", "d-MMM-y", "d-MMMM-y", "d-M-yyyy", "d-MMM-yyyy", "d-MMMM-yyyy"]
    };
    NumberFormatGeneral.GeneralHourMinute = function()
    {
        return["H:m","h:m tt"]
    };
    NumberFormatGeneral.GeneralHourMinuteWithDate = function()
    {
        //return ["M/d H:m", "MMM/d H:m", "MMMM/d H:m", "d/M H:m", "d/MMM H:m", "d/MMMM H:m", "M/y H:m", "MMM/y H:m", "M/yyyy H:m", "MMM/yyyy H:m", "M/d/y H:m", "MMM/d/y H:m", "MMMM/d/y H:m", "M/d/yyyy H:m", "MMM/d/yyyy H:m", "MMMM/d/yyyy H:m", "M-d H:m", "MMM-d H:m", "MMMM-d H:m", "d-M H:m", "d-MMM H:m", "d-MMMM H:m", "M-y H:m", "MMM-y H:m", "M-yyyy H:m", "MMM-yyyy H:m", "M-d-y H:m", "MMM-d-y H:m", "MMMM-d-y H:m", "M-d-yyyy H:m", "MMM-d-yyyy H:m", "MMMM-d-yyyy H:m", "M/d h:m tt", "MMM/d h:m tt", "MMMM/d h:m tt", "d/M h:m tt", "d/MMM h:m tt", "d/MMMM h:m tt", "M/y h:m tt", "MMM/y h:m tt", "M/yyyy h:m tt", "MMM/yyyy h:m tt", "M/d/y h:m tt", "MMM/d/y h:m tt", "MMMM/d/y h:m tt", "M/d/yyyy h:m tt", "MMM/d/yyyy h:m tt", "MMMM/d/yyyy h:m tt", "M-d h:m tt", "MMM-d h:m tt", "MMMM-d h:m tt", "d-M h:m tt", "d-MMM h:m tt", "d-MMMM h:m tt", "M-y h:m tt", "MMM-y h:m tt", "M-yyyy h:m tt", "MMM-yyyy h:m tt", "M-d-y h:m tt", "MMM-d-y h:m tt", "MMMM-d-y h:m tt", "M-d-yyyy h:m tt", "MMM-d-yyyy h:m tt", "MMMM-d-yyyy h:m tt"]
        return ["d/M H:m", "d/MMM H:m", "d/MMMM H:m", "M/y H:m", "MMM/y H:m", "M/yyyy H:m", "MMM/yyyy H:m", "d-M H:m", "d-MMM H:m", "d-MMMM H:m", "M-y H:m", "MMM-y H:m", "M-yyyy H:m", "MMM-yyyy H:m", "d/M h:m tt", "d/MMM h:m tt", "d/MMMM h:m tt", "M/y h:m tt", "MMM/y h:m tt", "M/yyyy h:m tt", "MMM/yyyy h:m tt", "d-M h:m tt", "d-MMM h:m tt", "d-MMMM h:m tt", "M-y h:m tt", "MMM-y h:m tt", "M-yyyy h:m tt", "MMM-yyyy h:m tt"]
    };
    NumberFormatGeneral.GeneralHourMinuteSecond = function()
    {
        return["H:m:s","h:m:s tt","H:m:s","h:mm:ss tt"]
    };
    NumberFormatGeneral.GeneralHourMinuteSecondSubSecond = function()
    {
        return["H:m:s.FFF","h:m:s.FFF tt"]
    };
    NumberFormatGeneral.GeneralHourMinuteSecondWithDate = function()
    {
        //return ["M/d H:m:s", "MMM/d H:m:s", "MMMM/d H:m:s", "d/M H:m:s", "d/MMM H:m:s", "d/MMMM H:m:s", "M/y H:m:s", "MMM/y H:m:s", "M/yyyy H:m:s", "MMM/yyyy H:m:s", "M/d/y H:m:s", "MMM/d/y H:m:s", "MMMM/d/y H:m:s", "M/d/yyyy H:m:s", "MMM/d/yyyy H:m:s", "MMMM/d/yyyy H:m:s", "d/M/y H:m:s", "d/MMM/y H:m:s", "d/MMMM/y H:m:s", "d/M/yyyy H:m:s", "d/MMM/yyyy H:m:s", "d/MMMM/yyyy H:m:s", "yyyy/M/d H:m:s", "M-d H:m:s", "MMM-d H:m:s", "MMMM-d H:m:s", "d-M H:m:s", "d-MMM H:m:s", "d-MMMM H:m:s", "M-y H:m:s", "MMM-y H:m:s", "M-yyyy H:m:s", "MMM-yyyy H:m:s", "M-d-y H:m:s", "MMM-d-y H:m:s", "MMMM-d-y H:m:s", "M-d-yyyy H:m:s", "MMM-d-yyyy H:m:s", "MMMM-d-yyyy H:m:s", "d-M-y H:m:s", "d-MMM-y H:m:s", "d-MMMM-y H:m:s", "d-M-yyyy H:m:s", "d-MMM-yyyy H:m:s", "d-MMMM-yyyy H:m:s", "yyyy-M-d H:m:s", "M/d h:m:s tt", "MMM/d h:m:s tt", "MMMM/d h:m:s tt", "d/M h:m:s tt", "d/MMM h:m:s tt", "d/MMMM h:m:s tt", "M/y h:m:s tt", "MMM/y h:m:s tt", "M/yyyy h:m:s tt", "MMM/yyyy h:m:s tt", "M/d/y h:m:s tt", "MMM/d/y h:m:s tt", "MMMM/d/y h:m:s tt", "M/d/yyyy h:m:s tt", "MMM/d/yyyy h:m:s tt", "MMMM/d/yyyy h:m:s tt", "d/M/y h:m:s tt", "d/MMM/y h:m:s tt", "d/MMMM/y h:m:s tt", "d/M/yyyy h:m:s tt", "d/MMM/yyyy h:m:s tt", "d/MMMM/yyyy h:m:s tt", "yyyy/M/d h:m:s tt", "M/d/yyyy h:mm:ss tt", "M-d h:m:s tt", "MMM-d h:m:s tt", "MMMM-d h:m:s tt", "d-M h:m:s tt", "d-MMM h:m:s tt", "d-MMMM h:m:s tt", "M-y h:m:s tt", "MMM-y h:m:s tt", "M-yyyy h:m:s tt", "MMM-yyyy h:m:s tt", "M-d-y h:m:s tt", "MMM-d-y h:m:s tt", "MMMM-d-y h:m:s tt", "M-d-yyyy h:m:s tt", "MMM-d-yyyy h:m:s tt", "MMMM-d-yyyy h:m:s tt", "d-M-y h:m:s tt", "d-MMM-y h:m:s tt", "d-MMMM-y h:m:s tt", "d-M-yyyy h:m:s tt", "d-MMM-yyyy h:m:s tt", "d-MMMM-yyyy h:m:s tt", "yyyy-M-d h:m:s tt"]
        return ["d/M H:m:s", "d/MMM H:m:s", "d/MMMM H:m:s", "d/M H:m:s", "d/MMM H:m:s", "d/MMMM H:m:s", "M/y H:m:s", "MMM/y H:m:s", "M/yyyy H:m:s", "MMM/yyyy H:m:s", "d/M/y H:m:s", "d/MMM/y H:m:s", "d/MMMM/y H:m:s", "d/M/yyyy H:m:s", "d/MMM/yyyy H:m:s", "d/MMMM/yyyy H:m:s", "d/M/y H:m:s", "d/MMM/y H:m:s", "d/MMMM/y H:m:s", "d/M/yyyy H:m:s", "d/MMM/yyyy H:m:s", "d/MMMM/yyyy H:m:s", "d-M H:m:s", "d-MMM H:m:s", "d-MMMM H:m:s", "d-M H:m:s", "d-MMM H:m:s", "d-MMMM H:m:s", "M-y H:m:s", "MMM-y H:m:s", "M-yyyy H:m:s", "MMM-yyyy H:m:s", "d-M-y H:m:s", "d-MMM-y H:m:s", "d-MMMM-y H:m:s", "d-M-yyyy H:m:s", "d-MMM-yyyy H:m:s", "d-MMMM-yyyy H:m:s", "d-M-y H:m:s", "d-MMM-y H:m:s", "d-MMMM-y H:m:s", "d-M-yyyy H:m:s", "d-MMM-yyyy H:m:s", "d-MMMM-yyyy H:m:s", "d/M h:m:s tt", "d/MMM h:m:s tt", "d/MMMM h:m:s tt", "d/M h:m:s tt", "d/MMM h:m:s tt", "d/MMMM h:m:s tt", "M/y h:m:s tt", "MMM/y h:m:s tt", "M/yyyy h:m:s tt", "MMM/yyyy h:m:s tt", "d/M/y h:m:s tt", "d/MMM/y h:m:s tt", "d/MMMM/y h:m:s tt", "d/M/yyyy h:m:s tt", "d/MMM/yyyy h:m:s tt", "d/MMMM/yyyy h:m:s tt", "d/M/y h:m:s tt", "d/MMM/y h:m:s tt", "d/MMMM/y h:m:s tt", "d/M/yyyy h:m:s tt", "d/MMM/yyyy h:m:s tt", "d/MMMM/yyyy h:m:s tt", "d/M/yyyy h:mm:ss tt", "d-M h:m:s tt", "d-MMM h:m:s tt", "d-MMMM h:m:s tt", "d-M h:m:s tt", "d-MMM h:m:s tt", "d-MMMM h:m:s tt", "M-y h:m:s tt", "MMM-y h:m:s tt", "M-yyyy h:m:s tt", "MMM-yyyy h:m:s tt", "d-M-y h:m:s tt", "d-MMM-y h:m:s tt", "d-MMMM-y h:m:s tt", "d-M-yyyy h:m:s tt", "d-MMM-yyyy h:m:s tt", "d-MMMM-yyyy h:m:s tt", "d-M-y h:m:s tt", "d-MMM-y h:m:s tt", "d-MMMM-y h:m:s tt", "d-M-yyyy h:m:s tt", "d-MMM-yyyy h:m:s tt", "d-MMMM-yyyy h:m:s tt"]
    };
    NumberFormatGeneral.GeneralHourMinuteSecondSubSecondWithDate = function()
    {
        //return ["M/d H:m:s.FFF", "MMM/d H:m:s.FFF", "MMMM/d H:m:s.FFF", "d/M H:m:s.FFF", "d/MMM H:m:s.FFF", "d/MMMM H:m:s.FFF", "M/y H:m:s.FFF", "MMM/y H:m:s.FFF", "M/yyyy H:m:s.FFF", "MMM/yyyy H:m:s.FFF", "d/M/y H:m", "d/MMM/y H:m", "d/MMMM/y H:m", "d/M/yyyy H:m", "d/mmm/yyyy H:m", "d/MMMM/yyyy H:m", "yyyy/M/d H:m", "M/d/y H:m:s.FFF", "MMM/d/y H:m:s.FFF", "MMMM/d/y H:m:s.FFF", "M/d/yyyy H:m:s", "MMM/d/yyyy H:m:s.FFF", "MMMM/d/yyyy H:m:s.FFF", "d/M/y H:m:s.FFF", "d/MMM/y H:m:s.FFF", "d/MMMM/y H:m:s.FFF", "d/M/yyyy H:m:s.FFF", "d/MMM/yyyy H:m:s.FFF", "d/MMMM/yyyy H:m:s.FFF", "yyyy/M/d H:m:s.FFF", "M-d H:m:s.FFF", "MMM-d H:m:s.FFF", "MMMM-d H:m:s.FFF", "d-M H:m:s.FFF", "d-MMM H:m:s.FFF", "d-MMMM H:m:s.FFF", "M-y H:m:s.FFF", "MMM-y H:m:s.FFF", "M-yyyy H:m:s.FFF", "MMM-Yyyy H:m:s.FFF", "d-M-y H:m", "d-MMM-y H:m", "d-MMMM-y H:m", "d-M-yyyy H:m", "d-MMM-yyyy H:m", "d-MMMM-yyyy H:m", "yyyy-M-d H:m", "M-d-y H:m:s.FFF", "MMM-d-y H:m:s.FFF", "MMMM-d-y H:m:s.FFF", "M-d-yyyy H:m:s", "MMM-d-yyyy H:m:s.FFF", "MMMM-d-yyyy H:m:s.FFF", "D-M-y H:m:s.FFF", "d-MMM-y H:m:s.FFF", "d-MMMM-y H:m:s.FFF", "D-M-yyyy H:m:s.FFF", "d-MMM-yyyy H:m:s.FFF", "d-MMMM-yyyy H:m:s.FFF", "yyyy-M-d H:m:s.FFF", "M/d h:m:s.FFF tt", "MMM/d h:m:s.FFF tt", "MMMM/d h:m:s.FFF tt", "d/M h:m:s.FFF tt", "d/MMM h:m:s.FFF tt", "d/MMMM h:m:s.FFF tt", "M/y h:m:s.FFF tt", "MMM/y h:m:s.FFF tt", "M/yyyy h:m:s.FFF tt", "MMM/yyyy h:m:s.FFF tt", "d/M/y h:m tt", "d/MMM/y h:m tt", "d/MMMM/y h:m tt", "d/M/yyyy h:m tt", "d/mmm/yyyy h:m tt", "d/MMMM/yyyy h:m tt", "yyyy/M/d h:m tt", "M/d/y h:m:s.FFF tt", "MMM/d/y h:m:s.FFF tt", "MMMM/d/y h:m:s.FFF tt", "M/d/yyyy h:m:s tt", "MMM/d/yyyy h:m:s.FFF tt", "MMMM/d/yyyy h:m:s.FFF tt", "d/M/y h:m:s.FFF tt", "d/MMM/y h:m:s.FFF tt", "d/MMMM/y h:m:s.FFF tt", "d/M/yyyy h:m:s.FFF tt", "d/MMM/yyyy h:m:s.FFF tt", "d/MMMM/yyyy h:m:s.FFF tt", "yyyy/M/d h:m:s.FFF tt", "M-d h:m:s.FFF tt", "MMM-d h:m:s.FFF tt", "MMMM-d h:m:s.FFF tt", "d-M h:m:s.FFF tt", "d-MMM h:m:s.FFF tt", "d-MMMM h:m:s.FFF tt", "M-y h:m:s.FFF tt", "MMM-y h:m:s.FFF tt", "M-yyyy h:m:s.FFF tt", "MMM-Yyyy h:m:s.FFF tt", "d-M-y h:m tt", "d-MMM-y h:m tt", "d-MMMM-y h:m tt", "d-M-yyyy h:m tt", "d-MMM-yyyy h:m tt", "d-MMMM-yyyy h:m tt", "yyyy-M-d h:m tt", "M-d-y h:m:s.FFF tt", "MMM-d-y h:m:s.FFF tt", "MMMM-d-y h:m:s.FFF tt", "M-d-yyyy H:m:s tt", "MMM-d-yyyy H:m:s.FFF tt", "MMMM-d-yyyy h:m:s.FFF tt", "d-M-y h:m:s.FFF tt", "d-MMM-y h:m:s.FFF tt", "d-MMMM-y h:m:s.FFF tt", "d-M-yyyy h:m:s.FFF tt", "d-MMM-yyyy h:m:s.FFF tt", "d-MMMM-yyyy h:m:s.FFF tt", "yyyy-M-d h:m:s.FFF tt"]
        return ["d/M H:m:s.FFF", "d/MMM H:m:s.FFF", "d/MMMM H:m:s.FFF", "d/M H:m:s.FFF", "d/MMM H:m:s.FFF", "d/MMMM H:m:s.FFF", "M/y H:m:s.FFF", "MMM/y H:m:s.FFF", "M/yyyy H:m:s.FFF", "MMM/yyyy H:m:s.FFF", "d/M/y H:m", "d/MMM/y H:m", "d/MMMM/y H:m", "d/M/yyyy H:m", "d/mmm/yyyy H:m", "d/MMMM/yyyy H:m", "d/M/y H:m:s.FFF", "d/MMM/y H:m:s.FFF", "d/MMMM/y H:m:s.FFF", "d/M/yyyy H:m:s", "d/MMM/yyyy H:m:s.FFF", "d/MMMM/yyyy H:m:s.FFF", "d/M/y H:m:s.FFF", "d/MMM/y H:m:s.FFF", "d/MMMM/y H:m:s.FFF", "d/M/yyyy H:m:s.FFF", "d/MMM/yyyy H:m:s.FFF", "d/MMMM/yyyy H:m:s.FFF", "d-M H:m:s.FFF", "d-MMM H:m:s.FFF", "d-MMMM H:m:s.FFF", "d-M H:m:s.FFF", "d-MMM H:m:s.FFF", "d-MMMM H:m:s.FFF", "M-y H:m:s.FFF", "MMM-y H:m:s.FFF", "M-yyyy H:m:s.FFF", "MMM-Yyyy H:m:s.FFF", "d-M-y H:m", "d-MMM-y H:m", "d-MMMM-y H:m", "d-M-yyyy H:m", "d-MMM-yyyy H:m", "d-MMMM-yyyy H:m", "d-M-y H:m:s.FFF", "d-MMM-y H:m:s.FFF", "d-MMMM-y H:m:s.FFF", "d-M-yyyy H:m:s", "d-MMM-yyyy H:m:s.FFF", "d-MMMM-yyyy H:m:s.FFF", "D-M-y H:m:s.FFF", "d-MMM-y H:m:s.FFF", "d-MMMM-y H:m:s.FFF", "D-M-yyyy H:m:s.FFF", "d-MMM-yyyy H:m:s.FFF", "d-MMMM-yyyy H:m:s.FFF", "d/M h:m:s.FFF tt", "d/MMM h:m:s.FFF tt", "d/MMMM h:m:s.FFF tt", "d/M h:m:s.FFF tt", "d/MMM h:m:s.FFF tt", "d/MMMM h:m:s.FFF tt", "M/y h:m:s.FFF tt", "MMM/y h:m:s.FFF tt", "M/yyyy h:m:s.FFF tt", "MMM/yyyy h:m:s.FFF tt", "d/M/y h:m tt", "d/MMM/y h:m tt", "d/MMMM/y h:m tt", "d/M/yyyy h:m tt", "d/mmm/yyyy h:m tt", "d/MMMM/yyyy h:m tt", "d/M/y h:m:s.FFF tt", "d/MMM/y h:m:s.FFF tt", "d/MMMM/y h:m:s.FFF tt", "d/M/yyyy h:m:s tt", "d/MMM/yyyy h:m:s.FFF tt", "d/MMMM/yyyy h:m:s.FFF tt", "d/M/y h:m:s.FFF tt", "d/MMM/y h:m:s.FFF tt", "d/MMMM/y h:m:s.FFF tt", "d/M/yyyy h:m:s.FFF tt", "d/MMM/yyyy h:m:s.FFF tt", "d/MMMM/yyyy h:m:s.FFF tt", "d-M h:m:s.FFF tt", "d-MMM h:m:s.FFF tt", "d-MMMM h:m:s.FFF tt", "d-M h:m:s.FFF tt", "d-MMM h:m:s.FFF tt", "d-MMMM h:m:s.FFF tt", "M-y h:m:s.FFF tt", "MMM-y h:m:s.FFF tt", "M-yyyy h:m:s.FFF tt", "MMM-Yyyy h:m:s.FFF tt", "d-M-y h:m tt", "d-MMM-y h:m tt", "d-MMMM-y h:m tt", "d-M-yyyy h:m tt", "d-MMM-yyyy h:m tt", "d-MMMM-yyyy h:m tt", "d-M-y h:m:s.FFF tt", "d-MMM-y h:m:s.FFF tt", "d-MMMM-y h:m:s.FFF tt", "d-M-yyyy H:m:s tt", "d-MMM-yyyy H:m:s.FFF tt", "d-MMMM-yyyy h:m:s.FFF tt", "d-M-y h:m:s.FFF tt", "d-MMM-y h:m:s.FFF tt", "d-MMMM-y h:m:s.FFF tt", "d-M-yyyy h:m:s.FFF tt", "d-MMM-yyyy h:m:s.FFF tt", "d-MMMM-yyyy h:m:s.FFF tt"]
    };
    NumberFormatGeneral.EvaluateFormat = function(format)
    {
        if(!format || format === string.Empty)
            return false;
        return NumberFormatBase.ContainsKeywords(format,[NumberFormatBase.General.toLowerCase()])
    }
})(window);
(function(window, $)
{
    "use strict";;
    var const_undefined = "undefined";
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === const_undefined)
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === const_undefined)
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    var document = window.document;
    var Image = window.Image;
    var cssPositoin = "position",
        cssAbsolute = "absolute",
        cssMargin = "margin",
        cssFont = "font",
        cssClass = "class",
        cssLeft = "left",
        cssRight = "right",
        cssTop = "top",
        cssBottom = "bottom",
        cssMiddle = "middle",
        cssCenter = "center",
        cssAttr = "gcUIElement",
        cssNone = "none",
        zero = "0",
        selectedFillColor = "#9fa9c5",
        gradientColorStop1 = "#F6FAFB",
        gradientColorStop2 = "#D2DBEB",
        buttonDownColor = "#34B4E3",
        buttonHoverColor = "#A6F4FF",
        _gcEditingInput = ".gcEditingInput",
        _keyDown_gcEditingInput = "keydown" + _gcEditingInput,
        _keyUp_gcEditingInput = "keyup" + _gcEditingInput,
        _paste_gcEditingInput = "paste" + _gcEditingInput,
        _mousedown_gcEditingInput = "mousedown" + _gcEditingInput,
        _mouseup_gcEditingInput = "mouseup" + _gcEditingInput,
        _mouseover_gcEditingInput = "mouseover" + _gcEditingInput,
        _mouseout_gcEditingInput = "mouseout" + _gcEditingInput,
        _mousemove_gcEditingInput = "mousemove" + _gcEditingInput,
        _click_gcEditingInput = "click" + _gcEditingInput,
        cssWidth = "width",
        cssHeight = "height",
        cssPadding = "padding",
        cssPaddingLeft = "padding-left",
        cssPaddingRight = "padding-right",
        cssPaddingTop = "padding-top",
        cssPaddingBottom = "padding-bottom",
        cssTextAlign = "text-align",
        cssHidden = "hidden",
        cssVisibility = "visibility",
        cssWordWrap = "word-wrap",
        cssOverflow = "overflow",
        cssNormal = "normal",
        cssBreakWord = "break-word",
        cssOverflowY = "overflow-y",
        cssResize = "resize",
        cssBorder = "border",
        cssOutline = "outline",
        cssBoxShadow = "box-shadow",
        cssBoxSizing = "box-sizing",
        cssAutoComplete = "autocomplete",
        cssColor = "color",
        cssBorderWidth = "border-width",
        cssBorderStyle = "border-style",
        cssBorderColor = "border-color",
        cssWhiteSpace = "white-space",
        cssBackgroundColor = "background-color",
        cssMaxWidth = "max-width",
        cssMaxHeight = "max-height",
        cssZIndex = "z-index";
    UI.CheckBoxTextAlign = {
        top: 0,
        bottom: 1,
        left: 2,
        right: 3
    };
    UI.EditorValueType = {
        Text: 0,
        Index: 1,
        Value: 2
    };
    UI.CellTypeKind = {
        BaseCellType: 0,
        TextCellType: 1,
        ColumnHeaderCellType: 2,
        RowHeaderCellType: 3,
        CornerCellType: 4,
        CheckBoxCellType: 5,
        ButtonCellType: 6,
        ComboBoxCellType: 7,
        HyperLinkCellType: 8
    };
    function BaseCellType(){}
    BaseCellType.prototype = {
        paint: function(ctx, value, x, y, w, h, style, options){},
        createEditorElement: function()
        {
            return null
        },
        _getLocator: function()
        {
            var span = document.createElement("span");
            $(span).css(cssPositoin,cssAbsolute);
            $(span).css(cssVisibility,cssHidden);
            $(span).css(cssBackgroundColor,"#68E");
            $(span).css(cssMargin,zero);
            $(span).css(cssPadding,"2px");
            $(span).css(cssFont,"normal 12px Arial");
            $(span).css(cssColor,"white");
            $(span).css(cssBorderWidth,zero);
            $(span).attr(cssClass,"gc-input-locator");
            span.setAttribute(cssAttr,"gcEditingLocator");
            return span
        },
        getEditorValue: function(editorContext)
        {
            return null
        },
        _formatEditorValue: function(editorContext, cellStyle, value)
        {
            return value
        },
        setEditorValue: function(editorContext, value){},
        parse: function(text, formatStr)
        {
            if(formatStr === undefined || formatStr === null || text === undefined || text === null)
                return text === undefined || text === null ? "" : text.toString();
            else
                try
                {
                    var formatter = new UI.GeneralFormatter(formatStr);
                    return formatter.Parse(text)
                }
                catch(ex)
                {
                    return text.toString()
                }
        },
        format: function(value, format, conditionalForeColor)
        {
            if(format === undefined || format === null || value === undefined || value === null)
            {
                if(value === undefined || value === null)
                    return'';
                if(typeof value === 'boolean' || value instanceof Boolean)
                    return value.toString().toUpperCase();
                else if(value instanceof Date)
                    return new UI._DateTimeHelper(value).localeFormat("d/M/yyyy h:mm:ss");
                return value.toString()
            }
            else
                try
                {
                    var formatter = null;
                    if(format instanceof UI.AutoFormatter || format instanceof UI.GeneralFormatter)
                        formatter = format;
                    else
                        formatter = new UI.GeneralFormatter(format);
                    return formatter.Format(value,conditionalForeColor)
                }
                catch(ex)
                {
                    return value.toString()
                }
        },
        focus: function(editorContext)
        {
            if(editorContext && editorContext.focus)
                editorContext.focus()
        },
        getThemeStyle: function(visualState)
        {
            return UI.Global.prototype.getWijmoThemeStyle(visualState)
        },
        activateEditor: function(editorContext, cellStyle, cellRect){},
        deactivateEditor: function(editorContext){},
        selectAll: function(editorContext)
        {
            if(editorContext && editorContext.select)
                editorContext.select()
        },
        updateEditor: function(editorContext, cellStyle, cellRect){},
        _updateEditorLocator: function(editorContext)
        {
            if(editorContext && editorContext.sheet)
            {
                var editor = editorContext;
                var sheet = editor.sheet;
                if(sheet.isEditing() && sheet.showEditingLocator)
                    if(editor._editingLocator)
                    {
                        var locator = editor._editingLocator;
                        $(locator).css(cssTop,parseInt($(editor).css(cssTop),10) - 18);
                        $(locator).css(cssLeft,parseInt($(editor).css(cssLeft),10))
                    }
            }
        },
        getHitInfo: function(x, y, row, col, cellStyle, cellRect, sheetArea)
        {
            return null
        },
        processMouseDown: function(hitInfo)
        {
            return false
        },
        processMouseMove: function(hitInfo)
        {
            return false
        },
        processMouseUp: function(hitInfo)
        {
            return false
        },
        processMouseEnter: function(hitInfo){},
        processMouseLeave: function(hitInfo){},
        isReservedKey: function(e)
        {
            return false
        },
        isEditingValueChanged: function(oldValue, newValue)
        {
            return oldValue !== newValue
        },
        _getAutoFitWidth: function(sheet, value, text, cellStyle, isFilterHeader)
        {
            return 0
        },
        _getAutoFitHeight: function(sheet, value, text, cellStyle)
        {
            return 0
        },
        toJSON: function()
        {
            return{type: UI.CellTypeKind.BaseCellType}
        },
        fromJSON: function(settings){}
    };
    UI.BaseCellType = BaseCellType;
    function TextCellType(){}
    TextCellType.prototype = new BaseCellType;
    TextCellType.prototype.paint = function(ctx, value, x, y, w, h, style, options)
    {
        if(!ctx)
            return;
        ctx.save();
        ctx.rect(x,y,w,h);
        ctx.clip();
        ctx.beginPath();
        if(style && style.backColor)
        {
            ctx.fillStyle = style.backColor;
            ctx.fillRect(x,y,w,h)
        }
        else if(!GrapeCity.UI._useDoubleBuffer())
            if(style && options.visualState === GrapeCity.UI.VisualState.Selected)
            {
                ctx.fillStyle = "#9fa9c5";
                ctx.fillRect(x,y,w,h)
            }
            else if(style && options.visualState === GrapeCity.UI.VisualState.Active)
            {
                ctx.fillStyle = "#9fafc5";
                ctx.fillRect(x,y,w,h)
            }
        try
        {
            if(style && style.backgroundImage && style.backgroundImage !== cssNone)
                if(options.imageLoader)
                    if(options.imageLoader.getState(style.backgroundImage))
                        ctx.drawImage(options.imageLoader.getImage(style.backgroundImage),x,y,w,h);
                    else
                        options.imageLoader.addImage(style.backgroundImage)
        }
        catch(ex){}
        var sheet = options.sheet,
            row = options.row,
            col = options.col;
        if(sheet && row !== null && row !== undefined && col !== null && col !== undefined)
        {
            var conditionalFormats = sheet._conditionalFormats;
            if(conditionalFormats && conditionalFormats.count() > 0)
            {
                var rules = conditionalFormats.getRules(row,col);
                for(var n = 0; n < rules.length; n++)
                {
                    var rule = rules[rules.length - 1 - n];
                    if(rule instanceof UI.DataBarRule)
                    {
                        var obj = rule.evaluate(sheet,row,col,value);
                        if(obj)
                        {
                            var rc = new UI.Rect(x + 2,y + 2,w - 4,h - 4);
                            var startX = rc.x,
                                startY = rc.y,
                                width = parseInt(Math.abs(rc.width * obj.scale)),
                                height = rc.height;
                            if(obj.axisLocation === 0)
                            {
                                if(obj.scale <= 0)
                                    width = 0
                            }
                            else if(obj.axisLocation === 1)
                                if(obj.scale < 0)
                                    startX = startX + rc.width - width;
                                else
                                    width = 0;
                            else
                            {
                                var axisX = parseInt(rc.width * obj.axisLocation + startX) + 0.5;
                                var axisY = rc.y;
                                var axisWidth = 1.0;
                                var axisHeight = h - 1;
                                ctx.lineWidth = axisWidth;
                                ctx.strokeStyle = obj.axisColor;
                                ctx.beginPath();
                                for(var len = 0; len <= axisHeight; len += 2)
                                    if(obj.direction === UI.BarDirection.RightToLeft)
                                    {
                                        ctx.moveTo(2 * x + w - axisX,axisY + len);
                                        ctx.lineTo(2 * x + w - axisX,axisY + len + 1)
                                    }
                                    else
                                    {
                                        ctx.moveTo(axisX,axisY + len);
                                        ctx.lineTo(axisX,axisY + len + 1)
                                    }
                                ctx.stroke();
                                if(obj.scale > 0.0)
                                    startX = axisX + axisWidth;
                                else if(obj.scale < 0.0)
                                    startX = axisX - width;
                                else
                                    width = 0.0
                            }
                            if(obj.showBorder)
                            {
                                startX = parseInt(startX) + 0.5;
                                width -= 1;
                                startY += 0.5;
                                height -= 1
                            }
                            if(width >= 0 && height >= 0)
                            {
                                var fillStyle = obj.fillColor;
                                if(obj.isGradient)
                                {
                                    var factor = 0.9;
                                    if(obj.direction === UI.BarDirection.RightToLeft)
                                        fillStyle = ctx.createLinearGradient(2 * x + w - startX - width,startY,2 * x + w - startX,startY + height);
                                    else
                                        fillStyle = ctx.createLinearGradient(startX,startY,startX + width,startY + height);
                                    var tempColor = UI._Color.parse(obj.fillColor);
                                    var color1 = new UI._Color(tempColor.a,parseInt(255 * factor + tempColor.r * (1 - factor)),parseInt(255 * factor + tempColor.g * (1 - factor)),parseInt(255 * factor + tempColor.b * (1 - factor))).toString();
                                    var offset1 = obj.scale < 0 ? 1 - factor : factor;
                                    var color2 = obj.fillColor;
                                    var offset2 = obj.scale < 0 ? 1 : 0;
                                    if(obj.direction === UI.BarDirection.RightToLeft)
                                    {
                                        fillStyle.addColorStop(offset1,color2);
                                        fillStyle.addColorStop(offset2,color1)
                                    }
                                    else
                                    {
                                        fillStyle.addColorStop(offset1,color1);
                                        fillStyle.addColorStop(offset2,color2)
                                    }
                                }
                                ctx.fillStyle = fillStyle;
                                if(obj.direction === UI.BarDirection.RightToLeft)
                                    ctx.fillRect(2 * x + w - startX - width,startY,width,height);
                                else
                                    ctx.fillRect(startX,startY,width,height);
                                if(obj.showBorder && width > 0 && height > 0)
                                {
                                    ctx.strokeStyle = obj.borderColor;
                                    if(obj.direction === UI.BarDirection.RightToLeft)
                                        ctx.strokeRect(2 * x + w - startX - width,startY,width,height);
                                    else
                                        ctx.strokeRect(startX,startY,width,height)
                                }
                            }
                            this._showBarOnly = obj.showBarOnly
                        }
                    }
                    else if(rule instanceof UI.IconSetRule)
                    {
                        var startX = x + 1,
                            startY = y + 2,
                            width = 16.0,
                            height = 16.0;
                        var obj = rule.evaluate(sheet,row,col,value);
                        if(obj)
                        {
                            if(obj.showIconOnly)
                            {
                                var hAlign = UI._SheetRender.getHAlignByValueType(style.hAlign,value);
                                if(hAlign == UI.HorizontalAlign.center)
                                    startX = x + w / 2.0 - width / 2.0;
                                else if(hAlign == UI.HorizontalAlign.right)
                                    startX = x + w - width - 2
                            }
                            if(style.vAlign == UI.VerticalAlign.center)
                                startY = y + h / 2.0 - height / 2.0;
                            else if(style.vAlign == UI.VerticalAlign.bottom)
                                startY = y + h - height - 2;
                            var icon = UI.IconSetRule.getIcon(obj.iconSetType,obj.iconIndex);
                            try
                            {
                                if(icon && options.imageLoader)
                                    if(typeof icon === "string")
                                        if(options.imageLoader.getState(icon))
                                            ctx.drawImage(options.imageLoader.getImage(icon),startX,startY,width,height);
                                        else
                                            options.imageLoader.addImage(icon);
                                    else if(options.imageLoader.getState(icon.image))
                                        ctx.drawImage(options.imageLoader.getImage(icon.image),icon.x,icon.y,icon.w,icon.h,startX,startY,width,height);
                                    else
                                        options.imageLoader.addImage(icon.image)
                            }
                            catch(ex){}
                            this._showIconOnly = obj.showIconOnly
                        }
                    }
                }
            }
        }
        if(options.sparkline)
            options.sparkline.paintSparkline(ctx,x,y,w,h);
        if(!options.cellOverflowLayout && !this._showBarOnly && !this._showIconOnly)
            this.paintValue(ctx,value,x,y,w,h,style,options);
        this._showBarOnly = false;
        this._showIconOnly = false;
        ctx.restore()
    };
    TextCellType.prototype.paintValue = function(ctx, value, x, y, w, h, style, options)
    {
        if(!ctx)
            return;
        ctx.save();
        if(options.cellOverflowLayout)
        {
            var layout = options.cellOverflowLayout.layout;
            if(layout)
            {
                ctx.rect(layout.x,layout.y,layout.width,layout.height);
                ctx.clip();
                ctx.beginPath()
            }
        }
        else
        {
            ctx.rect(x,y,w,h);
            ctx.clip();
            ctx.beginPath()
        }
        var conditionalForeColor = {value: null};
        var text = this.format(value,style.formatter ? style.formatter : style._autoFormatter,conditionalForeColor);
        if(text !== undefined && text !== null && text.length > 0)
        {
            if(style.foreColor)
                ctx.fillStyle = style.foreColor;
            if(conditionalForeColor.value)
                ctx.fillStyle = conditionalForeColor.value;
            if(style.font)
                ctx.font = style.font;
            var indent = 0;
            if(style.textIndent > 0)
                indent = style.textIndent * 8;
            var hAlign = this._getHAlignByValueType(style.hAlign,value,text);
            var adjX = 2;
            adjX += indent;
            ctx.textAlign = cssLeft;
            if(hAlign === UI.HorizontalAlign.center)
            {
                adjX = w / 2;
                ctx.textAlign = cssCenter
            }
            else if(hAlign === UI.HorizontalAlign.right)
            {
                adjX = w - 1 - 2;
                adjX -= indent;
                ctx.textAlign = cssRight
            }
            var lines = text.split(/\r\n|\r|\n/);
            var textHeight = 0;
            if(style.wordWrap && lines.length > 1 && style.vAlign !== UI.VerticalAlign.top)
                textHeight = (lines.length - 1) * options.lineHeight;
            var adjY = 2;
            ctx.textBaseline = cssTop;
            if(style.vAlign === UI.VerticalAlign.center)
            {
                adjY = (h - textHeight) / 2;
                ctx.textBaseline = cssMiddle
            }
            else if(style.vAlign === UI.VerticalAlign.bottom)
            {
                adjY = h - textHeight - 2;
                ctx.textBaseline = cssBottom
            }
            if(style.wordWrap)
            {
                var vpos = y + adjY;
                for(var i = 0; i < lines.length; i++)
                {
                    ctx.fillText(lines[i],x + adjX,vpos);
                    vpos += options.lineHeight
                }
            }
            else
                ctx.fillText(text,x + adjX,y + adjY)
        }
        ctx.restore()
    };
    TextCellType.prototype._getHAlignByValueType = function(hAlign, value, text)
    {
        return UI._SheetRender.getHAlignByValueType(hAlign,value)
    };
    TextCellType.prototype.createEditorElement = function()
    {
        var editor = document.createElement("textarea");
        var $editor = $(editor);
        $editor.css(cssPositoin,cssAbsolute);
        $editor.css(cssMargin,zero);
        $editor.css(cssFont,"normal 10pt Arial");
        $editor.css(cssPadding,"1px");
        $editor.css(cssWordWrap,"normal");
        $editor.css(cssOverflow,cssHidden);
        $editor.css(cssResize,cssNone);
        $editor.css(cssBorder,"2px #5292f7 solid");
        $editor.css(cssOutline,cssNone);
        $editor.css(cssBoxShadow,"1px 2px 5px rgba(0,0,0,0.4)");
        $editor.css(cssBoxSizing,"content-box");
        $editor.css(cssZIndex,"2000");
        $editor.attr(cssClass,"gc-input-box");
        $editor.attr(cssAutoComplete,"off");
        $editor.attr(cssAttr,"gcEditingInput");
        return editor
    };
    TextCellType.prototype.getEditorValue = function(editorContext)
    {
        var v = editorContext ? editorContext.value : null;
        return v !== undefined && v !== null && v.length > 0 ? v : null
    };
    TextCellType.prototype.setEditorValue = function(editorContext, value)
    {
        var startEditByKeydown = editorContext && editorContext.sheet && editorContext.sheet._startEditByKeydown ? true : false;
        if(editorContext && value !== undefined && value !== null && !startEditByKeydown)
            editorContext.value = value
    };
    TextCellType.prototype.focus = function(editorContext)
    {
        if(editorContext)
        {
            editorContext.focus();
            editorContext.selectionStart = editorContext.value.length
        }
    };
    TextCellType.prototype._formatEditorValue = function(editorContext, cellStyle, value)
    {
        if(editorContext && editorContext.sheet)
        {
            var sheet = editorContext.sheet;
            var r = sheet._activeRowIndex,
                c = sheet._activeColIndex;
            var formatter = cellStyle.formatter ? cellStyle.formatter : cellStyle._autoFormatter;
            if(value !== null && value !== undefined)
                if(formatter instanceof UI.AutoFormatter && formatter._innerFormatter instanceof UI.GeneralFormatter)
                    value = formatter._innerFormatter.GetPreferredEditingFormatter(value).Format(value);
                else
                {
                    var formatString = null;
                    if(typeof formatter === "string")
                    {
                        formatString = formatter;
                        formatter = new UI.GeneralFormatter(formatter)
                    }
                    if(formatter)
                        try
                        {
                            var numberFormatType = formatter.GetFormatType(value);
                            if(numberFormatType === UI.NumberFormatType.Number || numberFormatType === UI.NumberFormatType.Text)
                                formatter = new UI.GeneralFormatter("General");
                            else if (numberFormatType === UI.NumberFormatType.DateTime && formatString && formatString.indexOf("y") < 0 && formatString.indexOf("d") < 0 && formatString.indexOf("mmm") < 0) {
                                //formatter = new UI.GeneralFormatter("yyyy/mm/dd hh:mm:ss");
                                formatter = new UI.GeneralFormatter("dd/mm/yyyy hh:mm:ss");
                                sheet._editingTimeValue = true
                            }
                            else if (numberFormatType === UI.NumberFormatType.DateTime) {
                                formatter = new UI.GeneralFormatter("dd/MM/yyyy");
                                sheet._editingTimeValue = false;
                            }
                        value = formatter.Format(value)
                        }
                        catch(ex)
                        {
                            value = sheet.getText(r,c)
                        }
                    else
                        value = sheet.getText(r,c)
                }
        }
        return value
    };
    TextCellType.prototype.activateEditor = function(editorContext, cellStyle, cellRect)
    {
        if(editorContext && editorContext.sheet)
        {
            var $editor = $(editorContext);
            var sheet = editorContext.sheet;
            var offset = sheet._eventHandler._getCanvasOffset();
            var originalTop = offset.top + sheet._bounds.y + cellRect.y - 2;
            var originalLeft = offset.left + sheet._bounds.x + cellRect.x - 2;
            $editor.css(cssTop,originalTop);
            $editor.css(cssLeft,originalLeft);
            var self = this;
            $editor.bind(_keyDown_gcEditingInput,function(event)
            {
                self._updateEditorImp(editorContext,cellStyle,cellRect,event);
                if(self._processEditingWrap(event))
                    return;
                var pressLeftOnStart = editorContext.selectionStart === 0 && event.keyCode === UI.Key.left && !event.ctrlKey && !event.shiftKey && !event.altKey,
                    pressRightOnEnd = editorContext.selectionStart === editorContext.value.length && event.keyCode === UI.Key.right && !event.ctrlKey && !event.shiftKey && !event.altKey,
                    noSelection = editorContext.selectionStart === editorContext.selectionEnd;
                if(noSelection && (pressLeftOnStart || pressRightOnEnd) && sheet._editorStatus === UI.EditorStatus.Edit)
                    UI.cancelDefault(event)
            });
            $editor.bind(_keyUp_gcEditingInput,function(event)
            {
                if(event.keyCode === UI.Key.del || event.keyCode === UI.Key.backspace || (event.keyCode === UI.Key.z || event.keyCode === UI.Key.y) && event.ctrlKey && !event.altKey)
                    self._updateEditorImp(editorContext,cellStyle,cellRect,event);
                if((event.keyCode === UI.Key.enter || event.keyCode === UI.Key.tab) && !event.ctrlKey && !event.altKey);
                else
                    sheet._trigger(UI.Events.EditChange,{
                        sheet: sheet,
                        sheetName: sheet._name,
                        row: sheet._activeRowIndex,
                        col: sheet._activeColIndex,
                        editingText: self.getEditorValue(editorContext)
                    })
            });
            $editor.bind(_paste_gcEditingInput,function(event)
            {
                self._pasteInputSizingToken = window.setTimeout(function()
                {
                    self._updateEditorImp(editorContext,cellStyle,cellRect,event);
                    if(self._pasteInputSizingToken)
                    {
                        window.clearTimeout(self._pasteInputSizingToken);
                        delete self._pasteInputSizingToken
                    }
                },10)
            });
            $editor.bind(_mousedown_gcEditingInput,function(event)
            {
                if(sheet._editorStatus === UI.EditorStatus.Enter)
                {
                    sheet._editorStatus = UI.EditorStatus.Edit;
                    sheet._trigger(UI.Events.EditorStatusChanged,{
                        sheet: sheet,
                        sheetName: sheet._name,
                        oldStatus: UI.EditorStatus.Enter,
                        newStatus: UI.EditorStatus.Edit
                    })
                }
            });
            document.body.insertBefore(editorContext,null)
        }
    };
    TextCellType.prototype.deactivateEditor = function(editorContext)
    {
        if(editorContext)
        {
            $(editorContext).unbind(_gcEditingInput);
            if(editorContext._editingLocator)
                document.body.removeChild(editorContext._editingLocator);
            document.body.removeChild(editorContext)
        }
    };
    TextCellType.prototype._updateEditorImp = function(editorContext, cellStyle, cellRect, event)
    {
        if(editorContext && editorContext.sheet)
        {
            var editor = editorContext;
            var sheet = editor.sheet;
            if(sheet.isEditing())
            {
                var hAlign = null;
                var font = null;
                var HorizontalAlign = UI.HorizontalAlign;
                if(cellStyle)
                {
                    hAlign = UI._SheetRender.getHAlignByValueType(cellStyle.hAlign,editor._originalValue);
                    if(hAlign === HorizontalAlign.left)
                        $(editor).css(cssTextAlign,cssLeft);
                    else if(hAlign === HorizontalAlign.center)
                        $(editor).css(cssTextAlign,cssCenter);
                    else if(hAlign === HorizontalAlign.right)
                        $(editor).css(cssTextAlign,cssRight);
                    else
                        $(editor).css(cssTextAlign,cssLeft);
                    if(cellStyle.backColor)
                        $(editor).css(cssBackgroundColor,cellStyle.backColor);
                    if(cellStyle.foreColor)
                        $(editor).css(cssColor,cellStyle.foreColor);
                    if(cellStyle.font)
                        font = cellStyle.font;
                    else
                        font = sheet._render._getDefaultFont();
                    if(font)
                    {
                        if(sheet._zoomFactor > 1)
                            font = sheet._render._getZoomFont(font);
                        $(editor).css(cssFont,font)
                    }
                }
                if(cellRect)
                {
                    var sheetLayout = sheet._getSheetLayout();
                    var adjustWidth = 1;
                    var adjustHeight = 1;
                    adjustWidth = adjustWidth + parseInt($(editor).css(cssPaddingLeft),10) + parseInt($(editor).css(cssPaddingRight),10);
                    adjustHeight = adjustHeight + parseInt($(editor).css(cssPaddingTop),10) + parseInt($(editor).css(cssPaddingBottom),10);
                    var width = cellRect.width;
                    if(cellRect.x + cellRect.width > sheetLayout.width)
                        width = sheetLayout.width - cellRect.x;
                    $(editor).css(cssWidth,width - adjustWidth);
                    $(editor).css(cssHeight,cellRect.height - adjustHeight);
                    if(hAlign === undefined || hAlign === null || hAlign === HorizontalAlign.left)
                        editor.maxWidth = sheetLayout.width - cellRect.x - adjustWidth;
                    else if(hAlign === HorizontalAlign.center)
                        editor.maxWidth = Math.max(0,Math.min(sheetLayout.width - cellRect.x - cellRect.width - adjustWidth,cellRect.x - sheetLayout.rowHeaderWidth - adjustWidth)) * 2 + cellRect.width;
                    else if(hAlign === HorizontalAlign.right)
                        editor.maxWidth = cellRect.x + cellRect.width - sheetLayout.rowHeaderWidth - adjustWidth;
                    editor.maxHeight = sheetLayout.height - cellRect.y - adjustHeight;
                    editor.minWidth = parseInt(editor.style.width,10);
                    editor.minHeight = parseInt(editor.style.height,10)
                }
                var value = editor.value;
                font = editor.style.font;
                if(!font)
                    font = sheet._render._getZoomFont(sheet._render._getDefaultFont());
                var lineHeight = sheet._getFontHeight(font);
                if(event)
                    if(sheet._eventHandler.allowEnterEditing(event))
                    {
                        var index = editor.selectionStart;
                        var preValue = value.substr(0,index);
                        var endValue = value.substr(index,value.length - index);
                        value = preValue + String.fromCharCode(event.keyCode) + endValue
                    }
                var lines = value.split(/\r\n|\r|\n/);
                var i,
                    lineWidth,
                    rowCount;
                if(editor.maxWidth < editor.minWidth)
                    editor.minWidth = editor.maxWidth;
                if(editor.maxHeight < editor.minHeight)
                    editor.minHeight = editor.maxHeight;
                if(cellStyle && cellStyle.wordWrap || lines.length > 0)
                {
                    var maxLineWidth = 0;
                    var lineWidthTemps = [];
                    for(i = 0; i < lines.length; i++)
                    {
                        var lineValue = lines[i];
                        lineWidth = sheet._getStringWidth(lineValue,font);
                        lineWidthTemps.push(lineWidth);
                        maxLineWidth = Math.max(maxLineWidth,lineWidth)
                    }
                    var wrapHeight = lines.length * lineHeight;
                    if(maxLineWidth <= editor.minWidth)
                    {
                        $(editor).css(cssWidth,editor.minWidth);
                        $(editor).css(cssWordWrap,cssNormal);
                        $(editor).css(cssOverflow,cssHidden)
                    }
                    else if(maxLineWidth > editor.minWidth && maxLineWidth <= editor.maxWidth)
                    {
                        $(editor).css(cssWidth,maxLineWidth);
                        $(editor).css(cssWordWrap,cssNormal);
                        $(editor).css(cssOverflow,cssHidden)
                    }
                    else if(maxLineWidth > editor.maxWidth)
                    {
                        $(editor).css(cssWidth,editor.maxWidth);
                        $(editor).css(cssWordWrap,cssBreakWord);
                        $(editor).css(cssOverflow,cssHidden);
                        wrapHeight = 0;
                        for(i = 0; i < lineWidthTemps.length; i++)
                        {
                            lineWidth = lineWidthTemps[i];
                            if(lineWidth <= editor.maxWidth)
                                wrapHeight += lineHeight;
                            else if(lineWidth > editor.maxWidth)
                            {
                                rowCount = Math.ceil(lineWidth / editor.maxWidth);
                                wrapHeight += lineHeight * rowCount
                            }
                        }
                    }
                    if(wrapHeight <= editor.minHeight)
                        $(editor).css(cssHeight,editor.minHeight);
                    else if(wrapHeight > editor.minHeight && wrapHeight <= editor.maxHeight)
                        $(editor).css(cssHeight,wrapHeight);
                    else if(wrapHeight > editor.maxHeight)
                    {
                        $(editor).css(cssHeight,editor.maxHeight);
                        $(editor).css(cssOverflowY,"scroll")
                    }
                }
                else
                {
                    var width = sheet._getStringWidth(value,font);
                    if(width > editor.minWidth && width <= editor.maxWidth)
                    {
                        $(editor).css(cssWidth,width);
                        $(editor).css(cssHeight,editor.minHeight);
                        $(editor).css(cssWordWrap,cssNormal);
                        $(editor).css(cssOverflow,cssHidden)
                    }
                    else if(width <= editor.minWidth)
                    {
                        $(editor).css(cssWidth,editor.minWidth);
                        $(editor).css(cssHeight,editor.minHeight);
                        $(editor).css(cssWordWrap,cssNormal);
                        $(editor).css(cssOverflow,cssHidden)
                    }
                    else if(width > editor.maxWidth)
                    {
                        $(editor).css(cssWidth,editor.maxWidth);
                        $(editor).css(cssWordWrap,cssBreakWord);
                        rowCount = Math.ceil(width / editor.maxWidth);
                        var actualHeight = lineHeight * rowCount;
                        if(actualHeight <= editor.minHeight)
                        {
                            $(editor).css(cssHeight,editor.minHeight);
                            $(editor).css(cssOverflow,cssHidden)
                        }
                        else if(actualHeight <= editor.maxHeight)
                        {
                            $(editor).css(cssHeight,actualHeight);
                            $(editor).css(cssOverflow,cssHidden)
                        }
                        else if(actualHeight > editor.maxHeight)
                        {
                            $(editor).css(cssHeight,editor.maxHeight);
                            $(editor).css(cssOverflowY,"scroll")
                        }
                    }
                }
                hAlign = $(editor).css(cssTextAlign);
                var flowWidth = $(editor).width() - editor.minWidth;
                if(hAlign !== undefined && hAlign !== null && hAlign !== cssLeft)
                    if(hAlign === cssCenter)
                        $(editor).css(cssLeft,editor.originalLeft - flowWidth / 2);
                    else if(hAlign === cssRight)
                        $(editor).css(cssLeft,editor.originalLeft - flowWidth);
                this._updateEditorLocator(editorContext)
            }
        }
    };
    TextCellType.prototype.updateEditor = function(editorContext, cellStyle, cellRect)
    {
        this._updateEditorImp(editorContext,cellStyle,cellRect,null)
    };
    TextCellType.prototype.isReservedKey = function(event)
    {
        var src = event.srcElement || event.target;
        if(src)
        {
            var attr = src.getAttribute(cssAttr);
            if(attr === "gcEditingInput")
                return event.keyCode === UI.Key.enter && (event.ctrlKey || event.altKey) || event.keyCode === UI.Key.z && event.ctrlKey && !event.altKey || event.keyCode === UI.Key.y && event.ctrlKey && !event.altKey
        }
        return false
    };
    TextCellType.prototype._processEditingWrap = function(event)
    {
        var src = event.srcElement || event.target;
        if(src)
        {
            var attr = src.getAttribute(cssAttr);
            if(attr === "gcEditingInput")
                if(event.keyCode === UI.Key.enter && (event.ctrlKey || event.altKey))
                {
                    var index = src.selectionStart;
                    var value = src.value;
                    var preValue = value.substr(0,index);
                    var endValue = value.substr(index,value.length - index);
                    src.value = preValue + "\n" + endValue;
                    src.selectionStart = index + 1;
                    src.selectionEnd = index + 1;
                    var ae = window.gcGlobal.activeElement;
                    if(ae instanceof UI.GcSheet)
                    {
                        var ct = ae.getCellType(ae._activeRowIndex,ae._activeColIndex);
                        if(ct)
                            ct.updateEditor(ae._editor,null,null)
                    }
                    UI.cancelDefault(event);
                    return true
                }
        }
        return false
    };
    TextCellType.prototype._getAutoFitWidth = function(sheet, value, text, cellStyle, isFilterHeader)
    {
        var width = 0;
        if(typeof text === "string" && text !== "")
        {
            if(cellStyle.wordWrap)
            {
                var lines = text.split(/\r\n|\r|\n/);
                for(var i = 0; i < lines.length; i++)
                    width = Math.max(width,sheet._getStringWidth(lines[i],cellStyle.font) / sheet._zoomFactor)
            }
            else
                width = sheet._getStringWidth(text,cellStyle.font) / sheet._zoomFactor;
            if(cellStyle.hAlign !== UI.HorizontalAlign.center && cellStyle.textIndent > 0)
                width += cellStyle.textIndent * 8 / sheet._zoomFactor;
            if(isFilterHeader)
            {
                var filterButtonZoom = sheet._zoomFactor > 1 ? 1 : sheet._zoomFactor;
                var buttonwidth = parseInt(sheet.defaults.rowHeight * filterButtonZoom,10);
                width += buttonwidth
            }
        }
        return width
    };
    TextCellType.prototype._getAutoFitHeight = function(sheet, value, text, cellStyle)
    {
        var height = 0;
        if(typeof text === "string" && text !== "")
        {
            var lineHeight = sheet._getFontHeight(cellStyle.font) / sheet._zoomFactor;
            if(cellStyle.wordWrap)
            {
                var lines = text.split(/\r\n|\r|\n/);
                height = lines.length * lineHeight
            }
            else
                height = lineHeight
        }
        return height
    };
    TextCellType.prototype.toJSON = function()
    {
        return{type: UI.CellTypeKind.TextCellType}
    };
    UI.TextCellType = TextCellType;
    function ColumnHeaderCellType(){}
    ColumnHeaderCellType.prototype = new BaseCellType;
    ColumnHeaderCellType.prototype.paint = function(ctx, value, x, y, w, h, style, options)
    {
        if(!ctx)
            return;
        ctx.save();
        ctx.rect(x,y,w,h);
        ctx.clip();
        ctx.beginPath();
        var themeStyle = null;
        if(options && options.visualState !== undefined)
            if(options.useWijmoTheme)
                try
                {
                    themeStyle = this.getThemeStyle(options.visualState);
                    if(themeStyle && themeStyle.backgroundColor && themeStyle.backgroundColor !== "")
                    {
                        ctx.fillStyle = themeStyle.backgroundColor;
                        ctx.fillRect(x,y,w,h)
                    }
                    else if(themeStyle && themeStyle.backgroundImage && themeStyle.backgroundImage !== cssNone && themeStyle.backgroundImage !== "")
                    {
                        var img = new Image;
                        var src = themeStyle.backgroundImage;
                        src = src.replace("url(\"",'');
                        src = src.replace("\")",'');
                        img.src = src;
                        ctx.drawImage(img,x,y,w,h)
                    }
                }
                catch(ex){}
            else
            {
                var VisualState = UI.VisualState;
                var gradient = ctx.createLinearGradient(x + w / 2,y,x + w / 2,y + h);
                if(options.visualState === VisualState.Normal)
                    if(style.backColor)
                        ctx.fillStyle = style.backColor;
                    else
                    {
                        gradient.addColorStop(0.125,gradientColorStop1);
                        gradient.addColorStop(1.0,gradientColorStop2);
                        ctx.fillStyle = gradient
                    }
                if(options.visualState === VisualState.Highlight)
                {
                    gradient.addColorStop(0.125,'#ffd45a');
                    gradient.addColorStop(1.0,'#ffef72');
                    ctx.fillStyle = gradient
                }
                if(options.visualState === VisualState.Selected)
                    ctx.fillStyle = selectedFillColor;
                if(options.visualState === VisualState.Hover)
                {
                    gradient.addColorStop(0.125,'#ffe165');
                    gradient.addColorStop(1.0,'#fff9a3');
                    ctx.fillStyle = gradient
                }
                ctx.fillRect(x,y,w,h)
            }
        var text = this.format(value,style.formatter);
        if(text !== undefined && text !== null && text.length > 0)
        {
            if(options && options.useWijmoTheme && themeStyle)
                ctx.fillStyle = themeStyle.color;
            else if(style.foreColor)
                ctx.fillStyle = style.foreColor;
            if(style.font)
                ctx.font = style.font;
            var indent = 0;
            if(style.textIndent > 0)
                indent = style.textIndent * 8;
            var HorizontalAlign = UI.HorizontalAlign,
                VerticalAlign = UI.VerticalAlign;
            var adjX = 2;
            adjX += indent;
            ctx.textAlign = cssLeft;
            if(style.hAlign === HorizontalAlign.center)
            {
                adjX = w / 2;
                ctx.textAlign = cssCenter
            }
            else if(style.hAlign === HorizontalAlign.right)
            {
                adjX = w - 1;
                adjX -= indent;
                ctx.textAlign = cssRight
            }
            var adjY = 3;
            ctx.textBaseline = cssTop;
            if(style.vAlign === VerticalAlign.center)
            {
                adjY = h / 2 + 1;
                ctx.textBaseline = cssMiddle
            }
            else if(style.vAlign === VerticalAlign.bottom)
            {
                adjY = h - 1;
                ctx.textBaseline = cssBottom
            }
            ctx.fillText(text,x + adjX,y + adjY)
        }
        ctx.restore()
    };
    ColumnHeaderCellType.prototype.toJSON = function()
    {
        return{type: UI.CellTypeKind.ColumnHeaderCellType}
    };
    UI.ColumnHeaderCellType = ColumnHeaderCellType;
    function RowHeaderCellType(){}
    RowHeaderCellType.prototype = new BaseCellType;
    RowHeaderCellType.prototype.paint = function(ctx, value, x, y, w, h, style, options)
    {
        if(!ctx)
            return;
        ctx.save();
        ctx.rect(x,y,w,h);
        ctx.clip();
        ctx.beginPath();
        var themeStyle = null;
        if(options && options.visualState !== undefined)
            if(options.useWijmoTheme)
                try
                {
                    themeStyle = this.getThemeStyle(options.visualState);
                    if(themeStyle && themeStyle.backgroundColor && themeStyle.backgroundColor !== "")
                    {
                        ctx.fillStyle = themeStyle.backgroundColor;
                        ctx.fillRect(x,y,w,h)
                    }
                    else if(themeStyle && themeStyle.backgroundImage && themeStyle.backgroundImage !== cssNone && themeStyle.backgroundImage !== "")
                    {
                        var img = new Image;
                        var src = themeStyle.backgroundImage;
                        src = src.replace("url(\"",'');
                        src = src.replace("\")",'');
                        img.src = src;
                        ctx.drawImage(img,x,y,w,h)
                    }
                }
                catch(ex){}
            else
            {
                var VisualState = UI.VisualState;
                if(options.visualState === VisualState.Normal)
                    if(style.backColor)
                        ctx.fillStyle = style.backColor;
                    else
                        ctx.fillStyle = "#E4ECF7";
                if(options.visualState === VisualState.Highlight)
                    ctx.fillStyle = "#ffdc61";
                if(options.visualState === VisualState.Selected)
                    ctx.fillStyle = selectedFillColor;
                if(options.visualState === VisualState.Hover)
                    ctx.fillStyle = "#fde47b";
                ctx.fillRect(x,y,w,h)
            }
        var text = this.format(value,style.formatter);
        if(text !== undefined && text !== null && text.length > 0)
        {
            if(options && options.useWijmoTheme && themeStyle)
                ctx.fillStyle = themeStyle.color;
            else if(style.foreColor)
                ctx.fillStyle = style.foreColor;
            if(style.font)
                ctx.font = style.font;
            var indent = 0;
            if(style.textIndent > 0)
                indent = style.textIndent * 8;
            var HorizontalAlign = UI.HorizontalAlign,
                VerticalAlign = UI.VerticalAlign;
            var adjX = 2;
            adjX += indent;
            ctx.textAlign = cssLeft;
            if(style.hAlign === HorizontalAlign.center)
            {
                adjX = w / 2;
                ctx.textAlign = cssCenter
            }
            else if(style.hAlign === HorizontalAlign.right)
            {
                adjX = w - 1;
                adjX -= indent;
                ctx.textAlign = cssRight
            }
            var adjY = 3;
            ctx.textBaseline = cssTop;
            if(style.vAlign === VerticalAlign.center)
            {
                adjY = h / 2 + 1;
                ctx.textBaseline = cssMiddle
            }
            else if(style.vAlign === VerticalAlign.bottom)
            {
                adjY = h - 1;
                ctx.textBaseline = cssBottom
            }
            ctx.fillText(text,x + adjX,y + adjY)
        }
        ctx.restore()
    };
    RowHeaderCellType.prototype.toJSON = function()
    {
        return{type: UI.CellTypeKind.RowHeaderCellType}
    };
    UI.RowHeaderCellType = RowHeaderCellType;
    function CornerCellType(){}
    CornerCellType.prototype = new BaseCellType;
    CornerCellType.prototype.paint = function(ctx, value, x, y, w, h, style, options)
    {
        if(!ctx)
            return;
        ctx.save();
        ctx.rect(x,y,w,h);
        ctx.clip();
        ctx.beginPath();
        if(options && options.visualState !== undefined)
        {
            var VisualState = UI.VisualState;
            if(options.useWijmoTheme)
                try
                {
                    var tmpStyle = this.getThemeStyle(options.visualState);
                    if(tmpStyle && tmpStyle.backgroundColor && tmpStyle.backgroundColor !== "")
                    {
                        ctx.fillStyle = tmpStyle.backgroundColor;
                        ctx.fillRect(x,y,w,h)
                    }
                    else if(tmpStyle && tmpStyle.backgroundImage && tmpStyle.backgroundImage !== cssNone)
                    {
                        var img = new Image;
                        var src = tmpStyle.backgroundImage;
                        src = src.replace("url(\"",'');
                        src = src.replace("\")",'');
                        img.src = src;
                        ctx.drawImage(img,x,y,w,h)
                    }
                }
                catch(ex){}
            else
            {
                if(options.visualState === VisualState.Normal)
                    ctx.fillStyle = "#A9C4E9";
                if(options.visualState === VisualState.Selected)
                    ctx.fillStyle = selectedFillColor;
                if(options.visualState === VisualState.Hover)
                    ctx.fillStyle = "#a1b0c8";
                ctx.fillRect(x,y,w,h)
            }
            var gradient = ctx.createLinearGradient(x + w / 2,y,x + w / 2,y + h);
            gradient.addColorStop(0.125,gradientColorStop1);
            if(options && options.useWijmoTheme)
            {
                var hoverStyle = this.getThemeStyle(VisualState.Hover);
                gradient.addColorStop(1.0,hoverStyle.backgroundColor)
            }
            else
                gradient.addColorStop(1.0,gradientColorStop2);
            ctx.fillStyle = gradient;
            ctx.beginPath();
            var padding = 3;
            var size = h;
            if(w < h)
                size = w;
            ctx.moveTo(x + w - size + padding,y + h - padding);
            ctx.lineTo(x + w - padding,y + h - padding);
            ctx.lineTo(x + w - padding,y + h - size + padding);
            ctx.fill()
        }
        ctx.restore()
    };
    CornerCellType.prototype.toJSON = function()
    {
        return{type: UI.CellTypeKind.CornerCellType}
    };
    UI.CornerCellType = CornerCellType;
    function CheckBoxCellType()
    {
        this._checkboxSize = 12;
        this._caption = "";
        this._textTrue = "";
        this._textIndeterminate = "";
        this._textFalse = "";
        this._textAlign = UI.CheckBoxTextAlign.right;
        this._isThreeState = false
    }
    CheckBoxCellType.prototype = new TextCellType;
    CheckBoxCellType.prototype.paintValue = function(ctx, value, x, y, w, h, style, options)
    {
        if(!ctx)
            return;
        ctx.save();
        var text = this._getDisplayText(value);
        var textWidth = 0,
            textHeight = 0;
        if(options.sheet)
        {
            textWidth = options.sheet._getStringWidth(text,style.font);
            textHeight = options.sheet._getFontHeight(style.font);
            if(text)
            {
                var lines = text.split(/\r\n|\r|\n/);
                textHeight *= lines.length
            }
        }
        var radius = this._checkboxSize / 2,
            rect = new UI.Rect(++x,++y,--w,--h),
            startX = parseInt(x + this._getCheckBoxLeft(style,rect,textWidth)),
            startY = parseInt(y + this._getCheckBoxTop(style,rect,textHeight));
        var textAlign = cssLeft;
        var textStartX = startX + 1;
        if(style.hAlign === UI.HorizontalAlign.center)
        {
            textAlign = cssCenter;
            textStartX += radius
        }
        else if(style.hAlign === UI.HorizontalAlign.right)
        {
            textAlign = cssRight;
            textStartX += radius * 2
        }
        if(style.font)
            ctx.font = style.font;
        if(style.foreColor)
            ctx.fillStyle = style.foreColor;
        if(this._textAlign === UI.CheckBoxTextAlign.left)
        {
            ctx.textAlign = cssRight;
            ctx.textBaseline = cssMiddle;
            ctx.fillText(text,startX + 1 - 2,startY + radius)
        }
        else if(this._textAlign === UI.CheckBoxTextAlign.top)
        {
            ctx.textAlign = textAlign;
            ctx.textBaseline = cssBottom;
            ctx.fillText(text,textStartX,startY - 2)
        }
        ctx.strokeStyle = "black";
        ctx.strokeRect(startX + 0.5,startY + 0.5,radius * 2 + 0.05,radius * 2 + 0.05);
        ctx.fillStyle = "white";
        ctx.fillRect(startX + 1,startY + 1,radius * 2 - 1,radius * 2 - 1);
        if(this._isThreeState && (value === null || value === undefined))
        {
            ctx.beginPath();
            ctx.fillStyle = "green";
            ctx.rect(startX + 3,startY + 3,(radius - 2.5) * 2,(radius - 2.5) * 2);
            ctx.fill()
        }
        else if(!!value === true)
        {
            ctx.beginPath();
            ctx.lineWidth = 2.5;
            ctx.moveTo(startX + 3,startY + radius);
            ctx.lineTo(startX + radius,startY + radius * 2 - 3.5);
            ctx.lineTo(startX + radius * 2 - 1.5,startY + 3);
            ctx.stroke()
        }
        if(style.foreColor)
            ctx.fillStyle = style.foreColor;
        if(this._textAlign === UI.CheckBoxTextAlign.right)
        {
            ctx.textAlign = cssLeft;
            ctx.textBaseline = cssMiddle;
            ctx.fillText(text,startX + 1 + radius * 2 + 2,startY + radius)
        }
        else if(this._textAlign === UI.CheckBoxTextAlign.bottom)
        {
            ctx.textAlign = textAlign;
            ctx.textBaseline = cssTop;
            ctx.fillText(text,textStartX,startY + radius * 2 + 2)
        }
        ctx.restore()
    };
    CheckBoxCellType.prototype.focus = function(editorContext)
    {
        if(editorContext)
            editorContext.focus()
    };
    CheckBoxCellType.prototype._formatEditorValue = function(editorContext, cellStyle, value)
    {
        return value
    };
    CheckBoxCellType.prototype._getCheckBoxLeft = function(cellStyle, cellRect, textWidth, isMargin)
    {
        var x = cellRect.x - 1,
            w = cellRect.width + 1,
            totalWidth = 0,
            startX = 0;
        if(this._textAlign === UI.CheckBoxTextAlign.top || this._textAlign === UI.CheckBoxTextAlign.bottom)
        {
            startX = x + 5;
            if(cellStyle.hAlign === UI.HorizontalAlign.center)
                startX = x + w / 2 - this._checkboxSize / 2;
            else if(cellStyle.hAlign === UI.HorizontalAlign.right)
                startX = x + w - 5 - this._checkboxSize
        }
        else if(this._textAlign === UI.CheckBoxTextAlign.left)
        {
            totalWidth = this._checkboxSize + textWidth;
            startX = x + 5 + textWidth;
            if(cellStyle.hAlign === UI.HorizontalAlign.center)
                startX = x + w / 2 - totalWidth / 2 + textWidth;
            else if(cellStyle.hAlign === UI.HorizontalAlign.right)
                startX = x + w - 5 - totalWidth + textWidth
        }
        else
        {
            totalWidth = this._checkboxSize + textWidth;
            startX = x + 5;
            if(cellStyle.hAlign === UI.HorizontalAlign.center)
                startX = x + w / 2 - totalWidth / 2;
            else if(cellStyle.hAlign === UI.HorizontalAlign.right)
                startX = x + w - 5 - totalWidth
        }
        var checkboxLeft = startX - x;
        if(isMargin)
            if($.browser.msie)
                checkboxLeft -= 3;
        return checkboxLeft
    };
    CheckBoxCellType.prototype._getCheckBoxTop = function(cellStyle, cellRect, textHeight, isMargin)
    {
        var y = cellRect.y - 1,
            h = cellRect.height + 1,
            totalHeight = 0,
            startY = 0;
        if(this._textAlign === UI.CheckBoxTextAlign.top)
        {
            totalHeight = this._checkboxSize + textHeight;
            startY = y + 5 + textHeight;
            if(cellStyle.vAlign === UI.VerticalAlign.center)
                startY = y + h / 2 - totalHeight / 2 + textHeight;
            else if(cellStyle.vAlign === UI.VerticalAlign.bottom)
                startY = y + h - 5 - totalHeight + textHeight
        }
        else if(this._textAlign === UI.CheckBoxTextAlign.bottom)
        {
            totalHeight = this._checkboxSize + textHeight;
            startY = y + 5;
            if(cellStyle.vAlign === UI.VerticalAlign.center)
                startY = y + h / 2 - totalHeight / 2;
            else if(cellStyle.vAlign === UI.VerticalAlign.bottom)
                startY = y + h - 5 - totalHeight
        }
        else
        {
            startY = y + 5;
            if(cellStyle.vAlign === UI.VerticalAlign.center)
                startY = y + h / 2 - this._checkboxSize / 2;
            else if(cellStyle.vAlign === UI.VerticalAlign.bottom)
                startY = y + h - 5 - this._checkboxSize
        }
        var checkboxTop = startY - y;
        if(isMargin)
            if($.browser.msie)
                checkboxTop -= 3;
        return checkboxTop
    };
    CheckBoxCellType.prototype.createEditorElement = function()
    {
        var div = document.createElement("div");
        var $div = $(div);
        $div.css(cssPositoin,cssAbsolute);
        $div.css(cssMargin,zero);
        $div.css(cssPadding,zero);
        $div.css(cssOverflow,cssHidden);
        $div.css(cssResize,cssNone);
        $div.css(cssBorder,"2px #5292f7 solid");
        $div.css(cssOutline,cssNone);
        $div.css(cssBoxShadow,"1px 2px 5px rgba(0,0,0,0.4)");
        $div.css(cssBoxSizing,"content-box");
        $div.attr("tabindex",1);
        $div.attr(cssAttr,"gcEditingInput");
        var $input = $("<input/>");
        $input.attr("type","checkbox");
        $div.append($input);
        var $span = $("<span></span>");
        $span.css(cssPositoin,cssAbsolute);
        $span.css(cssFont,"normal 10pt Arial");
        $span.css("cursor","default");
        $span.css(cssWhiteSpace,"nowrap");
        $div.append($span);
        var $childDiv = $("<div></div>");
        $childDiv.css(cssPositoin,cssAbsolute);
        $childDiv.css(cssBackgroundColor,"green");
        $childDiv.css(cssWidth,(this._checkboxSize / 2 - 2.5) * 2);
        $childDiv.css(cssHeight,(this._checkboxSize / 2 - 2.5) * 2);
        $div.append($childDiv);
        return div
    };
    CheckBoxCellType.prototype._getNextState = function(value)
    {
        var newValue;
        if(this._isThreeState)
            if(value === null || value === undefined)
                newValue = false;
            else if(!!value === true)
                newValue = null;
            else
                newValue = true;
        else
            newValue = !value;
        return newValue
    };
    CheckBoxCellType.prototype.setEditorValue = function(editorContext, value)
    {
        if(editorContext && editorContext.children.length === 3)
        {
            var startEditNotBySpace = editorContext.sheet && editorContext.sheet._startEditByKeydown && !this._isSpaceKeyDown;
            if(!startEditNotBySpace)
                value = this._getNextState(value);
            if(this._isThreeState)
                if(value === null || value === undefined)
                {
                    editorContext.children[0].checked = false;
                    $(editorContext.children[2]).show()
                }
                else
                {
                    editorContext.children[0].checked = !!value;
                    $(editorContext.children[2]).hide()
                }
            else
                editorContext.children[0].checked = !!value;
            $(editorContext.children[1]).text(this._getDisplayText(value))
        }
    };
    CheckBoxCellType.prototype.getEditorValue = function(editorContext)
    {
        if(editorContext && editorContext.children.length === 3)
            if(this._isThreeState)
                if($(editorContext.children[2]).is(":visible"))
                    return null;
                else
                    return editorContext.children[0].checked;
            else
                return editorContext.children[0].checked;
        return null
    };
    CheckBoxCellType.prototype._triggerButtonClicked = function(sheet, row, col)
    {
        if(sheet.parent)
            sheet.parent._trigger(UI.Events.ButtonClicked,{
                sheet: sheet,
                sheetName: sheet._name,
                row: row,
                col: col
            })
    };
    CheckBoxCellType.prototype.activateEditor = function(editorContext, cellStyle, cellRect)
    {
        if(editorContext && editorContext.sheet)
        {
            var sheet = editorContext.sheet;
            var $editor = $(editorContext);
            var offset = sheet._eventHandler._getCanvasOffset();
            var originalTop = offset.top + sheet._bounds.y + cellRect.y - 2;
            var originalLeft = offset.left + sheet._bounds.x + cellRect.x - 2;
            $editor.css(cssTop,originalTop);
            $editor.css(cssLeft,originalLeft);
            $editor.css(cssBackgroundColor,cellStyle.backColor ? cellStyle.backColor : "white");
            var self = this;
            $editor.bind(_mousedown_gcEditingInput,function(e)
            {
                UI.cancelDefault(e)
            });
            $editor.bind(_mouseup_gcEditingInput,function(e)
            {
                var oldValue = self.getEditorValue(editorContext);
                self.setEditorValue(editorContext,oldValue);
                self.updateEditor(editorContext,cellStyle,cellRect);
                self._triggerButtonClicked(sheet,sheet._activeRowIndex,sheet._activeColIndex)
            });
            $editor.bind(_keyDown_gcEditingInput,function(e)
            {
                if(e.keyCode === UI.Key.space && !e.ctrlKey && !e.shiftKey && !e.altKey)
                {
                    self._isKeyDown = true;
                    UI.cancelDefault(e);
                    return false
                }
                else if(e.keyCode === UI.Key.backspace && !e.ctrlKey && !e.shiftKey && !e.altKey)
                    UI.cancelDefault(e)
            });
            $editor.bind(_keyUp_gcEditingInput,function(e)
            {
                if(self._isKeyDown && e.keyCode === UI.Key.space && !e.ctrlKey && !e.shiftKey && !e.altKey)
                {
                    self._isKeyDown = false;
                    var oldValue = self.getEditorValue(editorContext);
                    self.setEditorValue(editorContext,oldValue);
                    self.updateEditor(editorContext,cellStyle,cellRect);
                    self._triggerButtonClicked(sheet,sheet._activeRowIndex,sheet._activeColIndex)
                }
            });
            if($editor.children().length === 3)
                $($editor.children()[0]).bind("click",function(e)
                {
                    UI.cancelDefault(e)
                });
            document.body.insertBefore(editorContext,null)
        }
    };
    CheckBoxCellType.prototype.updateEditor = function(editorContext, cellStyle, cellRect)
    {
        if(editorContext && editorContext.sheet)
        {
            $(editorContext).width(cellRect.width).height(cellRect.height);
            var sheet = editorContext.sheet;
            var $editor = $(editorContext);
            if($editor.children().length === 3)
            {
                var $span = $($editor.children()[1]);
                var text = $span.text();
                if(cellStyle.foreColor)
                    $span.css(cssColor,cellStyle.foreColor);
                var font = null;
                if(cellStyle.font)
                    font = cellStyle.font;
                else
                    font = sheet._render._getDefaultFont();
                if(sheet._zoomFactor > 1)
                    font = sheet._render._getZoomFont(font);
                $span.css(cssFont,font);
                var textWidth = sheet._getStringWidth(text,font);
                var textHeight = sheet._getFontHeight(font);
                if(text)
                {
                    var lines = text.split(/\r\n|\r|\n/);
                    textHeight *= lines.length
                }
                var marginLeft = this._getCheckBoxLeft(cellStyle,cellRect,textWidth,true),
                    marginTop = this._getCheckBoxTop(cellStyle,cellRect,textHeight,true);
                var checkbox = $editor.children()[0];
                $(checkbox).css("margin-left",marginLeft).css("margin-top",marginTop);
                var textLeft = 0,
                    textTop = 0;
                if(this._textAlign === UI.CheckBoxTextAlign.top)
                {
                    textLeft = checkbox.offsetLeft;
                    if(cellStyle.hAlign === UI.HorizontalAlign.center)
                        textLeft = checkbox.offsetLeft + this._checkboxSize / 2 - textWidth / 2;
                    else if(cellStyle.hAlign === UI.HorizontalAlign.right)
                        textLeft = checkbox.offsetLeft + this._checkboxSize - textWidth;
                    textTop = checkbox.offsetTop - textHeight
                }
                else if(this._textAlign === UI.CheckBoxTextAlign.bottom)
                {
                    textLeft = checkbox.offsetLeft;
                    if(cellStyle.hAlign === UI.HorizontalAlign.center)
                        textLeft = checkbox.offsetLeft + this._checkboxSize / 2 - textWidth / 2;
                    else if(cellStyle.hAlign === UI.HorizontalAlign.right)
                        textLeft = checkbox.offsetLeft + this._checkboxSize - textWidth;
                    textTop = checkbox.offsetTop + checkbox.offsetHeight
                }
                else if(this._textAlign === UI.CheckBoxTextAlign.left)
                {
                    textLeft = checkbox.offsetLeft - textWidth - 2;
                    textTop = checkbox.offsetTop + this._checkboxSize / 2 - textHeight / 2
                }
                else
                {
                    textLeft = checkbox.offsetLeft + checkbox.offsetWidth + 2;
                    textTop = checkbox.offsetTop + this._checkboxSize / 2 - textHeight / 2
                }
                if($.browser.msie)
                {
                    textLeft += 3;
                    textTop += 3
                }
                $span.css(cssLeft,textLeft).css(cssTop,textTop);
                if(this._isThreeState)
                {
                    var greenDiv = $editor.children()[2];
                    var left = checkbox.offsetLeft + (checkbox.offsetWidth - greenDiv.offsetWidth) / 2,
                        top = checkbox.offsetTop + (checkbox.offsetHeight - greenDiv.offsetHeight) / 2;
                    $(greenDiv).css(cssLeft,left).css(cssTop,top).toggle().toggle()
                }
                else
                    $($editor.children()[2]).hide()
            }
        }
    };
    CheckBoxCellType.prototype.caption = function(value)
    {
        if(arguments.length === 0)
            return this._caption;
        else
        {
            this._caption = value;
            return this
        }
    };
    CheckBoxCellType.prototype.textTrue = function(value)
    {
        if(arguments.length === 0)
            return this._textTrue;
        else
        {
            this._textTrue = value;
            return this
        }
    };
    CheckBoxCellType.prototype.textIndeterminate = function(value)
    {
        if(arguments.length === 0)
            return this._textIndeterminate;
        else
        {
            this._textIndeterminate = value;
            return this
        }
    };
    CheckBoxCellType.prototype.textFalse = function(value)
    {
        if(arguments.length === 0)
            return this._textFalse;
        else
        {
            this._textFalse = value;
            return this
        }
    };
    CheckBoxCellType.prototype._getDisplayText = function(value)
    {
        if(this._isThreeState && (value === null || value === undefined))
            return this._textIndeterminate || this._caption;
        else if(!!value === true)
            return this._textTrue || this._caption;
        else
            return this._textFalse || this._caption
    };
    CheckBoxCellType.prototype.textAlign = function(value)
    {
        if(arguments.length === 0)
            return this._textAlign;
        else
        {
            this._textAlign = value;
            return this
        }
    };
    CheckBoxCellType.prototype.isThreeState = function(value)
    {
        if(arguments.length === 0)
            return this._isThreeState;
        else
        {
            this._isThreeState = value;
            return this
        }
    };
    CheckBoxCellType.prototype.getHitInfo = function(x, y, row, col, cellStyle, cellRect, sheetArea)
    {
        return{
                x: x,
                y: y,
                row: row,
                col: col,
                cellRect: cellRect,
                sheetArea: sheetArea,
                isReservedLocation: true
            }
    };
    CheckBoxCellType.prototype.processMouseDown = function(hitInfo)
    {
        if(!hitInfo)
            return false;
        if(hitInfo.isReservedLocation)
            this._isMouseDownReservedLocation = true
    };
    CheckBoxCellType.prototype.processMouseUp = function(hitInfo)
    {
        if(!hitInfo)
            return false;
        var sheet = hitInfo.sheet;
        if(this._isMouseDownReservedLocation && sheet && hitInfo.isReservedLocation)
        {
            this._isMouseDownReservedLocation = false;
            var row = hitInfo.row,
                col = hitInfo.col,
                sheetArea = hitInfo.sheetArea;
            var cellNode = sheet._getModel(sheetArea).getNode(row,col,true);
            if(!cellNode._isFirstMouseUp)
            {
                var oldValue = sheet.getValue(row,col,sheetArea);
                var newValue = this._getNextState(oldValue);
                var cellEditInfo = {
                        row: row,
                        col: col,
                        newValue: newValue,
                        autoFormat: true
                    };
                var undoAction = new UI.UndoRedo.CellEditUndoAction(sheet,cellEditInfo);
                sheet._doCommand(undoAction);
                this._triggerButtonClicked(sheet,row,col);
                cellNode._isFirstMouseUp = true;
                cellNode._mouseupToken = window.setTimeout(function()
                {
                    delete cellNode._isFirstMouseUp;
                    if(cellNode._mouseupToken)
                    {
                        window.clearTimeout(cellNode._mouseupToken);
                        delete cellNode._mouseupToken
                    }
                },250);
                return true
            }
            else
            {
                delete cellNode._isFirstMouseUp;
                if(cellNode._mouseupToken)
                {
                    window.clearTimeout(cellNode._mouseupToken);
                    delete cellNode._mouseupToken
                }
            }
        }
        return false
    };
    CheckBoxCellType.prototype.processMouseLeave = function(hitInfo)
    {
        if(!hitInfo)
            return false;
        this._isMouseDownReservedLocation = false
    };
    CheckBoxCellType.prototype.isReservedKey = function(e)
    {
        this._isSpaceKeyDown = e.keyCode === UI.Key.space && !e.ctrlKey && !e.shiftKey && !e.altKey;
        return false
    };
    CheckBoxCellType.prototype._getAutoFitWidth = function(sheet, value, text, cellStyle, isFilterHeader)
    {
        var width = TextCellType.prototype._getAutoFitWidth.apply(this,[sheet,value,this._getDisplayText(value),cellStyle,isFilterHeader]);
        if(this._textAlign === UI.CheckBoxTextAlign.top || this._textAlign === UI.CheckBoxTextAlign.bottom)
            width = Math.max(width,this._checkboxSize);
        else
            width += this._checkboxSize;
        return width + 5
    };
    CheckBoxCellType.prototype._getAutoFitHeight = function(sheet, value, text, cellStyle)
    {
        var height = TextCellType.prototype._getAutoFitHeight.apply(this,[sheet,value,this._getDisplayText(value),cellStyle]);
        if(this._textAlign === UI.CheckBoxTextAlign.top || this._textAlign === UI.CheckBoxTextAlign.bottom)
            height += this._checkboxSize;
        else
            height = Math.max(height,this._checkboxSize);
        return height + 5
    };
    CheckBoxCellType.prototype._cancelDefaultKeydown = function(event)
    {
        if(event.keyCode === UI.Key.space && !event.ctrlKey && !event.shiftKey && !event.altKey)
            UI.cancelDefault(event)
    };
    CheckBoxCellType.prototype.toJSON = function()
    {
        return{
                type: UI.CellTypeKind.CheckBoxCellType,
                caption: this.caption(),
                textTrue: this.textTrue(),
                textIndeterminate: this.textIndeterminate(),
                textFalse: this.textFalse(),
                textAlign: this.textAlign(),
                isThreeState: this.isThreeState()
            }
    };
    CheckBoxCellType.prototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.caption)
            this.caption(settings.caption);
        if(settings.textTrue)
            this.textTrue(settings.textTrue);
        if(settings.textIndeterminate)
            this.textIndeterminate(settings.textIndeterminate);
        if(settings.textFalse)
            this.textFalse(settings.textFalse);
        if(settings.textAlign !== null && settings.textAlign !== undefined)
            this.textAlign(settings.textAlign);
        if(settings.isThreeState !== null && settings.isThreeState !== undefined)
            this.isThreeState(settings.isThreeState)
    };
    UI.CheckBoxCellType = CheckBoxCellType;
    function ButtonCellType()
    {
        this._marginTop = 2;
        this._marginRight = 2;
        this._marginBottom = 2;
        this._marginLeft = 2;
        this._text = "";
        this._buttonBackColor = null;
        this._buttonBorderColor = "#707070"
    }
    ButtonCellType.prototype = new TextCellType;
    ButtonCellType.prototype.paintValue = function(ctx, value, x, y, w, h, style, options)
    {
        if(!ctx)
            return;
        ctx.save();
        var startX = x + this._marginLeft,
            startY = y + this._marginTop,
            width = w - this._marginLeft - this._marginRight,
            height = h - this._marginTop - this._marginBottom;
        if(width - 2 > 0 && height - 2 > 0)
        {
            ctx.strokeStyle = this._buttonBorderColor;
            ctx.strokeRect(startX + 0.5,startY + 0.5,width - 1,height - 1);
            if(this._buttonBackColor)
                ctx.fillStyle = this._buttonBackColor;
            else
            {
                var gradient = ctx.createLinearGradient(x + w / 2,y,x + w / 2,y + h);
                gradient.addColorStop(0.125,gradientColorStop1);
                gradient.addColorStop(1.0,gradientColorStop2);
                ctx.fillStyle = gradient
            }
            ctx.fillRect(startX + 1,startY + 1,width - 2,height - 2);
            TextCellType.prototype.paintValue.apply(this,[ctx,this._text,startX,startY,width,height,style,options])
        }
        ctx.restore()
    };
    ButtonCellType.prototype.focus = function(editorContext)
    {
        if(editorContext)
            editorContext.focus()
    };
    ButtonCellType.prototype._formatEditorValue = function(editorContext, cellStyle, value)
    {
        return value
    };
    ButtonCellType.prototype.createEditorElement = function()
    {
        var editor = document.createElement("div");
        var $editor = $(editor);
        $editor.css(cssPositoin,cssAbsolute);
        $editor.css(cssMargin,zero);
        $editor.css(cssPadding,zero);
        $editor.css(cssOverflow,cssHidden);
        $editor.css(cssResize,cssNone);
        $editor.css(cssBorder,"2px #5292f7 solid");
        $editor.css(cssOutline,cssNone);
        $editor.css(cssBoxShadow,"1px 2px 5px rgba(0,0,0,0.4)");
        $editor.css(cssBoxSizing,"content-box");
        $editor.css("user-select","none");
        $editor.attr("tabindex",1);
        $editor.attr(cssAttr,"gcEditingInput");
        var $div = $("<div></div>");
        $div.css(cssPositoin,cssAbsolute);
        $div.css(cssOverflow,cssHidden);
        $div.css(cssBorder,"1px " + this._buttonBorderColor + " solid");
        $editor.append($div);
        var $span = $("<span></span>");
        $span.css(cssPositoin,cssAbsolute);
        $span.css(cssWhiteSpace,"nowrap");
        $span.css(cssFont,"normal 10pt Arial");
        $span.css("cursor","default");
        $div.append($span);
        return editor
    };
    ButtonCellType.prototype.setEditorValue = function(editorContext, value)
    {
        if(editorContext && editorContext.children[0] && editorContext.children[0].children[0])
            $(editorContext.children[0].children[0]).html(this._text)
    };
    ButtonCellType.prototype.getEditorValue = function(editorContext)
    {
        if(editorContext)
            return editorContext._oldValue;
        return null
    };
    ButtonCellType.prototype._triggerButtonClicked = function(sheet, row, col)
    {
        if(sheet.parent)
            sheet.parent._trigger(UI.Events.ButtonClicked,{
                sheet: sheet,
                sheetName: sheet._name,
                row: row,
                col: col
            })
    };
    ButtonCellType.prototype._setButtonBackgroundColor = function($button, backgroundColor)
    {
        $button.removeClass("gradientButton");
        if(backgroundColor)
            $button.css(cssBackgroundColor,backgroundColor);
        else
            $button.addClass("gradientButton")
    };
    ButtonCellType.prototype.activateEditor = function(editorContext, cellStyle, cellRect)
    {
        if(editorContext && editorContext.sheet)
        {
            var sheet = editorContext.sheet;
            var $editor = $(editorContext);
            var offset = sheet._eventHandler._getCanvasOffset();
            var originalTop = offset.top + sheet._bounds.y + cellRect.y - 2;
            var originalLeft = offset.left + sheet._bounds.x + cellRect.x - 2;
            $editor.css(cssTop,originalTop);
            $editor.css(cssLeft,originalLeft);
            $editor.css(cssBackgroundColor,cellStyle.backColor ? cellStyle.backColor : "white");
            var self = this;
            var button = editorContext.children[0];
            if(button)
            {
                var $button = $(button);
                $editor.bind(_keyDown_gcEditingInput,function(e)
                {
                    if(e.keyCode === UI.Key.space && !e.ctrlKey && !e.shiftKey && !e.altKey)
                    {
                        if(!self._isKeyDown)
                            self._setButtonBackgroundColor($button,buttonDownColor);
                        self._isKeyDown = true;
                        UI.cancelDefault(e);
                        return false
                    }
                    else if(e.keyCode === UI.Key.backspace && !e.ctrlKey && !e.shiftKey && !e.altKey)
                        UI.cancelDefault(e)
                });
                $editor.bind(_keyUp_gcEditingInput,function(e)
                {
                    if(self._isKeyDown && e.keyCode === UI.Key.space && !e.ctrlKey && !e.shiftKey && !e.altKey)
                    {
                        self._isKeyDown = false;
                        self._setButtonBackgroundColor($button,self._buttonBackColor);
                        self._triggerButtonClicked(sheet,sheet._activeRowIndex,sheet._activeColIndex)
                    }
                });
                $button.bind(_mousedown_gcEditingInput,function(e)
                {
                    if(e.button !== 2)
                    {
                        self._isMouseDown = true;
                        self._setButtonBackgroundColor($button,buttonDownColor)
                    }
                });
                $button.bind(_mouseup_gcEditingInput,function(e)
                {
                    if(e.button !== 2)
                    {
                        self._isMouseDown = false;
                        self._setButtonBackgroundColor($button,buttonHoverColor)
                    }
                });
                $button.bind(_click_gcEditingInput,function(e)
                {
                    self._triggerButtonClicked(sheet,sheet._activeRowIndex,sheet._activeColIndex)
                });
                $button.bind(_mousemove_gcEditingInput,function(e)
                {
                    if(!self._isMouseDown)
                        self._setButtonBackgroundColor($button,buttonHoverColor);
                    else
                        self._setButtonBackgroundColor($button,buttonDownColor)
                });
                $button.bind(_mouseout_gcEditingInput,function(e)
                {
                    var target = e.relatedTarget;
                    if(target !== button && target !== button.children[0])
                        self._setButtonBackgroundColor($button,self._buttonBackColor)
                })
            }
            document.body.insertBefore(editorContext,null)
        }
    };
    ButtonCellType.prototype.updateEditor = function(editorContext, cellStyle, cellRect)
    {
        if(editorContext && editorContext.sheet)
        {
            var sheet = editorContext.sheet;
            var $editor = $(editorContext);
            $editor.width(cellRect.width).height(cellRect.height);
            if(editorContext.children[0])
            {
                var $button = $(editorContext.children[0]);
                $button.width(cellRect.width - this._marginLeft - this._marginRight).height(cellRect.height - this._marginTop - this._marginBottom).css("margin-top",this._marginTop - 1).css("margin-left",this._marginLeft - 1);
                this._setButtonBackgroundColor($button,this._buttonBackColor);
                if(editorContext.children[0].children[0])
                {
                    var $span = $(editorContext.children[0].children[0]);
                    if(cellStyle.foreColor)
                        $span.css(cssColor,cellStyle.foreColor);
                    var font = null;
                    if(cellStyle.font)
                        font = cellStyle.font;
                    else
                        font = sheet._render._getDefaultFont();
                    if(sheet._zoomFactor > 1)
                        font = sheet._render._getZoomFont(font);
                    $span.css(cssFont,font);
                    var textWidth = sheet._getStringWidth(this._text,font);
                    var textHeight = sheet._getFontHeight(font);
                    if(this._text)
                    {
                        var lines = this._text.split(/\r\n|\r|\n/);
                        textHeight *= lines.length
                    }
                    var top = 0;
                    if(cellStyle.vAlign === UI.VerticalAlign.center)
                        top = $button.height() / 2 - textHeight / 2;
                    else if(cellStyle.vAlign === UI.VerticalAlign.bottom)
                        top = $button.height() - textHeight;
                    var left = 0;
                    if(cellStyle.hAlign === UI.HorizontalAlign.center)
                        left = $button.width() / 2 - textWidth / 2;
                    else if(cellStyle.hAlign === UI.HorizontalAlign.right)
                        left = $button.width() - textWidth;
                    $span.css(cssTop,top).css(cssLeft,left)
                }
            }
        }
    };
    ButtonCellType.prototype.marginTop = function(value)
    {
        if(arguments.length === 0)
            return this._marginTop;
        else
        {
            this._marginTop = value;
            return this
        }
    };
    ButtonCellType.prototype.marginRight = function(value)
    {
        if(arguments.length === 0)
            return this._marginRight;
        else
        {
            this._marginRight = value;
            return this
        }
    };
    ButtonCellType.prototype.marginBottom = function(value)
    {
        if(arguments.length === 0)
            return this._marginBottom;
        else
        {
            this._marginBottom = value;
            return this
        }
    };
    ButtonCellType.prototype.marginLeft = function(value)
    {
        if(arguments.length === 0)
            return this._marginLeft;
        else
        {
            this._marginLeft = value;
            return this
        }
    };
    ButtonCellType.prototype.text = function(value)
    {
        if(arguments.length === 0)
            return this._text;
        else
        {
            this._text = value;
            return this
        }
    };
    ButtonCellType.prototype.buttonBackColor = function(value)
    {
        if(arguments.length === 0)
            return this._buttonBackColor;
        else
        {
            this._buttonBackColor = value;
            return this
        }
    };
    ButtonCellType.prototype.getHitInfo = function(x, y, row, col, cellStyle, cellRect, sheetArea)
    {
        if(cellRect)
        {
            var leftX = cellRect.x + this._marginLeft,
                rightX = cellRect.x + cellRect.width - this._marginRight,
                topY = cellRect.y + this._marginTop,
                bottomY = cellRect.y + cellRect.height - this._marginBottom;
            var info = {
                    x: x,
                    y: y,
                    row: row,
                    col: col,
                    cellRect: cellRect,
                    sheetArea: sheetArea
                };
            if(leftX <= x && x <= rightX && topY <= y && y <= bottomY)
                info.isReservedLocation = true;
            return info
        }
        return null
    };
    ButtonCellType.prototype.processMouseDown = function(hitInfo)
    {
        if(!hitInfo)
            return false;
        var sheet = hitInfo.sheet;
        if(sheet && hitInfo.isReservedLocation)
        {
            this._originButtonBackColor = this._buttonBackColor;
            this._isMouseDownReservedLocation = true;
            this._buttonBackColor = buttonDownColor;
            sheet.repaint(hitInfo.cellRect);
            return true
        }
        return false
    };
    ButtonCellType.prototype.processMouseUp = function(hitInfo)
    {
        if(!hitInfo)
            return false;
        var sheet = hitInfo.sheet;
        if(this._isMouseDownReservedLocation && sheet && hitInfo.isReservedLocation)
        {
            this._buttonBackColor = this._originButtonBackColor;
            sheet.repaint(hitInfo.cellRect);
            this._isMouseDownReservedLocation = false;
            var row = hitInfo.row,
                col = hitInfo.col,
                sheetArea = hitInfo.sheetArea;
            var cellNode = sheet._getModel(sheetArea).getNode(row,col,true);
            if(!cellNode._isFirstMouseUp)
            {
                this._triggerButtonClicked(sheet,row,col);
                cellNode._isFirstMouseUp = true;
                cellNode._mouseupToken = window.setTimeout(function()
                {
                    delete cellNode._isFirstMouseUp;
                    if(cellNode._mouseupToken)
                    {
                        window.clearTimeout(cellNode._mouseupToken);
                        delete cellNode._mouseupToken
                    }
                },250);
                return true
            }
            else
            {
                delete cellNode._isFirstMouseUp;
                if(cellNode._mouseupToken)
                {
                    window.clearTimeout(cellNode._mouseupToken);
                    delete cellNode._mouseupToken
                }
            }
        }
        return false
    };
    ButtonCellType.prototype.processMouseLeave = function(hitInfo)
    {
        if(!hitInfo)
            return false;
        var sheet = hitInfo.sheet;
        if(sheet)
            if(this._isMouseDownReservedLocation)
            {
                this._buttonBackColor = this._originButtonBackColor;
                sheet.repaint(hitInfo.cellRect);
                this._isMouseDownReservedLocation = false
            }
    };
    ButtonCellType.prototype._getAutoFitWidth = function(sheet, value, text, cellStyle, isFilterHeader)
    {
        var width = TextCellType.prototype._getAutoFitWidth.apply(this,[sheet,value,this._text,cellStyle,isFilterHeader]);
        return width + this._marginLeft + this._marginRight
    };
    ButtonCellType.prototype._getAutoFitHeight = function(sheet, value, text, cellStyle)
    {
        var height = TextCellType.prototype._getAutoFitHeight.apply(this,[sheet,value,this._text,cellStyle]);
        return height + this._marginTop + this._marginBottom
    };
    ButtonCellType.prototype._cancelDefaultKeydown = function(event)
    {
        if(event.keyCode === UI.Key.space && !event.ctrlKey && !event.shiftKey && !event.altKey)
            UI.cancelDefault(event)
    };
    ButtonCellType.prototype.toJSON = function()
    {
        return{
                type: UI.CellTypeKind.ButtonCellType,
                marginTop: this.marginTop(),
                marginRight: this.marginRight(),
                marginBottom: this.marginBottom(),
                marginLeft: this.marginLeft(),
                text: this.text(),
                buttonBackColor: this.buttonBackColor()
            }
    };
    ButtonCellType.prototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.marginTop !== null && settings.marginTop !== undefined)
            this.marginTop(settings.marginTop);
        if(settings.marginRight !== null && settings.marginRight !== undefined)
            this.marginRight(settings.marginRight);
        if(settings.marginBottom !== null && settings.marginBottom !== undefined)
            this.marginBottom(settings.marginBottom);
        if(settings.marginLeft !== null && settings.marginLeft !== undefined)
            this.marginLeft(settings.marginLeft);
        if(settings.text)
            this.text(settings.text);
        if(settings.buttonBackColor)
            this.buttonBackColor(settings.buttonBackColor)
    };
    UI.ButtonCellType = ButtonCellType;
    var DefaultDropDownButtonWidth = 17;
    var DefaultMaxVisibleItemCount = 20;
    function ComboBoxCellType()
    {
        this._editorValueType = UI.EditorValueType.Text;
        this._items = null;
        this._hasInPlaceEditor = false
    }
    ComboBoxCellType.prototype = new TextCellType;
    ComboBoxCellType.prototype._basePaintValue = TextCellType.prototype.paintValue;
    ComboBoxCellType.prototype._baseFormat = TextCellType.prototype.format;
    ComboBoxCellType.prototype._baseParse = TextCellType.prototype.parse;
    ComboBoxCellType.prototype._baseDeactivateEditor = TextCellType.prototype.deactivateEditor;
    ComboBoxCellType.prototype.getHitInfo = function(x, y, row, col, cellStyle, cellRect, sheetArea)
    {
        if(this._hasInPlaceEditor)
            return null;
        if(cellRect)
        {
            var x2 = cellRect.x + cellRect.width;
            var info = {
                    x: x,
                    y: y,
                    row: row,
                    col: col,
                    cellStyle: cellStyle,
                    cellRect: cellRect,
                    sheetArea: sheetArea
                };
            if(x2 - DefaultDropDownButtonWidth <= x && x < x2)
                info.isReservedLocation = true;
            return info
        }
        return null
    };
    ComboBoxCellType.prototype.processMouseDown = function(hitInfo)
    {
        if(!hitInfo || !hitInfo.sheet || this._hasInPlaceEditor)
            return;
        if(hitInfo.isReservedLocation)
            this._expandItems(hitInfo)
    };
    ComboBoxCellType.prototype.isReservedKey = function(e)
    {
        if(this._hasInPlaceEditor)
        {
            var modifyKey = e.ctrlKey || e.shiftKey || e.altKey;
            if(!modifyKey && (e.which === UI.Key.enter || e.which === UI.Key.esc || e.which === UI.Key.tab || e.which === UI.Key.left || e.which === UI.Key.right))
                return false;
            return true
        }
        return false
    };
    ComboBoxCellType.prototype._expandItems = function(context, maxVisibleItem)
    {
        if(!context || !context.sheet)
            return;
        if(typeof maxVisibleItem === const_undefined)
            maxVisibleItem = DefaultMaxVisibleItemCount;
        var sheet = context.sheet;
        var row = context.row;
        var col = context.col;
        var cellRect = context.cellRect;
        var sheetArea = context.sheetArea;
        var cellStyle = context.cellStyle;
        var dummy = this.createEditorElement();
        var $dummy = $(dummy);
        var offset = sheet._eventHandler._getCanvasOffset();
        var originalTop = offset.top + sheet._bounds.y + cellRect.y + cellRect.height;
        var originalLeft = offset.left + sheet._bounds.x + cellRect.x - 1;
        $dummy.css(cssTop,originalTop);
        $dummy.css(cssLeft,originalLeft);
        $dummy.css(cssBoxSizing,"content-box");
        $dummy.css(cssWidth,cellRect.width - 2);
        $dummy.css(cssOutline,cssNone);
        if(cellStyle.backColor)
            $dummy.css(cssBackgroundColor,cellStyle.backColor);
        if(cellStyle.foreColor)
            $dummy.css(cssColor,cellStyle.foreColor);
        var font = cellStyle && cellStyle.font ? cellStyle.font : sheet._render._getDefaultFont();
        if(sheet._zoomFactor > 1)
            font = sheet._render._getZoomFont(font);
        $dummy.css(cssFont,font);
        var self = this;
        self._renderItems(dummy,self._items);
        if(dummy.length > maxVisibleItem)
            $dummy.prop("size",maxVisibleItem);
        else
            $dummy.prop("size",Math.max(2,dummy.length));
        self.setEditorValue(dummy,sheet.getValue(row,col,sheetArea));
        var closeCallback = function(cancel)
            {
                $dummy.unbind("blur");
                $dummy.unbind("click");
                $dummy.unbind("keydown");
                document.body.removeChild(dummy);
                self._hasInPlaceEditor = false;
                if(cancel)
                    sheet.repaint(cellRect);
                else
                {
                    var v = self.getEditorValue(dummy);
                    var cellEditInfo = {
                            row: row,
                            col: col,
                            newValue: v,
                            autoFormat: false
                        };
                    var undoAction = new UI.UndoRedo.CellEditUndoAction(sheet,cellEditInfo);
                    sheet._doCommand(undoAction)
                }
            };
        $dummy.bind("keydown",function(e)
        {
            if(e.ctrlKey || e.shiftKey || e.altKey)
                return;
            if(e.which === UI.Key.esc)
                closeCallback(true);
            else if(e.which === UI.Key.enter)
                closeCallback();
            else if(e.which === UI.Key.left)
            {
                e.preventDefault();
                closeCallback();
                UI.SpreadActions.navigationLeft.apply(sheet)
            }
            else if(e.which === UI.Key.right)
            {
                e.preventDefault();
                closeCallback();
                UI.SpreadActions.navigationRight.apply(sheet)
            }
        });
        $dummy.bind("click",function(e)
        {
            if(e.target.tagName === "SELECT" || e.target.tagName === "OPTION")
                closeCallback()
        });
        $dummy.bind("blur",function()
        {
            closeCallback()
        });
        document.body.insertBefore(dummy,null);
        $dummy.focus();
        self._hasInPlaceEditor = true;
        sheet.repaint(cellRect)
    };
    ComboBoxCellType.prototype.paintValue = function(ctx, value, x, y, w, h, style, options)
    {
        var btnWidth = DefaultDropDownButtonWidth;
        var txtWidth = Math.max(0,w - btnWidth - 1);
        this._basePaintValue(ctx,value,x,y,txtWidth,h,style,options);
        ctx.save();
        if(this._hasInPlaceEditor && options.sheet.getActiveRowIndex() === options.row && options.sheet.getActiveColumnIndex() === options.col)
        {
            ctx.beginPath();
            ctx.rect(x + w - btnWidth - 0.5,y + 1.5 + 0.05,btnWidth - 1,h - 3 + 0.05);
            ctx.fillStyle = "#DDEDFC";
            ctx.fill();
            ctx.lineWidth = 1;
            ctx.strokeStyle = "#7EB4EA";
            ctx.stroke();
            ctx.beginPath();
            ctx.lineWidth = 2;
            ctx.strokeStyle = "black";
            ctx.moveTo(x + w - btnWidth + 5,y + 1 + (h - 2) / 2 - 1.5);
            ctx.lineTo(x + w - btnWidth + 8.5,y + 1 + (h - 2) / 2 + 2);
            ctx.lineTo(x + w - btnWidth + 12,y + 1 + (h - 2) / 2 - 1.5);
            ctx.stroke()
        }
        else
        {
            ctx.beginPath();
            ctx.lineWidth = 2;
            ctx.fillStyle = "#B2B2B2";
            ctx.moveTo(x + w - btnWidth + 5,y + 1 + (h - 2) / 2 - 2);
            ctx.lineTo(x + w - btnWidth + 8.5,y + 1 + (h - 2) / 2 + 2);
            ctx.lineTo(x + w - btnWidth + 12,y + 1 + (h - 2) / 2 - 2);
            ctx.fill()
        }
        ctx.restore()
    };
    ComboBoxCellType.prototype._getHAlignByValueType = function(hAlign, value, text)
    {
        var aValRef = {};
        try
        {
            var af = (new UI.GeneralFormatter).GetPreferredDisplayFormatter(text,aValRef)
        }
        catch(ex){}
        return UI._SheetRender.getHAlignByValueType(hAlign,aValRef.value !== null ? aValRef.value : text)
    };
    ComboBoxCellType.prototype.createEditorElement = function()
    {
        var editor = document.createElement("select");
        var $editor = $(editor);
        $editor.css(cssPositoin,cssAbsolute);
        $editor.css(cssMargin,zero);
        $editor.attr(cssAttr,"gcEditor");
        return editor
    };
    ComboBoxCellType.prototype.setEditorValue = function(editorContext, value)
    {
        if(!editorContext || !this._items)
            return;
        var element = editorContext;
        if(this._editorValueType === UI.EditorValueType.Text)
        {
            var text = value !== undefined && value !== null ? value.toString().toLowerCase() : null;
            var index = -1,
                count = this._items.length;
            for(var i = 0; i < count; i++)
            {
                var item = this._items[i];
                var txt = item && item.hasOwnProperty("text") ? item.text : item;
                txt = txt !== undefined && txt !== null ? txt.toString().toLowerCase() : null;
                if(txt === text)
                {
                    index = i;
                    break
                }
            }
            element.selectedIndex = index
        }
        else if(this._editorValueType === UI.EditorValueType.Index)
        {
            var index = parseInt(value,10);
            if(isNaN(index))
                element.selectedIndex = -1;
            else
                element.selectedIndex = index
        }
        else if(this._editorValueType === UI.EditorValueType.Value)
        {
            var index = -1,
                count = this._items.length;
            for(var i = 0; i < count; i++)
            {
                var item = this._items[i];
                if(item && item.hasOwnProperty("value") && item.value === value)
                {
                    index = i;
                    break
                }
            }
            element.selectedIndex = index
        }
    };
    ComboBoxCellType.prototype.getEditorValue = function(editorContext)
    {
        if(!editorContext || !this._items)
            return null;
        var sheet = editorContext.sheet;
        if(sheet && sheet.isEditing())
            sheet._eventHandler._setFocus();
        var selectedIndex = editorContext.selectedIndex;
        if(selectedIndex >= 0)
            if(this._editorValueType === UI.EditorValueType.Text)
            {
                var item = this._items[selectedIndex];
                return item && item.hasOwnProperty("text") ? item.text : item
            }
            else if(this._editorValueType === UI.EditorValueType.Index)
                return selectedIndex;
            else if(this._editorValueType === UI.EditorValueType.Value)
            {
                var item = this._items[selectedIndex];
                if(item && item.hasOwnProperty("value"))
                    return item.value
            }
        return null
    };
    ComboBoxCellType.prototype.activateEditor = function(editorContext, cellStyle, cellRect)
    {
        if(!editorContext || !editorContext.sheet)
            return;
        var editor = editorContext;
        var sheet = editorContext.sheet;
        var $editor = $(editor);
        var offset = sheet._eventHandler._getCanvasOffset();
        var originalTop = offset.top + sheet._bounds.y + cellRect.y - 1;
        var originalLeft = offset.left + sheet._bounds.x + cellRect.x - 1;
        $editor.css(cssTop,originalTop);
        $editor.css(cssLeft,originalLeft);
        $editor.css(cssBoxSizing,"content-box");
        $editor.css(cssBorder,"1px solid black");
        $editor.css(cssOutline,cssNone);
        this._renderItems(editor,this._items);
        $editor.bind("keydown",function(e)
        {
            if(e.keyCode === UI.Key.backspace && !e.ctrlKey && !e.shiftKey && !e.altKey)
                UI.cancelDefault(e);
            if(e.ctrlKey || e.shiftKey || e.altKey)
                return;
            if(sheet.isEditing() && sheet.editorStatus() === UI.EditorStatus.Enter)
                if(e.which === UI.Key.left)
                {
                    e.preventDefault();
                    sheet.endEdit();
                    UI.SpreadActions.navigationLeft.apply(sheet)
                }
                else if(e.which === UI.Key.right)
                {
                    e.preventDefault();
                    sheet.endEdit();
                    UI.SpreadActions.navigationRight.apply(sheet)
                }
        });
        document.body.insertBefore(editor,null);
        $editor.focus();
        this._hasInPlaceEditor = true
    };
    ComboBoxCellType.prototype.deactivateEditor = function(editorContext)
    {
        if(editorContext)
        {
            var editor = editorContext;
            $(editor).unbind("keydown")
        }
        this._hasInPlaceEditor = false;
        this._baseDeactivateEditor(editorContext);
        if(editorContext && editorContext.sheet)
            editorContext.sheet.repaint()
    };
    ComboBoxCellType.prototype.updateEditor = function(editorContext, cellStyle, cellRect)
    {
        if(!editorContext || !editorContext.sheet)
            return;
        var editor = editorContext;
        var sheet = editorContext.sheet;
        var $editor = $(editor);
        if(cellStyle)
        {
            if(cellStyle.backColor)
                $editor.css(cssBackgroundColor,cellStyle.backColor);
            if(cellStyle.foreColor)
                $editor.css(cssColor,cellStyle.foreColor);
            var font = cellStyle.font ? cellStyle.font : sheet._render._getDefaultFont();
            if(sheet._zoomFactor > 1)
                font = sheet._render._getZoomFont(font);
            $editor.css(cssFont,font)
        }
        if(cellRect)
        {
            $editor.css(cssWidth,cellRect.width - 1);
            $editor.css(cssHeight,cellRect.height - 1)
        }
    };
    ComboBoxCellType.prototype._formatEditorValue = function(editorContext, cellStyle, value)
    {
        return value
    };
    ComboBoxCellType.prototype.format = function(value, format, conditionalForeColor)
    {
        if(this._editorValueType === UI.EditorValueType.Text);
        else if(this._editorValueType === UI.EditorValueType.Index)
        {
            var count = this._items ? this._items.length : 0;
            var index = parseInt(value);
            if(0 <= index && index < count)
            {
                var item = this._items[index];
                if(item !== undefined && item !== null)
                    value = item.hasOwnProperty("text") ? item.text : item
            }
        }
        else if(this._editorValueType === UI.EditorValueType.Value)
        {
            var count = this._items ? this._items.length : 0;
            for(var i = 0; i < count; i++)
            {
                var item = this._items[i];
                if(item && item.hasOwnProperty("value") && item.value === value)
                {
                    value = item.hasOwnProperty("text") ? item.text : item;
                    break
                }
            }
        }
        return this._baseFormat(value,format,conditionalForeColor)
    };
    ComboBoxCellType.prototype.parse = function(text, formatStr)
    {
        var text = this._baseParse(text,formatStr);
        if(this._editorValueType === UI.EditorValueType.Text)
            return text;
        else if(this._editorValueType === UI.EditorValueType.Index)
        {
            var count = this._items ? this._items.length : 0;
            for(var i = 0; i < count; i++)
            {
                var item = this._items[i];
                if(item && item.hasOwnProperty("text") && item.text === text || item === text)
                    return i
            }
        }
        else if(this._editorValueType === UI.EditorValueType.Value)
        {
            var count = this._items ? this._items.length : 0;
            for(var i = 0; i < count; i++)
            {
                var item = this._items[i];
                if(item && item.hasOwnProperty("text") && item.text === text || item === text)
                    return item.value
            }
        }
        return text
    };
    ComboBoxCellType.prototype.editorValueType = function(value)
    {
        if(arguments.length === 0)
            return this._editorValueType;
        this._editorValueType = value;
        return this
    };
    ComboBoxCellType.prototype.items = function(items)
    {
        if(arguments.length === 0)
            return this._items;
        this._items = items;
        return this
    };
    ComboBoxCellType.prototype._renderItems = function(element, items)
    {
        if(!items || !element)
            return;
        var count = items.length;
        for(var i = 0; i < count; i++)
        {
            var option = new window.Option;
            var item = items[i];
            if(item !== undefined && item !== null)
                if(item.hasOwnProperty("text"))
                    option.text = item.text;
                else
                    option.text = item;
            element.add(option)
        }
    };
    ComboBoxCellType.prototype._getAutoFitWidth = function(sheet, value, text, cellStyle, isFilterHeader)
    {
        var width = TextCellType.prototype._getAutoFitWidth.apply(this,arguments);
        return width + DefaultDropDownButtonWidth
    };
    ComboBoxCellType.prototype._getAutoFitHeight = function(sheet, value, text, cellStyle)
    {
        return TextCellType.prototype._getAutoFitHeight.apply(this,arguments)
    };
    ComboBoxCellType.prototype.toJSON = function()
    {
        return{
                type: UI.CellTypeKind.ComboBoxCellType,
                editorValueType: this.editorValueType(),
                items: this.items()
            }
    };
    ComboBoxCellType.prototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.editorValueType !== null && settings.editorValueType !== undefined)
            this.editorValueType(settings.editorValueType);
        if(settings.items !== null && settings.items !== undefined)
            this.items(settings.items)
    };
    UI.ComboBoxCellType = ComboBoxCellType;
    function HyperLinkCellType()
    {
        this._link = "";
        this._linkColor = "#0066CC";
        this._visitedLinkColor = "#3399FF";
        this._text = "";
        this._linkToolTip = "";
        this._id = HyperLinkCellType._getUniqueId()
    }
    HyperLinkCellType.prototype = new TextCellType;
    HyperLinkCellType.prototype._baseDeactivateEditor = TextCellType.prototype.deactivateEditor;
    HyperLinkCellType.prototype.paintValue = function(ctx, value, x, y, w, h, style, options)
    {
        if(this._actived || !ctx)
            return;
        var text = this._text || value;
        if(!text)
            return;
        text = text.toString();
        text = text.replace(/\s+/g," ");
        var visited = false;
        var node = options.sheet._getModel().getNode(options.row,options.col);
        if(node && node.hyperlinkInfo)
            if(this._id === node.hyperlinkInfo.id)
                visited = node.hyperlinkInfo.visited;
            else
                delete node.hyperlinkInfo;
        ctx.save();
        if(options.cellOverflowLayout)
        {
            var layout = options.cellOverflowLayout.layout;
            if(layout)
            {
                ctx.rect(layout.x,layout.y,layout.width,layout.height);
                ctx.clip();
                ctx.beginPath()
            }
        }
        else
        {
            ctx.rect(x,y,w,h);
            ctx.clip();
            ctx.beginPath()
        }
        if(visited)
            ctx.fillStyle = this._visitedLinkColor;
        else
            ctx.fillStyle = this._linkColor;
        if(style.font)
            ctx.font = style.font;
        var linkRect = this._getLinkRect(text,style.hAlign,style.vAlign,style.font,new UI.Rect(x,y,w,h));
        var adjX = 2;
        ctx.textAlign = cssLeft;
        if(style.hAlign === UI.HorizontalAlign.center)
        {
            if(linkRect.width < w - 3)
            {
                adjX = w / 2;
                ctx.textAlign = cssCenter
            }
        }
        else if(style.hAlign === UI.HorizontalAlign.right)
            if(linkRect.width < w - 3)
            {
                adjX = w - 2;
                ctx.textAlign = cssRight
            }
        var adjY = $.browser.mozilla ? 5 : 2;
        ctx.textBaseline = cssTop;
        if(style.vAlign === UI.VerticalAlign.center)
        {
            if(linkRect.height < h)
            {
                if($.browser.mozilla)
                    adjY = h / 2 + 1;
                else if($.browser.msie)
                    adjY = h / 2 + 0.5;
                else
                    adjY = h / 2;
                if(parseInt(adjY,10) !== adjY)
                    adjY = adjY + 0.5;
                ctx.textBaseline = cssMiddle
            }
        }
        else if(style.vAlign === UI.VerticalAlign.bottom)
        {
            adjY = h - 2.5;
            ctx.textBaseline = cssBottom
        }
        ctx.fillText(text,x + adjX,y + adjY);
        if(value)
        {
            var x1 = x + linkRect.x;
            var x2 = x + linkRect.x + linkRect.width;
            if(style.hAlign === UI.HorizontalAlign.right);
            else
            {
                x1 = x1 + 1;
                x2 = x2 + 1
            }
            var y1 = y + linkRect.y + linkRect.height;
            if(style.vAlign === UI.VerticalAlign.bottom)
                y1 = y1 - 0.5;
            y1 = y1 - Math.max(0,Math.round(linkRect.height / 9) - 1);
            if(parseInt(y1,10) === y1)
                y1 = y1 + 0.5;
            var y2 = y1;
            ctx.beginPath();
            if(visited)
                ctx.strokeStyle = this._visitedLinkColor;
            else
                ctx.strokeStyle = this._linkColor;
            ctx.lineWidth = 1;
            ctx.moveTo(x1,y1);
            ctx.lineTo(x2,y2);
            ctx.stroke()
        }
        ctx.restore()
    };
    HyperLinkCellType.prototype.createEditorElement = function()
    {
        var editor = document.createElement("div");
        var $editor = $(editor);
        $editor.css(cssMargin,zero);
        $editor.css(cssPadding,zero);
        $editor.css(cssBorder,cssNone);
        $editor.css(cssPositoin,cssAbsolute);
        $editor.css(cssOverflow,cssHidden);
        $editor.css(cssBoxSizing,"content-box");
        $editor.css(cssWhiteSpace,"nowrap");
        $editor.attr(cssAttr,"gcEditor");
        var a = document.createElement("a");
        var $a = $(a);
        $a.css(cssMargin,zero);
        $a.attr("target","_blank");
        $a.css(cssOutline,cssNone);
        $a.css("display","block");
        $a.appendTo($editor);
        return editor
    };
    HyperLinkCellType.prototype._triggerButtonClicked = function(sheet, row, col)
    {
        if(sheet.parent)
            if(!sheet._startEditByKeydown)
                sheet.parent._trigger(UI.Events.ButtonClicked,{
                    sheet: sheet,
                    sheetName: sheet._name,
                    row: row,
                    col: col
                })
    };
    HyperLinkCellType.prototype.activateEditor = function(editorContext, cellStyle, cellRect)
    {
        if(!editorContext || !cellRect)
            return;
        var editor = editorContext;
        var sheet = editor.sheet;
        var $editor = $(editor);
        if(sheet)
        {
            var offset = sheet._eventHandler._getCanvasOffset();
            var top = offset.top + sheet._bounds.y + cellRect.y;
            var left = offset.left + sheet._bounds.x + cellRect.x;
            $editor.css(cssTop,top).css(cssLeft,left)
        }
        $editor.bind("keydown",function(e)
        {
            if(e.keyCode === UI.Key.backspace && !e.ctrlKey && !e.shiftKey && !e.altKey)
                UI.cancelDefault(e)
        });
        var a = $editor.children()[0];
        if(a)
        {
            var self = this;
            var $a = $(a);
            $a.bind("click",function()
            {
                $a.css(cssColor,self._visitedLinkColor);
                if(sheet)
                {
                    var row = sheet._activeRowIndex;
                    var col = sheet._activeColIndex;
                    var node = sheet._getModel().getNode(row,col,true);
                    node.hyperlinkInfo = {
                        id: self._id,
                        visited: true
                    };
                    self._triggerButtonClicked(sheet,row,col)
                }
            })
        }
        var clip = document.createElement("div");
        var $clip = $(clip);
        $clip.css(cssMargin,zero);
        $clip.css(cssPositoin,cssAbsolute);
        $clip.css(cssOverflow,cssHidden);
        $clip.css(cssBoxSizing,"content-box");
        $clip.attr(cssAttr,"gcEditingInput");
        $clip.css(cssBorderWidth,"1px");
        $clip.css(cssBorderStyle,"solid");
        $clip.css(cssBackgroundColor,"transparent");
        document.body.insertBefore(clip,null);
        this._clip = clip;
        document.body.insertBefore(editor,null);
        this._actived = true
    };
    HyperLinkCellType.prototype.deactivateEditor = function(editorContext)
    {
        if(this._clip)
            document.body.removeChild(this._clip);
        if(editorContext)
        {
            $(editorContext).unbind("keydown");
            var a = $(editorContext).children()[0];
            if(a)
                $(a).unbind("click")
        }
        this._baseDeactivateEditor(editorContext);
        this._actived = false;
        if(editorContext && editorContext.sheet)
            editorContext.sheet.repaint()
    };
    HyperLinkCellType.prototype.focus = function(editorContext)
    {
        if(editorContext)
            editorContext.focus()
    };
    HyperLinkCellType.prototype.updateEditor = function(editorContext, cellStyle, cellRect)
    {
        if(!editorContext || !cellRect)
            return;
        var sheet = editorContext.sheet;
        var editor = editorContext;
        var $editor = $(editor);
        var a = $editor.children()[0];
        if(a)
        {
            var $a = $(a);
            if(this._link)
                $a.attr("href",this._link);
            var text = this._text || this._link;
            if(text)
                $a.text(text);
            $a.attr("title",this._linkToolTip);
            var visited = false;
            var node = sheet ? sheet._getModel().getNode(sheet._activeRowIndex,sheet._activeColIndex) : null;
            if(node && node.hyperlinkInfo)
                if(this._id === node.hyperlinkInfo.id)
                    visited = node.hyperlinkInfo.visited;
                else
                    delete node.hyperlinkInfo;
            if(visited)
                $a.css(cssColor,this._visitedLinkColor);
            else
                $a.css(cssColor,this._linkColor);
            var font = cellStyle && cellStyle.font ? cellStyle.font : null;
            if(!font && sheet)
                font = sheet._render._getDefaultFont();
            if(sheet && sheet._zoomFactor > 1)
                font = sheet._render._getZoomFont(font);
            if(font)
                $a.css(cssFont,font)
        }
        if(cellStyle && sheet)
        {
            $editor.css(cssMaxWidth,cellRect.width - 2);
            $editor.css(cssMaxHeight,cellRect.height - 2);
            var offset = sheet._eventHandler._getCanvasOffset();
            var top = offset.top + sheet._bounds.y + cellRect.y;
            var left = offset.left + sheet._bounds.x + cellRect.x;
            var editorPosition = this._calcPosition(cellRect,$editor.width(),$editor.height(),cellStyle.hAlign,cellStyle.vAlign);
            $editor.css(cssTop,top + editorPosition.y).css(cssLeft,left + editorPosition.x);
            if(this._clip)
            {
                var $clip = $(this._clip);
                $clip.css(cssTop,top).css(cssLeft,left);
                $clip.height(cellRect.height - 3).width(cellRect.width - 3);
                if(cellStyle.backColor)
                    $clip.css(cssBorderColor,cellStyle.backColor);
                else
                    $clip.css(cssBorderColor,UI._SheetRender.prototype._getDefaultBackground())
            }
        }
    };
    HyperLinkCellType.prototype.getEditorValue = function(editorContext)
    {
        return this._link
    };
    HyperLinkCellType.prototype.setEditorValue = function(editorContext, value)
    {
        this._link = value
    };
    HyperLinkCellType.prototype._formatEditorValue = function(editorContext, cellStyle, value)
    {
        return value
    };
    HyperLinkCellType.prototype.getHitInfo = function(x, y, row, col, cellStyle, cellRect, sheetArea)
    {
        if(!cellStyle || !cellRect)
            return null;
        return{
                x: x,
                y: y,
                row: row,
                col: col,
                cellStyle: cellStyle,
                cellRect: cellRect,
                sheetArea: sheetArea
            }
    };
    HyperLinkCellType.prototype.processMouseDown = function(hitInfo)
    {
        if(!hitInfo)
            return;
        var sheet = hitInfo.sheet;
        if(!sheet || sheet.isEditing())
            return;
        if(this._isHitHyperlink(hitInfo,sheet))
            hitInfo.isReservedLocation = true;
        else
            hitInfo.isReservedLocation = false;
        if(hitInfo.isReservedLocation)
            this._isMouseDownLink = true
    };
    HyperLinkCellType.prototype.processMouseUp = function(hitInfo)
    {
        if(!hitInfo)
            return;
        var sheet = hitInfo.sheet;
        if(!sheet || sheet.isEditing())
            return;
        if(this._isHitHyperlink(hitInfo,sheet))
            hitInfo.isReservedLocation = true;
        else
            hitInfo.isReservedLocation = false;
        if(hitInfo.isReservedLocation && this._isMouseDownLink)
        {
            var link = sheet.getValue(hitInfo.row,hitInfo.col,hitInfo.sheetArea);
            if(link)
                window.open(link);
            var node = sheet._getModel(hitInfo.sheetArea).getNode(hitInfo.row,hitInfo.col,true);
            node.hyperlinkInfo = {
                id: this._id,
                visited: true
            };
            sheet.repaint(hitInfo.cellRect);
            this._triggerButtonClicked(sheet,hitInfo.row,hitInfo.col)
        }
        this._isMouseDownLink = false
    };
    HyperLinkCellType.prototype.processMouseMove = function(hitInfo)
    {
        if(!hitInfo)
            return;
        var sheet = hitInfo.sheet;
        if(!sheet)
            return;
        if(sheet.isEditing() && sheet.getActiveRowIndex() === hitInfo.row && sheet.getActiveColumnIndex() === hitInfo.col)
            return;
        if(this._isHitHyperlink(hitInfo,sheet))
            hitInfo.isReservedLocation = true;
        else
            hitInfo.isReservedLocation = false;
        if(hitInfo.isReservedLocation)
        {
            this._showLinkToolTip(sheet,hitInfo);
            var canvas = sheet._getCanvas();
            if(canvas)
            {
                var link = sheet.getValue(hitInfo.row,hitInfo.col,hitInfo.sheetArea);
                if(link)
                    canvas.style.cursor = "pointer";
                else
                    canvas.style.cursor = "text"
            }
        }
        else
        {
            this._hideLinkToolTip();
            var canvas = sheet._getCanvas();
            if(canvas)
                canvas.style.cursor = "default"
        }
    };
    HyperLinkCellType.prototype.processMouseLeave = function(hitInfo)
    {
        this._isMouseDownLink = false;
        this._hideLinkToolTip();
        var sheet = hitInfo.sheet;
        if(sheet)
        {
            var canvas = sheet._getCanvas();
            if(canvas)
                canvas.style.cursor = "default"
        }
    };
    HyperLinkCellType.prototype._isHitHyperlink = function(hitInfo, sheet)
    {
        var cellStyle = hitInfo.cellStyle,
            cellRect = hitInfo.cellRect;
        var x = hitInfo.x,
            y = hitInfo.y;
        var text = this._text || this._link;
        if(!text)
            text = sheet.getValue(hitInfo.row,hitInfo.col,hitInfo.sheetArea);
        var font = cellStyle && cellStyle.font ? cellStyle.font : sheet._render._getDefaultFont();
        if(sheet._zoomFactor > 1)
            font = sheet._render._getZoomFont(font);
        var linkRect = this._getLinkRect(text,cellStyle.hAlign,cellStyle.vAlign,font,cellRect);
        var x1 = cellRect.x + linkRect.x;
        var x2 = Math.min(cellRect.x + linkRect.x + linkRect.width,cellRect.x + cellRect.width);
        var y1 = cellRect.y + linkRect.y;
        var y2 = Math.min(cellRect.y + linkRect.y + linkRect.height,cellRect.y + cellRect.height);
        if(x1 <= x && x < x2 && y1 <= y && y < y2)
            return true;
        return false
    };
    HyperLinkCellType.prototype._showLinkToolTip = function(sheet, hitInfo)
    {
        if(this._linkToolTip)
        {
            var tip = this._getLinkToolTipElement();
            var $tip = $(tip);
            $tip.text(this._linkToolTip);
            var offset = sheet._eventHandler._getCanvasOffset();
            var left = offset.left + hitInfo.x;
            var top = offset.top + hitInfo.y + 20;
            if($tip.parent().length === 0)
            {
                document.body.insertBefore(tip,null);
                $tip.css(cssTop,top).css(cssLeft,left)
            }
        }
    };
    HyperLinkCellType.prototype._hideLinkToolTip = function()
    {
        if(this._linkToolTipElement)
        {
            document.body.removeChild(this._linkToolTipElement);
            this._linkToolTipElement = null
        }
    };
    HyperLinkCellType.prototype._getLinkToolTipElement = function()
    {
        if(!this._linkToolTipElement)
        {
            var div = document.createElement("div");
            var $div = $(div);
            div.className = "gcHyperLinkCellTypeToolTip";
            $div.css(cssPositoin,cssAbsolute);
            $div.css(cssMargin,zero);
            $div.css(cssPadding,2);
            $div.css(cssBorder,"1px #C0C0C0 solid");
            $div.css(cssBoxShadow,"1px 2px 5px rgba(0,0,0,0.4)");
            $div.css(cssBoxSizing,"content-box");
            $div.css(cssBackgroundColor,"#FFFFFF");
            $div.css(cssFont,"9pt Arial");
            this._linkToolTipElement = div
        }
        return this._linkToolTipElement
    };
    HyperLinkCellType.prototype._getLinkRect = function(text, hAlign, vAlign, font, cellRect)
    {
        var dummy = HyperLinkCellType._getEditorElement();
        var $dummy = $(dummy);
        var a = $dummy.children()[0];
        if(a)
        {
            var $a = $(a);
            $a.text(text ? text : "");
            if(font)
                $a.css(cssFont,font)
        }
        var w = $dummy.width();
        var h = $dummy.height();
        var ret = {
                x: 0,
                y: 0,
                width: w,
                height: h
            };
        if(cellRect)
        {
            var p = this._calcPosition(cellRect,w,h,hAlign,vAlign);
            ret.x = p.x;
            ret.y = p.y
        }
        return ret
    };
    HyperLinkCellType.prototype._calcPosition = function(cellRect, editorWidth, editorHeight, hAlign, vAlign)
    {
        var x = 1;
        var y = 1;
        var width = cellRect.width - 3;
        var height = cellRect.height - 3;
        if(hAlign === UI.HorizontalAlign.center)
            x = x + Math.max(0,(width - editorWidth) / 2);
        else if(hAlign === UI.HorizontalAlign.right)
            x = x + Math.max(0,width - editorWidth);
        if(vAlign === UI.VerticalAlign.center)
            y = y + Math.max(0,(height - editorHeight) / 2);
        else if(vAlign === UI.VerticalAlign.bottom)
            y = y + Math.max(0,height - editorHeight);
        return{
                x: x,
                y: y
            }
    };
    HyperLinkCellType.prototype.linkColor = function(value)
    {
        if(arguments.length === 0)
            return this._linkColor;
        this._linkColor = value;
        return this
    };
    HyperLinkCellType.prototype.visitedLinkColor = function(value)
    {
        if(arguments.length === 0)
            return this._visitedLinkColor;
        this._visitedLinkColor = value;
        return this
    };
    HyperLinkCellType.prototype.text = function(value)
    {
        if(arguments.length === 0)
            return this._text;
        this._text = value;
        return this
    };
    HyperLinkCellType.prototype.linkToolTip = function(value)
    {
        if(arguments.length === 0)
            return this._linkToolTip;
        this._linkToolTip = value;
        return this
    };
    HyperLinkCellType.prototype._getAutoFitWidth = function(sheet, value, text, cellStyle, isFilterHeader)
    {
        var text = this._text || value;
        var font = cellStyle && cellStyle.font ? cellStyle.font : sheet._render._getDefaultFont();
        if(sheet._zoomFactor > 1)
            font = sheet._render._getZoomFont(font);
        var linkRect = this._getLinkRect(text,cellStyle.hAlign,cellStyle.vAlign,font);
        return linkRect.width
    };
    HyperLinkCellType.prototype._getAutoFitHeight = function(sheet, value, text, cellStyle)
    {
        var text = this._text || value;
        var font = cellStyle && cellStyle.font ? cellStyle.font : sheet._render._getDefaultFont();
        if(sheet._zoomFactor > 1)
            font = sheet._render._getZoomFont(font);
        var linkRect = this._getLinkRect(text,cellStyle.hAlign,cellStyle.vAlign,font);
        return linkRect.height
    };
    HyperLinkCellType.prototype.toJSON = function()
    {
        return{
                type: UI.CellTypeKind.HyperLinkCellType,
                linkColor: this._linkColor,
                visitedLinkColor: this._visitedLinkColor,
                text: this._text,
                linkToolTip: this._linkToolTip
            }
    };
    HyperLinkCellType.prototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.linkColor !== undefined && settings.linkColor !== null)
            this._linkColor = settings.linkColor;
        if(settings.visitedLinkColor !== undefined && settings.visitedLinkColor !== null)
            this._visitedLinkColor = settings.visitedLinkColor;
        if(settings.text !== undefined && settings.text !== null)
            this._text = settings.text;
        if(settings.linkToolTip !== undefined && settings.linkToolTip !== null)
            this._linkToolTip = settings.linkToolTip
    };
    HyperLinkCellType._getUniqueId = function()
    {
        if(this._id === undefined || this._id === null)
            this._id = 0;
        return"id_" + this._id++
    };
    HyperLinkCellType._getEditorElement = function()
    {
        if(!this._editorElement)
        {
            var div = HyperLinkCellType.prototype.createEditorElement();
            document.body.insertBefore(div,null);
            div.style.visibility = cssHidden;
            div.style.top = "-10000px";
            div.style.left = "-10000px";
            div.className = "gcHyperLinkCellTypeEditor";
            this._editorElement = div
        }
        return this._editorElement
    };
    UI.HyperLinkCellType = HyperLinkCellType;
    function CustomCellType(){}
    CustomCellType.prototype = new BaseCellType;
    CustomCellType.prototype.paintValue = TextCellType.prototype.paintValue;
    CustomCellType.prototype._getHAlignByValueType = TextCellType.prototype._getHAlignByValueType;
    CustomCellType.prototype.paint = function(context, value, x, y, width, height, style, extra)
    {
        TextCellType.prototype.paint.apply(this,arguments)
    };
    CustomCellType.prototype.createEditorElement = function(){};
    CustomCellType.prototype.getEditorValue = function(editorContext){};
    CustomCellType.prototype.setEditorValue = function(editorContext, value){};
    CustomCellType.prototype.activateEditor = function(editorContext, cellStyle, cellRect)
    {
        if(editorContext && editorContext.sheet)
        {
            var $editor = $(editorContext);
            var sheet = editorContext.sheet;
            var offset = sheet._eventHandler._getCanvasOffset();
            var originalTop = offset.top + sheet._bounds.y + cellRect.y - 2;
            var originalLeft = offset.left + sheet._bounds.x + cellRect.x - 2;
            $editor.css("top",originalTop);
            $editor.css("left",originalLeft);
            document.body.insertBefore(editorContext,null)
        }
    };
    CustomCellType.prototype.deactivateEditor = function(editorContext)
    {
        if(editorContext)
        {
            if(editorContext._editingLocator)
                document.body.removeChild(editorContext._editingLocator);
            document.body.removeChild(editorContext)
        }
    };
    CustomCellType.prototype.updateEditor = function(editorContext, cellStyle, cellRect)
    {
        if(editorContext)
            $(editorContext).width(cellRect.width).height(cellRect.height)
    };
    CustomCellType.prototype.getHitInfo = function(x, y, row, col, cellStyle, cellRect, sheetArea){};
    CustomCellType.prototype.processMouseDown = function(hitInfo){};
    CustomCellType.prototype.processMouseMove = function(hitInfo){};
    CustomCellType.prototype.processMouseUp = function(hitInfo){};
    CustomCellType.prototype.processMouseEnter = function(hitInfo){};
    CustomCellType.prototype.processMouseLeave = function(hitInfo){};
    CustomCellType.prototype.isReservedKey = function(event){};
    CustomCellType.prototype.isEditingValueChanged = function(oldValue, newValue)
    {
        return oldValue !== newValue
    };
    CustomCellType.prototype._getAutoFitWidth = function(sheet, value, text, cellStyle, isFilterHeader)
    {
        var width = this.getAutoFitWidth(value,text,cellStyle,sheet._zoomFactor);
        if(isFilterHeader)
        {
            var filterButtonZoom = sheet._zoomFactor > 1 ? 1 : sheet._zoomFactor;
            var buttonwidth = parseInt(sheet.defaults.rowHeight * filterButtonZoom,10);
            width += buttonwidth
        }
        return width
    };
    CustomCellType.prototype._getAutoFitHeight = function(sheet, value, text, cellStyle)
    {
        return this.getAutoFitHeight(value,text,cellStyle,sheet._zoomFactor)
    };
    CustomCellType.prototype.getAutoFitWidth = function(value, text, cellStyle, zoomFactor)
    {
        return 0
    };
    CustomCellType.prototype.getAutoFitHeight = function(value, text, cellStyle, zoomFactor)
    {
        return 0
    };
    UI.CustomCellType = CustomCellType
})(window,jQuery);
(function(window, $, undefined)
{
    "use strict";;
    var const_undefined = "undefined",
        const_number = "number",
        const_string = "string",
        const_boolean = "boolean",
        const_true = "TRUE",
        const_false = "FALSE";
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === const_undefined)
        GrapeCity = {};
    if(typeof GrapeCity.UI === const_undefined)
        GrapeCity.UI = {};
    var ui = GrapeCity.UI;
    if(typeof GrapeCity.Calc === const_undefined)
        GrapeCity.Calc = {};
    var Calc = GrapeCity.Calc;
    if(typeof Calc.Expressions === const_undefined)
        Calc.Expressions = {};
    if(typeof Calc.Operators === const_undefined)
        Calc.Operators = {};
    if(typeof Calc.Functions === const_undefined)
        Calc.Functions = {};
    var Expressions = Calc.Expressions;
    var Operators = Calc.Operators;
    var Functions = Calc.Functions;
    var const_invalidArgument = "InvalidArgument",
        const_invalidCast = "InvalidCastException",
        const_notSupported = "Not supported",
        const_formulaError = "The formula you typed contains an invalid char: ",
        const_formulaInvalidToken = "invalid token at ",
        const_formulaInvalidArray = "Invalid array at ",
        const_formulaInvalidCellRef = "Invalid cell reference or name at ",
        const_array = "ARRAY",
        const_arrayRow = "ARRAYROW";
    var const_null = "#NULL!",
        const_div0 = "#DIV/0!",
        const_value = "#VALUE!",
        const_ref = "#REF!",
        const_name = "#NAME?",
        const_na = "#N/A",
        const_num = "#NUM!";
    var ErrorList = [const_null,const_div0,const_value,const_ref,const_name,const_na,const_num];
    var ErrorCodeList = [0x00,0x07,0x0F,0x17,0x1D,0x2A,0x24];
    var LetterPows = [1,26,676];
    Calc.missingArgument = {};
    function CalcError(err, code)
    {
        this._error = err;
        this._code = code
    }
    CalcError.prototype = {toString: function()
        {
            return this._error
        }};
    CalcError.parse = function(err)
    {
        if(err !== undefined && err !== null && err !== '')
            for(var i = 0; i < ErrorList.length; i++)
            {
                var errItem = ErrorList[i];
                if(errItem === err || errItem === err.toUpperCase())
                    return new CalcError(errItem,ErrorCodeList[i])
            }
    };
    Calc.Error = CalcError;
    var CalcErrorsNull = new CalcError(const_null,0x00),
        CalcErrorsDivideByZero = new CalcError(const_div0,0x07),
        CalcErrorsValue = new CalcError(const_value,0x0F),
        CalcErrorsReference = new CalcError(const_ref,0x17),
        CalcErrorsName = new CalcError(const_name,0x1D),
        CalcErrorsNotAvailable = new CalcError(const_na,0x2A),
        CalcErrorsNumber = new CalcError(const_num,0x24);
    Calc.Errors = {
        Null: CalcErrorsNull,
        DivideByZero: CalcErrorsDivideByZero,
        Value: CalcErrorsValue,
        Reference: CalcErrorsReference,
        Name: CalcErrorsName,
        NotAvailable: CalcErrorsNotAvailable,
        Number: CalcErrorsNumber
    };
    function CalcArray(){}
    CalcArray.prototype = {
        getRowCount: function()
        {
            return 1
        },
        getColumnCount: function()
        {
            return 1
        },
        length: function()
        {
            return this.getRowCount() * this.getColumnCount()
        },
        getValueByIndex: function(index)
        {
            return this.getValue(parseInt(index / this.getColumnCount(),10),index % this.getColumnCount())
        },
        getValue: function(row, column){}
    };
    Calc.Array = CalcArray;
    function ConcreteArray(value, colCount)
    {
        this._value = value;
        this._rowCount = value.length;
        if(arguments.length < 2)
        {
            colCount = 0;
            for(var r = 0; r < value.length; r++)
                colCount = Math.max(value[r].length,colCount)
        }
        this._columnCount = colCount
    }
    ConcreteArray.prototype = new CalcArray;
    ConcreteArray.prototype.getRowCount = function()
    {
        return this._rowCount
    };
    ConcreteArray.prototype.getColumnCount = function()
    {
        return this._columnCount
    };
    ConcreteArray.prototype.getValue = function(row, column)
    {
        return this._value[row][column]
    };
    Calc._ConcreteArray = ConcreteArray;
    function SliceArray(array, row, column, rowCount, columnCount)
    {
        this._array = array;
        this._row = row;
        this._column = column;
        this._rowCount = rowCount;
        this._columnCount = columnCount
    }
    SliceArray.prototype = new CalcArray;
    SliceArray.prototype.getRowCount = function()
    {
        return this._rowCount
    };
    SliceArray.prototype.getColumnCount = function()
    {
        return this._columnCount
    };
    SliceArray.prototype.getValue = function(row, column)
    {
        return Calc._ArrayHelper.getValue(this._array,this._row + row,this._column + column)
    };
    Calc._SliceArray = SliceArray;
    function ArrayWrappingRange(value)
    {
        this._value = value
    }
    ArrayWrappingRange.prototype = new CalcArray;
    ArrayWrappingRange.prototype.getRowCount = function()
    {
        return this._value.getRowCount(0)
    };
    ArrayWrappingRange.prototype.getColumnCount = function()
    {
        return this._value.getColumnCount(0)
    };
    ArrayWrappingRange.prototype.getValue = function(row, column)
    {
        return this._value.getValue(0,row,column)
    };
    Calc._ArrayWrappingRange = ArrayWrappingRange;
    function ArrayWrappingScalar(value)
    {
        this._value = value
    }
    ArrayWrappingScalar.prototype = new CalcArray;
    ArrayWrappingScalar.prototype.getRowCount = function()
    {
        return 1
    };
    ArrayWrappingScalar.prototype.getColumnCount = function()
    {
        return 1
    };
    ArrayWrappingScalar.prototype.getValue = function(row, column)
    {
        return this._value
    };
    Calc._ArrayWrappingScalar = ArrayWrappingScalar;
    function OneDimensionalArray(_values)
    {
        this.values = _values
    }
    OneDimensionalArray.prototype = new CalcArray;
    OneDimensionalArray.prototype.getRowCount = function()
    {
        return this.values.length
    };
    OneDimensionalArray.prototype.getColumnCount = function()
    {
        return 1
    };
    OneDimensionalArray.prototype.getValue = function(row, column)
    {
        return this.values[row]
    };
    Calc._OneDimensionalArray = OneDimensionalArray;
    function CalcReference(){}
    CalcReference.prototype = {
        getRangeCount: function()
        {
            return 1
        },
        getRow: function(range){},
        getColumn: function(range){},
        getRowCount: function(range){},
        getColumnCount: function(range){},
        getValue: function(range, rowOffset, columnOffset){},
        isSubtotal: function(range, rowOffset, columnOffset){},
        getSource: function(){}
    };
    Calc.Reference = CalcReference;
    function SheetRangeReference(references)
    {
        if(!references || references.length === 0)
            throw const_invalidArgument;
        var sourceReference = null;
        for(var i = 0; i < references.length; i++)
        {
            var ref = references[i];
            if(!sourceReference)
                sourceReference = ref;
            else
            {
                if(ref.getRangeCount() !== sourceReference.getRangeCount())
                    throw const_invalidArgument;
                for(var r = 0; r < ref.getRangeCount(); r++)
                    if(ref.getColumn(r) !== sourceReference.getColumn(r) || ref.getColumnCount(r) !== sourceReference.getColumnCount(r) || ref.getRow(r) !== sourceReference.getRow(r) || ref.getRowCount(r) !== sourceReference.getRowCount(r))
                        throw const_invalidArgument;
            }
        }
        this._references = references.slice(0)
    }
    SheetRangeReference.prototype = new CalcReference;
    SheetRangeReference.prototype.getSheetCount = function()
    {
        return this._references.length
    };
    SheetRangeReference.prototype.getRangeCount = function()
    {
        return this._references[0].getRangeCount()
    };
    SheetRangeReference.prototype.getRow = function(range)
    {
        return this._references[0].getRow(range)
    };
    SheetRangeReference.prototype.getColumn = function(range)
    {
        return this._references[0].getColumn(range)
    };
    SheetRangeReference.prototype.getRowCount = function(range)
    {
        return this._references[0].getRowCount(range)
    };
    SheetRangeReference.prototype.getColumnCount = function(range)
    {
        return this._references[0].getColumnCount(range)
    };
    SheetRangeReference.prototype.getSource = function(sourceIndex)
    {
        return this._references[sourceIndex].getSource()
    };
    SheetRangeReference.prototype.getValue = function(sourceIndex, range, rowOffset, columnOffset)
    {
        if(sourceIndex === undefined || sourceIndex === null)
            sourceIndex = 0;
        return this._references[sourceIndex].getValue(range,rowOffset,columnOffset)
    };
    SheetRangeReference.prototype.isSubtotal = function(sourceIndex, range, rowOffset, columnOffset)
    {
        if(sourceIndex === undefined || sourceIndex === null)
            sourceIndex = 0;
        return this._references[sourceIndex].isSubtotal(range,rowOffset,columnOffset)
    };
    Calc._SheetRangeReference = SheetRangeReference;
    function ConcreteReference(source, ranges)
    {
        this._source = source;
        this._ranges = ranges
    }
    ConcreteReference.prototype = new CalcReference;
    ConcreteReference.prototype.getRangeCount = function()
    {
        return this._ranges.length
    };
    ConcreteReference.prototype.getRow = function(range)
    {
        return this._ranges[range].row
    };
    ConcreteReference.prototype.getColumn = function(range)
    {
        return this._ranges[range].col
    };
    ConcreteReference.prototype.getRowCount = function(range)
    {
        return this._ranges[range].rowCount
    };
    ConcreteReference.prototype.getColumnCount = function(range)
    {
        return this._ranges[range].colCount
    };
    ConcreteReference.prototype.getSource = function()
    {
        return this._source
    };
    ConcreteReference.prototype.getValue = function(range, rowOffset, columnOffset)
    {
        return this.getActualValue(range,rowOffset,columnOffset)
    };
    ConcreteReference.prototype.getActualValue = function(range, rowOffset, columnOffset)
    {
        var source = this._source;
        return source.getValue(range,this.getRow(range) - source.getRow(range) + rowOffset,this.getColumn(range) - source.getColumn(range) + columnOffset)
    };
    ConcreteReference.prototype.isSubtotal = function(range, rowOffset, columnOffset)
    {
        var source = this._source;
        return source.isSubtotal(range,this.getRow(range) - source.getRow(range) + rowOffset,this.getColumn(range) - source.getColumn(range) + columnOffset)
    };
    Calc._ConcreteReference = ConcreteReference;
    function SheetReference(sheetSource)
    {
        this._sheet = sheetSource
    }
    SheetReference.prototype = new CalcReference;
    SheetReference.prototype.getRangeCount = function()
    {
        return 1
    };
    SheetReference.prototype.getRow = function(range)
    {
        return 0
    };
    SheetReference.prototype.getColumn = function(range)
    {
        return 0
    };
    SheetReference.prototype.getRowCount = function(range)
    {
        return this._sheet.getRowCount()
    };
    SheetReference.prototype.getColumnCount = function(range)
    {
        return this._sheet.getColumnCount()
    };
    SheetReference.prototype.getValue = function(range, rowOffset, columnOffset)
    {
        if(rowOffset >= 0 && rowOffset < this._sheet.getRowCount() || columnOffset >= 0 && columnOffset < this._sheet.getColumnCount())
            return this._sheet.getValue(rowOffset,columnOffset);
        return CalcErrorsReference
    };
    SheetReference.prototype.getSource = function()
    {
        return new SheetReference(this._sheet)
    };
    SheetReference.prototype.isSubtotal = function(range, rowOffset, columnOffset)
    {
        var sheetSource = this._sheet;
        if(range === 0 && rowOffset < sheetSource.getRowCount() && columnOffset < sheetSource.getColumnCount())
        {
            var expr = sheetSource.getExpression(rowOffset,columnOffset);
            return expr && expr instanceof Expressions.FunctionExpression && expr.getFunctionName().toUpperCase() === "SUBTOTAL"
        }
        return false
    };
    Calc._SheetReference = SheetReference;
    function CalcSheetRangeWrappingCalcRange(values)
    {
        this._values = values
    }
    CalcSheetRangeWrappingCalcRange.prototype = new CalcArray;
    CalcSheetRangeWrappingCalcRange.prototype.getRowCount = function()
    {
        return this._values.length * this._values[0].getRowCount(0)
    };
    CalcSheetRangeWrappingCalcRange.prototype.getColumnCount = function()
    {
        return this._values[0].getColumnCount(0)
    };
    CalcSheetRangeWrappingCalcRange.prototype.getValue = function(row, column)
    {
        var sheetIndex = column / this._values[0].getRowCount(0);
        var rowIndex = row / this._values[0].getRowCount(0);
        return this._values[sheetIndex].getValue(0,rowIndex,column)
    };
    Calc._CalcSheetRangeWrappingCalcRange = CalcSheetRangeWrappingCalcRange;
    function TernaryCompositeConcreteReference(source, row, column, rowCount, columnCount, action, operand1, operand2)
    {
        Calc._ConcreteReference.call(source,new ui.Range(row,column,rowCount,columnCount));
        this.Action = action;
        this.Operand1 = operand1;
        this.Operand2 = operand2
    }
    TernaryCompositeConcreteReference.prototype = new Calc._ConcreteReference(null,null);
    TernaryCompositeConcreteReference.prototype.getValue = function(area, rowOffset, columnOffset)
    {
        return this.Action(this.getActualValue(area,rowOffset,columnOffset),this.Operand1,this.Operand2)
    };
    Calc._TernaryCompositeConcreteReference = TernaryCompositeConcreteReference;
    function UnaryCompositeConcreteReference(source, row, column, rowCount, columnCount, action)
    {
        Calc._ConcreteReference.call(source,row,column,rowCount,columnCount);
        this.Action = action
    }
    UnaryCompositeConcreteReference.prototype = new Calc._ConcreteReference(null,null);
    UnaryCompositeConcreteReference.prototype.getValue = function(area, rowOffset, columnOffset)
    {
        return this.Action(this.getActualValue(area,rowOffset,columnOffset))
    };
    Calc._UnaryCompositeConcreteReference = UnaryCompositeConcreteReference;
    function BinaryCompositeConcreteReference(source, row, column, rowCount, columnCount, action, operand1, switchOrder)
    {
        Calc._ConcreteReference.call(source,new ui.Range(row,column,rowCount,columnCount));
        this.Action = action;
        this.Operand1 = operand1;
        this.SwithOrder = switchOrder
    }
    BinaryCompositeConcreteReference.prototype = new Calc._ConcreteReference(null,null);
    BinaryCompositeConcreteReference.prototype.getValue = function(area, rowOffset, columnOffset)
    {
        var actualValue = this.getActualValue(area,rowOffset,columnOffset);
        return this.SwithOrder ? this.Action(this.Operand1,actualValue) : this.Action(actualValue,this.Operand1)
    };
    Calc._BinaryCompositeConcreteReference = BinaryCompositeConcreteReference;
    var Convert = {
            isNumber: function(value)
            {
                return typeof value === const_number || !isNaN(value) && !isNaN(parseFloat(value)) || value instanceof Date
            },
            isError: function(value)
            {
                return value instanceof CalcError
            },
            _isCalcArray: function(value)
            {
                return value instanceof CalcArray
            },
            _isCalcReference: function(value)
            {
                return value instanceof CalcReference
            },
            toResult: function(value)
            {
                if(isNaN(value) || !isFinite(value))
                    return CalcErrorsNumber;
                return value
            },
            _toArray: function(value)
            {
                if(value instanceof CalcArray)
                    return value;
                else if(value instanceof CalcReference)
                {
                    if(value.getRangeCount() === 1)
                        return new ArrayWrappingRange(value)
                }
                else
                    return new ArrayWrappingScalar(value)
            },
            _isNaNOrInfinite: function(value)
            {
                return isNaN(value) || !isFinite(value)
            },
            toInt: function(value)
            {
                var dVal = Convert.toDouble(value);
                if(Math.abs(dVal) < 1E+21)
                    return parseInt(dVal,10);
                throw const_invalidCast;
            },
            toDouble: function(value)
            {
                var result = null;
                if(value === undefined || value === null)
                    result = 0;
                else
                {
                    var typestr = typeof value;
                    if(typestr === const_number)
                    {
                        result = new Number(value).valueOf();
                        if(Convert._isNaNOrInfinite(result))
                            throw const_invalidCast;
                    }
                    else if(typestr === const_string)
                    {
                        var date = ui._DateTimeHelper.parseLocale(value);
                        if(date !== undefined && date !== null)
                            result = new ui._DateTimeHelper(date).toOADate();
                        else
                        {
                            result = new Number(value).valueOf();
                            if(Convert._isNaNOrInfinite(result))
                                throw const_invalidCast;
                        }
                    }
                    else if(typestr === const_boolean)
                        result = value ? 1 : 0;
                    else if(value instanceof Date)
                        result = new ui._DateTimeHelper(value).toOADate();
                    else
                        throw const_invalidCast;
                }
                return result
            },
            toBool: function(value)
            {
                if(value === undefined || value === null)
                    return false;
                else if(typeof value === const_boolean)
                    return value;
                else if(value instanceof Date)
                    return new ui._DateTimeHelper(value).toOADate() !== 0;
                else if(Convert.isNumber(value))
                    return value !== 0;
                else
                    throw const_invalidCast;
            },
            toString: function(value)
            {
                try
                {
                    if(value === undefined || value === null)
                        return'';
                    else if(typeof value === const_boolean)
                        return value ? const_true : const_false;
                    else if(typeof value === const_string)
                        return value;
                    else if(value instanceof Date)
                        return new ui._DateTimeHelper(value).localeFormat("d/M/yyyy h:mm:ss");
                    else if(value instanceof CalcArray)
                        throw const_invalidCast;
                    else
                        return value.toString()
                }
                catch(err)
                {
                    throw const_invalidCast;
                }
            },
            toDateTime: function(value)
            {
                var dateTimeHelper = ui._DateTimeHelper;
                if(value === undefined || value === null)
                    return ui._DateTimeHelper.fromOADate(0);
                else if(value instanceof Date)
                    return new Date(value.toUTCString());
                    //return value;
            else if (typeof value === const_string)
                {
                    var dateTime = dateTimeHelper.parseLocale(value);
                    if((dateTime === undefined || dateTime === null) && !isNaN(value))
                        dateTime = dateTimeHelper.fromOADate(parseFloat(value));
                    if(dateTime === undefined || dateTime === null)
                        throw const_invalidArgument;
                    return dateTime
                }
                else if(typeof value === const_number)
                    return dateTimeHelper.fromOADate(value);
                else
                    throw const_invalidCast;
            }
        };
    Calc.Convert = Convert;
    var Helper = {
            _argumentExists: function(args, index)
            {
                return args && index < args.length && args[index] !== Calc.missingArgument
            },
            getArrayValue: function(array, i, j)
            {
                if(array === undefined || array === null)
                    return CalcErrorsValue;
                if(i < array.getRowCount() && j < array.getColumnCount())
                    return array.getValue(i,j);
                if(j >= array.getColumnCount() && array.getColumnCount() === 1 && i < array.getRowCount())
                    return array.getValue(i,0);
                if(i >= array.getRowCount() && array.getColumnCount() === 1 && j < array.getColumnCount())
                    return array.getValue(0,j);
                return CalcErrorsValue
            },
            tryExtractToSingleValue: function(arg)
            {
                if(arg instanceof CalcReference)
                {
                    var array = Convert._toArray(arg);
                    if(array.getRowCount() === 1 && array.getColumnCount() === 1)
                        arg = array.getValue(0,0);
                    else
                        arg = array
                }
                if(arg instanceof CalcArray)
                    if(arg.getRowCount() === 1 && arg.getColumnCount() === 1)
                        arg = arg.getValue(0,0);
                return{
                        value: arg,
                        success: !(arg instanceof CalcArray)
                    }
            }
        };
    Calc._Helper = Helper;
    function ___invokeInnerAction(action, obj, rangeId, defVal, refOnly)
    {
        if(rangeId === undefined || rangeId === null)
            rangeId = 0;
        if(!refOnly && obj instanceof CalcArray)
            return action.call(obj);
        else if(obj instanceof CalcReference)
            return action.call(obj,rangeId);
        return defVal
    }
    Calc._ArrayHelper = {
        isArrayOrReference: function(obj)
        {
            return obj instanceof CalcArray || obj instanceof CalcReference
        },
        getRangeCount: function(obj)
        {
            return obj instanceof CalcReference ? obj.getRangeCount() : 1
        },
        getLength: function(obj, rangeId)
        {
            return ___invokeInnerAction(function()
                {
                    if(arguments.length === 0)
                        return obj.getRowCount() * obj.getColumnCount();
                    return obj.getRowCount(arguments[0]) * obj.getColumnCount(arguments[0])
                },obj,rangeId,1)
        },
        getRowCount: function(obj, rangeId)
        {
            return ___invokeInnerAction(obj.getRowCount,obj,rangeId,1)
        },
        getColumnCount: function(obj, rangeId)
        {
            return ___invokeInnerAction(obj.getColumnCount,obj,rangeId,1)
        },
        getValueByIndex: function(obj, i, rangeId)
        {
            return ___invokeInnerAction(function()
                {
                    if(arguments.length === 0)
                        return obj.getValueByIndex(i);
                    var cc = obj.getColumnCount(arguments[0]);
                    return obj.getValue(arguments[0],parseInt(i / cc,10),i % cc)
                },obj,rangeId,obj)
        },
        getValue: function(obj, row, column, rangeId)
        {
            return ___invokeInnerAction(function()
                {
                    if(arguments.length === 0)
                        return obj.getValue(row,column);
                    return obj.getValue(arguments[0],row,column)
                },obj,rangeId,obj)
        },
        isSubtotalByIndex: function(obj, i, rangeId)
        {
            return ___invokeInnerAction(function()
                {
                    var columnCount = obj.getColumnCount(arguments[0]);
                    return obj.isSubtotal(arguments[0],i / columnCount,i % columnCount)
                },obj,rangeId,false,true)
        },
        isSubtotal: function(obj, row, column, rangeId)
        {
            return ___invokeInnerAction(function()
                {
                    return obj.isSubtotal(arguments[0],row,column)
                },obj,rangeId,false,true)
        }
    };
    var NumberState = {
            None: 0,
            Sign: 1,
            Int: 2,
            Dot: 3,
            Decimal: 4,
            Exponent: 5,
            SignExponent: 6,
            ScientificNotation: 7,
            Number: 8
        };
    var LatinUnicodeCategory = {
            UppercaseLetter: 0x00,
            LowercaseLetter: 0x01,
            DecimalDigitNumber: 0x08,
            OtherNumber: 0x0a,
            SpaceSeparator: 0x0b,
            Control: 0x0e,
            ConnectorPunctuation: 0x12,
            DashPunctuation: 0x13,
            OpenPunctuation: 0x14,
            ClosePunctuation: 0x15,
            InitialQuotePunctuation: 0x16,
            FinalQuotePunctuation: 0x17,
            OtherPunctuation: 0x18,
            MathSymbol: 0x19,
            CurrencySymbol: 0x1a,
            ModifierSymbol: 0x1b,
            OtherSymbol: 0x1c
        };
    var categoryForLatin1 = [0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xb,0x18,0x18,0x18,0x1a,0x18,0x18,0x18,0x14,0x15,0x18,0x19,0x18,0x13,0x18,0x18,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x18,0x18,0x19,0x19,0x19,0x18,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x14,0x18,0x15,0x1b,0x12,0x1b,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x14,0x19,0x15,0x19,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xb,0x18,0x1a,0x1a,0x1a,0x1a,0x1c,0x1c,0x1b,0x1c,0x1,0x16,0x19,0x13,0x1c,0x1b,0x1c,0x19,0xa,0xa,0x1b,0x1,0x1c,0x18,0x1b,0xa,0x1,0x17,0xa,0xa,0xa,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x19,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1];
    function isLatin1(cc)
    {
        return cc <= 0x00ff
    }
    function isAscii(cc)
    {
        return cc <= 0x007f
    }
    function isDigit(c)
    {
        var cc = c.charCodeAt(0);
        if(!isLatin1(cc))
            return false;
        return cc >= 48 && cc <= 57
    }
    function isLetter(c)
    {
        var cc = c.charCodeAt(0);
        if(!isLatin1(cc))
            return false;
        if(!isAscii(cc))
            return categoryForLatin1[cc] === LatinUnicodeCategory.UppercaseLetter || categoryForLatin1[cc] === LatinUnicodeCategory.LowercaseLetter;
        cc |= 0x20;
        return cc >= 96 && cc <= 122
    }
    function isLetterOrDigit(c)
    {
        var cc = c.charCodeAt(0);
        if(!isLatin1(cc))
            return false;
        if(!isAscii(cc))
            return categoryForLatin1[cc] === LatinUnicodeCategory.UppercaseLetter || categoryForLatin1[cc] === LatinUnicodeCategory.LowercaseLetter;
        if(cc <= 57)
            return cc >= 48;
        cc |= 0x20;
        return cc >= 96 && cc <= 122
    }
    function isSymbol(c)
    {
        var cc = c.charCodeAt(0);
        if(!isLatin1(cc))
            return false;
        return categoryForLatin1[cc] === LatinUnicodeCategory.MathSymbol || categoryForLatin1[cc] === LatinUnicodeCategory.CurrencySymbol || categoryForLatin1[cc] === LatinUnicodeCategory.ModifierSymbol || categoryForLatin1[cc] === LatinUnicodeCategory.OtherSymbol
    }
    function isNumber(c)
    {
        var cc = c.charCodeAt(0);
        if(!isLatin1(cc))
            return false;
        if(!isAscii(cc))
            return categoryForLatin1[cc] === LatinUnicodeCategory.DecimalDigitNumber || categoryForLatin1[cc] === LatinUnicodeCategory.OtherNumber;
        return cc >= 48 && cc <= 57
    }
    function isNumber2(str, startIndex, numberDecimalSeparator)
    {
        var len = str.length;
        var state = NumberState.None;
        for(var i = startIndex; i < len; i++)
        {
            var currentChar = str.charAt(i);
            if(isDigit(currentChar))
            {
                if(state === NumberState.None)
                    state = NumberState.Int;
                else if(state === NumberState.Dot)
                    state = NumberState.Decimal;
                else if(state === NumberState.Sign)
                    state = NumberState.Int;
                else if(state === NumberState.Exponent || state === NumberState.SignExponent)
                    state = NumberState.ScientificNotation
            }
            else if(currentChar === numberDecimalSeparator)
                if(state === NumberState.Int)
                    state = NumberState.Decimal;
                else if(state === NumberState.None || state === NumberState.Sign)
                    state = NumberState.Dot;
                else
                    return{result: false};
            else if(currentChar === '+' || currentChar === '-')
                if(state === NumberState.None)
                    state = NumberState.Sign;
                else if(state === NumberState.Exponent)
                    state = NumberState.SignExponent;
                else
                    return{
                            result: true,
                            endIndex: i - 1
                        };
            else if(currentChar === 'E' || currentChar === 'e')
                if(state === NumberState.Int || state === NumberState.Decimal)
                    state = NumberState.Exponent;
                else
                    return{result: false};
            else if(state === NumberState.Int || state === NumberState.Decimal || state === NumberState.ScientificNotation)
                return{
                        result: true,
                        endIndex: i - 1
                    }
        }
        if(state === NumberState.Int || state === NumberState.Decimal || state === NumberState.ScientificNotation)
            return{
                    result: true,
                    endIndex: len - 1
                };
        return{result: false}
    }
    function Parser(option)
    {
        this._init(option)
    }
    Parser.BAND_INDEX_CONST = -2147483648;
    Parser.maxRowCount = 1048576;
    Parser.maxColumnCount = 16384;
    Parser._isLetter = isLetter;
    Parser._isLetterOrDigit = isLetterOrDigit;
    var RangeType = {
            cell: 0,
            row: 1,
            column: 2,
            sheet: 3
        };
    function getRangeForCellReference(baseRow, baseColumn)
    {
        var myRange = {
                row: this.row,
                column: this.column,
                rowCount: 1,
                columnCount: 1
            };
        if(this.rowRelative)
            myRange.row += baseRow;
        if(this.columnRelative)
            myRange.column += baseColumn;
        return myRange
    }
    function getRangeForRangeReference(baseRow, baseColumn)
    {
        var range = {},
            startRow,
            endRow,
            startCol,
            endCol;
        if(this.isFullRow && this.isFullColumn)
        {
            range.row = -1;
            range.column = -1;
            range.rowCount = -1;
            range.columnCount = -1
        }
        else if(this.isFullRow)
        {
            range.column = -1;
            range.columnCount = -1;
            startRow = this.startRowRelative ? this.startRow + baseRow : this.startRow;
            endRow = this.endRowRelative ? this.endRow + baseRow : this.endRow;
            range.row = Math.min(startRow,endRow);
            range.rowCount = Math.abs(startRow - endRow) + 1
        }
        else if(this.isFullColumn)
        {
            range.row = -1;
            range.rowCount = -1;
            startCol = this.startColumnRelative ? this.startColumn + baseColumn : this.startColumn;
            endCol = this.endColumnRelative ? this.endColumn + baseColumn : this.endColumn;
            range.column = Math.min(startCol,endCol);
            range.columnCount = Math.abs(startCol - endCol) + 1
        }
        else
        {
            startRow = this.startRowRelative ? this.startRow + baseRow : this.startRow;
            endRow = this.endRowRelative ? this.endRow + baseRow : this.endRow;
            startCol = this.startColumnRelative ? this.startColumn + baseColumn : this.startColumn;
            endCol = this.endColumnRelative ? this.endColumn + baseColumn : this.endColumn;
            range.row = Math.min(startRow,endRow);
            range.rowCount = Math.abs(startRow - endRow) + 1;
            range.column = Math.min(startCol,endCol);
            range.columnCount = Math.abs(startCol - endCol) + 1
        }
        return range
    }
    function Expression(){}
    Expressions.Expression = Expression;
    function ParenthesesExpression(arg)
    {
        this.argument = arg
    }
    ParenthesesExpression.prototype = new Expression;
    Expressions.ParenthesesExpression = ParenthesesExpression;
    function FunctionExpression(fn, arg)
    {
        this.fn = fn;
        this.args = arg
    }
    FunctionExpression.prototype = new Expression;
    FunctionExpression.prototype.argCount = function()
    {
        return this.args ? this.args.length : 0
    };
    FunctionExpression.prototype.getArg = function(index)
    {
        return this.args ? this.args[index] : null
    };
    FunctionExpression.prototype.getFunctionName = function()
    {
        return typeof this.fn === const_string ? this.fn : this.fn.name
    };
    Expressions.FunctionExpression = FunctionExpression;
    function NameExpression(name)
    {
        this.name = name
    }
    NameExpression.prototype = new Expression;
    Expressions.NameExpression = NameExpression;
    function BangNameExpression(name)
    {
        this.name = name
    }
    BangNameExpression.prototype = new NameExpression;
    Expressions.BangNameExpression = BangNameExpression;
    function ExternalNameExpression(source, name)
    {
        this.source = source;
        this.name = name
    }
    ExternalNameExpression.prototype = new Expression;
    Expressions.ExternalNameExpression = ExternalNameExpression;
    function ConstantExpression(value)
    {
        this.value = value
    }
    ConstantExpression.prototype = new Expression;
    Expressions.ConstantExpression = ConstantExpression;
    function BooleanExpression(boolValue)
    {
        this.value = boolValue
    }
    BooleanExpression.prototype = new ConstantExpression;
    Expressions.BooleanExpression = BooleanExpression;
    function DoubleExpression(num, originalNumAsString)
    {
        this.value = num;
        this.originalValue = originalNumAsString
    }
    DoubleExpression.prototype = new ConstantExpression;
    Expressions.DoubleExpression = DoubleExpression;
    function StringExpression(strValue)
    {
        this.value = strValue
    }
    StringExpression.prototype = new ConstantExpression;
    Expressions.StringExpression = StringExpression;
    function ErrorExpression(errorValue)
    {
        this.value = errorValue
    }
    ErrorExpression.prototype = new ConstantExpression;
    Expressions.ErrorExpression = ErrorExpression;
    function ExternalErrorExpression(source, errorValue)
    {
        this.source = source;
        this.value = errorValue
    }
    ExternalErrorExpression.prototype = new ErrorExpression;
    Expressions.ExternalErrorExpression = ExternalErrorExpression;
    function SheetRangeErrorExpression(startSource, endSource, errorValue)
    {
        this.startSource = startSource;
        this.endSource = endSource;
        this.value = errorValue
    }
    SheetRangeErrorExpression.prototype = new ErrorExpression;
    Expressions.SheetRangeErrorExpression = SheetRangeErrorExpression;
    function BangErrorExpression(errorValue)
    {
        this.value = errorValue
    }
    BangErrorExpression.prototype = new ErrorExpression;
    Expressions.BangErrorExpression = BangErrorExpression;
    function ArrayExpression(arrayValue)
    {
        this.value = new ConcreteArray(arrayValue)
    }
    ArrayExpression.prototype = new ConstantExpression;
    Expressions.ArrayExpression = ArrayExpression;
    function MissingArgumentExpression()
    {
        this.value = Calc.missingArgument
    }
    MissingArgumentExpression.prototype = new ConstantExpression;
    Expressions.MissingArgumentExpression = MissingArgumentExpression;
    function OperatorExpression(){}
    OperatorExpression.prototype = new Expression;
    Expressions.OperatorExpression = OperatorExpression;
    function UnaryOperatorExpression(operator, operand)
    {
        this.operator = operator;
        this.operand = operand
    }
    UnaryOperatorExpression.prototype = new OperatorExpression;
    Expressions.UnaryOperatorExpression = UnaryOperatorExpression;
    function BinaryOperatorExpression(operator, left, right)
    {
        this.operator = operator;
        this.left = left;
        this.right = right
    }
    BinaryOperatorExpression.prototype = new OperatorExpression;
    Expressions.BinaryOperatorExpression = BinaryOperatorExpression;
    function ReferenceExpression(){}
    ReferenceExpression.prototype = new Expression;
    ReferenceExpression.prototype.getRange = function(baseRow, baseColumn){};
    Expressions.ReferenceExpression = ReferenceExpression;
    function ExternalReferenceExpression(source)
    {
        this.source = source
    }
    ExternalReferenceExpression.prototype = new ReferenceExpression;
    Expressions.ExternalReferenceExpression = ExternalReferenceExpression;
    function CellExpression(row, column, rowRelative, columnRelative)
    {
        this.row = row;
        this.column = column;
        this.rowRelative = rowRelative;
        this.columnRelative = columnRelative
    }
    CellExpression.prototype = new ReferenceExpression;
    CellExpression.prototype.getRange = function(baseRow, baseColumn)
    {
        return getRangeForCellReference.call(this,baseRow,baseColumn)
    };
    Expressions.CellExpression = CellExpression;
    function BangCellExpression(row, column, rowRelative, columnRelative)
    {
        this.row = row;
        this.column = column;
        this.rowRelative = rowRelative;
        this.columnRelative = columnRelative
    }
    BangCellExpression.prototype = new CellExpression;
    Expressions.BangCellExpression = BangCellExpression;
    function ExternalCellExpression(source, row, column, rowRelative, columnRelative)
    {
        this.source = source;
        this.row = row;
        this.column = column;
        this.rowRelative = rowRelative;
        this.columnRelative = columnRelative
    }
    ExternalCellExpression.prototype = new ExternalReferenceExpression;
    ExternalCellExpression.prototype.getRange = function(baseRow, baseColumn)
    {
        return getRangeForCellReference.call(this,baseRow,baseColumn)
    };
    Expressions.ExternalCellExpression = ExternalCellExpression;
    function RangeExpression(startRow, startColumn, endRow, endColumn, startRowRelative, startColumnRelative, endRowRelative, endColumnRelative)
    {
        if(arguments.length === 0)
            this._initDefault();
        else
            this.init(startRow,startColumn,endRow,endColumn,startRowRelative,startColumnRelative,endRowRelative,endColumnRelative)
    }
    RangeExpression.prototype = new ReferenceExpression;
    RangeExpression.prototype._initDefault = function()
    {
        var bandIndex = Parser.BAND_INDEX_CONST;
        this.init(bandIndex,bandIndex,bandIndex,bandIndex,false,false,false,false);
        this.isFullRow = true;
        this.isFullColumn = true
    };
    RangeExpression.prototype.init = function(startRow, startColumn, endRow, endColumn, startRowRelative, startColumnRelative, endRowRelative, endColumnRelative)
    {
        if(startRow > endRow)
        {
            this.startRow = endRow;
            this.endRow = startRow;
            this.startRowRelative = endRowRelative;
            this.endRowRelative = startRowRelative
        }
        else
        {
            this.startRow = startRow;
            this.endRow = endRow;
            this.startRowRelative = startRowRelative;
            this.endRowRelative = endRowRelative
        }
        if(startColumn > endColumn)
        {
            this.startColumn = endColumn;
            this.endColumn = startColumn;
            this.startColumnRelative = endColumnRelative;
            this.endColumnRelative = startColumnRelative
        }
        else
        {
            this.startColumn = startColumn;
            this.endColumn = endColumn;
            this.startColumnRelative = startColumnRelative;
            this.endColumnRelative = endColumnRelative
        }
        this.isFullRow = false;
        this.isFullColumn = false
    };
    RangeExpression.prototype.initBand = function(startBandIndex, endBandIndex, startBandRelative, endBandRelative, isRowBand)
    {
        if(startBandIndex > endBandIndex)
        {
            var tmpIndex = startBandIndex;
            startBandIndex = endBandIndex;
            endBandIndex = tmpIndex;
            var tmpRelative = startBandRelative;
            startBandRelative = endBandRelative;
            endBandRelative = tmpRelative
        }
        if(isRowBand)
        {
            this.startRow = startBandIndex;
            this.startColumn = Parser.BAND_INDEX_CONST;
            this.endRow = endBandIndex;
            this.endColumn = Parser.BAND_INDEX_CONST;
            this.startRowRelative = startBandRelative;
            this.startColumnRelative = false;
            this.endRowRelative = endBandRelative;
            this.endColumnRelative = false;
            this.isFullRow = true;
            this.isFullColumn = false
        }
        else
        {
            this.startRow = Parser.BAND_INDEX_CONST;
            this.startColumn = startBandIndex;
            this.endRow = Parser.BAND_INDEX_CONST;
            this.endColumn = endBandIndex;
            this.startRowRelative = false;
            this.startColumnRelative = startBandRelative;
            this.endRowRelative = false;
            this.endColumnRelative = endBandRelative;
            this.isFullRow = false;
            this.isFullColumn = true
        }
    };
    RangeExpression.prototype._getRangeType = function()
    {
        if(this.isFullRow && this.isFullColumn)
            return RangeType.sheet;
        else if(this.isFullRow)
            return RangeType.row;
        else if(this.isFullColumn)
            return RangeType.column;
        else
            return RangeType.cell
    };
    RangeExpression.prototype.getRange = function(baseRow, baseColumn)
    {
        return getRangeForRangeReference.call(this,baseRow,baseColumn)
    };
    Expressions.RangeExpression = RangeExpression;
    function BangRangeExpression(startRow, startColumn, endRow, endColumn, startRowRelative, startColumnRelative, endRowRelative, endColumnRelative)
    {
        if(arguments.length === 0)
            this._initDefault();
        else
            this.init(startRow,startColumn,endRow,endColumn,startRowRelative,startColumnRelative,endRowRelative,endColumnRelative)
    }
    BangRangeExpression.prototype = new RangeExpression;
    Expressions.BangRangeExpression = BangRangeExpression;
    function ExternalRangeExpression(source, startRow, startColumn, endRow, endColumn, startRowRelative, startColumnRelative, endRowRelative, endColumnRelative)
    {
        if(arguments.length === 1)
            this._initDefault(source);
        else
            this.init(source,startRow,startColumn,endRow,endColumn,startRowRelative,startColumnRelative,endRowRelative,endColumnRelative)
    }
    ExternalRangeExpression.prototype = new ExternalReferenceExpression;
    ExternalRangeExpression.prototype._initDefault = function(source)
    {
        var bandIndex = Parser.BAND_INDEX_CONST;
        this.init(source,bandIndex,bandIndex,bandIndex,bandIndex,false,false,false,false);
        this.isFullRow = true;
        this.isFullColumn = true
    };
    ExternalRangeExpression.prototype.init = function(source, startRow, startColumn, endRow, endColumn, startRowRelative, startColumnRelative, endRowRelative, endColumnRelative)
    {
        this.source = source;
        if(startRow > endRow)
        {
            this.startRow = endRow;
            this.endRow = startRow;
            this.startRowRelative = endRowRelative;
            this.endRowRelative = startRowRelative
        }
        else
        {
            this.startRow = startRow;
            this.endRow = endRow;
            this.startRowRelative = startRowRelative;
            this.endRowRelative = endRowRelative
        }
        if(startColumn > endColumn)
        {
            this.startColumn = endColumn;
            this.endColumn = startColumn;
            this.startColumnRelative = endColumnRelative;
            this.endColumnRelative = startColumnRelative
        }
        else
        {
            this.startColumn = startColumn;
            this.endColumn = endColumn;
            this.startColumnRelative = startColumnRelative;
            this.endColumnRelative = endColumnRelative
        }
    };
    ExternalRangeExpression.prototype.initBand = function(source, startBandIndex, endBandIndex, startBandRelative, endBandRelative, isRowBand)
    {
        this.source = source;
        if(startBandIndex > endBandIndex)
        {
            var tmpIndex = startBandIndex;
            startBandIndex = endBandIndex;
            endBandIndex = tmpIndex;
            var tmpRelative = startBandRelative;
            startBandRelative = endBandRelative;
            endBandRelative = tmpRelative
        }
        if(isRowBand)
        {
            this.startRow = startBandIndex;
            this.startColumn = Parser.BAND_INDEX_CONST;
            this.endRow = endBandIndex;
            this.endColumn = Parser.BAND_INDEX_CONST;
            this.startRowRelative = startBandRelative;
            this.startColumnRelative = false;
            this.endRowRelative = endBandRelative;
            this.endColumnRelative = false;
            this.isFullRow = true
        }
        else
        {
            this.startRow = Parser.BAND_INDEX_CONST;
            this.startColumn = startBandIndex;
            this.endRow = Parser.BAND_INDEX_CONST;
            this.endColumn = endBandIndex;
            this.startRowRelative = false;
            this.startColumnRelative = startBandRelative;
            this.endRowRelative = false;
            this.endColumnRelative = endBandRelative;
            this.isFullColumn = true
        }
    };
    ExternalRangeExpression.prototype._getRangeType = function()
    {
        if(this.isFullRow && this.isFullColumn)
            return RangeType.sheet;
        else if(this.isFullRow)
            return RangeType.row;
        else if(this.isFullColumn)
            return RangeType.column;
        else
            return RangeType.cell
    };
    ExternalRangeExpression.prototype.getRange = function(baseRow, baseColumn)
    {
        return getRangeForRangeReference.call(this,baseRow,baseColumn)
    };
    Expressions.ExternalRangeExpression = ExternalRangeExpression;
    function SheetRangeExpression(startSource, endSource, startRow, startColumn, endRow, endColumn, startRowRelative, startColumnRelative, endRowRelative, endColumnRelative)
    {
        this.init(startSource,endSource,startRow,startColumn,endRow,endColumn,startRowRelative,startColumnRelative,endRowRelative,endColumnRelative)
    }
    SheetRangeExpression.prototype = new ReferenceExpression;
    SheetRangeExpression.prototype.init = function(startSource, endSource, startRow, startColumn, endRow, endColumn, startRowRelative, startColumnRelative, endRowRelative, endColumnRelative)
    {
        this.startSource = startSource;
        this.endSource = endSource;
        if(startRow > endRow)
        {
            this.startRow = endRow;
            this.endRow = startRow;
            this.startRowRelative = endRowRelative;
            this.endRowRelative = startRowRelative
        }
        else
        {
            this.startRow = startRow;
            this.endRow = endRow;
            this.startRowRelative = startRowRelative;
            this.endRowRelative = endRowRelative
        }
        if(startColumn > endColumn)
        {
            this.startColumn = endColumn;
            this.endColumn = startColumn;
            this.startColumnRelative = endColumnRelative;
            this.endColumnRelative = startColumnRelative
        }
        else
        {
            this.startColumn = startColumn;
            this.endColumn = endColumn;
            this.startColumnRelative = startColumnRelative;
            this.endColumnRelative = endColumnRelative
        }
    };
    SheetRangeExpression.prototype.initBand = function(startSource, endSource, startBandIndex, endBandIndex, startBandRelative, endBandRelative, isRowBand)
    {
        this.startSource = startSource;
        this.endSource = endSource;
        if(startBandIndex > endBandIndex)
        {
            var tmpIndex = startBandIndex;
            startBandIndex = endBandIndex;
            endBandIndex = tmpIndex;
            var tmpRelative = startBandRelative;
            startBandRelative = endBandRelative;
            endBandRelative = tmpRelative
        }
        if(isRowBand)
        {
            this.startRow = startBandIndex;
            this.startColumn = Parser.BAND_INDEX_CONST;
            this.endRow = endBandIndex;
            this.endColumn = Parser.BAND_INDEX_CONST;
            this.startRowRelative = startBandRelative;
            this.startColumnRelative = false;
            this.endRowRelative = endBandRelative;
            this.endColumnRelative = false;
            this.isFullRow = true
        }
        else
        {
            this.startRow = Parser.BAND_INDEX_CONST;
            this.startColumn = startBandIndex;
            this.endRow = Parser.BAND_INDEX_CONST;
            this.endColumn = endBandIndex;
            this.startRowRelative = false;
            this.startColumnRelative = startBandRelative;
            this.endRowRelative = false;
            this.endColumnRelative = endBandRelative;
            this.isFullColumn = true
        }
    };
    Expressions.SheetRangeExpression = SheetRangeExpression;
    function StructReferenceExpression(structRef)
    {
        this.structReference = structRef
    }
    StructReferenceExpression.prototype = new ReferenceExpression;
    Expressions.StructReferenceExpression = StructReferenceExpression;
    function compareStringIgnoreCase(s1, s2)
    {
        if((s1 === undefined || s1 === null) && (s2 === undefined || s2 === null))
            return true;
        if((s1 === undefined || s1 === null) && s2 !== undefined && s2 !== null || s1 !== undefined && s1 !== null && (s2 === undefined || s2 === null))
            return false;
        return s1.toLowerCase() === s2.toLowerCase()
    }
    function Operator(name)
    {
        this.name = name
    }
    Operator.prototype = {
        getName: function()
        {
            return this.name
        },
        compareTo: function(other)
        {
            return compareStringIgnoreCase(this.name,other.name)
        },
        toString: function()
        {
            return this.getName()
        }
    };
    Operators.Operator = Operator;
    function UnaryOperator(name)
    {
        this.name = name
    }
    UnaryOperator.prototype = new Operator;
    UnaryOperator.prototype._evaluateSingle = function(operand, context){};
    UnaryOperator.prototype._evaluateImp = function(operand, context)
    {
        var array = null;
        if(operand instanceof CalcArray)
            array = operand;
        if(operand instanceof CalcReference)
            array = Convert._toArray(operand);
        if(array === undefined || array === null)
            return this._evaluateSingle(operand,context);
        if(array.length() < 1)
            return CalcErrorsNumber;
        var objs = [];
        for(var r = 0; r < array.getRowCount(); r++)
        {
            objs[r] = [];
            for(var c = 0; c < array.getColumnCount(); c++)
                objs[r][c] = this._evaluateSingle(array.getValue(r,c),context)
        }
        return new ConcreteArray(objs,array.getColumnCount())
    };
    UnaryOperator.prototype.evaluate = function(operand, context){};
    Operators.UnaryOperator = UnaryOperator;
    var opPlus = Operators.plus = new UnaryOperator("+");
    opPlus.evaluate = function(operand, context)
    {
        return this._evaluateImp(operand,context)
    };
    opPlus._evaluateSingle = function(operand, context)
    {
        if(operand === undefined || operand === null)
            return 0;
        return Convert.toDouble(operand)
    };
    var opNegate = Operators.negate = new UnaryOperator("-");
    opNegate.evaluate = function(operand, context)
    {
        return this._evaluateImp(operand,context)
    };
    opNegate._evaluateSingle = function(operand, context)
    {
        if(operand === undefined || operand === null)
            return 0;
        return-Convert.toDouble(operand)
    };
    var opPercent = Operators.percent = new UnaryOperator("%");
    opPercent.evaluate = function(operand, context)
    {
        return this._evaluateImp(operand,context)
    };
    opPercent._evaluateSingle = function(operand, context)
    {
        if(operand === undefined || operand === null)
            return 0;
        return Convert.toDouble(operand) / 100
    };
    function _approxEqual(x, y)
    {
        if(x === y)
            return true;
        return Math.abs(x - y) < Math.abs(x) / (16777216.0 * 16777216.0)
    }
    function BinaryOperator(name, acceptsReference)
    {
        this.name = name;
        this.acceptsReference = acceptsReference
    }
    BinaryOperator.prototype = new Operator;
    BinaryOperator.prototype._evaluateSingle = function(left, right, context){};
    BinaryOperator.prototype.evaluate = function(left, right, context)
    {
        var isArg0Simple = Helper.tryExtractToSingleValue(left);
        var isArg1Simple = Helper.tryExtractToSingleValue(right);
        if(isArg0Simple.success && isArg1Simple.success)
            return this._evaluateSingle(isArg0Simple.value,isArg1Simple.value);
        left = isArg0Simple.value;
        right = isArg1Simple.value;
        var rowArg0 = isArg0Simple.success ? -1 : left.getRowCount();
        var colArg0 = isArg0Simple.success ? -1 : left.getColumnCount();
        var rowArg1 = isArg1Simple.success ? -1 : right.getRowCount();
        var colArg1 = isArg1Simple.success ? -1 : right.getColumnCount();
        var row = -1,
            col = -1;
        if(!isArg0Simple.success)
        {
            row = rowArg0;
            col = colArg0
        }
        if(!isArg1Simple.success)
        {
            row = row === -1 ? rowArg1 : rowArg1 > 1 ? row > 1 ? Math.min(rowArg1,row) : rowArg1 : row;
            col = col === -1 ? colArg1 : colArg1 > 1 ? col > 1 ? Math.min(colArg1,col) : colArg1 : col
        }
        var result = [];
        for(var i = 0; i < row; i++)
        {
            result[i] = [];
            for(var j = 0; j < col; j++)
                result[i][j] = this._evaluateSingle(isArg0Simple.success ? left : Helper.getArrayValue(left,i,j),isArg1Simple.success ? right : Helper.getArrayValue(right,i,j))
        }
        return new ConcreteArray(result,col)
    };
    Operators.BinaryOperator = BinaryOperator;
    var opAdd = Operators.add = new BinaryOperator("+",false);
    opAdd._evaluateSingle = function(left, right, context)
    {
        if(Convert.isError(left))
            return left;
        if(Convert.isError(right))
            return right;
        if(left === undefined || left === null)
            left = 0;
        if(right === undefined || right === null)
            right = 0;
        return Convert.toDouble(left) + Convert.toDouble(right)
    };
    var opSubtract = Operators.subtract = new BinaryOperator("-",false);
    opSubtract._evaluateSingle = function(left, right, contxt)
    {
        if(Convert.isError(left))
            return left;
        if(Convert.isError(right))
            return right;
        if(left === undefined || left === null)
            left = 0;
        if(right === undefined || right === null)
            right = 0;
        return Convert.toDouble(left) - Convert.toDouble(right)
    };
    var opMultiply = Operators.multiply = new BinaryOperator("*",false);
    opMultiply._evaluateSingle = function(left, right, context)
    {
        if(Convert.isError(left))
            return left;
        if(Convert.isError(right))
            return right;
        if(left === undefined || left === null)
            left = 0;
        if(right === undefined || right === null)
            right = 0;
        return Convert.toDouble(left) * Convert.toDouble(right)
    };
    var opDivide = Operators.divide = new BinaryOperator("/",false);
    opDivide._evaluateSingle = function(left, right, context)
    {
        if(right === undefined || right === null || right === '' || right === 0)
            return CalcErrorsDivideByZero;
        if(Convert.isError(left))
            return left;
        if(Convert.isError(right))
            return right;
        if(left === undefined || left === null)
            left = 0;
        if((right = Convert.toDouble(right)) === 0)
            return CalcErrorsDivideByZero;
        return Convert.toDouble(left) / right
    };
    var opExponent = Operators.exponent = new BinaryOperator("^",false);
    opExponent._evaluateSingle = function(left, right, context)
    {
        if(Convert.isError(left))
            return left;
        if(Convert.isError(right))
            return right;
        if(left === undefined || left === null)
            left = 0;
        if(right === undefined || right === null)
            right = 0;
        if(left === 0.0 && right < 0)
            return CalcErrorsDivideByZero;
        return Math.pow(Convert.toDouble(left),Convert.toDouble(right))
    };
    var opConcatenate = Operators.concatenate = new BinaryOperator("&",false);
    opConcatenate._evaluateSingle = function(left, right, context)
    {
        if(Convert.isError(left))
            return left;
        if(Convert.isError(right))
            return right;
        if(left === undefined || left === null)
            left = "";
        if(right === undefined || right === null)
            right = "";
        return left.toString() + right.toString()
    };
    var opEqual = Operators.equal = new BinaryOperator("=",false);
    opEqual._evaluateSingle = function(left, right, context)
    {
        if(Convert.isError(left))
            return left;
        if(Convert.isError(right))
            return right;
        if(left === undefined || left === null)
            left = 0;
        if(right === undefined || right === null)
            right = 0;
        if(typeof left === const_string || typeof right === const_string)
            return left.toString().toUpperCase() === right.toString().toUpperCase();
        else
        {
            var x = Convert.toDouble(left);
            var y = Convert.toDouble(right);
            return _approxEqual(x,y)
        }
    };
    var opNotEqual = Operators.notEqual = new BinaryOperator("<>",false);
    opNotEqual._evaluateSingle = function(left, right, context)
    {
        if(Convert.isError(left))
            return left;
        if(Convert.isError(right))
            return right;
        if(left === undefined || left === null)
            left = 0;
        if(right === undefined || right === null)
            right = 0;
        if(typeof left === const_string || typeof right === const_string)
            return left.toString().toUpperCase() !== right.toString().toUpperCase();
        else
        {
            var x = Convert.toDouble(left);
            var y = Convert.toDouble(right);
            return!_approxEqual(x,y)
        }
    };
    var opLessThan = Operators.lessThan = new BinaryOperator("<",false);
    opLessThan._evaluateSingle = function(left, right, context)
    {
        if(Convert.isError(left))
            return left;
        if(Convert.isError(right))
            return right;
        if(left === undefined || left === null)
            left = 0;
        if(right === undefined || right === null)
            right = 0;
        if(typeof left === const_string || typeof right === const_string)
            return left.toString().toUpperCase() < right.toString().toUpperCase();
        else
        {
            var x = Convert.toDouble(left);
            var y = Convert.toDouble(right);
            return x < y && !_approxEqual(x,y)
        }
    };
    var opGreaterThan = Operators.greaterThan = new BinaryOperator(">",false);
    opGreaterThan._evaluateSingle = function(left, right, context)
    {
        if(Convert.isError(left))
            return left;
        if(Convert.isError(right))
            return right;
        if(left === undefined || left === null)
            left = 0;
        if(right === undefined || right === null)
            right = 0;
        if(typeof left === const_string || typeof right === const_string)
            return left.toString().toUpperCase() > right.toString().toUpperCase();
        else
        {
            var x = Convert.toDouble(left);
            var y = Convert.toDouble(right);
            return x > y && !_approxEqual(x,y)
        }
    };
    var opLessThanOrEqual = Operators.lessThanOrEqual = new BinaryOperator("<=",false);
    opLessThanOrEqual._evaluateSingle = function(left, right, context)
    {
        if(Convert.isError(left))
            return left;
        if(Convert.isError(right))
            return right;
        if(left === undefined || left === null)
            left = 0;
        if(right === undefined || right === null)
            right = 0;
        if(typeof left === const_string || typeof right === const_string)
            return left.toString().toUpperCase() <= right.toString().toUpperCase();
        else
        {
            var x = Convert.toDouble(left);
            var y = Convert.toDouble(right);
            return x < y || _approxEqual(x,y)
        }
    };
    var opGreaterThanOrEqual = Operators.greaterThanOrEqual = new BinaryOperator(">=",false);
    opGreaterThanOrEqual._evaluateSingle = function(left, right, context)
    {
        if(Convert.isError(left))
            return left;
        if(Convert.isError(right))
            return right;
        if(left === undefined || left === null)
            left = 0;
        if(right === undefined || right === null)
            right = 0;
        if(typeof left === const_string || typeof right === const_string)
            return left.toString().toUpperCase() >= right.toString().toUpperCase();
        else
        {
            var x = Convert.toDouble(left);
            var y = Convert.toDouble(right);
            return x > y || _approxEqual(x,y)
        }
    };
    var opRange = Operators.range = new BinaryOperator(":",true);
    opRange.evaluate = function(left, right, context)
    {
        if(left === undefined || left === null || right === undefined || right === null || left.getRangeCount() !== 1 || right.getRangeCount() !== 1)
            return CalcErrorsValue;
        var leftSource = left.getSource();
        if(!leftSource || leftSource !== right.getSource())
            return CalcErrorsValue;
        var leftRow = left.getRow(0);
        var leftColumn = left.getColumn(0);
        var rightRow = right.getRow(0);
        var rightColumn = right.getColumn(0);
        var row = Math.min(leftRow,rightRow);
        var column = Math.min(leftColumn,rightColumn);
        var rowCount = Math.max(leftRow + left.getRowCount(0),rightRow + right.getRowCount(0)) - row;
        var columnCount = Math.max(leftColumn + left.getColumnCount(0),rightColumn + right.getColumnCount(0)) - column;
        return new ConcreteReference(leftSource,[{
                    row: row,
                    col: column,
                    rowCount: rowCount,
                    colCount: columnCount
                }])
    };
    var opUnion = Operators.union = new BinaryOperator(",",true);
    opUnion.evaluate = function(left, right, context)
    {
        if(left === undefined || left === null || right === undefined || right === null || left.getRangeCount() !== 1 || right.getRangeCount() !== 1)
            return CalcErrorsValue;
        var ranges = [],
            i = 0;
        for(i = 0; i < left.getRangeCount(); i++)
            ranges.push({
                row: left.getRow(i),
                col: left.getColumn(i),
                rowCount: left.getRowCount(i),
                colCount: left.getColumnCount(i)
            });
        for(i = 0; i < right.getRangeCount(); i++)
            ranges.push({
                row: right.getRow(i),
                col: right.getColumn(i),
                rowCount: right.getRowCount(i),
                colCount: right.getColumnCount(i)
            });
        return new ConcreteReference(left.getSource(),ranges)
    };
    var opIntersection = Operators.intersection = new BinaryOperator(" ",true);
    opIntersection.evaluate = function(left, right, context)
    {
        if(left === undefined || left === null || right === undefined || right === null || left.getRangeCount() !== 1 || right.getRangeCount() !== 1)
            return CalcErrorsValue;
        var leftSource = left.getSource();
        if(!leftSource || leftSource !== right.getSource())
            return CalcErrorsValue;
        var leftRow = left.getRow(0);
        var leftColumn = left.getColumn(0);
        var rightRow = right.getRow(0);
        var rightColumn = right.getColumn(0);
        var row = Math.max(leftRow,rightRow);
        var column = Math.max(leftColumn,rightColumn);
        var rowCount = Math.min(leftRow + left.getRowCount(0),rightRow + right.getRowCount(0)) - row;
        var columnCount = Math.min(leftColumn + left.getColumnCount(0),rightColumn + right.getColumnCount(0)) - column;
        return new ConcreteReference(leftSource,[{
                    row: row,
                    col: column,
                    rowCount: rowCount,
                    colCount: columnCount
                }])
    };
    function _Function(name, minArgs, maxArgs)
    {
        this.name = name;
        this.minArgs = minArgs;
        this.maxArgs = maxArgs
    }
    _Function.prototype = {
        _init: function(name, minArgs, maxArgs)
        {
            this.name = name;
            this.minArgs = minArgs;
            this.maxArgs = maxArgs
        },
        acceptsArray: function(argIndex)
        {
            return false
        },
        acceptsReference: function(argIndex)
        {
            return false
        },
        acceptsError: function(argIndex)
        {
            return false
        },
        acceptsMissingArgument: function(argIndex)
        {
            return false
        },
        isVolatile: function()
        {
            return false
        },
        isContextSensitive: function()
        {
            return false
        },
        isBranch: function()
        {
            return false
        },
        findTestArgument: function()
        {
            return-1
        },
        findBranchArgument: function(test)
        {
            return-1
        },
        evaluate: function(args){},
        evaluateWithContext: function(args, context)
        {
            return this.evaluate(args)
        },
        toString: function()
        {
            return this.name
        }
    };
    _Function._create = function(name, fnEvaluate, options)
    {
        var fn = new Functions.Function(name,0,255);
        if(fnEvaluate && typeof fnEvaluate === "function")
            fn.evaluate = fnEvaluate;
        if(options)
            for(var prop in options)
                if(options.hasOwnProperty(prop) && prop !== 'override')
                    fn[prop] = options[prop];
        return fn
    };
    Functions.Function = _Function;
    Functions._customFunctions = {};
    Functions.defineGlobalCustomFunction = function(name, fnEvaluate, options)
    {
        if(name === undefined || name === null)
            throw"Invalid function name";
        var fn;
        name = name.toUpperCase();
        if(Functions._builtInFunctions.hasOwnProperty(name))
            throw"Can not override built-in function";
        if(!Functions._customFunctions.hasOwnProperty(name))
        {
            fn = new Functions.Function(name,0,255);
            Functions._customFunctions[name] = fn
        }
        else
        {
            fn = Functions._customFunctions[name];
            if(!fn)
            {
                Functions._customFunctions[name] = new Functions.Function(name,0,255);
                fn = Functions[name.toUpperCase()]
            }
            else if(!options || !options.override)
                throw"Attempt to override function while override is not allowed";
        }
        if(fnEvaluate && typeof fnEvaluate === "function")
            fn.evaluate = fnEvaluate;
        if(options)
            for(var prop in options)
                if(options.hasOwnProperty(prop) && prop !== 'override')
                    fn[prop] = options[prop];
        return fn
    };
    Functions.findGlobalFunction = function(name)
    {
        if(name === undefined || name === null)
            return null;
        name = name.toUpperCase();
        if(Functions._builtInFunctions && Functions._builtInFunctions.hasOwnProperty(name))
            return Functions._builtInFunctions[name];
        if(Functions._customFunctions.hasOwnProperty(name))
            return Functions._customFunctions[name];
        return null
    };
    $.extend({ce: {
            createFunction: function(name, fnEvaluate, options)
            {
                return Functions.Function._create(name,fnEvaluate,options)
            },
            defineGlobalCustomFunction: function(name, fnEvaluate, options)
            {
                return Functions.defineGlobalCustomFunction(name,fnEvaluate,options)
            },
            findGlobalFunction: function(name)
            {
                return Functions.findGlobalFunction(name)
            }
        }});
    var ExcelFormulaTokenType = {
            Operand: 0,
            Function: 1,
            Subexpression: 2,
            Argument: 3,
            OperatorPrefix: 4,
            OperatorInfix: 5,
            OperatorPostfix: 6,
            Whitespace: 7,
            Unknown: 8
        };
    var ExcelFormulaTokenSubtype = {
            Nothing: 0,
            Start: 1,
            Stop: 2,
            Text: 3,
            Number: 4,
            Logical: 5,
            Error: 6,
            RangeOrName: 7,
            Concatenation: 8,
            Intersection: 9,
            Union: 10,
            RangeOp: 11
        };
    function FormulaToken(value, type, index, subType)
    {
        if(subType === undefined || subType === null)
            subType = ExcelFormulaTokenSubtype.Nothing;
        this.value = value;
        this.type = type;
        this.index = index;
        this.subType = subType;
        this.children = []
    }
    function FormulaTokenList()
    {
        this._index = -1;
        this._tokens = []
    }
    FormulaTokenList.prototype = {
        count: function()
        {
            return this._tokens.length
        },
        BOF: function()
        {
            return this._index <= 0
        },
        EOF: function()
        {
            return this._index >= this._tokens.length - 1
        },
        current: function()
        {
            return this._index === -1 ? null : this._tokens[this._index]
        },
        next: function()
        {
            return this.EOF() ? null : this._tokens[this._index + 1]
        },
        previous: function()
        {
            return this._index < 1 ? null : this._tokens[this._index - 1]
        },
        add: function(token)
        {
            this._tokens.push(token);
            return token
        },
        removeAt: function(index)
        {
            this._tokens.splice(index,1)
        },
        moveNext: function()
        {
            if(this.EOF())
                return false;
            this._index++;
            return true
        },
        reset: function()
        {
            this._index = -1
        },
        getItem: function(index)
        {
            return index < 0 || index >= this.count() ? null : this._tokens[index]
        }
    };
    function ExcelFormulaStack()
    {
        this._stack = []
    }
    ExcelFormulaStack.prototype = {
        push: function(token)
        {
            this._stack.push(token)
        },
        pop: function()
        {
            if(this._stack.length === 0)
                return null;
            var token = this._stack.pop();
            return new FormulaToken("",token.type,token.index,ExcelFormulaTokenSubtype.Stop)
        },
        current: function()
        {
            return this._stack.length > 0 ? this._stack[this._stack.length - 1] : null
        }
    };
    function readString(formula, startIndex, startSign, endSign)
    {
        var len = formula.length;
        var startSignCount = startSign === endSign ? 0 : 1;
        var text = "";
        for(var index = startIndex + 1; index < len; index++)
        {
            var currentChar = formula.charAt(index);
            if(currentChar === startSign)
                startSignCount++;
            if(currentChar === endSign)
            {
                startSignCount--;
                if(startSign === endSign && index + 2 < len && formula.charAt(index + 1) === startSign)
                {
                    text += startSign;
                    index++
                }
                else if(startSignCount !== 0)
                    text += currentChar;
                else
                    return{
                            result: text,
                            endIndex: index
                        }
            }
            else
                text += currentChar
        }
        throw"no syntax '" + endSign + "'to match the syntax '" + startSign + "'.";
    }
    function readError(formula, startIndex)
    {
        var len = formula.length;
        var surplusLen = len - startIndex;
        for(var i = 0; i < ErrorList.length; i++)
        {
            var err = ErrorList[i];
            if(startIndex + err.length > len)
                continue;
            var errStr = formula.slice(startIndex,startIndex + err.length);
            if(err.length <= surplusLen && (err === errStr || err === errStr.toUpperCase()))
                return{
                        result: err,
                        endIndex: startIndex + err.length - 1
                    }
        }
        throw"'" + formula.slice(startIndex) + "' is invalid.";
    }
    function getOpeatorPriority(op)
    {
        if(op === "^" || op === ":")
            return 1;
        else if(op === "*" || op === "/" || op === " ")
            return 2;
        else if(op === "+" || op === "-" || op === ",")
            return 3;
        else if(op === "&")
            return 4;
        else
            return 5
    }
    function getBinaryOperator(token)
    {
        if(token.subType === ExcelFormulaTokenSubtype.RangeOp)
            return Operators.range;
        else if(token.subType === ExcelFormulaTokenSubtype.Union)
            return Operators.union;
        else if(token.subType === ExcelFormulaTokenSubtype.Intersection)
            return Operators.intersection;
        var value = token.value;
        if(value === "^")
            return Operators.exponent;
        else if(value === "*")
            return Operators.multiply;
        else if(value === "/")
            return Operators.divide;
        else if(value === "+")
            return Operators.add;
        else if(value === "-")
            return Operators.subtract;
        else if(value === "&")
            return Operators.concatenate;
        else if(value === "<")
            return Operators.lessThan;
        else if(value === "=")
            return Operators.equal;
        else if(value === ">")
            return Operators.greaterThan;
        else if(value === ">=")
            return Operators.greaterThanOrEqual;
        else if(value === "<=")
            return Operators.lessThanOrEqual;
        else if(value === "<>")
            return Operators.notEqual;
        else if(value === ":" || value === "!")
            return Operators.range;
        else if(value === " " || value === ",")
            return Operators.intersection;
        return Operators.add
    }
    function getProperty(obj, name, fallback)
    {
        return obj && obj.hasOwnProperty(name) ? obj[name] : fallback
    }
    function normalizeIndex(index, maxIndex)
    {
        var firstInvalidIndex = maxIndex + 1;
        if(index < 0)
            return index % firstInvalidIndex + firstInvalidIndex;
        if(index > maxIndex)
            return index % firstInvalidIndex;
        return index
    }
    function ParserContext(useR1C1, baseRow, baseColumn, option)
    {
        this.useR1C1 = useR1C1;
        this.row = baseRow;
        this.column = baseColumn;
        this.option = option
    }
    ParserContext.prototype = {
        getExternalSource: function(bookName, sheetName){},
        getExternalSourceToken: function(source){}
    };
    Calc.ParserContext = ParserContext;
    Parser.prototype = {
        _init: function(option)
        {
            this.listSeparator = getProperty(option,'listSeparator',',');
            this.numberDecimalSeparator = getProperty(option,'numberDecimalSeparator','.');
            this.arrayGroupSeparator = getProperty(option,'arrayGroupSeparator',';');
            this._arrayArgumentSepatator = this.listSeparator === this.arrayGroupSeparator ? '\\' : this.listSeparator;
            this._operatorInfix = '\\+-*/^&=><: ' + this.listSeparator
        },
        parse: function(formula, context)
        {
            if(!context)
                context = {
                    useR1C1: false,
                    row: 0,
                    column: 0,
                    option: null
                };
            if(context.option)
                this._init(context.option);
            var tokens = this._parseToToken(formula);
            return this._buildExpressionTree(context,tokens)
        },
        unparse: function(expr, context)
        {
            if(!expr)
                return'';
            if(!context)
                context = {
                    useR1C1: false,
                    row: 0,
                    column: 0,
                    option: null
                };
            if(context.option)
                this._init(context.option);
            var formula = {content: ""};
            this._unparseExpression(expr,context,formula);
            return formula.content
        },
        _unparseExpression: function(expr, context, formula)
        {
            if(expr instanceof ConstantExpression)
                this._unparseConstantExpression(expr,context,formula);
            else if(expr instanceof OperatorExpression)
                this._unParseOperatorExpressions(expr,context,formula);
            else if(expr instanceof ReferenceExpression)
                this._unParseRefenceExpressions(expr,context,formula);
            else if(expr instanceof BangNameExpression)
            {
                formula.content += '!';
                formula.content += expr.name
            }
            else if(expr instanceof NameExpression)
                formula.content += expr.name;
            else if(expr instanceof ExternalNameExpression)
            {
                this._unparseSource(expr.source,context,formula);
                formula.content += '!';
                formula.content += expr.name
            }
            else if(expr instanceof ParenthesesExpression)
            {
                formula.content += "(";
                this._unparseExpression(expr.argument,context,formula);
                formula.content += ")"
            }
            else if(expr instanceof FunctionExpression)
            {
                formula.content += expr.getFunctionName();
                formula.content += "(";
                for(var i = 0; i < expr.argCount(); i++)
                {
                    if(i !== 0)
                        formula.content += this.listSeparator;
                    this._unparseExpression(expr.getArg(i),context,formula)
                }
                formula.content += ")"
            }
            else
                throw const_notSupported;
        },
        _unparseSource: function(source, context, formula)
        {
            if(context.getExternalSourceToken)
                formula.content += this._getValidSource(context.getExternalSourceToken(source),context.useR1C1)
        },
        _unparseSource2: function(startSource, endSource, context, formula)
        {
            var start = {content: ''};
            this._unparseSource(startSource,context,start);
            var containsSpecial = this._removeApostrophe(start);
            var workBookNameResult = this._removeWorkbook(start);
            var containsWorkbook = workBookNameResult.success;
            var workBookName = workBookNameResult.workBookName;
            var end = {content: ''};
            this._unparseSource(endSource,context,end);
            containsSpecial |= this._removeApostrophe(end);
            workBookName = this._removeWorkbook(end).workBookName;
            if(containsSpecial)
            {
                formula.content += "'";
                if(containsWorkbook)
                    formula.content += workBookName;
                formula.content += start.content;
                formula.content += ':';
                formula.content += end.content;
                formula.content += "'"
            }
            else
            {
                if(containsWorkbook)
                    formula.content += workBookName;
                formula.content += start.content;
                formula.content += ':';
                formula.content += end.content
            }
        },
        _getValidSource: function(value, isR1C1)
        {
            if(value === undefined || value === null || value === '')
                return'';
            var sheetRef = this._readSheetReference(value,isR1C1,true);
            if(sheetRef.success)
            {
                if(value.charAt(0) === "'" && value.charAt(value.length - 1) === "'")
                {
                    value = value.substr(1,value.length - 2);
                    value = value.replace("'","''");
                    value = "'" + value + "'"
                }
                return value
            }
            sheetRef = this._readSheetReference("'" + value + "'",isR1C1,true);
            if(sheetRef.success)
            {
                value = value.replace("'","''");
                value = "'" + value + "'";
                return value
            }
            return''
        },
        _unParseRefenceExpressions: function(expr, context, formula)
        {
            var baseRow = context.row,
                baseColumn = context.column;
            if(expr instanceof BangCellExpression)
            {
                formula.content += '!';
                this._unParseCell(context.useR1C1,baseRow,baseColumn,expr.row,expr.column,expr.rowRelative,expr.columnRelative,formula)
            }
            else if(expr instanceof CellExpression)
                this._unParseCell(context.useR1C1,baseRow,baseColumn,expr.row,expr.column,expr.rowRelative,expr.columnRelative,formula);
            else if(expr instanceof ExternalCellExpression)
            {
                this._unparseSource(expr.source,context,formula);
                formula.content += '!';
                this._unParseCell(context.useR1C1,baseRow,baseColumn,expr.row,expr.column,expr.rowRelative,expr.columnRelative,formula)
            }
            else if(expr instanceof BangRangeExpression)
            {
                formula.content += '!';
                this._unParseRange(context.useR1C1,baseRow,baseColumn,expr.startRow,expr.startColumn,expr.endRow,expr.endColumn,expr.startRowRelative,expr.startColumnRelative,expr.endRowRelative,expr.endColumnRelative,formula,expr._getRangeType())
            }
            else if(expr instanceof RangeExpression)
                this._unParseRange(context.useR1C1,baseRow,baseColumn,expr.startRow,expr.startColumn,expr.endRow,expr.endColumn,expr.startRowRelative,expr.startColumnRelative,expr.endRowRelative,expr.endColumnRelative,formula,expr._getRangeType());
            else if(expr instanceof ExternalRangeExpression)
            {
                this._unparseSource(expr.source,context,formula);
                formula.content += '!';
                this._unParseRange(context.useR1C1,baseRow,baseColumn,expr.startRow,expr.startColumn,expr.endRow,expr.endColumn,expr.startRowRelative,expr.startColumnRelative,expr.endRowRelative,expr.endColumnRelative,formula,expr._getRangeType())
            }
            else if(expr instanceof SheetRangeExpression)
            {
                this._unparseSource2(expr.startSource,expr.endSource,context,formula);
                formula.content += '!';
                this._unParseRange(context.useR1C1,baseRow,baseColumn,expr.startRow,expr.startColumn,expr.endRow,expr.endColumn,expr.startRowRelative,expr.startColumnRelative,expr.endRowRelative,expr.endColumnRelative,formula,expr._getRangeType())
            }
            else if(expr instanceof StructReferenceExpression)
                formula.content += expr.structReference;
            else
                throw const_notSupported;
        },
        _removeApostrophe: function(formula)
        {
            if(formula.content.charAt(formula.content.length - 1) === "'")
            {
                formula.content = formula.content.substr(1,formula.content.length - 2);
                return true
            }
            return false
        },
        _removeWorkbook: function(formula)
        {
            if(formula.content.charAt(0) !== '[')
                return{
                        success: false,
                        workBookName: ''
                    };
            var index = formula.content.indexOf(']');
            var workBookName = formula.content.substr(0,index + 1);
            formula.content = formula.content.substr(index);
            return{
                    success: true,
                    workBookName: workBookName
                }
        },
        _unParseCell: function(useR1C1, baseRow, baseColumn, row, column, rowRelative, columnRelative, formula, rangeType)
        {
            if(rangeType === undefined || rangeType === null)
                rangeType = RangeType.cell;
            var bandIndex = Parser.BAND_INDEX_CONST;
            var maxRowIndex = Parser.maxRowCount - 1;
            if((rangeType === RangeType.cell || rangeType === RangeType.row) && row !== bandIndex && row > -maxRowIndex && row <= 2 * maxRowIndex)
            {
                row += rowRelative ? baseRow : 0;
                row = normalizeIndex(row,maxRowIndex)
            }
            var maxColumnIndex = Parser.maxColumnCount - 1;
            if((rangeType === RangeType.cell || rangeType === RangeType.column) && column !== bandIndex && column > -maxColumnIndex && column <= 2 * maxColumnIndex)
            {
                column += columnRelative ? baseColumn : 0;
                column = normalizeIndex(column,maxColumnIndex)
            }
            if(this._isCellIndexsError(baseRow,baseColumn,row,column,rowRelative,columnRelative,rangeType))
            {
                formula.content += CalcErrorsReference.toString();
                return
            }
            var rowStr = "",
                colStr = '';
            if(useR1C1)
            {
                rowStr = 'R';
                if(row >= 0 && (baseRow !== row || !rowRelative))
                    if(rowRelative)
                        rowStr = rowStr + '[' + (row - baseRow) + ']';
                    else
                    {
                        row++;
                        rowStr = rowStr + row
                    }
                if(column < 0)
                {
                    formula.content += rowStr;
                    return
                }
                colStr = 'C';
                if(baseColumn !== column || !columnRelative)
                    if(columnRelative)
                        colStr = colStr + '[' + (column - baseColumn) + ']';
                    else
                    {
                        column++;
                        colStr = colStr + column
                    }
                if(rowRelative && row < 0 || !rowRelative && row <= 0)
                {
                    formula.content += colStr;
                    return
                }
                formula.content += rowStr;
                formula.content += colStr
            }
            else
            {
                row++;
                rowStr = "" + row;
                if(!rowRelative)
                    rowStr = '$' + row;
                if(column < 0)
                {
                    formula.content += rowStr;
                    return
                }
                var remainder = column;
                for(var i = 1; i < LetterPows.length; i++)
                {
                    var pow = LetterPows[LetterPows.length - i];
                    column = parseInt(remainder / pow,10);
                    remainder = remainder % pow;
                    if(column !== 0)
                        colStr = colStr + String.fromCharCode(column + 65 - 1)
                }
                colStr = colStr + String.fromCharCode(remainder + 65);
                if(!columnRelative)
                    colStr = '$' + colStr;
                if(row <= 0)
                {
                    formula.content += colStr;
                    return
                }
                formula.content += colStr;
                formula.content += rowStr
            }
        },
        _isCellIndexsError: function(baseRow, baseColumn, row, column, rowRelative, columnRelative, rangeType)
        {
            var hasError = false;
            switch(rangeType)
            {
                case RangeType.sheet:
                    break;
                case RangeType.cell:
                    hasError |= row < 0;
                    hasError |= column < 0;
                    break;
                case RangeType.row:
                    hasError |= row < 0;
                    break;
                case RangeType.column:
                    hasError |= column < 0;
                    break
            }
            return hasError
        },
        _unParseRange: function(useR1C1, baseRow, baseColumn, startRow, startColumn, endRow, endColumn, startRowRelative, startColumnRelative, endRowRelative, endColumnRelative, formula, rangeType)
        {
            if(rangeType === undefined || rangeType === null)
                rangeType = RangeType.cell;
            this._unParseCell(useR1C1,baseRow,baseColumn,startRow,startColumn,startRowRelative,startColumnRelative,formula,rangeType);
            var bandIndex = Parser.BAND_INDEX_CONST;
            if(useR1C1 && startRow === endRow && startColumn === endColumn && (endRow === bandIndex || endColumn === bandIndex))
                return;
            if(endRow !== bandIndex || endColumn !== bandIndex)
            {
                formula.content += ':';
                this._unParseCell(useR1C1,baseRow,baseColumn,endRow,endColumn,endRowRelative,endColumnRelative,formula,rangeType)
            }
        },
        _unParseOperatorExpressions: function(expr, context, formula)
        {
            if(expr instanceof UnaryOperatorExpression)
            {
                var op = expr.operator;
                if(op === Operators.percent)
                {
                    this._unparseExpression(expr.operand,context,formula);
                    formula.content += op.name
                }
                else
                {
                    formula.content += op.name;
                    this._unparseExpression(expr.operand,context,formula)
                }
            }
            else if(expr instanceof BinaryOperatorExpression)
            {
                var leftPart = {content: ""};
                var rightPart = {content: ""};
                this._unparseExpression(expr.right,context,rightPart);
                var leftIsBin = expr.left instanceof BinaryOperatorExpression;
                var rightIsBin = expr.right instanceof BinaryOperatorExpression;
                var priority = getOpeatorPriority(expr.operator.name);
                if(leftIsBin && getOpeatorPriority(expr.left.operator.name) > priority)
                {
                    leftPart.content += '(';
                    this._unparseExpression(expr.left,context,leftPart);
                    leftPart.content += ')'
                }
                else
                    this._unparseExpression(expr.left,context,leftPart);
                if(rightIsBin && getOpeatorPriority(expr.right.operator.name) > priority)
                {
                    rightPart.content += '(';
                    this._unparseExpression(expr.right,context,rightPart);
                    rightPart.content += '('
                }
                formula.content += leftPart.content;
                formula.content += expr.operator instanceof Operators.UnaryOperator ? this.listSeparator : expr.operator.name;
                formula.content += rightPart.content
            }
            else
                throw const_notSupported;
        },
        _unparseConstantExpression: function(expr, context, formula)
        {
            var errMsg = "Invalid array";
            if(expr instanceof StringExpression)
            {
                formula.content += '"';
                formula.content += expr.value;
                formula.content += '"'
            }
            else if(expr instanceof DoubleExpression)
                formula.content += expr.originalValue;
            else if(expr instanceof BooleanExpression)
                formula.content += expr.value ? const_true : const_false;
            else if(expr instanceof ArrayExpression)
            {
                formula.content += '{';
                var array = expr.value;
                if(array.getRowCount() <= 0)
                    throw errMsg;
                var bandIndex = Parser.BAND_INDEX_CONST;
                var colCount = bandIndex;
                for(var rowIndex = 0; rowIndex < array.getRowCount(); rowIndex++)
                {
                    if(rowIndex >= 1)
                        formula.content += this.arrayGroupSeparator;
                    for(var columnIndex = 0; columnIndex < array.getColumnCount(); columnIndex++)
                    {
                        if(colCount !== bandIndex && (colCount !== array.getColumnCount() || array.getColumnCount() === 0))
                            throw errMsg;
                        if(columnIndex !== 0)
                            formula.content += this._arrayArgumentSepatator;
                        var v = array.getValue(rowIndex,columnIndex);
                        if(v === undefined || v === null)
                            throw errMsg;
                        if(v instanceof Expression)
                            this._unparseExpression(v,context,formula);
                        else if(v instanceof String)
                        {
                            formula.content += '"';
                            formula.content += v;
                            formula.content += '"'
                        }
                        else if(v instanceof Boolean)
                            formula.content += v ? const_true : const_false;
                        else
                            formula.content += v.toString()
                    }
                }
                formula.content += '}'
            }
            else if(expr instanceof ExternalErrorExpression)
            {
                this._unparseSource(expr.source,context,formula);
                formula.content += '!';
                formula.content += expr.value.toString()
            }
            else if(expr instanceof SheetRangeErrorExpression)
            {
                this._unparseSource2(expr.startSource,expr.endSource,context,formula);
                formula.content += '!';
                formula.content += expr.value.toString()
            }
            else if(expr instanceof BangErrorExpression)
            {
                formula.content += '!';
                formula.content += expr.value.toString()
            }
            else if(expr instanceof ErrorExpression)
                formula.content += expr.value.toString();
            else if(expr instanceof MissingArgumentExpression);
            else
                throw const_notSupported;
        },
        _parseToToken: function(formula)
        {
            var len = formula.length;
            var tokens1 = new FormulaTokenList;
            var stack = new ExcelFormulaStack;
            var value = "";
            var const_atIndexOn = "' at index on ";
            var tokenStartIndex = 0;
            var startIndex = 0;
            while(startIndex < len && formula.charAt(startIndex) === ' ')
                startIndex++;
            if(formula.charAt(startIndex) === '=')
                startIndex++;
            for(var index = startIndex; index < len; index++)
            {
                var currentChar = formula.charAt(index);
                var rs;
                if(currentChar === '"')
                {
                    rs = readString(formula,index,'"','"');
                    tokens1.add(new FormulaToken(rs.result,ExcelFormulaTokenType.Operand,index,ExcelFormulaTokenSubtype.Text));
                    index = rs.endIndex;
                    tokenStartIndex = index + 1
                }
                else if(currentChar === "'")
                {
                    rs = readString(formula,index,"'","'");
                    value += "'";
                    value += rs.result;
                    value += "'";
                    index = rs.endIndex
                }
                else if(currentChar === '[')
                {
                    rs = readString(formula,index,'[',']');
                    value += '[';
                    value += rs.result;
                    value += ']';
                    index = rs.endIndex
                }
                else if(currentChar === '\r' || currentChar === '\n')
                    continue;
                else if(currentChar === "#")
                {
                    var re = readError(formula,index);
                    var nextChar = index < len ? formula.charAt(index + 1) : '\0';
                    if(index > 0 && formula.charAt(index - 1) === '!')
                        value += re.result;
                    else if(const_ref === re.result.toUpperCase() && index < len && (isLetterOrDigit(nextChar) || nextChar === '$'))
                        value += re.result;
                    else
                    {
                        tokens1.add(new FormulaToken(re.result,ExcelFormulaTokenType.Operand,index,ExcelFormulaTokenSubtype.Error));
                        tokenStartIndex = index + 1
                    }
                    index = re.endIndex
                }
                else if(currentChar === '+' || currentChar === '-')
                {
                    var previous = tokens1.getItem(tokens1.count() - 1);
                    if(value.length !== 0)
                    {
                        tokens1.add(new FormulaToken(value,ExcelFormulaTokenType.Operand,tokenStartIndex));
                        tokens1.add(new FormulaToken(currentChar,ExcelFormulaTokenType.OperatorInfix,index));
                        value = "";
                        tokenStartIndex = index + 1
                    }
                    else
                    {
                        if(previous && previous.type === ExcelFormulaTokenType.Whitespace)
                        {
                            tokens1.removeAt(tokens1.count() - 1);
                            previous = tokens1.getItem(tokens1.count() - 1)
                        }
                        if(previous && (previous.type === ExcelFormulaTokenType.Function && previous.subType === ExcelFormulaTokenSubtype.Stop || previous.type === ExcelFormulaTokenType.Subexpression && previous.subType === ExcelFormulaTokenSubtype.Stop || previous.type === ExcelFormulaTokenType.OperatorPostfix || previous.type === ExcelFormulaTokenType.Operand))
                        {
                            tokens1.add(new FormulaToken(currentChar,ExcelFormulaTokenType.OperatorInfix,index));
                            tokenStartIndex = index + 1
                        }
                        else
                        {
                            tokens1.add(new FormulaToken(currentChar,ExcelFormulaTokenType.OperatorPrefix,index));
                            tokenStartIndex = index + 1
                        }
                    }
                }
                else if(currentChar === this.numberDecimalSeparator || isDigit(currentChar))
                {
                    var isNum;
                    if(value.length > 0)
                        value += currentChar;
                    else if((isNum = isNumber2(formula,index,this.numberDecimalSeparator)).result)
                    {
                        var endIndex = isNum.endIndex;
                        var num = formula.slice(index,endIndex + 1);
                        while(endIndex <= len - 2 && formula.charAt(endIndex + 1) === ' ')
                            endIndex++;
                        if(endIndex <= len - 2 && formula.charAt(endIndex + 1) === ':')
                        {
                            value += num;
                            value += ':';
                            endIndex++
                        }
                        else
                            tokens1.add(new FormulaToken(num,ExcelFormulaTokenType.Operand,index,ExcelFormulaTokenSubtype.Number));
                        tokenStartIndex = index + 1;
                        index = endIndex
                    }
                    else
                        value += currentChar
                }
                else if(currentChar === '{')
                {
                    if(value.length > 0)
                        throw const_formulaError + "'{" + const_atIndexOn + index + ".";
                    stack.push(tokens1.add(new FormulaToken(const_array,ExcelFormulaTokenType.Function,index,ExcelFormulaTokenSubtype.Start)));
                    stack.push(tokens1.add(new FormulaToken(const_arrayRow,ExcelFormulaTokenType.Function,index,ExcelFormulaTokenSubtype.Start)));
                    tokenStartIndex = index + 1
                }
                else if(currentChar === this.arrayGroupSeparator && stack.current() && (stack.current().value === const_array || stack.current().value === const_arrayRow))
                {
                    if(value.length > 0)
                    {
                        tokens1.add(new FormulaToken(value,ExcelFormulaTokenType.Operand,tokenStartIndex));
                        value = ""
                    }
                    if(!stack.current())
                        throw const_formulaError + "'" + currentChar + const_atIndexOn + index + ".";
                    tokens1.add(stack.pop());
                    tokens1.add(new FormulaToken(this.listSeparator,ExcelFormulaTokenType.Argument,index));
                    stack.push(tokens1.add(new FormulaToken(const_arrayRow,ExcelFormulaTokenType.Function,index + 1,ExcelFormulaTokenSubtype.Start)));
                    tokenStartIndex = index + 1
                }
                else if(currentChar === '}')
                {
                    if(value.length > 0)
                    {
                        tokens1.add(new FormulaToken(value,ExcelFormulaTokenType.Operand,tokenStartIndex));
                        value = ""
                    }
                    if(!stack.current())
                        throw const_formulaError + "'" + currentChar + const_atIndexOn + index + ".";
                    tokens1.add(stack.pop());
                    tokens1.add(stack.pop());
                    tokenStartIndex = index + 1
                }
                else if(currentChar === ' ')
                {
                    var sIndex = index;
                    index++;
                    while(index < len && value.charAt(value.length - 1) === ' ')
                        index++;
                    if(value.length > 0 && value.charAt(value.length - 1) !== ':' && index < len && formula.charAt(index) !== ':')
                    {
                        tokens1.add(new FormulaToken(value,ExcelFormulaTokenType.Operand,tokenStartIndex));
                        value = "";
                        tokens1.add(new FormulaToken("",ExcelFormulaTokenType.Whitespace,sIndex));
                        tokenStartIndex = index
                    }
                    index--
                }
                else if(index + 2 <= len && currentChar === '<' && formula.charAt(index + 1) === '=' || currentChar === '>' && formula.charAt(index + 1) === '=' || currentChar === '<' && formula.charAt(index + 1) === '>')
                {
                    if(value.length > 0)
                    {
                        tokens1.add(new FormulaToken(value,ExcelFormulaTokenType.Operand,tokenStartIndex));
                        value = ""
                    }
                    tokens1.add(new FormulaToken(formula.slice(index,index + 2),ExcelFormulaTokenType.OperatorInfix,index,ExcelFormulaTokenSubtype.Logical));
                    index++;
                    tokenStartIndex = index + 1
                }
                else if(currentChar === '%')
                {
                    if(value.length > 0)
                    {
                        tokens1.add(new FormulaToken(value,ExcelFormulaTokenType.Operand,tokenStartIndex));
                        value = ""
                    }
                    tokens1.add(new FormulaToken(formula.charAt(index),ExcelFormulaTokenType.OperatorPostfix,index));
                    tokenStartIndex = index + 1
                }
                else if(currentChar === '+' || currentChar === '-' || currentChar === '*' || currentChar === '/' || currentChar === '=' || currentChar === '>' || currentChar === '<' || currentChar === '&' || currentChar === '^')
                {
                    if(value.length > 0)
                    {
                        tokens1.add(new FormulaToken(value,ExcelFormulaTokenType.Operand,tokenStartIndex));
                        value = ""
                    }
                    tokens1.add(new FormulaToken(currentChar,ExcelFormulaTokenType.OperatorInfix,index));
                    tokenStartIndex = index + 1
                }
                else if(currentChar === '(')
                {
                    if(value.length > 0)
                    {
                        var lastChar = value.charAt(value.length - 1);
                        if(lastChar === ':' || lastChar === this.listSeparator || lastChar === ' ')
                        {
                            value = value.slice(0,value.length - 1);
                            tokens1.add(new FormulaToken(value,ExcelFormulaTokenType.Operand,tokenStartIndex,ExcelFormulaTokenSubtype.RangeOrName));
                            tokens1.add(new FormulaToken(lastChar,ExcelFormulaTokenType.OperatorInfix,index - 1,ExcelFormulaTokenSubtype.Nothing));
                            stack.push(tokens1.add(new FormulaToken("",ExcelFormulaTokenType.Subexpression,index,ExcelFormulaTokenSubtype.Start)))
                        }
                        else
                        {
                            var refOpIndex = value.indexOf(':');
                            var refOpToken = ':';
                            if(refOpIndex === -1)
                            {
                                refOpIndex = value.indexOf(this.listSeparator);
                                refOpToken = this.listSeparator
                            }
                            if(refOpIndex === -1)
                            {
                                refOpIndex = value.indexOf(' ');
                                refOpToken = ' '
                            }
                            if(refOpIndex !== -1 && refOpIndex > 0)
                            {
                                tokens1.add(new FormulaToken(value.substr(0,refOpIndex),ExcelFormulaTokenType.Operand,tokenStartIndex,ExcelFormulaTokenSubtype.RangeOrName));
                                tokens1.add(new FormulaToken(refOpToken,ExcelFormulaTokenType.OperatorInfix,refOpIndex,ExcelFormulaTokenSubtype.Nothing));
                                value = value.slice(refOpIndex + 1);
                                stack.push(tokens1.add(new FormulaToken(value.toUpperCase(),ExcelFormulaTokenType.Function,index,ExcelFormulaTokenSubtype.Start)))
                            }
                            else
                                stack.push(tokens1.add(new FormulaToken(value.toUpperCase(),ExcelFormulaTokenType.Function,index,ExcelFormulaTokenSubtype.Start)))
                        }
                        value = ""
                    }
                    else
                        stack.push(tokens1.add(new FormulaToken("",ExcelFormulaTokenType.Subexpression,index,ExcelFormulaTokenSubtype.Start)));
                    tokenStartIndex = index + 1
                }
                else if(currentChar === this.listSeparator || currentChar === this.arrayGroupSeparator)
                {
                    if(value.length > 0)
                    {
                        tokens1.add(new FormulaToken(value,ExcelFormulaTokenType.Operand,tokenStartIndex));
                        value = ""
                    }
                    if(!stack.current() || stack.current().type !== ExcelFormulaTokenType.Function)
                        tokens1.add(new FormulaToken(this.listSeparator,ExcelFormulaTokenType.OperatorInfix,index,ExcelFormulaTokenSubtype.Union));
                    else
                        tokens1.add(new FormulaToken(this._arrayArgumentSepatator,ExcelFormulaTokenType.Argument,index));
                    tokenStartIndex = index + 1
                }
                else if(currentChar === ')')
                {
                    if(value.length > 0)
                    {
                        tokens1.add(new FormulaToken(value,ExcelFormulaTokenType.Operand,tokenStartIndex));
                        value = ""
                    }
                    if(!stack.current())
                        throw const_formulaError + "'" + currentChar + const_atIndexOn + index + ".";
                    tokens1.add(stack.pop());
                    tokenStartIndex = index + 1
                }
                else if(currentChar === ':')
                    if(value.length === 0 && tokens1.getItem(tokens1.count() - 1).subType === ExcelFormulaTokenSubtype.Stop)
                    {
                        tokens1.add(new FormulaToken(":",ExcelFormulaTokenType.OperatorInfix,index,ExcelFormulaTokenSubtype.RangeOp));
                        tokenStartIndex = index + 1
                    }
                    else
                        value += ':';
                else
                    value += currentChar
            }
            if(value.length > 0)
                tokens1.add(new FormulaToken(value,ExcelFormulaTokenType.Operand,tokenStartIndex));
            return this._processTokens(tokens1)
        },
        _processTokens: function(tokens1)
        {
            var tokens2 = this._removeWhiteSpace(tokens1);
            var stack = [];
            var rootToken = new FormulaToken("",ExcelFormulaTokenType.Unknown,0,ExcelFormulaTokenSubtype.Start);
            stack.push(rootToken);
            var parent;
            while(tokens2.moveNext())
            {
                var currentToken = tokens2.current();
                if(!currentToken)
                    continue;
                var previous = tokens2.previous();
                var next = tokens2.next();
                if(currentToken.type === ExcelFormulaTokenType.Operand && currentToken.subType === ExcelFormulaTokenSubtype.Nothing)
                {
                    var value = currentToken.value.toUpperCase();
                    if(value === const_true || value === const_false)
                    {
                        currentToken.subType = ExcelFormulaTokenSubtype.Logical;
                        currentToken.value = value
                    }
                    else
                        currentToken.subType = ExcelFormulaTokenSubtype.RangeOrName
                }
                else if(currentToken.type === ExcelFormulaTokenType.Function)
                    if(currentToken.value.length > 0)
                        if(currentToken.value.charAt(0) === '@')
                            currentToken.value = currentToken.value.substr(1);
                if(stack.length === 0)
                    throw const_formulaInvalidToken + currentToken.index + ".";
                parent = stack[stack.length - 1];
                if(parent.value === const_arrayRow)
                    if(currentToken.type !== ExcelFormulaTokenType.Argument && currentToken.subType !== ExcelFormulaTokenSubtype.Error && currentToken.subType !== ExcelFormulaTokenSubtype.Stop && currentToken.subType !== ExcelFormulaTokenSubtype.Logical && currentToken.subType !== ExcelFormulaTokenSubtype.Number && currentToken.subType !== ExcelFormulaTokenSubtype.Text && currentToken.type !== ExcelFormulaTokenType.OperatorPrefix)
                        throw"Invalid array at" + currentToken.index + ".";
                switch(currentToken.type)
                {
                    case ExcelFormulaTokenType.Operand:
                        if(previous && (previous.type === ExcelFormulaTokenType.Operand || previous.type === ExcelFormulaTokenType.OperatorPostfix || previous.type === ExcelFormulaTokenType.Function && previous.subType === ExcelFormulaTokenSubtype.Stop || previous.type === ExcelFormulaTokenType.Subexpression && previous.subType === ExcelFormulaTokenSubtype.Stop))
                            throw const_formulaInvalidToken + currentToken.index + ".";
                        break;
                    case ExcelFormulaTokenType.Function:
                    case ExcelFormulaTokenType.Subexpression:
                        if(currentToken.value === const_array && currentToken.type === ExcelFormulaTokenType.Function && currentToken.subType === ExcelFormulaTokenSubtype.Start && !previous)
                            break;
                        else if(currentToken.subType === ExcelFormulaTokenSubtype.Stop && (!previous || previous.type === ExcelFormulaTokenType.OperatorPrefix || previous.type === ExcelFormulaTokenType.OperatorInfix) || currentToken.subType === ExcelFormulaTokenSubtype.Start && previous && (!next || previous.type === ExcelFormulaTokenType.OperatorPostfix || previous.subType === ExcelFormulaTokenSubtype.Stop))
                            throw const_formulaInvalidToken + currentToken.index + ".";
                        if(currentToken.subType === ExcelFormulaTokenSubtype.Stop && currentToken.type === ExcelFormulaTokenType.Subexpression && previous.subType === ExcelFormulaTokenSubtype.Start)
                            throw const_formulaInvalidToken + currentToken.index + ".";
                        if(currentToken.subType === ExcelFormulaTokenSubtype.Stop && currentToken.type === ExcelFormulaTokenType.Function && previous.type === ExcelFormulaTokenType.Subexpression && previous.subType === ExcelFormulaTokenSubtype.Start)
                            throw const_formulaInvalidToken + currentToken.index + ".";
                        break;
                    case ExcelFormulaTokenType.Argument:
                        if(!next || !previous || previous.type === ExcelFormulaTokenType.OperatorInfix || previous.type === ExcelFormulaTokenType.OperatorPrefix)
                            throw const_formulaInvalidToken + currentToken.index + ".";
                        break;
                    case ExcelFormulaTokenType.OperatorPrefix:
                        if(!next || previous && previous.type === ExcelFormulaTokenType.OperatorPostfix)
                            throw const_formulaInvalidToken + currentToken.index + ".";
                        break;
                    case ExcelFormulaTokenType.OperatorInfix:
                        if(!next || !previous || previous.type === ExcelFormulaTokenType.OperatorInfix || previous.type === ExcelFormulaTokenType.OperatorPrefix || previous.type === ExcelFormulaTokenType.Argument || previous.type === ExcelFormulaTokenType.Function && previous.subType === ExcelFormulaTokenSubtype.Start || previous.type === ExcelFormulaTokenType.Subexpression && previous.subType === ExcelFormulaTokenSubtype.Start)
                            throw const_formulaInvalidToken + currentToken.index + ".";
                        break;
                    case ExcelFormulaTokenType.OperatorPostfix:
                        if(!previous || previous.type === ExcelFormulaTokenType.OperatorPrefix || previous.type === ExcelFormulaTokenType.OperatorInfix || previous.type === ExcelFormulaTokenType.Function && previous.subType === ExcelFormulaTokenSubtype.Start || previous.type === ExcelFormulaTokenType.Subexpression && previous.subType === ExcelFormulaTokenSubtype.Start)
                            throw const_formulaInvalidToken + currentToken.index + ".";
                        break;
                    default:
                        throw const_formulaInvalidToken + currentToken.index + ".";
                }
                if(currentToken.subType === ExcelFormulaTokenSubtype.Start)
                {
                    stack.push(currentToken);
                    parent.children.push(currentToken)
                }
                else if(currentToken.subType === ExcelFormulaTokenSubtype.Stop)
                {
                    if(stack.length === 0)
                    {
                        var currentChar;
                        if(currentToken.value === const_array || currentToken.value === const_arrayRow)
                            currentChar = '}';
                        else
                            currentChar = ')';
                        throw const_formulaError + "'" + currentChar + "' at " + currentToken.index + ".";
                    }
                    stack.pop()
                }
                else
                    parent.children.push(currentToken)
            }
            return rootToken.children
        },
        _removeWhiteSpace: function(tokens1)
        {
            var tokens2 = new FormulaTokenList;
            while(tokens1.moveNext())
            {
                var token = tokens1.current();
                if(!token)
                    continue;
                if(token.type !== ExcelFormulaTokenType.Whitespace)
                {
                    tokens2.add(token);
                    continue
                }
                if(tokens1.BOF() || tokens1.EOF())
                    continue;
                var previous = tokens1.previous();
                var next = tokens1.next();
                if(!previous || !next)
                    continue;
                if((previous.type === ExcelFormulaTokenType.Function && previous.subType === ExcelFormulaTokenSubtype.Stop || previous.type === ExcelFormulaTokenType.Subexpression && previous.subType === ExcelFormulaTokenSubtype.Stop || previous.type === ExcelFormulaTokenType.Operand) && (next.type === ExcelFormulaTokenType.Function && next.subType === ExcelFormulaTokenSubtype.Start || next.type === ExcelFormulaTokenType.Subexpression && next.subType === ExcelFormulaTokenSubtype.Start || next.type === ExcelFormulaTokenType.Operand))
                    tokens2.add(new FormulaToken(" ",ExcelFormulaTokenType.OperatorInfix,token.index,ExcelFormulaTokenSubtype.Intersection))
            }
            return tokens2
        },
        _buildExpressionNode: function(context, token)
        {
            var currentExpression;
            if(token.type === ExcelFormulaTokenType.Function)
                if(token.value === const_array)
                    currentExpression = this._buildArraryExpression(context,token);
                else
                    currentExpression = this._buildFunctionExpression(context,token);
            else if(token.type === ExcelFormulaTokenType.Subexpression)
                currentExpression = this._buildSubExpression(context,token);
            else if(token.type === ExcelFormulaTokenType.Operand)
                if(token.subType === ExcelFormulaTokenSubtype.Number)
                    currentExpression = new DoubleExpression(parseFloat(token.value),token.value);
                else if(token.subType === ExcelFormulaTokenSubtype.Error)
                    currentExpression = new ErrorExpression(CalcError.parse(token.value));
                else if(token.subType === ExcelFormulaTokenSubtype.Logical)
                {
                    if(compareStringIgnoreCase(token.value,const_true))
                        currentExpression = new BooleanExpression(true);
                    else if(compareStringIgnoreCase(token.value,const_false))
                        currentExpression = new BooleanExpression(false)
                }
                else if(token.subType === ExcelFormulaTokenSubtype.RangeOrName)
                    currentExpression = this._buildCellReferenceOrNameExpressions(context,token);
                else
                    currentExpression = new StringExpression(token.value);
            return currentExpression
        },
        _buildExpressionTree: function(context, tokens)
        {
            var results = this._parseToBinaryOperatorList(context,tokens);
            var currentExpression,
                lastExpression,
                nextExpression,
                index;
            for(index = 3; index < results.length; )
            {
                var nextToken = results[index];
                var currentToken = results[index - 2];
                if(nextToken && nextToken.type === ExcelFormulaTokenType.OperatorInfix)
                {
                    while(index >= 3 && getOpeatorPriority(nextToken.value) >= getOpeatorPriority(currentToken.value))
                    {
                        lastExpression = results[index - 3];
                        nextExpression = results[index - 1];
                        currentExpression = new BinaryOperatorExpression(getBinaryOperator(currentToken),lastExpression,nextExpression);
                        results.splice(index - 3,1);
                        results.splice(index - 3,1);
                        results.splice(index - 3,1);
                        results.splice(index - 3,0,currentExpression);
                        index -= 2;
                        if(index >= 3)
                            currentToken = results[index - 2]
                    }
                    index += 2
                }
                else
                    index++
            }
            if(results.length === 1)
                return results[0];
            else
            {
                for(index = results.length - 2; index > 0; index -= 2)
                {
                    lastExpression = results[index - 1];
                    nextExpression = results[index + 1];
                    currentExpression = new BinaryOperatorExpression(getBinaryOperator(results[index]),lastExpression,nextExpression);
                    results.splice(index - 1,1);
                    results.splice(index - 1,1);
                    results.splice(index - 1,1);
                    results.push(currentExpression)
                }
                return currentExpression
            }
        },
        _parseToBinaryOperatorList: function(context, tokens)
        {
            var results = [];
            var currentExpression;
            for(var i = 0; i < tokens.length; i++)
            {
                var currentToken = tokens[i];
                if(currentToken.type === ExcelFormulaTokenType.OperatorPrefix)
                {
                    var opStack = [];
                    while(currentToken.type === ExcelFormulaTokenType.OperatorPrefix)
                    {
                        opStack.push(currentToken.value === '+' ? Operators.plus : Operators.negate);
                        i++;
                        currentToken = tokens[i]
                    }
                    var nextToken = tokens[i];
                    currentExpression = new UnaryOperatorExpression(opStack.pop(),this._buildExpressionNode(context,nextToken));
                    while(opStack.length > 0)
                        currentExpression = new UnaryOperatorExpression(opStack.pop(),currentExpression);
                    results.push(currentExpression)
                }
                else if(currentToken.type === ExcelFormulaTokenType.OperatorPostfix)
                {
                    var lastExpression = results[results.length - 1];
                    currentExpression = new UnaryOperatorExpression(Operators.percent,lastExpression);
                    results.pop();
                    results.push(currentExpression)
                }
                else if(currentToken.type === ExcelFormulaTokenType.OperatorInfix)
                    results.push(currentToken);
                else
                {
                    currentExpression = this._buildExpressionNode(context,currentToken);
                    results.push(currentExpression)
                }
            }
            return results
        },
        _buildFunctionExpression: function(context, rootToken)
        {
            var args = [];
            var subTokens = [];
            for(var i = 0; i < rootToken.children.length; i++)
            {
                var token = rootToken.children[i];
                if(token.type !== ExcelFormulaTokenType.Argument)
                    subTokens.push(token);
                else if(subTokens.length === 0)
                    args.push(new MissingArgumentExpression);
                else
                {
                    args.push(this._buildExpressionTree(context,subTokens));
                    subTokens = []
                }
            }
            if(subTokens.length !== 0)
                args.push(this._buildExpressionTree(context,subTokens));
            else if(rootToken.children.length !== 0)
                args.push(new MissingArgumentExpression);
            var fn = Functions.findGlobalFunction(rootToken.value);
            if(fn)
            {
                if(args.length < fn.minArgs || args.length > fn.maxArgs)
                    throw"Invalid function parameters at " + rootToken.index;
                return new FunctionExpression(fn,args)
            }
            else
                return new FunctionExpression(rootToken.value,args)
        },
        _buildSubExpression: function(context, rootToken)
        {
            return new ParenthesesExpression(this._buildExpressionTree(context,rootToken.children))
        },
        _buildArraryExpression: function(context, rootToken)
        {
            var args = [];
            var colCount = -1;
            var arrayRowToken;
            for(var i = 0; i < rootToken.children.length; i++)
            {
                arrayRowToken = rootToken.children[i];
                var previous = i === 0 ? null : rootToken.children[i - 1];
                if(arrayRowToken.type === ExcelFormulaTokenType.Argument)
                {
                    if(!previous || previous.type === ExcelFormulaTokenType.Argument || i === rootToken.children.length - 1)
                        throw const_formulaInvalidArray + arrayRowToken.index;
                    continue
                }
                else if(previous && previous.type !== ExcelFormulaTokenType.Argument)
                    throw const_formulaInvalidArray + arrayRowToken.index;
                else if(arrayRowToken.type !== ExcelFormulaTokenType.Function || arrayRowToken.value !== const_arrayRow)
                    throw const_formulaInvalidArray + arrayRowToken.index;
                else if(colCount !== -1 && arrayRowToken.children.length !== colCount)
                    throw"The length of array columns are unequal at " + arrayRowToken.index;
                else
                    colCount = arrayRowToken.children.length;
                var row = [];
                args.push(row);
                var length = arrayRowToken.children.length;
                var lastToken = null;
                for(var index = 0; index < length; index++)
                {
                    var token = arrayRowToken.children[index];
                    if(token.type === ExcelFormulaTokenType.Argument)
                    {
                        if(!lastToken || lastToken.type === ExcelFormulaTokenType.Argument || index === length - 1)
                            row.push(Calc.missingArgument)
                    }
                    else if(lastToken && lastToken.type !== ExcelFormulaTokenType.Argument)
                        throw const_formulaInvalidArray + arrayRowToken.index;
                    else
                    {
                        var nextToken;
                        if(token.type === ExcelFormulaTokenType.OperatorPrefix)
                        {
                            if(index + 1 >= length)
                                throw const_formulaInvalidArray + arrayRowToken.index;
                            nextToken = arrayRowToken.children[index + 1];
                            nextToken.value = "" + token.value + nextToken.value;
                            token = nextToken;
                            index++
                        }
                        else if(index + 1 < length && arrayRowToken.children[index + 1].type === ExcelFormulaTokenType.OperatorPostfix)
                        {
                            nextToken = arrayRowToken.children[index + 1];
                            token.value = "" + token.value + nextToken.value;
                            index++
                        }
                        var expr = this._buildExpressionNode(context,token);
                        if(expr)
                            row.push(expr.value)
                    }
                    lastToken = token
                }
            }
            if(args.length === 0 || args[0] === undefined || args[0] === null || args[0].length === 0)
                throw const_formulaInvalidArray + arrayRowToken.index;
            return new ArrayExpression(args)
        },
        _buildCellReferenceOrNameExpressions: function(context, token)
        {
            var value = token.value;
            var result = this._buildCellReferenceOrNameExpressions2(context,value);
            var endIndex = result.endIndex;
            var expression = result.expression;
            if(!expression)
                throw const_formulaInvalidCellRef + token.index + ".";
            if(endIndex <= 0 || endIndex >= value.length)
                return expression;
            if(value.charAt(endIndex) !== ':')
                throw const_formulaInvalidCellRef + token.index + ".";
            endIndex++;
            var expressions = [];
            expressions.push(expression);
            while(endIndex++ && endIndex < value.length)
            {
                var startIndex = endIndex;
                result = this._buildCellReferenceOrNameExpressions2(context,value.substr(endIndex));
                endIndex = result.endIndex;
                expression = result.expression;
                if(!expression)
                    throw const_formulaInvalidCellRef + token.index + ".";
                expressions.push(expression);
                endIndex += startIndex;
                if(endIndex < value.length && value.charAt(endIndex) !== ':')
                    throw const_formulaInvalidCellRef + token.index + ".";
                endIndex++
            }
            expression = expressions[0];
            for(var i = 1; i < expressions.length; i++)
                expression = new BinaryOperatorExpression(Operators.range,expression,expressions[i]);
            return expression
        },
        _buildCellReferenceOrNameExpressions2: function(context, value)
        {
            if(value === undefined || value === null || value === '')
                return{
                        endIndex: 0,
                        expression: null
                    };
            var length = value.length;
            if(value.match("^#REF"))
                return{
                        endIndex: length,
                        expression: new ErrorExpression(CalcErrorsReference)
                    };
            var isBang = value.charAt(0) === '!';
            var startIndex = isBang ? 1 : 0;
            if(length < 1)
                return{
                        endIndex: 0,
                        expression: null
                    };
            var cellOrNameStartIndex = -1;
            var sheetRefStr = "";
            var endIndex = 0;
            if(isBang)
                cellOrNameStartIndex = startIndex;
            else if(value.charAt(0) === "'")
            {
                cellOrNameStartIndex = value.indexOf('!') + 1;
                sheetRefStr = value.substr(0,cellOrNameStartIndex - 1)
            }
            else
            {
                var excalmatoryIndex = value.indexOf('!');
                var rangeIndex = value.indexOf(':');
                if(excalmatoryIndex !== -1)
                {
                    var startString = value.substr(startIndex,excalmatoryIndex - startIndex);
                    if(rangeIndex < 0 || excalmatoryIndex < rangeIndex)
                    {
                        if(excalmatoryIndex < length - 1)
                        {
                            sheetRefStr = startString;
                            cellOrNameStartIndex = excalmatoryIndex + 1
                        }
                    }
                    else
                    {
                        var expr = this._parseToCellReference(context,startString,isBang);
                        if(expr.expression)
                            return expr;
                        else if(excalmatoryIndex < length - 1)
                        {
                            sheetRefStr = startString;
                            cellOrNameStartIndex = excalmatoryIndex + 1
                        }
                        endIndex = expr.endIndex
                    }
                }
            }
            var workBookName = "";
            var startSheetName = "";
            var endSheetName = "";
            if(cellOrNameStartIndex !== -1 && sheetRefStr && sheetRefStr !== "")
            {
                var result = this._readSheetReference(sheetRefStr,context.useR1C1,false);
                if(!result.success)
                    return{
                            endIndex: endIndex,
                            expression: null
                        };
                workBookName = result.workBookName;
                startSheetName = result.startSheetName;
                endSheetName = result.endSheetName
            }
            else
                cellOrNameStartIndex = startIndex;
            var src,
                startSource,
                endSource;
            var isExternalExpression = false;
            if(workBookName && workBookName !== '' || startSheetName && startSheetName !== '')
            {
                isExternalExpression = true;
                if(context.getExternalSource)
                {
                    startSource = context.getExternalSource(workBookName,startSheetName);
                    if(!startSource)
                        return{
                                endIndex: endIndex,
                                expression: new ErrorExpression(CalcErrorsReference)
                            }
                }
            }
            if(endSheetName && endSheetName !== '')
            {
                isExternalExpression = true;
                if(context.getExternalSource)
                {
                    endSource = context.getExternalSource(workBookName,endSheetName);
                    if(!endSource)
                        return{
                                endIndex: endIndex,
                                expression: new ErrorExpression(CalcErrorsReference)
                            }
                }
            }
            var cellOrNameStr = value.substr(cellOrNameStartIndex);
            var exprResult = this._parseToCellReference(context,cellOrNameStr,isBang,startSource,endSource);
            var expr2 = exprResult.expression;
            endIndex = exprResult.endIndex;
            if(!expr2)
            {
                if(isBang)
                    expr2 = new BangNameExpression(cellOrNameStr);
                else if(startSheetName && startSheetName !== '')
                {
                    if(endSheetName && endSheetName !== '')
                        return{
                                endIndex: endIndex,
                                expression: null
                            };
                    if(!context.getExternalSource)
                        return{
                                endIndex: endIndex,
                                expression: new ErrorExpression(CalcErrorsReference)
                            };
                    src = context.getExternalSource(workBookName,startSheetName);
                    if(!src)
                        return{
                                endIndex: endIndex,
                                expression: new ErrorExpression(CalcErrorsReference)
                            };
                    expr2 = new ExternalNameExpression(src,cellOrNameStr)
                }
                else
                    expr2 = new NameExpression(cellOrNameStr);
                endIndex = length
            }
            else if(isExternalExpression && !(expr2 instanceof SheetRangeExpression) && !(expr2 instanceof ExternalReferenceExpression) && !(expr2 instanceof ExternalErrorExpression) && !(expr2 instanceof SheetRangeErrorExpression) && !(expr2 instanceof ExternalNameExpression))
                return{
                        endIndex: length,
                        expression: new ErrorExpression(CalcErrorsReference)
                    };
            else if(endIndex < cellOrNameStr.length - 1)
            {
                endIndex = length - (cellOrNameStr.length - 1);
                if(value.charAt(endIndex) !== ':')
                {
                    if(startSheetName && startSheetName !== '')
                    {
                        if(endSheetName && endSheetName !== '')
                            return{
                                    endIndex: endIndex,
                                    expression: null
                                };
                        if(!context.getExternalSource)
                            return{
                                    endIndex: endIndex,
                                    expression: new ErrorExpression(CalcErrorsReference)
                                };
                        src = context.getExternalSource(workBookName,startSheetName);
                        if(!src)
                            return{
                                    endIndex: endIndex,
                                    expression: new ErrorExpression(CalcErrorsReference)
                                };
                        expr2 = new ExternalNameExpression(src,cellOrNameStr)
                    }
                    else
                        expr2 = new NameExpression(cellOrNameStr);
                    endIndex = length
                }
            }
            else
                endIndex = length;
            if(expr2 instanceof NameExpression || expr2 instanceof ExternalNameExpression)
            {
                var structExpr = this._createStructExpression(cellOrNameStr);
                if(structExpr)
                    expr2 = structExpr;
                else if(!this._validateName(cellOrNameStr))
                    return{
                            endIndex: endIndex,
                            expression: null
                        }
            }
            return{
                    endIndex: endIndex,
                    expression: expr2
                }
        },
        _parseToCellReference: function(context, value, isBang, startSource, endSource)
        {
            var endIndex = 0;
            if(value.charAt(0) === '#')
            {
                var rs = readError(value,0);
                endIndex = rs.endIndex + 1;
                var err = CalcError.parse(rs.result);
                if(err)
                    return{
                            endIndex: endIndex,
                            expression: this._createExternalErrorExpression(err,isBang,startSource,endSource)
                        }
            }
            var result = context.useR1C1 ? this._isR1C1CellReferance(value,context.row,context.column) : this._isA1CellReferance(value);
            var bandIndex = Parser.BAND_INDEX_CONST;
            if(result.success && context.useR1C1 && result.startRow !== bandIndex && result.startColumn === bandIndex && result.endRow === bandIndex && result.endColumn === bandIndex)
            {
                result.endRow = result.startRow;
                result.endRowRelative = result.startRowRelative
            }
            else if(result.success && context.useR1C1 && result.startColumn !== bandIndex && result.startRow === bandIndex && result.endRow === bandIndex && result.endColumn === bandIndex)
            {
                result.endColumn = result.startColumn;
                result.endColumnRelative = result.startColumnRelative
            }
            else if(!result.success || result.startRow === bandIndex && (result.startColumn === bandIndex || result.endColumn === bandIndex) || result.startColumn === bandIndex && result.endRow === bandIndex)
                return{
                        endIndex: result.endIndex,
                        expression: null
                    };
            var cellExpr = this._createCellReferenceExpression(context,context.row,context.column,result.startRow,result.startColumn,result.endRow,result.endColumn,result.startRowRelative,result.startColumnRelative,result.endRowRelative,result.endColumnRelative,isBang,startSource,endSource);
            return{
                    endIndex: result.endIndex,
                    expression: cellExpr
                }
        },
        _createExternalErrorExpression: function(err, isBang, startSource, endSource)
        {
            if(isBang)
                return new BangErrorExpression(err);
            else if(startSource && endSource)
                return new SheetRangeErrorExpression(startSource,endSource,err);
            else if(startSource)
                return new ExternalErrorExpression(startSource,err);
            else
                return new ErrorExpression(err)
        },
        _createCellReferenceExpression: function(context, baseRowIndex, baseColIndex, startRow, startColumn, endRow, endColumn, startRowRelative, startColumnRelative, endRowRelative, endColumnRelative, isBang, startSource, endSource)
        {
            var bandIndex = Parser.BAND_INDEX_CONST;
            var expr;
            if(startSource && endSource)
            {
                expr = new SheetRangeExpression;
                if(startRow === bandIndex)
                {
                    startColumn -= startColumnRelative ? baseColIndex : 0;
                    endColumn -= endColumnRelative ? baseColIndex : 0;
                    expr.initBand(startSource,endSource,startColumn,endColumn,startColumnRelative,endColumnRelative,false)
                }
                else if(startColumn === bandIndex)
                {
                    startRow -= startRowRelative ? baseRowIndex : 0;
                    endRow -= endRowRelative ? baseRowIndex : 0;
                    expr.initBand(startSource,endSource,startRow,endRow,startRowRelative,endRowRelative,true)
                }
                else
                {
                    startRow -= startRowRelative ? baseRowIndex : 0;
                    startColumn -= startColumnRelative ? baseColIndex : 0;
                    endRow -= endRowRelative ? baseRowIndex : 0;
                    endColumn -= endColumnRelative ? baseColIndex : 0;
                    expr.init(startSource,endSource,startRow,startColumn,endRow,endColumn,startRowRelative,startColumnRelative,endRowRelative,endColumnRelative)
                }
                return expr
            }
            else if(endRow === bandIndex && endColumn === bandIndex)
            {
                startRow -= startRowRelative ? baseRowIndex : 0;
                startColumn -= startColumnRelative ? baseColIndex : 0;
                if(isBang)
                {
                    if(startSource || endSource)
                        return null;
                    return new BangCellExpression(startRow,startColumn,startRowRelative,startColumnRelative)
                }
                else if(startSource)
                    return new ExternalCellExpression(startSource,startRow,startColumn,startRowRelative,startColumnRelative);
                else
                    return new CellExpression(startRow,startColumn,startRowRelative,startColumnRelative)
            }
            else if(isBang)
            {
                if(startSource && endSource)
                    return null;
                expr = new BangRangeExpression;
                if(startRow === bandIndex)
                {
                    startColumn -= startColumnRelative ? baseColIndex : 0;
                    endColumn -= endColumnRelative ? baseColIndex : 0;
                    expr.initBand(startColumn,endColumn,startColumnRelative,endColumnRelative,false)
                }
                else if(startColumn === bandIndex)
                {
                    startRow -= startRowRelative ? baseRowIndex : 0;
                    endRow -= endRowRelative ? baseRowIndex : 0;
                    expr.initBand(startRow,endRow,startRowRelative,endRowRelative,true)
                }
                else
                {
                    startRow -= startRowRelative ? baseRowIndex : 0;
                    startColumn -= startColumnRelative ? baseColIndex : 0;
                    endRow -= endRowRelative ? baseRowIndex : 0;
                    endColumn -= endColumnRelative ? baseColIndex : 0;
                    expr.init(startRow,startColumn,endRow,endColumn,startRowRelative,startColumnRelative,endRowRelative,endColumnRelative)
                }
                return expr
            }
            else if(startSource)
            {
                expr = new ExternalRangeExpression;
                if(startRow === bandIndex)
                {
                    startColumn -= startColumnRelative ? baseColIndex : 0;
                    endColumn -= endColumnRelative ? baseColIndex : 0;
                    expr.initBand(startSource,startColumn,endColumn,startColumnRelative,endColumnRelative,false)
                }
                else if(startColumn === bandIndex)
                {
                    startRow -= startRowRelative ? baseRowIndex : 0;
                    endRow -= endRowRelative ? baseRowIndex : 0;
                    expr.initBand(startSource,startRow,endRow,startRowRelative,endRowRelative,true)
                }
                else
                {
                    startRow -= startRowRelative ? baseRowIndex : 0;
                    startColumn -= startColumnRelative ? baseColIndex : 0;
                    endRow -= endRowRelative ? baseRowIndex : 0;
                    endColumn -= endColumnRelative ? baseColIndex : 0;
                    expr.init(startSource,startRow,startColumn,endRow,endColumn,startRowRelative,startColumnRelative,endRowRelative,endColumnRelative)
                }
                return expr
            }
            else
            {
                expr = new RangeExpression;
                if(startRow === bandIndex)
                {
                    startColumn -= startColumnRelative ? baseColIndex : 0;
                    endColumn -= endColumnRelative ? baseColIndex : 0;
                    expr.initBand(startColumn,endColumn,startColumnRelative,endColumnRelative,false)
                }
                else if(startColumn === bandIndex)
                {
                    startRow -= startRowRelative ? baseRowIndex : 0;
                    endRow -= endRowRelative ? baseRowIndex : 0;
                    expr.initBand(startRow,endRow,startRowRelative,endRowRelative,true)
                }
                else
                {
                    startRow -= startRowRelative ? baseRowIndex : 0;
                    startColumn -= startColumnRelative ? baseColIndex : 0;
                    endRow -= endRowRelative ? baseRowIndex : 0;
                    endColumn -= endColumnRelative ? baseColIndex : 0;
                    expr.init(startRow,startColumn,endRow,endColumn,startRowRelative,startColumnRelative,endRowRelative,endColumnRelative)
                }
                return expr
            }
        },
        _readSheetReference: function(value, isR1C1, isUnparse)
        {
            var result = {};
            var containsSpecial = false;
            if(value.charAt(0) === "'" && value.charAt(value.length - 1) === "'")
            {
                containsSpecial = true;
                value = value.substr(1,value.length - 2)
            }
            var workBookIndex = value.indexOf('[');
            if(workBookIndex !== -1)
            {
                if(workBookIndex !== 0)
                {
                    result.workBookName = value.substr(0,workBookIndex);
                    if(result.workBookName.charAt(workBookIndex - 1) !== '\\')
                        result.workBookName = result.workBookName + '\\'
                }
                var rs = readString(value,workBookIndex,'[',']');
                result.workBookName = result.workBookName + rs.result;
                value = value.substr(rs.endIndex + 1)
            }
            var colonIndex = value.indexOf(':');
            if(colonIndex === -1)
                result.startSheetName = value;
            else
            {
                result.startSheetName = value.substr(0,colonIndex);
                result.endSheetName = value.substr(colonIndex + 1)
            }
            if(!result.startSheetName || result.startSheetName === '')
            {
                result.success = false;
                return result
            }
            var valid = this._validateWorkbook(result.workBookName,containsSpecial);
            valid = this._validateSheetName(result.startSheetName,isR1C1,containsSpecial);
            valid &= this._validateSheetName(result.endSheetName,isR1C1,containsSpecial);
            if(isUnparse)
            {
                if(result.startSheetName)
                    result.startSheetName = result.startSheetName.replace("'","''");
                if(result.endSheetName)
                    result.endSheetName = result.endSheetName.replace("'","''")
            }
            result.success = valid;
            return result
        },
        _isStartWithCellReference: function(value, isR1C1)
        {
            return isR1C1 ? this._isR1C1CellReferance(value) : this._isA1CellReferance(value)
        },
        _isA1CellReferance: function(value)
        {
            var bandIndex = Parser.BAND_INDEX_CONST;
            var result = {
                    startRow: bandIndex,
                    startColumn: bandIndex,
                    endRow: bandIndex,
                    endColumn: bandIndex,
                    startRowRelative: true,
                    startColumnRelative: true,
                    endRowRelative: true,
                    endColumnRelative: true,
                    success: false,
                    endIndex: 0
                };
            value = value.replace(' ','');
            var length = value.length;
            var read = this._readOneA1Element(value,0);
            result.endIndex = read.endIndex;
            if(!read.success || result.endIndex > length)
                return result;
            var read2;
            if(read.isRow)
            {
                result.startRow = read.elementIndex;
                result.startRowRelative = read.isRelative;
                if(value.charAt(result.endIndex) === ':')
                {
                    result.endIndex++;
                    read2 = this._readOneA1Element(value,result.endIndex);
                    result.endIndex = read2.endIndex;
                    if(!read2.success || !read2.isRow)
                        return result;
                    result.endRow = read2.elementIndex;
                    result.endRowRelative = read2.isRelative;
                    result.success = true;
                    return result
                }
                else
                    return result
            }
            else
            {
                result.startColumn = read.elementIndex;
                result.startColumnRelative = read.isRelative;
                if(value.charAt(result.endIndex) === ':')
                {
                    result.endIndex++;
                    read2 = this._readOneA1Element(value,result.endIndex);
                    result.endIndex = read2.endIndex;
                    if(!read2.success || read2.isRow)
                        return result;
                    result.endColumn = read2.elementIndex;
                    result.endColumnRelative = read2.isRelative;
                    result.success = true;
                    return result
                }
            }
            if(isNumber(value.charAt(result.endIndex)) || value.charAt(result.endIndex) === '$')
            {
                read2 = this._readOneA1Element(value,result.endIndex);
                result.endIndex = read2.endIndex;
                if(!read2.success || !read2.isRow)
                    return result;
                result.startRow = read2.elementIndex;
                result.startRowRelative = read2.isRelative;
                var cellEndIndex = result.endIndex;
                if(result.endIndex < length - 1 && value.charAt(result.endIndex) === ':')
                {
                    if(value.charAt(result.endIndex + 1) === "'")
                    {
                        result.success = true;
                        return result
                    }
                    var nextSheetIndex = value.indexOf('!',result.endIndex + 1);
                    var nextRangeIndex = value.indexOf(':',result.endIndex + 1);
                    if(nextSheetIndex !== -1 && (nextRangeIndex === -1 || nextRangeIndex > nextSheetIndex))
                    {
                        result.success = true;
                        return result
                    }
                    result.endIndex++;
                    var read3 = this._readOneA1Element(value,result.endIndex);
                    if(!read3.success || read3.isRow)
                    {
                        result.endIndex = cellEndIndex;
                        result.success = true;
                        return result
                    }
                    else
                        result.endIndex = read3.endIndex;
                    result.endColumn = read3.elementIndex;
                    result.endColumnRelative = read3.isRelative;
                    var read4 = this._readOneA1Element(value,result.endIndex);
                    if(!read4.success || !read4.isRow)
                    {
                        result.endIndex = cellEndIndex;
                        result.success = true;
                        return result
                    }
                    else
                        result.endIndex = read4.endIndex;
                    result.endRow = read4.elementIndex;
                    result.endRowRelative = read4.isRelative;
                    result.success = true;
                    return result
                }
                else
                {
                    result.success = true;
                    return result
                }
            }
            else
                return result
        },
        _isR1C1CellReferance: function(value, baseRow, baseColumn)
        {
            var bandIndex = Parser.BAND_INDEX_CONST;
            var result = {
                    startRow: bandIndex,
                    startColumn: bandIndex,
                    endRow: bandIndex,
                    endColumn: bandIndex,
                    startRowRelative: true,
                    startColumnRelative: true,
                    endRowRelative: true,
                    endColumnRelative: true,
                    success: false,
                    endIndex: 0
                };
            value = value.replace(' ','');
            var length = value.length;
            var read = this._readOneR1C1Element(value,baseRow,baseColumn,0);
            result.endIndex = read.endIndex;
            if(!read.success)
                return result;
            var read2;
            if(read.isRow)
            {
                result.startRow = read.elementIndex;
                result.startRowRelative = read.isRelative;
                if(result.endIndex >= length)
                {
                    result.success = true;
                    return result
                }
                if(value.charAt(result.endIndex) === ':')
                {
                    result.endIndex++;
                    read2 = this._readOneR1C1Element(value,baseRow,baseColumn,result.endIndex);
                    result.endIndex = read2.endIndex;
                    if(!read2.success || !read2.isRow)
                        return result;
                    result.endRow = read2.elementIndex;
                    result.endRowRelative = read2.isRelative;
                    result.success = true;
                    return result
                }
            }
            else
            {
                result.startColumn = read.elementIndex;
                result.startColumnRelative = read.isRelative;
                if(result.endIndex >= length)
                {
                    result.success = true;
                    return result
                }
                if(value.charAt(result.endIndex) === ':')
                {
                    result.endIndex++;
                    read2 = this._readOneR1C1Element(value,baseRow,baseColumn,result.endIndex);
                    result.endIndex = read2.endIndex;
                    if(!read2.success || read2.isRow)
                        return result;
                    result.endColumn = read2.elementIndex;
                    result.endColumnRelative = read2.isRelative;
                    result.success = true;
                    return result
                }
                else
                    return result
            }
            if(value.charAt(result.endIndex) === 'C' || value.charAt(result.endIndex) === 'c')
            {
                read2 = this._readOneR1C1Element(value,baseRow,baseColumn,result.endIndex);
                result.endIndex = read2.endIndex;
                if(!read2.success || read2.isRow)
                    return result;
                result.startColumn = read2.elementIndex;
                result.startColumnRelative = read2.isRelative;
                var cellEndIndex = result.endIndex;
                if(result.endIndex < length - 1 && value.charAt(result.endIndex) === ':')
                {
                    if(value.charAt(result.endIndex + 1) === "'")
                    {
                        result.success = true;
                        return result
                    }
                    var nextSheetIndex = value.indexOf('!',result.endIndex + 1);
                    var nextRangeIndex = value.indexOf(':',result.endIndex + 1);
                    if(nextSheetIndex !== -1 && (nextRangeIndex === -1 || nextRangeIndex > nextSheetIndex))
                    {
                        result.success = true;
                        return result
                    }
                    result.endIndex++;
                    var read3 = this._readOneR1C1Element(value,baseRow,baseColumn,result.endIndex);
                    if(!read3.success || !read3.isRow)
                    {
                        result.endIndex = cellEndIndex;
                        result.success = true;
                        return result
                    }
                    else
                        result.endIndex = read3.endIndex;
                    result.endRow = read3.elementIndex;
                    result.endRowRelative = read3.isRelative;
                    var read4 = this._readOneR1C1Element(value,baseRow,baseColumn,result.endIndex);
                    if(!read4.success || read4.isRow)
                    {
                        result.endIndex = cellEndIndex;
                        result.success = true;
                        return result
                    }
                    else
                        result.endIndex = read4.endIndex;
                    result.endColumn = read4.elementIndex;
                    result.endColumnRelative = read4.isRelative;
                    result.success = true;
                    return result
                }
                else
                {
                    result.success = true;
                    return result
                }
            }
            else
                return result
        },
        _readOneA1Element: function(value, startIndex)
        {
            var bandIndex = Parser.BAND_INDEX_CONST;
            var result = {
                    endIndex: startIndex,
                    elementIndex: bandIndex,
                    isRow: true,
                    isRelative: true,
                    success: false
                };
            var length = value.length;
            if(startIndex >= length)
                return result;
            if(value.charAt(startIndex) === '$')
            {
                result.isRelative = false;
                startIndex++
            }
            if(startIndex >= length)
                return result;
            var index = startIndex,
                intValue;
            var currentChar = value.charAt(index);
            if(isNumber(currentChar) && currentChar !== '0')
            {
                result.isRow = true;
                while(index < length && isNumber(currentChar))
                {
                    index++;
                    if(index < length)
                        currentChar = value.charAt(index)
                }
                intValue = parseInt(value.substr(startIndex,index - startIndex),10);
                if(intValue >= 1 && intValue <= Parser.maxRowCount)
                {
                    result.elementIndex = intValue - 1;
                    result.endIndex = index;
                    result.success = true;
                    return result
                }
            }
            else if(isLetter(currentChar))
            {
                result.isRow = false;
                while(index < length && isLetter(currentChar))
                {
                    index++;
                    if(index < length)
                        currentChar = value.charAt(index)
                }
                var s = value.substr(startIndex,index - startIndex);
                if(s.length > 3)
                    return result;
                s = s.toUpperCase();
                intValue = 0;
                for(var i = s.length - 1; i >= 0; i--)
                    intValue += (s.charCodeAt(i) - 65 + 1) * LetterPows[s.length - i - 1];
                if(intValue <= Parser.maxColumnCount)
                {
                    result.elementIndex = intValue - 1;
                    result.endIndex = index;
                    result.success = true;
                    return result
                }
            }
            return result
        },
        _readOneR1C1Element: function(value, baseRow, baseColumn, startIndex)
        {
            var bandIndex = Parser.BAND_INDEX_CONST;
            var result = {
                    endIndex: startIndex,
                    elementIndex: bandIndex,
                    isRow: true,
                    isRelative: false,
                    success: false
                };
            var length = value.length;
            if(startIndex >= length)
                return result;
            var index = startIndex;
            var currentChar = value.charAt(index);
            var baseIndex;
            if(currentChar === 'R' || currentChar === 'r')
            {
                result.isRow = true;
                baseIndex = baseRow
            }
            else if(currentChar === 'C' || currentChar === 'c')
            {
                result.isRow = false;
                baseIndex = baseColumn
            }
            else
                return result;
            startIndex++;
            index++;
            if(startIndex >= length)
            {
                result.endIndex = startIndex;
                result.elementIndex = baseIndex;
                result.isRelative = true;
                result.success = true;
                return result
            }
            currentChar = value.charAt(index);
            if(currentChar === '[')
            {
                startIndex++;
                index++;
                result.isRelative = true
            }
            if(startIndex >= length)
                return result;
            var isNegative = false;
            currentChar = value.charAt(index);
            if(result.isRelative && currentChar === '-')
            {
                startIndex++;
                index++;
                isNegative = true
            }
            if(startIndex >= length)
                return result;
            currentChar = value.charAt(index);
            if(isNumber(currentChar))
            {
                while(index < length && isNumber(currentChar))
                {
                    index++;
                    if(index < length)
                        currentChar = value.charAt(index)
                }
                var intValue = parseInt(value.substr(startIndex,index - startIndex),10);
                if(result.isRelative)
                {
                    if(index >= length || value.charAt(index) !== ']')
                        return result;
                    index++
                }
                if(intValue < (result.isRow ? Parser.maxRowCount : Parser.maxColumnCount))
                {
                    if(result.isRelative)
                    {
                        intValue = isNegative ? -intValue : intValue;
                        result.elementIndex = intValue + baseIndex
                    }
                    else
                        result.elementIndex = intValue - 1;
                    result.endIndex = index;
                    result.success = true;
                    return result
                }
            }
            else if(result.isRelative)
                return result;
            else
            {
                result.endIndex = startIndex;
                result.elementIndex = baseIndex;
                result.isRelative = true;
                result.success = true;
                return result
            }
        },
        _validateWorkbook: function(name, containsSpecial)
        {
            if(name === undefined || name === null || name === '')
                return true;
            var currentChar;
            for(var i = 0; i < name.length; i++)
            {
                currentChar = name[i];
                if(containsSpecial)
                {
                    if(currentChar === '*' || currentChar === ':' || currentChar === '[' || currentChar === ']' || currentChar === '?')
                        return false;
                    if(currentChar === "'")
                        if(i === 0 || i >= name.length - 1 || name.charAt(i + 1) !== "'")
                            return false
                }
                else if(currentChar === "'" || currentChar === '[' || currentChar === ']' || currentChar === '?' || currentChar === '%' || this._operatorInfix.indexOf(currentChar) !== -1)
                    return false
            }
            return true
        },
        _validateSheetName: function(name, isR1C1, containsSpecial)
        {
            if(name === undefined || name === null || name === '')
                return true;
            if(!containsSpecial && isDigit(name.charAt(0)))
                return false;
            var isStartWithCellRef = this._isStartWithCellReference(name,isR1C1);
            if(!containsSpecial && isStartWithCellRef.success && isStartWithCellRef.endIndex === name.length)
                return false;
            var currentChar;
            for(var i = 0; i < name.length; i++)
            {
                currentChar = name.charAt(i);
                if(containsSpecial)
                {
                    if(currentChar === '*' || currentChar === ':' || currentChar === '[' || currentChar === ']' || currentChar === '?' || currentChar === '\\' || currentChar === '/')
                        return false
                }
                else if(currentChar === "'" || currentChar === '[' || currentChar === ']' || currentChar === '?' || currentChar === '\\' || currentChar === '%' || currentChar === '"' || this._operatorInfix.indexOf(currentChar) !== -1)
                    return false
            }
            return true
        },
        _createStructExpression: function(value)
        {
            if(value.charAt(value.length - 1) === ']')
                return new StructReferenceExpression(value);
            return null
        },
        _validateName: function(name)
        {
            if(name === undefined || name === null || name === '')
                return false;
            if(name.length === 1 && (name === 'R' || name === 'r' || name === 'C' || name === 'c'))
                return false;
            var currentChar = name.charAt(0);
            if(!(currentChar === '_' || currentChar === '\\' || isLetter(currentChar) || isSymbol(currentChar)))
                return false;
            for(var i = 1; i < name.length; i++)
            {
                currentChar = name.charAt(i);
                if(!(currentChar === '_' || currentChar === '\\' || currentChar === '?' || currentChar === '.' || isLetterOrDigit(currentChar) || isSymbol(currentChar)))
                    return false
            }
            return true
        }
    };
    Calc.Parser = Parser;
    function EvaluateContext(source, arrayFormula, baseRow, baseColumn, rowCount, columnCount)
    {
        this.source = source;
        this.arrayFormulaMode = arguments.length >= 2 ? arrayFormula : false;
        this.row = arguments.length >= 3 ? baseRow : 0;
        this.column = arguments.length >= 4 ? baseColumn : 0;
        this.rowCount = arguments.length >= 5 ? rowCount : 1;
        this.columnCount = arguments.length >= 6 ? columnCount : 1
    }
    EvaluateContext.prototype = {
        getValue: function(source, row, column)
        {
            if(!source)
                return CalcErrorsReference;
            return source.getValue(row,column)
        },
        getReference: function(source, row, column, rowCount, columnCount)
        {
            if(!source)
                return CalcErrorsReference;
            return source.getReference(row,column,rowCount,columnCount)
        },
        getFunction: function(name)
        {
            return this.source && this.source.getFunction ? this.source.getFunction(name) : null
        },
        getName: function(name)
        {
            return this.source && this.source.getName ? this.source.getName(name,this.row,this.column) : null
        },
        _expandArrayToMultiCallCount: 0,
        _isExpandArrayToMultiCall: function()
        {
            return this._expandArrayToMultiCallCount > 0
        },
        _beginExpandArrayToMultiCall: function()
        {
            this._expandArrayToMultiCallCount++
        },
        _endExpandArrayToMultiCall: function()
        {
            this._expandArrayToMultiCallCount--
        },
        _offset: function(row, column)
        {
            return new EvaluateContext(this.source,this.arrayFormulaMode,this.row + row,this.column + column,this.rowCount,this.columnCount)
        }
    };
    Calc.EvaluateContext = EvaluateContext;
    var defaultParser = new Parser;
    function Evaluator(){}
    Evaluator.prototype = {
        evaluateFormula: function(formula, parserContext, evaluatorContext)
        {
            return this.evaluateExpression(defaultParser.parse(formula,parserContext),evaluatorContext)
        },
        evaluateExpression: function(expression, evaluatorContext)
        {
            return this._evaluate(expression,evaluatorContext,false,false)
        },
        _evaluate: function(expr, context, acceptsArray, acceptsReference)
        {
            if(!expr)
                throw"Argument expr is null";
            while(expr instanceof ParenthesesExpression)
                expr = expr.argument;
            if(expr instanceof ConstantExpression)
                return this._evaluateConst(expr,context,acceptsArray);
            else if(expr instanceof ReferenceExpression)
                return this._evaluateReference(expr,context,acceptsReference);
            else if(expr instanceof ExternalNameExpression)
            {
                var svc,
                    nameContext = null;
                if(svc = expr.source.getCalcService())
                    nameContext = svc._getEvaluatorContext4Cell(expr.source._sheet,context.row,context.column,context.arrayFormulaMode);
                if(!nameContext)
                    return CalcErrorsName;
                return this._evaluateName(expr.name,nameContext,context,acceptsArray,acceptsReference)
            }
            else if(expr instanceof NameExpression)
                return this._evaluateName(expr.name,context,context,acceptsArray,acceptsReference);
            else if(expr instanceof UnaryOperatorExpression)
                return this._evaluateUnaryOperation(expr,context,acceptsArray);
            else if(expr instanceof BinaryOperatorExpression)
                return this._evaluateBinaryOperation(expr,context,acceptsArray);
            else if(expr instanceof FunctionExpression)
                return this._evaluateFunction(expr,context,acceptsArray,acceptsReference);
            throw const_notSupported;
        },
        _extractValueFromReference: function(reference, row, column)
        {
            if(reference instanceof SheetRangeReference && reference.getSheetCount() !== 1)
                return CalcErrorsReference;
            var rowCount = reference.getRowCount(0);
            var colCount = reference.getColumnCount(0);
            if(reference.getRangeCount() <= 0 || rowCount <= 0 || colCount <= 0)
                return CalcErrorsReference;
            try
            {
                if(reference.getRangeCount() !== 1 || rowCount > 1 && colCount > 1)
                    return CalcErrorsValue;
                var rowOffset = row - reference.getRow(0);
                var colOffset = column - reference.getColumn(0);
                if(rowCount === 1 && colCount === 1)
                    return reference.getValue(0,0,0);
                if(rowCount === 1 && colCount === 1 && colOffset >= 0 && colOffset < colCount)
                    return reference.getValue(0,0,colOffset);
                if(rowCount > 1 && colCount === 1 && rowCount >= 0 && rowOffset < rowCount)
                    return reference.getValue(0,rowOffset,0);
                return CalcErrorsValue
            }
            catch(err)
            {
                return CalcErrorsValue
            }
        },
        _evaluateConst: function(expr, context, acceptsArray)
        {
            var value = expr.value;
            if(value instanceof CalcArray)
            {
                if(acceptsArray || context && (context.arrayFormulaMode || context._isExpandArrayToMultiCall()))
                    return value;
                return Helper.getArrayValue(value,0,0)
            }
            return value
        },
        _evaluateReference: function(expr, context, acceptsReference)
        {
            if(!context)
                return CalcErrorsValue;
            if(expr instanceof StructReferenceExpression)
                return CalcErrorsReference;
            var source = context.source;
            if(expr instanceof ExternalReferenceExpression)
                source = expr.source;
            var baseRow = context.row,
                baseColumn = context.column;
            var pos = expr.getRange(baseRow,baseColumn);
            if(acceptsReference || context.arrayFormulaMode)
                return context.getReference(source,pos.row,pos.column,pos.rowCount,pos.columnCount);
            var row = pos.row,
                column = pos.column;
            if(pos.rowCount > 1 && expr.startRowRelative && expr.endRowRelative || pos.columnCount > 1 && expr.startColumnRelative && expr.endColumnRelative)
            {
                var rowInRange = context.row >= pos.row && context.row < pos.row + pos.rowCount;
                var colInRange = context.column >= pos.column && context.column < pos.column + pos.columnCount;
                if(!rowInRange && !colInRange)
                    return CalcErrorsReference;
                else if(rowInRange && !colInRange && pos.columnCount === 1)
                    row = context.row;
                else if(!rowInRange && colInRange && pos.rowCount === 1)
                    column = context.column;
                else if(pos.rowCount === 1 || pos.columnCount === 1)
                {
                    row = context.row;
                    column = context.column
                }
                else
                    return CalcErrorsReference
            }
            else if(pos.row === -1 && pos.rowCount === -1)
            {
                if(pos.columnCount !== 1)
                    return CalcErrorsReference;
                row = context.row
            }
            else if(pos.column === -1 && pos.columnCount === -1)
            {
                if(pos.rowCount !== 1)
                    return CalcErrorsReference;
                column = context.column
            }
            return context.getValue(source,row,column)
        },
        _evaluateName: function(name, nameContext, evalContext, acceptsArray, acceptsReference)
        {
            if(!nameContext || !evalContext)
                return CalcErrorsName;
            var sub = nameContext.getName(name);
            if(!sub)
                return CalcErrorsName;
            return this._evaluate(sub,evalContext,acceptsArray,acceptsReference)
        },
        _evaluateOperationArg: function(argExpr, context, acceptsArray, acceptsReference)
        {
            var arg = this._evaluate(argExpr,context,acceptsArray,acceptsReference);
            if(arg instanceof CalcError)
                return arg;
            if(arg === Calc.missingArgument)
                return CalcErrorsNotAvailable;
            return arg
        },
        _evaluateUnaryOperation: function(expr, context, acceptsArray)
        {
            var arg = this._evaluateOperationArg(expr.operand,context,acceptsArray,false);
            try
            {
                return expr.operator.evaluate(arg,context)
            }
            catch(err)
            {
                return CalcErrorsValue
            }
        },
        _evaluateBinaryOperation: function(expr, context, acceptsArray)
        {
            var arg0 = this._evaluateOperationArg(expr.left,context,acceptsArray,expr.operator.acceptsReference);
            if(arg0 instanceof CalcError)
                return arg0;
            var arg1 = this._evaluateOperationArg(expr.right,context,acceptsArray,expr.operator.acceptsReference);
            if(arg1 instanceof CalcError)
                return arg1;
            try
            {
                return expr.operator.evaluate(arg0,arg1,context)
            }
            catch(err)
            {
                return CalcErrorsValue
            }
        },
        _evaluateFunction: function(expr, context, acceptsArray, acceptsReference)
        {
            var fnArgsResult = this._prepareFunctionArguments(expr,context);
            if(!fnArgsResult || !fnArgsResult.success || !fnArgsResult.fn || !fnArgsResult.args)
                return fnArgsResult.error;
            var argCount = expr.argCount();
            var args = fnArgsResult.args;
            var fn = fnArgsResult.fn;
            if(context && context.arrayFormulaMode)
            {
                if(argCount > 0)
                    return this._evaluateFunctionWithArrayFormulaMode(fn,args,context,acceptsArray,acceptsReference);
                return this._evaluateFunctionImp(fn,args,context,acceptsArray,acceptsReference,0,0)
            }
            var arrayIndex = fnArgsResult.arrayIndex;
            if(context && context._isExpandArrayToMultiCall() && arrayIndex && arrayIndex.length > 0)
            {
                var expandArgs = [];
                var argAreReady = [];
                var allArgReady = true;
                var i;
                for(i = 0; i < argCount; i++)
                    if(arrayIndex.contains(i))
                    {
                        var o = args[i];
                        if(o instanceof CalcArray)
                            if(o.length() === 1)
                            {
                                expandArgs[i] = o.getValueByIndex(0);
                                argAreReady[i] = true
                            }
                            else
                            {
                                expandArgs[i] = o;
                                argAreReady[i] = false
                            }
                        else
                        {
                            expandArgs[i] = null;
                            argAreReady[i] = true
                        }
                    }
                    else
                        argAreReady[i] = true;
                if(!allArgReady)
                {
                    var rowCount = [],
                        colCount = [];
                    for(i = 0; i < argCount; i++)
                        if(!argAreReady[i])
                        {
                            rowCount[i] = expandArgs[i].getRowCount();
                            colCount[i] = expandArgs[i].getColumnCount()
                        }
                        else
                        {
                            rowCount[i] = -1;
                            colCount[i] = -1
                        }
                    var row = -1,
                        col = -1;
                    for(i = 0; i < argCount; i++)
                        if(!argAreReady[i])
                        {
                            var rc = rowCount[i],
                                cc = colCount[i];
                            row = row === -1 ? rc : rc > 1 ? row > 1 ? Math.min(rc,row) : rc : row;
                            col = col === -1 ? cc : cc > 1 ? col > 1 ? Math.min(cc,col) : cc : col
                        }
                    var result = [];
                    for(var r = 0; r < row; r++)
                    {
                        var rRow = result[r];
                        if(rRow === undefined || rRow === null)
                            result[r] = rRow = [];
                        for(var c = 0; c < col; c++)
                        {
                            var rebuildArgs = [];
                            for(i = 0; i < argCount; i++)
                                rebuildArgs[i] = args[i];
                            for(i = 0; i < arrayIndex.length; i++)
                                rebuildArgs[arrayIndex[i]] = expandArgs[arrayIndex[i]];
                            rRow[c] = this._evaluateFunctionImp(fn,rebuildArgs,context,acceptsArray,acceptsReference,r,c)
                        }
                    }
                    return new ConcreteArray(result,row,col)
                }
                for(i = 0; i < arrayIndex.length; i++)
                    args[arrayIndex[i]] = expandArgs[arrayIndex[i]]
            }
            return this._evaluateFunctionImp(fn,args,context,acceptsArray,acceptsReference,0,0)
        },
        _tryGetFunction: function(expr, context)
        {
            var argCount = expr.argCount();
            var fn = expr.fn;
            if(!(fn instanceof Functions.Function))
            {
                if(!context)
                    return{
                            success: false,
                            error: CalcErrorsName
                        };
                fn = context.getFunction(fn);
                if(!fn || !(fn instanceof Functions.Function))
                    return{
                            success: false,
                            error: CalcErrorsName
                        };
                if(argCount < fn.minArgs || fn.maxArgs < argCount)
                    return{
                            success: false,
                            error: CalcErrorsValue
                        }
            }
            return{
                    success: true,
                    fn: fn
                }
        },
        _evaluateFunctionWithArrayFormulaMode: function(fn, args, context, acceptsArray, acceptsReference)
        {
            if(!args || args.length === 0)
                return this._evaluateFunctionImp(fn,args,context,acceptsArray,acceptsReference,0,0);
            if(fn.maxArgs === 1)
                return this._evaluateFunctionOneArg(fn,args[0],context,acceptsArray,acceptsReference);
            return this._evaluateFunctionTwoOrMoreArgs(fn,args,context,acceptsArray,acceptsReference)
        },
        _evaluateFunctionTwoOrMoreArgs: function(fn, args, context, acceptsArray, acceptsReference)
        {
            var argCount = args.length;
            var argArgReady = [];
            var allArgReady = true;
            var i,
                isArgReady;
            for(i = 0; i < argCount; i++)
            {
                var fnAcceptsRef = fn.acceptsReference && fn.acceptsReference(i);
                var singleArg = Helper.tryExtractToSingleValue(args[i]);
                isArgReady = fnAcceptsRef || singleArg.success;
                allArgReady = allArgReady && isArgReady;
                argArgReady[i] = isArgReady;
                if(!fnAcceptsRef)
                    args[i] = singleArg.value
            }
            if(allArgReady)
                return this._evaluateFunctionImp(fn,args,context,acceptsArray,acceptsReference,0,0);
            var rowCounts = [];
            var colCounts = [];
            for(i = 0; i < argCount; i++)
            {
                var isArrayArg = args[i] instanceof CalcArray;
                rowCounts[i] = argArgReady || !isArrayArg ? -1 : args[i].getRowCount();
                colCounts[i] = argArgReady || !isArgReady ? -1 : args[i].getColumnCount()
            }
            var row = -1,
                col = -1;
            for(i = 0; i < argCount; i++)
                if(!argArgReady[i])
                {
                    var rc = rowCounts[i],
                        cc = colCounts[i];
                    row = row === -1 ? rc : rc > 1 ? row > 1 ? Math.min(rc,row) : rc : row;
                    col = col === -1 ? cc : cc > 1 ? col > 1 ? Math.min(cc,col) : cc : col
                }
            var result = [];
            context._beginExpandArrayToMultiCall();
            for(i = 0; i < row; i++)
            {
                var rr = result[i];
                if(rr === undefined || rr === null)
                    result[i] = rr = [];
                for(var j = 0; j < col; j++)
                {
                    var newArgs = [];
                    for(var k = 0; k < argCount; k++)
                        newArgs[k] = argArgReady[k] ? args[k] : Helper.getArrayValue(args[k],i,j);
                    rr[j] = this._evaluateFunctionImp(fn,newArgs,context,acceptsArray,acceptsReference,i,j)
                }
            }
            context._endExpandArrayToMultiCall();
            return new ConcreteArray(result,row,col)
        },
        _evaluateFunctionOneArg: function(fn, arg, context, acceptsArray, acceptsReference)
        {
            if(fn.acceptsReference(0))
                return this._evaluateFunctionImp(fn,[arg],context,acceptsArray,acceptsReference,0,0);
            var singleArg = Helper.tryExtractToSingleValue(arg);
            if(singleArg.success)
                return this._evaluateFunctionImp(fn,[singleArg.value],context,acceptsArray,acceptsReference,0,0);
            var array = singleArg.value;
            var result = [];
            var row = array.getRowCount(),
                col = array.getColumnCount();
            for(var i = 0; i < row; i++)
            {
                var rr = result[i];
                if(rr === undefined || rr === null)
                    result[i] = rr = [];
                for(var j = 0; j < col; j++)
                    rr[j] = this._evaluateFunctionImp(fn,[array.getValue(i,j)],context,acceptsArray,acceptsReference,j,j)
            }
            return new ConcreteArray(result,row,col)
        },
        _evaluateFunctionImp: function(fn, args, context, acceptsArray, acceptsReference, offsetRow, offsetColumn)
        {
            try
            {
                var newContext = offsetRow !== 0 && offsetColumn !== 0 ? context ? context._offset(offsetRow,offsetColumn) : null : context;
                var result = fn.isContextSensitive() ? fn.evaluate(args,newContext) : fn.evaluate(args);
                if(result instanceof CalcReference)
                {
                    if(newContext)
                        if(newContext.arrayFormulaMode)
                        {
                            if(context._isExpandArrayToMultiCall())
                                result = Helper.getArrayValue(Convert._toArray(result),offsetRow,offsetColumn)
                        }
                        else if(!acceptsReference)
                            result = this._extractValueFromReference(result,newContext.row,newContext.column)
                }
                else if(result instanceof CalcArray)
                    if(!context)
                        result = Helper.getArrayValue(result,offsetRow,offsetColumn);
                    else if(context.arrayFormulaMode)
                    {
                        if(context._isExpandArrayToMultiCall())
                            result = Helper.getArrayValue(result,offsetRow,offsetColumn)
                    }
                    else if(!acceptsArray && context._isExpandArrayToMultiCall())
                        result = Helper.getArrayValue(result,offsetRow,offsetColumn);
                return result
            }
            catch(err)
            {
                return CalcErrorsValue
            }
        },
        _prepareFunctionArguments: function(expr, context)
        {
            var fnResult = this._tryGetFunction(expr,context);
            if(!fnResult.success)
                return fnResult;
            var fn = fnResult.fn;
            var expandArrayArgIndex = [];
            var argCount = expr.argCount();
            var args = [],
                i,
                arg,
                argResult;
            if(fn.isBranch())
            {
                i = fn.findTestArgument();
                if(i >= 0 && i < argCount)
                {
                    argResult = this._evaluateFunctionArgument(expr.getArg(i),context,fn.acceptsArray(i),fn.acceptsReference(i),fn.acceptsError(i),fn.acceptsMissingArgument(i));
                    arg = argResult.value;
                    if(!argResult.success)
                        return{
                                success: false,
                                error: arg
                            };
                    if(context && !context.arrayFormulaMode)
                        if(arg instanceof CalcReference)
                        {
                            arg = this._extractValueFromReference(arg,context.row,context.column);
                            if(!fn.acceptsError(i) && arg instanceof CalcError)
                                return{
                                        success: false,
                                        error: arg
                                    };
                            if(!fn.acceptsMissingArgument(i) && arg === Calc.missingArgument)
                                arg = null
                        }
                        else if(arg instanceof CalcArray && !fn.acceptsArray(i))
                            if(!context._isExpandArrayToMultiCall())
                            {
                                arg = Helper.getArrayValue(arg,0,0);
                                if(!fn.acceptsError(i) && arg instanceof CalcError)
                                    return{
                                            success: false,
                                            error: arg
                                        };
                                if(!fn.acceptsMissingArgument(i) && arg === Calc.missingArgument)
                                    arg = null
                            }
                            else
                                expandArrayArgIndex.push(i);
                    args[i] = arg
                }
                var branchArgIndex = [];
                var tests = Convert._toArray(args[i]);
                var trc = tests.getRowCount(),
                    tcc = tests.getColumnCount();
                for(var r = 0; r < trc; r++)
                    for(var c = 0; c < tcc; c++)
                    {
                        var index = fn.findBranchArgument(tests.getValue(r,c));
                        if(index === -1)
                            continue;
                        if(!branchArgIndex.contains(index))
                            branchArgIndex.push(index);
                        if(branchArgIndex.length >= fn.maxArgs - 1)
                            break
                    }
                if(branchArgIndex.length === 0)
                    return{
                            success: false,
                            error: CalcErrorsValue
                        };
                for(i = 0; i < branchArgIndex.length; i++)
                {
                    var argIndex = branchArgIndex[i];
                    if(argIndex >= 0 && argIndex < argCount)
                    {
                        argResult = this._evaluateFunctionArgument(expr.getArg(argIndex),context,fn.acceptsArray(argIndex),fn.acceptsReference(argIndex),fn.acceptsError(argIndex),fn.acceptsMissingArgument(argIndex));
                        arg = argResult.value;
                        if(!argResult.success)
                            return{
                                    success: false,
                                    error: arg
                                };
                        if(context && context.arrayFormulaMode)
                            if(arg instanceof CalcArray && !fn.acceptsArray(argIndex))
                                if(!context._isExpandArrayToMultiCall())
                                {
                                    arg = Helper.getArrayValue(arg,0,0);
                                    if(!fn.acceptsError(argIndex) && arg instanceof CalcError)
                                        return{
                                                success: false,
                                                error: arg
                                            };
                                    if(!fn.acceptsMissingArgument(argIndex) && arg === Calc.missingArgument)
                                        arg = null
                                }
                                else
                                    expandArrayArgIndex.push(argIndex);
                        args[argIndex] = arg
                    }
                }
                if(expandArrayArgIndex.length > 0)
                    expandArrayArgIndex.sort(function(a, b)
                    {
                        return a - b
                    })
            }
            else
                for(i = 0; i < argCount; i++)
                {
                    argResult = this._evaluateFunctionArgument(expr.getArg(i),context,fn.acceptsArray(i),fn.acceptsReference(i),fn.acceptsError(i),fn.acceptsMissingArgument(i));
                    arg = argResult.value;
                    if(!argResult.success)
                        return{
                                success: false,
                                error: arg
                            };
                    if(context && context.arrayFormulaMode)
                        if(arg instanceof CalcArray && !fn.acceptsArray(i))
                            if(!context._isExpandArrayToMultiCall())
                            {
                                arg = Helper.getArrayValue(arg,0,0);
                                if(!fn.acceptsError(i) && arg instanceof CalcError)
                                    return{
                                            success: false,
                                            error: arg
                                        };
                                if(!fn.acceptsMissingArgument(i) && arg === Calc.missingArgument)
                                    arg = null
                            }
                            else
                                expandArrayArgIndex.push(i);
                    args[i] = arg
                }
            return{
                    success: true,
                    args: args,
                    fn: fn,
                    arrayIndex: expandArrayArgIndex
                }
        },
        _evaluateFunctionArgumentImp: function(expr, context, acceptsArray, acceptsReference, acceptsError, acceptsMissArgument)
        {
            var result = this._evaluate(expr,context,acceptsArray,acceptsReference);
            if(!acceptsError && result instanceof CalcError)
                return{
                        success: false,
                        value: result
                    };
            if(!acceptsMissArgument && result === Calc.missingArgument)
                return{
                        success: true,
                        value: null
                    };
            if(context && context.arrayFormulaMode)
            {
                if(result instanceof CalcArray && !acceptsArray && !context._isExpandArrayToMultiCall())
                {
                    if(result.length() > 0)
                        return{
                                success: true,
                                value: result.getValueByIndex(0)
                            };
                    return{
                            success: false,
                            value: CalcErrorsNotAvailable
                        }
                }
                if(result instanceof CalcReference && !acceptsReference)
                {
                    result = this._extractValueFromReference(result,0,0);
                    if(result === undefined || result === null)
                        result = 0;
                    if(!acceptsError && result instanceof CalcError)
                        return{
                                success: false,
                                value: result
                            };
                    if(!acceptsMissArgument && result === Calc.missingArgument)
                        return{
                                success: true,
                                value: null
                            }
                }
            }
            return{
                    success: true,
                    value: result
                }
        },
        _evaluateFunctionArgument: function(expr, context, acceptsArray, acceptsReference, acceptsError, acceptsMissArgument)
        {
            if(context && (acceptsArray || context._isExpandArrayToMultiCall()) && !context.arrayFormulaMode)
            {
                context._beginExpandArrayToMultiCall();
                var result = this._evaluateFunctionArgumentImp(expr,context,acceptsArray,acceptsReference,acceptsError,acceptsMissArgument);
                context._endExpandArrayToMultiCall();
                return result
            }
            return this._evaluateFunctionArgumentImp(expr,context,acceptsArray,acceptsReference,acceptsError,acceptsMissArgument)
        }
    };
    Calc.Evaluator = Evaluator;
    function addListenerImp(obj, prop, item)
    {
        if(!obj.hasOwnProperty(prop) || !obj[prop])
            obj[prop] = [];
        obj[prop].push(item)
    }
    function removeListenerImp(obj, prop, item)
    {
        var collection = obj[prop];
        if(collection)
            for(var i = 0; i < collection.length; i++)
                if(collection[i] === item)
                {
                    collection.splice(i,1);
                    return
                }
    }
    function iteratedAction(obj, list, action)
    {
        $(list).each(function(i, e)
        {
            if(obj[e])
                $(obj[e]).each(function(ii, ee)
                {
                    ee[action]()
                })
        })
    }
    function intersectsListeners(obj, row, column, rowCount, columnCount)
    {
        return(obj.row === -1 || row === -1 || obj.row < row + rowCount && row < obj.row + obj.rowCount) && (obj.column === -1 || column === -1 || obj.column < column + columnCount && column < obj.column + obj.columnCount)
    }
    function containsCell(obj, row, column)
    {
        return(obj.row === -1 || obj.row <= row && row < obj.row + obj.rowCount) && (obj.column === -1 || obj.column <= column && column < obj.column + obj.columnCount)
    }
    var actionIntersects = "intersects",
        actionContains = "contains";
    function checkConditonImp(obj, opt)
    {
        if(opt.action === actionIntersects)
            return intersectsListeners(obj,opt.row,opt.column,opt.rowCount,opt.columnCount);
        else if(opt.action === actionContains)
            return containsCell(obj,opt.row,opt.column);
        return false
    }
    function iteratedAction2(obj, list, opt, action)
    {
        $(list).each(function(i, e)
        {
            if(obj[e])
                $(obj[e]).each(function(ii, ee)
                {
                    if(checkConditonImp(ee,opt))
                        ee.listener[action]()
                })
        })
    }
    function hasListensersImp(obj, list)
    {
        for(var i = 0; i < list.length; i++)
            if(obj.hasOwnProperty(list[i]) && obj[list[i]].length > 0)
                return true;
        return false
    }
    var cellListeners = "cellListeners",
        rowListeners = "rowListeners",
        columnListeners = "columnListeners",
        rowSliceListeners = "rowSliceListeners",
        columnSliceListeners = "columnSliceListeners";
    var addToAdjust = "addToAdjust",
        addToDirty = "addToDirty";
    var incrementDelay = "incrementDelay",
        decrementDelay = "decrementDelay";
    function BaseCalc(){}
    BaseCalc.prototype = {
        hasListeners: function()
        {
            return hasListensersImp(this,this._listeners)
        },
        addCellListener: function(cell)
        {
            addListenerImp(this,cellListeners,cell)
        },
        removeCellListener: function(cell)
        {
            removeListenerImp(this,cellListeners,cell)
        },
        addRowListener: function(row)
        {
            addListenerImp(this,rowListeners,row)
        },
        removeRowListener: function(row)
        {
            removeListenerImp(this,rowListeners,row)
        },
        addColumnListener: function(column)
        {
            addListenerImp(this,columnListeners,column)
        },
        removeColumnListener: function(column)
        {
            removeListenerImp(this,columnListeners,column)
        },
        addListenersToAdjust: function()
        {
            iteratedAction(this,this._listeners,addToAdjust)
        },
        addListenersToDirty: function()
        {
            iteratedAction(this,this._listeners,addToDirty)
        },
        incrementDelayOfListeners: function()
        {
            iteratedAction(this,this._listeners,incrementDelay)
        },
        decrementDelayOfListeners: function()
        {
            iteratedAction(this,this._listeners,decrementDelay)
        }
    };
    function CellCalc(sheetSource, row, column)
    {
        this.sheetSource = sheetSource;
        this.row = row;
        this.column = column;
        this.delay = 0;
        this._listeners = [cellListeners,rowListeners,columnListeners]
    }
    CellCalc.prototype = new BaseCalc;
    CellCalc.prototype.incrementDelay = function()
    {
        this.delay += 1
    };
    CellCalc.prototype.decrementDelay = function()
    {
        this.delay -= 1
    };
    CellCalc.prototype.startListening = function()
    {
        this.sheetSource.linkCellExpression(this.row,this.column,1,1)
    };
    CellCalc.prototype.stopListening = function()
    {
        this.sheetSource.unlinkCellExpression(this.row,this.column,1,1)
    };
    CellCalc.prototype.addToAdjust = function()
    {
        this.sheetSource._controller.addAdjustCell(this)
    };
    CellCalc.prototype.addToDirty = function()
    {
        this.sheetSource._controller.addDirtyCell(this)
    };
    function RowCalc(sheetSource, row)
    {
        this.sheetSource = sheetSource;
        this.row = row;
        this._listeners = [cellListeners,rowListeners,rowSliceListeners,columnListeners]
    }
    RowCalc.prototype = new BaseCalc;
    RowCalc.prototype.incrementDelay = function()
    {
        this.sheetSource.incrementDelay(this.row,-1,1,-1)
    };
    RowCalc.prototype.incrementDelay2 = function(column, columnCount)
    {
        this.sheetSource.incrementDelay(this.row,column,1,columnCount)
    };
    RowCalc.prototype.decrementDelay = function()
    {
        this.sheetSource.decrementDelay(this.row,-1,1,-1)
    };
    RowCalc.prototype.decrementDelay2 = function(column, columnCount)
    {
        this.sheetSource.decrementDelay(this.row,column,1,columnCount)
    };
    RowCalc.prototype.startListening = function()
    {
        this.sheetSource.linkRowExpression(this.row,1)
    };
    RowCalc.prototype.stopListening = function()
    {
        this.sheetSource.unlinkRowExpression(this.row,1)
    };
    RowCalc.prototype.addRowListener2 = function(row, columnOffset)
    {
        addListenerImp(this,rowSliceListeners,{
            row: row,
            columnOffset: columnOffset,
            columnCount: 1
        })
    };
    RowCalc.prototype.removeRowListenser2 = function(row, columnOffset)
    {
        removeListenerImp(this,rowSliceListeners,{
            row: row,
            columnOffset: columnOffset,
            columnCount: 1
        })
    };
    RowCalc.prototype.addToAdjust = function()
    {
        this.sheetSource._controller.addAdjustRow(this)
    };
    RowCalc.prototype.addToDirty = function()
    {
        this.sheetSource._controller.addDirtyRow(this)
    };
    function ColumnCalc(sheetSource, column)
    {
        this.sheetSource = sheetSource;
        this.column = column;
        this._listeners = [cellListeners,rowListeners,columnListeners,columnSliceListeners]
    }
    ColumnCalc.prototype = new BaseCalc;
    ColumnCalc.prototype.incrementDelay = function()
    {
        this.sheetSource.incrementDelay(-1,this.column,-1,1)
    };
    ColumnCalc.prototype.incrementDelay2 = function(row, rowCount)
    {
        this.sheetSource.incrementDelay(row,this.column,rowCount,1)
    };
    ColumnCalc.prototype.decrementDelay = function()
    {
        this.sheetSource.decrementDelay(-1,this.column,-1,1)
    };
    ColumnCalc.prototype.decrementDelay2 = function(row, rowCount)
    {
        this.sheetSource.decrementDelay(row,this.column,rowCount,1)
    };
    ColumnCalc.prototype.startListening = function()
    {
        this.sheetSource.linkColumnExpression(this.column,1)
    };
    ColumnCalc.prototype.stopListening = function()
    {
        this.sheetSource.unlinkColumnExpression(this.column,1)
    };
    ColumnCalc.prototype.addColumnListener2 = function(column, rowOffset)
    {
        addListenerImp(this,columnSliceListeners,{
            column: column,
            rowOffset: rowOffset,
            rowCount: 1
        })
    };
    ColumnCalc.prototype.removeColumnListenser2 = function(column, rowOffset)
    {
        removeListenerImp(this,columnSliceListeners,{
            column: column,
            rowOffset: rowOffset,
            rowCount: 1
        })
    };
    ColumnCalc.prototype.addToAdjust = function()
    {
        this.sheetSource._controller.addAdjustColumn(this)
    };
    ColumnCalc.prototype.addToDirty = function()
    {
        this.sheetSource._controller.addDirtyColumn(this)
    };
    function NameCalc(name)
    {
        this.name = name;
        this._listeners = [cellListeners,rowListeners,columnListeners]
    }
    NameCalc.prototype = new BaseCalc;
    NameCalc.prototype.incrementDelay = function()
    {
        this.delay += 1
    };
    NameCalc.prototype.decrementDelay = function()
    {
        this.delay -= 1
    };
    NameCalc.prototype.updateListening = function(stop, start)
    {
        var self = this;
        $(this._listeners).each(function(i, e)
        {
            if(self[e])
                $(self[e]).each(function(ii, ee)
                {
                    if(stop)
                        ee["stopListening"]();
                    if(start)
                        ee["startListening"]()
                })
        })
    };
    NameCalc.prototype.addToDirty = function()
    {
        iteratedAction(this,this._listeners,addToDirty)
    };
    var typeCell = "cell",
        typeRow = "row",
        typeCol = "column";
    function RangeCalc(sheetSource)
    {
        this.sheetSource = sheetSource;
        this._listeners = [cellListeners,rowListeners,columnListeners]
    }
    RangeCalc.prototype = {
        addCellListener: function(row, column, rowCount, columnCount, listener)
        {
            addListenerImp(this,cellListeners,{
                type: typeCell,
                row: row,
                column: column,
                rowCount: rowCount,
                columnCount: columnCount,
                listener: listener
            })
        },
        removeCellListener: function(row, column, rowCount, columnCount, listener)
        {
            removeListenerImp(this,cellListeners,{
                type: typeCell,
                row: row,
                column: column,
                rowCount: rowCount,
                columnCount: columnCount,
                listener: listener
            })
        },
        addRowListener: function(row, column, rowCount, columnCount, listener, all, sliceOffset, sliceCount)
        {
            addListenerImp(this,rowListeners,{
                type: typeRow,
                row: row,
                column: column,
                rowCount: rowCount,
                columnCount: columnCount,
                listener: listener
            })
        },
        removeRowListener: function(row, column, rowCount, columnCount, listener, all, sliceOffset, sliceCount)
        {
            removeListenerImp(this,rowListeners,{
                type: typeRow,
                row: row,
                column: column,
                rowCount: rowCount,
                columnCount: columnCount,
                listener: listener
            })
        },
        addColumnListener: function(row, column, rowCount, columnCount, listener, all, sliceOffset, sliceCount)
        {
            addListenerImp(this,columnListeners,{
                type: typeCol,
                row: row,
                column: column,
                rowCount: rowCount,
                columnCount: columnCount,
                listener: listener
            })
        },
        removeColumnListener: function(row, column, rowCount, columnCount, listener, all, sliceOffset, sliceCount)
        {
            removeListenerImp(this,columnListeners,{
                type: typeCol,
                row: row,
                column: column,
                rowCount: rowCount,
                columnCount: columnCount,
                listener: listener
            })
        },
        addListenersToAdjust: function(row, column, rowCount, columnCount)
        {
            iteratedAction2(this,this._listeners,{
                action: actionIntersects,
                row: row,
                column: column,
                rowCount: rowCount,
                columnCount: columnCount
            },addToAdjust)
        },
        addListenersToDirty: function(row, column, rowCount, columnCount)
        {
            iteratedAction2(this,this._listeners,{
                action: actionIntersects,
                row: row,
                column: column,
                rowCount: rowCount,
                columnCount: columnCount
            },addToDirty)
        },
        incrementDelayOfListeners: function(row, column)
        {
            iteratedAction2(this,this._listeners,{
                action: actionContains,
                row: row,
                column: column
            },incrementDelay)
        },
        decrementDelayOfListeners: function(row, column)
        {
            iteratedAction2(this,this._listeners,{
                action: actionContains,
                row: row,
                column: column
            },decrementDelay)
        }
    };
    function fnIntersectWith(current, range)
    {
        var top = Math.max(current.row,range.row);
        var left = Math.max(current.column,range.column);
        var bottom = Math.min(current.row + current.rowCount,range.row + range.rowCount);
        var right = Math.min(current.column + current.columnCount,range.column + range.columnCount);
        if(top >= bottom || left >= right)
            return null;
        return{
                row: top,
                column: left,
                rowCount: bottom - top,
                columnCount: right - left,
                intersectWith: fnIntersectWith
            }
    }
    function MoveSwapInfo(fromModel, fromRow, fromColumn, toModel, toRow, toColumn, rowCount, columnCount)
    {
        this._fromModel = fromModel;
        this._fromRow = fromRow;
        this._fromColumn = fromColumn;
        this._toModel = toModel;
        this._toRow = toRow;
        this._toColumn = toColumn;
        this._rowCount = rowCount;
        this._columnCount = columnCount
    }
    MoveSwapInfo.prototype = {
        getRowOffset: function()
        {
            return this._toRow - this._fromRow
        },
        getColumnOffset: function()
        {
            return this._toColumn - this._fromColumn
        },
        sourceContains: function(model, row, column, rowCount, columnCount)
        {
            return this._fromModel === model && (this._fromRow === -1 || this._fromRow <= row && row + rowCount <= this._fromRow + this._rowCount) && (this._fromColumn === -1 || this._fromColumn <= column && column + columnCount <= this._fromColumn + this._columnCount)
        },
        destinationContains: function(model, row, column, rowCount, columnCount)
        {
            return this._toModel === model && (this._toRow === -1 || this._toRow <= row && row + rowCount <= this._toRow + this._rowCount) && (this._toColumn === -1 || this._toColumn <= column && column + columnCount <= this._toColumn + this._columnCount)
        },
        sourceIntersectWith: function(model, row, column, rowCount, columnCount)
        {
            if(this._toModel !== model)
                return null;
            var range = this._createValidCellRange(model,row,column,rowCount,columnCount);
            var sourceRange = this._createValidCellRange(model,this._fromRow,this._fromColumn,rowCount,columnCount);
            return sourceRange.intersectWith(range)
        },
        _createValidCellRange: function(model, row, column, rowCount, columnCount)
        {
            var validFromRow = row === -1 ? 0 : row;
            var validFromRowCount = row === -1 || rowCount === -1 ? model.rowCount : rowCount;
            var validFromColumn = column === -1 ? 0 : column;
            var validFromColumnCount = column === -1 || columnCount === -1 ? model.columnCount : columnCount;
            return{
                    row: validFromRow,
                    column: validFromColumn,
                    rowCount: validFromRowCount,
                    columnCount: validFromColumnCount,
                    intersectWith: fnIntersectWith
                }
        }
    };
    function Controller()
    {
        this._dataModels = [];
        this._customFunctions = [];
        this._customNames = [];
        this.clearDirties()
    }
    Controller.prototype = {
        autoCalculation: true,
        iteration: false,
        maximumIterations: 1,
        maximumChange: 0.01,
        attachDataModel: function(sheetSource)
        {
            if(!this._dataModels.contains(sheetSource))
                this._dataModels.push(sheetSource)
        },
        detachDataModel: function(sheetSource)
        {
            var nextDirtyCell = null;
            for(var cell = this._headDirtyCells; cell; cell = nextDirtyCell)
            {
                nextDirtyCell = cell.nextDirty;
                if(cell.sheetSource === sheetSource)
                    this.removeDirtyCell(cell)
            }
            var nextDirtyRow = null;
            for(var row = this._headDirtyRows; row; row = nextDirtyRow)
            {
                nextDirtyRow = row.nextDirty;
                if(row.sheetSource === sheetSource)
                    this.removeDirtyRow(row)
            }
            var nextDirtyColumn = null;
            for(var column = this._headDirtyColumns; column; column = nextDirtyColumn)
            {
                nextDirtyColumn = column.nextDirty;
                if(column.sheetSource === sheetSource)
                    this.removeDirtyColumn(column)
            }
            for(var i = 0; i < this._dataModels.length; i++)
                if(this._dataModels[i] === sheetSource)
                {
                    this._dataModels.splice(i,1);
                    break
                }
        },
        addCustomFunctions: function(funcs)
        {
            if(funcs === undefined || funcs === null || funcs.length <= 0)
                return;
            for(var i = 0; i < funcs.length; i++)
            {
                var fn = funcs[i];
                this._customFunctions.push(fn)
            }
        },
        addCustomNames: function(names)
        {
            if(names === undefined || names === null || names.length <= 0)
                return;
            for(var i = 0; i < names.length; i++)
            {
                var n = names[i];
                this._customNames.push(n)
            }
        },
        isDirtyCell: function(cell)
        {
            return cell.previousDirty || cell === this._headDirtyCells
        },
        addDirtyCell: function(cell)
        {
            if(cell && !this.isDirtyCell(cell) && cell.row < cell.sheetSource.getRowCount() && cell.column < cell.sheetSource.getColumnCount())
            {
                if(this._headDirtyCells)
                    this._tailDirtyCells.nextDirty = cell;
                else
                    this._headDirtyCells = cell;
                cell.previousDirty = this._tailDirtyCells;
                cell.nextDirty = null;
                this._tailDirtyCells = cell
            }
        },
        removeDirtyCell: function(cell)
        {
            if(this.isDirtyCell(cell))
            {
                var prevDirty = cell.previousDirty;
                var nextDirty = cell.nextDirty;
                if(prevDirty)
                    prevDirty.nextDirty = nextDirty;
                else
                    this._headDirtyCells = nextDirty;
                if(nextDirty)
                    nextDirty.previousDirty = prevDirty;
                else
                    this._tailDirtyCells = prevDirty;
                cell.previousDirty = null;
                cell.nextDirty = null
            }
        },
        isDirtyRow: function(row)
        {
            return row.previousDirty || row === this._headDirtyRows
        },
        addDirtyRow: function(row)
        {
            if(row && !this.isDirtyRow(row) && row.row < row.sheetSource.getRowCount())
            {
                if(this._headDirtyRows)
                    this._tailDirtyRows.nextDirty = row;
                else
                    this._headDirtyRows = row;
                row.previousDirty = this._tailDirtyRows;
                row.nextDirty = null;
                this._tailDirtyRows = row
            }
        },
        removeDirtyRow: function(row)
        {
            if(this.isDirtyRow(row))
            {
                var prevDirty = row.previousDirty;
                var nextDirty = row.nextDirty;
                if(prevDirty)
                    prevDirty.nextDirty = nextDirty;
                else
                    this._headDirtyRows = nextDirty;
                if(nextDirty)
                    nextDirty.previousDirty = prevDirty;
                else
                    this._tailDirtyRows = prevDirty;
                row.previousDirty = null;
                row.nextDirty = null
            }
        },
        isDirtyColumn: function(column)
        {
            return column.previousDirty || column === this._headDirtyColumns
        },
        addDirtyColumn: function(column)
        {
            if(column && !this.isDirtyColumn(column) && column.column < column.sheetSource.getColumnCount())
            {
                if(this._headDirtyColumns)
                    this._tailDirtyColumns.nextDirty = column;
                else
                    this._headDirtyColumns = column;
                column.previousDirty = this._tailDirtyColumns;
                column.nextDirty = null;
                this._tailDirtyColumns = column
            }
        },
        removeDirtyColumn: function(column)
        {
            if(this.isDirtyColumn(column))
            {
                var prevDirty = column.previousDirty;
                var nextDirty = column.nextDirty;
                if(prevDirty)
                    prevDirty.nextDirty = nextDirty;
                else
                    this._headDirtyColumns = nextDirty;
                if(nextDirty)
                    nextDirty.previousDirty = prevDirty;
                else
                    this._tailDirtyColumns = prevDirty;
                column.previousDirty = null;
                column.nextDirty = null
            }
        },
        isVolatileCell: function(cell)
        {
            return cell.previousVolatile || cell === this._headVolatileCells
        },
        addVolatileCell: function(cell)
        {
            if(cell && !this.isVolatileCell(cell) && cell.row < cell.sheetSource.getRowCount() && cell.column < cell.sheetSource.getColumnCount())
            {
                if(this._headVolatileCells)
                    this._tailVolatileCells.nextDirty = cell;
                else
                    this._headVolatileCells = cell;
                cell.previousDirty = this._tailVolatileCells;
                cell.nextDirty = null;
                this._tailVolatileCells = cell
            }
        },
        removeVolatileCell: function(cell)
        {
            if(this.isVolatileCell(cell))
            {
                var prevDirty = cell.previousDirty;
                var nextDirty = cell.nextDirty;
                if(prevDirty)
                    prevDirty.nextDirty = nextDirty;
                else
                    this._headVolatileCells = nextDirty;
                if(nextDirty)
                    nextDirty.previousDirty = prevDirty;
                else
                    this._tailVolatileCells = prevDirty;
                cell.previousDirty = null;
                cell.nextDirty = null
            }
        },
        isAdjustCell: function(cell)
        {
            return cell.previousAdjust || cell === this._headAdjustCells
        },
        addAdjustCell: function(cell)
        {
            if(cell && !this.isAdjustCell(cell) && cell.row < cell.sheetSource.getRowCount() && cell.column < cell.sheetSource.getColumnCount())
            {
                if(this._headAdjustCells)
                    this._tailAdjustCells.nextAdjust = cell;
                else
                    this._headAdjustCells = cell;
                cell.previousAdjust = this._tailAdjustCells;
                cell.nextAdjust = null;
                this._tailAdjustCells = cell
            }
        },
        removeAdjustCell: function(cell)
        {
            if(this.isAdjustCell(cell))
            {
                var prevAdjust = cell.previousAdjust;
                var nextAdjust = cell.nextAdjust;
                if(prevAdjust)
                    prevAdjust.nextAdjust = nextAdjust;
                else
                    this._headAdjustCells = nextAdjust;
                if(nextAdjust)
                    nextAdjust.previousAdjust = prevAdjust;
                else
                    this._tailAdjustCells = prevAdjust;
                cell.previousAdjust = null;
                cell.nextAdjust = null
            }
        },
        isAdjustRow: function(row)
        {
            return row.previousAdjust || row === this._headAdjustRows
        },
        addAdjustRow: function(row)
        {
            if(row && !this.isAdjustRow(row) && row.row < row.sheetSource.getRowCount())
            {
                if(this._headAdjustRows)
                    this._tailAdjustRows.nextAdjust = row;
                else
                    this._headAdjustRows = row;
                row.previousAdjust = this._tailAdjustRows;
                row.nextAdjust = null;
                this._tailAdjustRows = row
            }
        },
        removeAdjustRow: function(row)
        {
            if(this.isAdjustRow(row))
            {
                var prevAdjust = row.previousAdjust;
                var nextAdjust = row.nextAdjust;
                if(prevAdjust)
                    prevAdjust.nextAdjust = nextAdjust;
                else
                    this._headAdjustRows = nextAdjust;
                if(nextAdjust)
                    nextAdjust.previousAdjust = prevAdjust;
                else
                    this._tailAdjustRows = prevAdjust;
                row.previousAdjust = null;
                row.nextAdjust = null
            }
        },
        isAdjustColumn: function(column)
        {
            return column.previousAdjust || column === this._headAdjustColumns
        },
        addAdjustColumn: function(column)
        {
            if(column && !this.isAdjustColumn(column) && column.column < column.sheetSource.getColumnCount())
            {
                if(this._headAdjustColumns)
                    this._tailAdjustColumns.nextAdjust = column;
                else
                    this._headAdjustColumns = column;
                column.previousAdjust = this._tailAdjustColumns;
                column.nextAdjust = null;
                this._tailAdjustColumns = column
            }
        },
        removeAdjustColumn: function(column)
        {
            if(this.isAdjustColumn(column))
            {
                var prevAdjust = column.previousAdjust;
                var nextAdjust = column.nextAdjust;
                if(prevAdjust)
                    prevAdjust.nextAdjust = nextAdjust;
                else
                    this._headAdjustColumns = nextAdjust;
                if(nextAdjust)
                    nextAdjust.previousAdjust = prevAdjust;
                else
                    this._tailAdjustColumns = prevAdjust;
                column.previousAdjust = null;
                column.nextAdjust = null
            }
        },
        recalculate: function()
        {
            var nextDirty = null,
                progress = false,
                sheetSource,
                r,
                c,
                row,
                col,
                rowCount,
                columnCount,
                cell,
                rowCalc,
                columnCalc,
                rangesCalc;
            while(this._headVolatileCells)
            {
                this.addDirtyCell(this._headVolatileCells);
                this.removeVolatileCell(this._headVolatileCells)
            }
            while(this._headDirtyRows)
            {
                sheetSource = this._headDirtyRows.sheetSource;
                columnCount = sheetSource.getColumnCount();
                for(c = 0; c < columnCount; c++)
                    this.addDirtyCell(sheetSource._getCellCalc(this._headDirtyRows.row,c,true));
                this.removeDirtyRow(this._headDirtyRows)
            }
            while(this._headDirtyColumns)
            {
                sheetSource = this._headDirtyColumns.sheetSource;
                rowCount = sheetSource.getRowCount();
                for(r = 0; r < rowCount; r++)
                    this.addDirtyCell(sheetSource._getCellCalc(r,this._headDirtyColumns.column,true));
                this.removeDirtyColumn(this._headDirtyColumns)
            }
            for(cell = this._headDirtyCells; cell; cell = cell.nextDirty)
            {
                sheetSource = cell.sheetSource;
                row = cell.row;
                col = cell.column;
                rowCalc = sheetSource._getRowCalc(row,false);
                columnCalc = sheetSource._getColumnCalc(col,false);
                rangesCalc = sheetSource._getRangeCalc(false);
                cell.addListenersToDirty();
                if(rowCalc)
                    rowCalc.addListenersToDirty(col);
                if(columnCalc)
                    columnCalc.addListenersToDirty(row);
                if(rangesCalc)
                    rangesCalc.addListenersToDirty(row,col,1,1);
                while(this._headDirtyRows)
                {
                    sheetSource = this._headDirtyRows.sheetSource;
                    columnCount = sheetSource.getColumnCount();
                    for(c = 0; c < columnCount; c++)
                        this.addDirtyCell(sheetSource._getCellCalc(this._headDirtyRows.row,c,true));
                    this.removeDirtyRow(this._headDirtyRows)
                }
                while(this._headDirtyColumns)
                {
                    sheetSource = this._headDirtyColumns.sheetSource;
                    rowCount = sheetSource.getRowCount();
                    for(r = 0; r < rowCount; r++)
                        this.addDirtyCell(sheetSource._getCellCalc(r,this._headDirtyColumns.column,true));
                    this.removeDirtyColumn(this._headDirtyColumns)
                }
            }
            for(cell = this._headDirtyCells; cell; cell = nextDirty)
            {
                sheetSource = cell.sheetSource;
                nextDirty = cell.nextDirty;
                if(cell.row < sheetSource.getRowCount() && cell.column < sheetSource.getColumnCount())
                    cell.delay = 0;
                else
                    this.removeDirtyCell(cell)
            }
            for(cell = this._headDirtyCells; cell; cell = cell.nextDirty)
            {
                sheetSource = cell.sheetSource;
                r = cell.row;
                c = cell.column;
                rowCalc = sheetSource._getRowCalc(r,false);
                columnCalc = sheetSource._getColumnCalc(c,false);
                rangesCalc = sheetSource._getRangeCalc(false);
                cell.incrementDelayOfListeners();
                if(rowCalc)
                    rowCalc.incrementDelayOfListeners(c);
                if(columnCalc)
                    columnCalc.incrementDelayOfListeners(r);
                if(rangesCalc)
                    rangesCalc.incrementDelayOfListeners(r,c)
            }
            do
            {
                progress = false;
                for(cell = this._headDirtyCells; cell; cell = nextDirty)
                {
                    nextDirty = cell.nextDirty;
                    if(cell.delay === 0)
                    {
                        sheetSource = cell.sheetSource;
                        r = cell.row;
                        c = cell.column;
                        rowCalc = sheetSource._getRowCalc(r,false);
                        columnCalc = sheetSource._getColumnCalc(c,false);
                        rangesCalc = sheetSource._getRangeCalc(false);
                        sheetSource.recalculateCell(r,c);
                        this.removeDirtyCell(cell);
                        cell.decrementDelayOfListeners();
                        if(rowCalc)
                            rowCalc.decrementDelayOfListeners(c);
                        if(columnCalc)
                            columnCalc.decrementDelayOfListeners(r);
                        if(rangesCalc)
                            rangesCalc.decrementDelayOfListeners(r,c);
                        progress = true
                    }
                }
            } while(progress);
            if(this.iteration && this._headDirtyCells)
            {
                var moreIterations = true;
                for(var i = 0; moreIterations && i < this.maximumIterations; i++)
                {
                    moreIterations = false;
                    for(cell = this._headDirtyCells; cell; cell = cell.nextDirty)
                    {
                        var formulaContext = cell.sheetSource;
                        r = cell.row;
                        c = cell.column;
                        var oldValue = formulaContext.getValue(r,c);
                        formulaContext.recalculateCell(r,c);
                        var newValue = formulaContext.getValue(r,c);
                        if(this._iterationChange(oldValue,newValue) >= this.maximumChange)
                            moreIterations = true
                    }
                }
            }
        },
        _iterationChange: function(oldValue, newValue)
        {
            var oldDouble = parseFloat(oldValue);
            var newDouble = parseFloat(newValue);
            return Math.abs(newDouble - oldDouble)
        },
        clearDirties: function()
        {
            this._headAdjustCells = null;
            this._tailAdjustCells = null;
            this._headAdjustRows = null;
            this._tailAdjustRows = null;
            this._headAdjustColumns = null;
            this._tailAdjustColumns = null;
            this._headDirtyCells = null;
            this._tailDirtyCells = null;
            this._headDirtyRows = null;
            this._tailDirtyRows = null;
            this._headDirtyColumns = null;
            this._tailDirtyColumns = null;
            this._headVolatileCells = null;
            this._tailVolatileCells = null
        },
        _adjustIndexOnAdd: function(baseModel, refModel, baseIndex, index, relative, addModel, addIndex, addCount)
        {
            if(baseIndex >= 0 || !relative)
            {
                var refIndex = index + (relative ? baseIndex : 0);
                if(addModel === baseModel)
                    if(addIndex + addCount <= baseIndex)
                        refIndex -= relative ? addCount : 0;
                if(addModel === refModel)
                    if(addIndex <= refIndex)
                        refIndex += addCount;
                refIndex -= relative ? baseIndex : 0;
                return refIndex
            }
            return index
        },
        _adjustCellExpressionOnAddRow: function(baseModel, refModel, baseRow, baseColumn, refExpr, addModel, addRow, addRowCount)
        {
            if(refModel)
            {
                var refRowRelative = refExpr.rowRelative;
                if(baseRow >= 0 || !refRowRelative)
                {
                    var refRow = this._adjustIndexOnAdd(baseModel,refModel,baseRow,refExpr.row,refExpr.rowRelative,addModel,addRow,addRowCount);
                    if(refRow !== refExpr.row)
                        if(baseModel === refModel)
                            return new CellExpression(refRow,refExpr.column,refRowRelative,refExpr.columnRelative);
                        else
                            return new ExternalCellExpression(refModel,refRow,refExpr.column,refRowRelative,refExpr.columnRelative)
                }
            }
            return refExpr
        },
        _adjustRangeExpressionOnAddRow: function(baseModel, refModel, baseRow, baseColumn, refExpr, addModel, addRow, addRowCount)
        {
            if(refModel)
            {
                var rangeType = refExpr._getRangeType();
                if(rangeType === RangeType.sheet || rangeType === RangeType.column)
                    return refExpr;
                var refStartRowRelative = refExpr.startRowRelative;
                var refEndRowRelative = refExpr.endRowRelative;
                if(baseRow >= 0 || !refStartRowRelative || !refEndRowRelative)
                {
                    var refStartRow = this._adjustIndexOnAdd(baseModel,refModel,baseRow,refExpr.startRow,refStartRowRelative,addModel,addRow,addRowCount);
                    var refEndRow = this._adjustIndexOnAdd(baseModel,refModel,baseRow,refExpr.endRow,refEndRowRelative,addModel,addRow,addRowCount);
                    if(rangeType === RangeType.row)
                    {
                        var newExpr;
                        if(baseModel === refModel)
                        {
                            newExpr = new RangeExpression;
                            newExpr.initBand(refStartRow,refEndRow,refStartRowRelative,refEndRowRelative,true)
                        }
                        else
                        {
                            newExpr = new ExternalRangeExpression;
                            newExpr.initBand(refModel,refStartRow,refEndRow,refStartRowRelative,refEndRowRelative,true)
                        }
                        return newExpr
                    }
                    if(baseModel === refModel)
                        return new RangeExpression(refStartRow,refExpr.startColumn,refEndRow,refExpr.endColumn,refStartRowRelative,refExpr.startColumnRelative,refEndRowRelative,refExpr.endColumnRelative);
                    else
                        return new ExternalRangeExpression(refModel,refStartRow,refExpr.startColumn,refEndRow,refExpr.endColumn,refStartRowRelative,refExpr.startColumnRelative,refEndRowRelative,refExpr.endColumnRelative)
                }
            }
            return refExpr
        },
        _adjustCellExpressionOnAddColumn: function(baseModel, refModel, baseRow, baseColumn, refExpr, addModel, addColumn, addColumnCount)
        {
            if(refModel)
            {
                var refColumnRelative = refExpr.columnRelative;
                if(baseColumn >= 0 || !refColumnRelative)
                {
                    var refColumn = this._adjustIndexOnAdd(baseModel,refModel,baseColumn,refExpr.column,refExpr.columnRelative,addModel,addColumn,addColumnCount);
                    if(refColumn !== refExpr.column)
                        if(baseModel === refModel)
                            return new CellExpression(refExpr.row,refColumn,refExpr.rowRelative,refColumnRelative);
                        else
                            return new ExternalCellExpression(refModel,refExpr.row,refColumn,refExpr.rowRelative,refColumnRelative)
                }
            }
            return refExpr
        },
        _adjustRangeExpressionOnAddColumn: function(baseModel, refModel, baseRow, baseColumn, refExpr, addModel, addColumn, addColumnCount)
        {
            if(refModel)
            {
                var rangeType = refExpr._getRangeType();
                if(rangeType === RangeType.sheet || rangeType === RangeType.row)
                    return refExpr;
                var refStartColumnRelative = refExpr.startColumnRelative;
                var refEndColumnRelative = refExpr.endColumnRelative;
                if(baseColumn >= 0 || !refStartColumnRelative || !refEndColumnRelative)
                {
                    var refStartColumn = this._adjustIndexOnAdd(baseModel,refModel,baseColumn,refExpr.startColumn,refStartColumnRelative,addModel,addColumn,addColumnCount);
                    var refEndColumn = this._adjustIndexOnAdd(baseModel,refModel,baseColumn,refExpr.endColumn,refEndColumnRelative,addModel,addColumn,addColumnCount);
                    if(rangeType === RangeType.column)
                    {
                        var newExpr;
                        if(baseModel === refModel)
                        {
                            newExpr = new RangeExpression;
                            newExpr.initBand(refStartColumn,refEndColumn,refStartColumnRelative,refEndColumnRelative,false)
                        }
                        else
                        {
                            newExpr = new ExternalRangeExpression;
                            newExpr.initBand(refModel,refStartColumn,refEndColumn,refStartColumnRelative,refEndColumnRelative,false)
                        }
                        return newExpr
                    }
                    if(baseModel === refModel)
                        return new RangeExpression(refExpr.startRow,refStartColumn,refExpr.endRow,refEndColumn,refExpr.startRowRelative,refStartColumnRelative,refExpr.endRowRelative,refEndColumnRelative);
                    else
                        return new ExternalRangeExpression(refModel,refExpr.startRow,refStartColumn,refExpr.endRow,refEndColumn,refExpr.startRowRelative,refStartColumnRelative,refExpr.endRowRelative,refEndColumnRelative)
                }
            }
            return refExpr
        },
        adjustFormulaOnAddRows: function(baseModel, baseRow, baseColumn, oldExpr, addModel, addRow, addRowCount)
        {
            var newExpr = oldExpr,
                arg;
            if(oldExpr instanceof CellExpression)
                newExpr = this._adjustCellExpressionOnAddRow(baseModel,baseModel,baseRow,baseColumn,oldExpr,addModel,addRow,addRowCount);
            else if(oldExpr instanceof RangeExpression)
                newExpr = this._adjustRangeExpressionOnAddRow(baseModel,baseModel,baseRow,baseColumn,oldExpr,addModel,addRow,addRowCount);
            else if(oldExpr instanceof ExternalCellExpression)
                newExpr = this._adjustCellExpressionOnAddRow(baseModel,oldExpr.source,baseRow,baseColumn,oldExpr,addModel,addRow,addRowCount);
            else if(oldExpr instanceof ExternalRangeExpression)
                newExpr = this._adjustRangeExpressionOnAddRow(baseModel,oldExpr.source,baseRow,baseColumn,oldExpr,addModel,addRow,addRowCount);
            else if(oldExpr instanceof ParenthesesExpression)
            {
                arg = this.adjustFormulaOnAddRows(baseModel,baseRow,baseColumn,oldExpr.argument,addModel,addRow,addRowCount);
                if(arg !== oldExpr.argument)
                    newExpr = new ParenthesesExpression(arg)
            }
            else if(oldExpr instanceof UnaryOperatorExpression)
            {
                arg = this.adjustFormulaOnAddRows(baseModel,baseRow,baseColumn,oldExpr.operand,addModel,addRow,addRowCount);
                if(arg !== oldExpr.operand)
                    newExpr = new UnaryOperatorExpression(oldExpr.operator,arg)
            }
            else if(oldExpr instanceof BinaryOperatorExpression)
            {
                var arg0 = this.adjustFormulaOnAddRows(baseModel,baseRow,baseColumn,oldExpr.left,addModel,addRow,addRowCount);
                var arg1 = this.adjustFormulaOnAddRows(baseModel,baseRow,baseColumn,oldExpr.right,addModel,addRow,addRowCount);
                if(arg0 !== oldExpr.left || arg1 !== oldExpr.right)
                    newExpr = new BinaryOperatorExpression(oldExpr.operator,arg0,arg1)
            }
            else if(oldExpr instanceof FunctionExpression)
            {
                var args = null,
                    fnArg,
                    i;
                var argCount = oldExpr.argCount();
                for(i = 0; i < argCount; i++)
                {
                    fnArg = oldExpr.getArg(i);
                    arg = this.adjustFormulaOnAddRows(baseModel,baseRow,baseColumn,fnArg,addModel,addRow,addRowCount);
                    if(arg !== fnArg)
                    {
                        args = [];
                        args[i] = arg;
                        break
                    }
                }
                if(args)
                {
                    for(i = 0; i < argCount; i++)
                    {
                        fnArg = oldExpr.getArg(i);
                        if(args[i] === undefined || args[i] === null)
                            args[i] = this.adjustFormulaOnAddRows(baseModel,baseRow,baseColumn,fnArg,addModel,addRow,addRowCount)
                    }
                    newExpr = new FunctionExpression(oldExpr.fn,args)
                }
            }
            return newExpr
        },
        adjustFormulasOnAddRows: function(addModel, addRow, addRowCount)
        {
            for(var i = 0; i < this._customNames.length; i++)
            {
                var name = this._customNames[i].value;
                name.expression = this.adjustFormulaOnAddRows(null,-1,-1,name.expression,addModel,addRow,addRowCount)
            }
            var sheetSource,
                row,
                column,
                expr;
            while(this._headAdjustCells)
            {
                sheetSource = this._headAdjustCells.sheetSource;
                row = this._headAdjustCells.row;
                column = this._headAdjustCells.column;
                expr = sheetSource.getExpression(row,column);
                if(expr)
                    sheetSource.setExpression(row,column,this.adjustFormulaOnAddRows(sheetSource,row,column,expr,addModel,addRow,addRowCount));
                else if(sheetSource.getRowExpression && sheetSource.getRowExpression(row) || sheetSource.getColumnExpression && sheetSource.getColumnExpression(column))
                    sheetSource._addCellsToDirty(row,column,1,1);
                this.removeAdjustCell(this._headAdjustCells)
            }
            while(this._headAdjustRows)
            {
                sheetSource = this._headAdjustRows.sheetSource;
                row = this._headAdjustRows.row;
                if(sheetSource.getExpression)
                {
                    expr = sheetSource.getRowExpression(row);
                    if(expr && sheetSource.setRowExpression)
                        sheetSource.setRowExpression(row,this.adjustFormulaOnAddRows(sheetSource,row,-1,expr,addModel,addRow,addRowCount))
                }
                this.removeAdjustRow(this._headAdjustRows)
            }
            while(this._headAdjustColumns)
            {
                sheetSource = this._headAdjustColumns.sheetSource;
                column = this._headAdjustColumns.column;
                if(sheetSource.getColumnExpression)
                {
                    expr = sheetSource.getColumnExpression(column);
                    if(expr && sheetSource._setColumnExpression)
                        sheetSource._setColumnExpression(column,this.adjustFormulaOnAddRows(sheetSource,-1,column,expr,addModel,addRow,addRowCount))
                }
                this.removeAdjustRow(this._headAdjustColumns)
            }
        },
        adjustFormulaOnAddColumns: function(baseModel, baseRow, baseColumn, oldExpr, addModel, addColumn, addColumnCount)
        {
            var newExpr = oldExpr,
                arg;
            if(oldExpr instanceof CellExpression)
                newExpr = this._adjustCellExpressionOnAddColumn(baseModel,baseModel,baseRow,baseColumn,oldExpr,addModel,addColumn,addColumnCount);
            else if(oldExpr instanceof RangeExpression)
                newExpr = this._adjustRangeExpressionOnAddColumn(baseModel,baseModel,baseRow,baseColumn,oldExpr,addModel,addColumn,addColumnCount);
            else if(oldExpr instanceof ExternalCellExpression)
                newExpr = this._adjustCellExpressionOnAddColumn(baseModel,oldExpr.source,baseRow,baseColumn,oldExpr,addModel,addColumn,addColumnCount);
            else if(oldExpr instanceof ExternalRangeExpression)
                newExpr = this._adjustRangeExpressionOnAddColumn(baseModel,oldExpr.source,baseRow,baseColumn,oldExpr,addModel,addColumn,addColumnCount);
            else if(oldExpr instanceof ParenthesesExpression)
            {
                arg = this.adjustFormulaOnAddColumns(baseModel,baseRow,baseColumn,oldExpr.argument,addModel,addColumn,addColumnCount);
                if(arg !== oldExpr.argument)
                    newExpr = new ParenthesesExpression(arg)
            }
            else if(oldExpr instanceof UnaryOperatorExpression)
            {
                arg = this.adjustFormulaOnAddColumns(baseModel,baseRow,baseColumn,oldExpr.operand,addModel,addColumn,addColumnCount);
                if(arg !== oldExpr.operand)
                    newExpr = new UnaryOperatorExpression(oldExpr.operator,arg)
            }
            else if(oldExpr instanceof BinaryOperatorExpression)
            {
                var arg0 = this.adjustFormulaOnAddColumns(baseModel,baseRow,baseColumn,oldExpr.left,addModel,addColumn,addColumnCount);
                var arg1 = this.adjustFormulaOnAddColumns(baseModel,baseRow,baseColumn,oldExpr.right,addModel,addColumn,addColumnCount);
                if(arg0 !== oldExpr.left || arg1 !== oldExpr.right)
                    newExpr = new BinaryOperatorExpression(oldExpr.operator,arg0,arg1)
            }
            else if(oldExpr instanceof FunctionExpression)
            {
                var args = null,
                    fnArg,
                    i;
                var argCount = oldExpr.argCount();
                for(i = 0; i < argCount; i++)
                {
                    fnArg = oldExpr.getArg(i);
                    arg = this.adjustFormulaOnAddColumns(baseModel,baseRow,baseColumn,fnArg,addModel,addColumn,addColumnCount);
                    if(arg !== fnArg)
                    {
                        args = [];
                        args[i] = arg;
                        break
                    }
                }
                if(args)
                {
                    for(i = 0; i < argCount; i++)
                    {
                        fnArg = oldExpr.getArg(i);
                        if(args[i] === undefined || args[i] === null)
                            args[i] = this.adjustFormulaOnAddColumns(baseModel,baseRow,baseColumn,fnArg,addModel,addColumn,addColumnCount)
                    }
                    newExpr = new FunctionExpression(oldExpr.fn,args)
                }
            }
            return newExpr
        },
        adjustFormulasOnAddColumns: function(addModel, addColumn, addColumnCount)
        {
            for(var i = 0; i < this._customNames.length; i++)
            {
                var name = this._customNames[i].value;
                name.expression = this.adjustFormulaOnAddColumns(null,-1,-1,name.expression,addModel,addColumn,addColumnCount)
            }
            var sheetSource,
                row,
                column,
                expr;
            while(this._headAdjustCells)
            {
                sheetSource = this._headAdjustCells.sheetSource;
                row = this._headAdjustCells.row;
                column = this._headAdjustCells.column;
                expr = sheetSource.getExpression(row,column);
                if(expr)
                    sheetSource.setExpression(row,column,this.adjustFormulaOnAddColumns(sheetSource,row,column,expr,addModel,addColumn,addColumnCount));
                else if(sheetSource.getRowExpression && sheetSource.getRowExpression(row) || sheetSource.getColumnExpression && sheetSource.getColumnExpression(column))
                    sheetSource._addCellsToDirty(row,column,1,1);
                this.removeAdjustCell(this._headAdjustCells)
            }
            while(this._headAdjustRows)
            {
                sheetSource = this._headAdjustRows.sheetSource;
                row = this._headAdjustRows.row;
                if(sheetSource.getRowExpression)
                {
                    expr = sheetSource.getRowExpression(row);
                    if(expr && sheetSource._setRowExpression)
                        sheetSource._setRowExpression(row,this.adjustFormulaOnAddColumns(sheetSource,row,-1,expr,addModel,addColumn,addColumnCount))
                }
                this.removeAdjustRow(this._headAdjustRows)
            }
            while(this._headAdjustColumns)
            {
                sheetSource = this._headAdjustColumns.sheetSource;
                column = this._headAdjustColumns.column;
                if(sheetSource.getColumnExpression)
                {
                    expr = sheetSource.getColumnExpression(column);
                    if(expr && sheetSource._setColumnExpression)
                        sheetSource._setColumnExpression(column,this.adjustFormulaOnAddColumns(sheetSource,-1,column,expr,addModel,addColumn,addColumnCount))
                }
                this.removeAdjustColumn(this._headAdjustColumns)
            }
        },
        _adjustIndexOnRemove: function(baseModel, refModel, baseIndex, index, relative, removeModel, removeIndex, removeCount)
        {
            if(baseIndex >= 0 || !relative)
            {
                var refIndex = index + (relative ? baseIndex : 0);
                if(removeModel === baseModel)
                    if(removeIndex <= baseIndex)
                        refIndex += relative ? removeCount : 0;
                if(removeModel === refModel)
                    if(removeIndex + removeCount <= refIndex)
                        refIndex -= removeCount;
                    else if(removeIndex <= refIndex)
                        return CalcErrorsReference;
                refIndex -= relative ? baseIndex : 0;
                return refIndex
            }
            return index
        },
        _adjustCellExpressionOnRemoveRow: function(baseModel, refModel, baseRow, baseColumn, refExpr, removeModel, removeRow, removeRowCount)
        {
            if(refModel)
            {
                var refRowRelative = refExpr.rowRelative;
                if(baseRow >= 0 || !refRowRelative)
                {
                    var refRow = this._adjustIndexOnRemove(baseModel,refModel,baseRow,refExpr.row,refExpr.rowRelative,removeModel,removeRow,removeRowCount);
                    if(refRow instanceof CalcError)
                        return new ErrorExpression(refRow);
                    if(refRow !== refExpr.row)
                        if(baseModel === refModel)
                            return new CellExpression(refRow,refExpr.column,refRowRelative,refExpr.columnRelative);
                        else
                            return new ExternalCellExpression(refModel,refRow,refExpr.column,refRowRelative,refExpr.columnRelative)
                }
            }
            return refExpr
        },
        _adjustRangeExpressionOnRemoveRow: function(baseModel, refModel, baseRow, baseColumn, refExpr, removeModel, removeRow, removeRowCount)
        {
            if(refModel)
            {
                var rangeType = refExpr._getRangeType();
                if(rangeType === RangeType.sheet || rangeType === RangeType.column)
                    return refExpr;
                var refStartRowRelative = refExpr.startRowRelative;
                var refEndRowRelative = refExpr.endRowRelative;
                if(baseRow >= 0 || !refStartRowRelative || !refEndRowRelative)
                {
                    var refStartRow = this._adjustIndexOnRemove(baseModel,refModel,baseRow,refExpr.startRow,refStartRowRelative,removeModel,removeRow,removeRowCount);
                    if(refStartRow instanceof CalcError)
                        return new ErrorExpression(refStartRow);
                    var refEndRow = this._adjustIndexOnAdd(baseModel,refModel,baseRow,refExpr.endRow,refEndRowRelative,removeModel,removeRow,removeRowCount);
                    if(refEndRow instanceof CalcError)
                        return new ErrorExpression(refEndRow);
                    if(rangeType === RangeType.column)
                    {
                        var newExpr;
                        if(baseModel === refModel)
                        {
                            newExpr = new RangeExpression;
                            newExpr.initBand(refStartRow,refEndRow,refStartRowRelative,refEndRowRelative,false)
                        }
                        else
                        {
                            newExpr = new ExternalRangeExpression;
                            newExpr.initBand(refModel,refStartRow,refEndRow,refStartRowRelative,refEndRowRelative,false)
                        }
                        return newExpr
                    }
                    if(baseModel === refModel)
                        return new RangeExpression(refStartRow,refExpr.startColumn,refEndRow,refExpr.endColumn,refStartRowRelative,refExpr.startColumnRelative,refEndRowRelative,refExpr.endColumnRelative);
                    else
                        return new ExternalRangeExpression(refModel,refStartRow,refExpr.startColumn,refEndRow,refExpr.endColumn,refStartRowRelative,refExpr.startColumnRelative,refEndRowRelative,refExpr.endColumnRelative)
                }
            }
            return refExpr
        },
        _adjustCellExpressionOnRemoveColumn: function(baseModel, refModel, baseRow, baseColumn, refExpr, removeModel, removeColumn, removeColumnCount)
        {
            if(refModel)
            {
                var refColumnRelative = refExpr.columnRelative;
                if(baseColumn >= 0 || !refColumnRelative)
                {
                    var refColumn = this._adjustIndexOnRemove(baseModel,refModel,baseColumn,refExpr.column,refExpr.columnRelative,removeModel,removeColumn,removeColumnCount);
                    if(refColumn instanceof CalcError)
                        return new ErrorExpression(refColumn);
                    if(refColumn !== refExpr.column)
                        if(baseModel === refModel)
                            return new CellExpression(refExpr.row,refColumn,refExpr.rowRelative,refColumnRelative);
                        else
                            return new ExternalCellExpression(refModel,refExpr.row,refColumn,refExpr.rowRelative,refColumnRelative)
                }
            }
            return refExpr
        },
        _adjustRangeExpressionOnRemoveColumn: function(baseModel, refModel, baseRow, baseColumn, refExpr, removeModel, removeColumn, removeColumnCount)
        {
            if(refModel)
            {
                var rangeType = refExpr._getRangeType();
                if(rangeType === RangeType.sheet || rangeType === RangeType.row)
                    return refExpr;
                var refStartColumnRelative = refExpr.startColumnRelative;
                var refEndColumnRelative = refExpr.endColumnRelative;
                if(baseColumn >= 0 || !refStartColumnRelative || !refEndColumnRelative)
                {
                    var refStartColumn = this._adjustIndexOnRemove(baseModel,refModel,baseColumn,refExpr.startColumn,refStartColumnRelative,removeModel,removeColumn,removeColumnCount);
                    if(refStartColumn instanceof CalcError)
                        return new ErrorExpression(refStartColumn);
                    var refEndColumn = this._adjustIndexOnRemove(baseModel,refModel,baseColumn,refExpr.endColumn,refEndColumnRelative,removeModel,removeColumn,removeColumnCount);
                    if(refEndColumn instanceof CalcError)
                        return new ErrorExpression(refEndColumn);
                    if(rangeType === RangeType.column)
                    {
                        var newExpr;
                        if(baseModel === refModel)
                        {
                            newExpr = new RangeExpression;
                            newExpr.initBand(refStartColumn,refEndColumn,refStartColumnRelative,refEndColumnRelative,false)
                        }
                        else
                        {
                            newExpr = new ExternalRangeExpression;
                            newExpr.initBand(refModel,refStartColumn,refEndColumn,refStartColumnRelative,refEndColumnRelative,false)
                        }
                        return newExpr
                    }
                    if(baseModel === refModel)
                        return new RangeExpression(refExpr.startRow,refStartColumn,refExpr.endRow,refEndColumn,refExpr.startRowRelative,refStartColumnRelative,refExpr.endRowRelative,refEndColumnRelative);
                    else
                        return new ExternalRangeExpression(refModel,refExpr.startRow,refStartColumn,refExpr.endRow,refEndColumn,refExpr.startRowRelative,refStartColumnRelative,refExpr.endRowRelative,refEndColumnRelative)
                }
            }
            return refExpr
        },
        adjustFormulaOnRemoveRows: function(baseModel, baseRow, baseColumn, oldExpr, removeModel, removeRow, removeRowCount)
        {
            var newExpr = oldExpr,
                arg,
                fnArg,
                i;
            if(oldExpr instanceof CellExpression)
                newExpr = this._adjustCellExpressionOnRemoveRow(baseModel,baseModel,baseRow,baseColumn,oldExpr,removeModel,removeRow,removeRowCount);
            else if(oldExpr instanceof RangeExpression)
                newExpr = this._adjustRangeExpressionOnRemoveRow(baseModel,baseModel,baseRow,baseColumn,oldExpr,removeModel,removeRow,removeRowCount);
            else if(oldExpr instanceof ExternalCellExpression)
                newExpr = this._adjustCellExpressionOnRemoveRow(baseModel,oldExpr.source,baseRow,baseColumn,oldExpr,removeModel,removeRow,removeRowCount);
            else if(oldExpr instanceof ExternalRangeExpression)
                newExpr = this._adjustRangeExpressionOnRemoveRow(baseModel,oldExpr.source,baseRow,baseColumn,oldExpr,removeModel,removeRow,removeRowCount);
            else if(oldExpr instanceof ParenthesesExpression)
            {
                arg = this.adjustFormulaOnRemoveRows(baseModel,baseRow,baseColumn,oldExpr.argument,removeModel,removeRow,removeRowCount);
                if(arg !== oldExpr.argument)
                    newExpr = new ParenthesesExpression(arg)
            }
            else if(oldExpr instanceof UnaryOperatorExpression)
            {
                arg = this.adjustFormulaOnRemoveRows(baseModel,baseRow,baseColumn,oldExpr.operand,removeModel,removeRow,removeRowCount);
                if(arg !== oldExpr.operand)
                    newExpr = new UnaryOperatorExpression(oldExpr.operator,arg)
            }
            else if(oldExpr instanceof BinaryOperatorExpression)
            {
                var arg0 = this.adjustFormulaOnRemoveRows(baseModel,baseRow,baseColumn,oldExpr.left,removeModel,removeRow,removeRowCount);
                var arg1 = this.adjustFormulaOnRemoveRows(baseModel,baseRow,baseColumn,oldExpr.right,removeModel,removeRow,removeRowCount);
                if(arg0 !== oldExpr.left || arg1 !== oldExpr.right)
                    newExpr = new BinaryOperatorExpression(oldExpr.operator,arg0,arg1)
            }
            else if(oldExpr instanceof FunctionExpression)
            {
                var args = null;
                var argCount = oldExpr.argCount();
                for(i = 0; i < argCount; i++)
                {
                    fnArg = oldExpr.getArg(i);
                    arg = this.adjustFormulaOnRemoveRows(baseModel,baseRow,baseColumn,fnArg,removeModel,removeRow,removeRowCount);
                    if(arg !== fnArg)
                    {
                        args = [];
                        args[i] = arg;
                        break
                    }
                }
                if(args)
                {
                    for(i = 0; i < argCount; i++)
                    {
                        fnArg = oldExpr.getArg(i);
                        if(args[i] === undefined || args[i] === null)
                            args[i] = this.adjustFormulaOnRemoveRows(baseModel,baseRow,baseColumn,fnArg,removeModel,removeRow,removeRowCount)
                    }
                    newExpr = new FunctionExpression(oldExpr.fn,args)
                }
            }
            return newExpr
        },
        adjustFormulasOnRemoveRows: function(removeModel, removeRow, removeRowCount)
        {
            for(var i = 0; i < this._customNames.length; i++)
            {
                var name = this._customNames[i].value;
                name.expression = this.adjustFormulaOnRemoveRows(null,-1,-1,name.expression,removeModel,removeRow,removeRowCount)
            }
            var sheetSource,
                row,
                column,
                expr;
            while(this._headAdjustCells)
            {
                sheetSource = this._headAdjustCells.sheetSource;
                row = this._headAdjustCells.row;
                column = this._headAdjustCells.column;
                expr = sheetSource.getExpression(row,column);
                if(expr)
                    sheetSource.setExpression(row,column,this.adjustFormulaOnRemoveRows(sheetSource,row,column,expr,removeModel,removeRow,removeRowCount));
                else if(sheetSource.getRowExpression && sheetSource.getRowExpression(row) || sheetSource.getColumnExpression && sheetSource.getColumnExpression(column))
                    sheetSource._addCellsToDirty(row,column,1,1);
                this.removeAdjustCell(this._headAdjustCells)
            }
            while(this._headAdjustRows)
            {
                sheetSource = this._headAdjustRows.sheetSource;
                row = this._headAdjustRows.row;
                if(sheetSource.getRowExpression)
                {
                    expr = sheetSource.getRowExpression(row);
                    if(expr && sheetSource.setRowExpression)
                        sheetSource.setRowExpression(row,this.adjustFormulaOnRemoveRows(sheetSource,row,-1,expr,removeModel,removeRow,removeRowCount))
                }
                this.removeAdjustRow(this._headAdjustRows)
            }
            while(this._headAdjustColumns)
            {
                sheetSource = this._headAdjustColumns.sheetSource;
                column = this._headAdjustColumns.column;
                if(sheetSource.getColumnExpression)
                {
                    expr = sheetSource.getColumnExpression(column);
                    if(expr && sheetSource.setColumnExpression)
                        sheetSource.setColumnExpression(column,this.adjustFormulaOnRemoveRows(sheetSource,-1,column,expr,removeModel,removeRow,removeRowCount))
                }
                this.removeAdjustRow(this._headAdjustColumns)
            }
        },
        adjustFormulaOnRemoveColumns: function(baseModel, baseRow, baseColumn, oldExpr, removeModel, removeColumn, removeColumnCount)
        {
            var newExpr = oldExpr,
                arg,
                fnArg,
                i;
            if(oldExpr instanceof CellExpression)
                newExpr = this._adjustCellExpressionOnRemoveColumn(baseModel,baseModel,baseRow,baseColumn,oldExpr,removeModel,removeColumn,removeColumnCount);
            else if(oldExpr instanceof RangeExpression)
                newExpr = this._adjustRangeExpressionOnRemoveColumn(baseModel,baseModel,baseRow,baseColumn,oldExpr,removeModel,removeColumn,removeColumnCount);
            else if(oldExpr instanceof ExternalCellExpression)
                newExpr = this._adjustCellExpressionOnRemoveColumn(baseModel,oldExpr.source,baseRow,baseColumn,oldExpr,removeModel,removeColumn,removeColumnCount);
            else if(oldExpr instanceof ExternalRangeExpression)
                newExpr = this._adjustRangeExpressionOnRemoveColumn(baseModel,oldExpr.source,baseRow,baseColumn,oldExpr,removeModel,removeColumn,removeColumnCount);
            else if(oldExpr instanceof ParenthesesExpression)
            {
                arg = this.adjustFormulaOnRemoveColumns(baseModel,baseRow,baseColumn,oldExpr.argument,removeModel,removeColumn,removeColumnCount);
                if(arg !== oldExpr.argument)
                    newExpr = new ParenthesesExpression(arg)
            }
            else if(oldExpr instanceof UnaryOperatorExpression)
            {
                arg = this.adjustFormulaOnRemoveColumns(baseModel,baseRow,baseColumn,oldExpr.operand,removeModel,removeColumn,removeColumnCount);
                if(arg !== oldExpr.operand)
                    newExpr = new UnaryOperatorExpression(oldExpr.operator,arg)
            }
            else if(oldExpr instanceof BinaryOperatorExpression)
            {
                var arg0 = this.adjustFormulaOnRemoveColumns(baseModel,baseRow,baseColumn,oldExpr.left,removeModel,removeColumn,removeColumnCount);
                var arg1 = this.adjustFormulaOnRemoveColumns(baseModel,baseRow,baseColumn,oldExpr.right,removeModel,removeColumn,removeColumnCount);
                if(arg0 !== oldExpr.left || arg1 !== oldExpr.right)
                    newExpr = new BinaryOperatorExpression(oldExpr.operator,arg0,arg1)
            }
            else if(oldExpr instanceof FunctionExpression)
            {
                var args = null;
                var argCount = oldExpr.argCount();
                for(i = 0; i < argCount; i++)
                {
                    fnArg = oldExpr.getArg(i);
                    arg = this.adjustFormulaOnRemoveColumns(baseModel,baseRow,baseColumn,fnArg,removeModel,removeColumn,removeColumnCount);
                    if(arg !== fnArg)
                    {
                        args = [];
                        args[i] = arg;
                        break
                    }
                }
                if(args)
                {
                    for(i = 0; i < argCount; i++)
                    {
                        fnArg = oldExpr.getArg(i);
                        if(args[i] === undefined || args[i] === null)
                            args[i] = this.adjustFormulaOnRemoveColumns(baseModel,baseRow,baseColumn,fnArg,removeModel,removeColumn,removeColumnCount)
                    }
                    newExpr = new FunctionExpression(oldExpr.fn,args)
                }
            }
            return newExpr
        },
        adjustFormulasOnRemoveColumns: function(removeModel, removeColumn, removeColumnCount)
        {
            var sheetSource,
                row,
                column,
                expr;
            for(var i = 0; i < this._customNames.length; i++)
            {
                var name = this._customNames[i].value;
                name.expression = this.adjustFormulaOnRemoveColumns(null,-1,-1,name.expression,removeModel,removeColumn,removeColumnCount)
            }
            while(this._headAdjustCells)
            {
                sheetSource = this._headAdjustCells.sheetSource;
                row = this._headAdjustCells.row;
                column = this._headAdjustCells.column;
                expr = sheetSource.getExpression(row,column);
                if(expr)
                    sheetSource.setExpression(row,column,this.adjustFormulaOnRemoveColumns(sheetSource,row,column,expr,removeModel,removeColumn,removeColumnCount));
                else if(sheetSource.getRowExpression && sheetSource.getRowExpression(row) || sheetSource.getColumnExpression && sheetSource.getColumnExpression(column))
                    sheetSource._addCellsToDirty(row,column,1,1);
                this.removeAdjustCell(this._headAdjustCells)
            }
            while(this._headAdjustRows)
            {
                sheetSource = this._headAdjustRows.sheetSource;
                row = this._headAdjustRows.row;
                if(sheetSource.getRowExpression)
                {
                    expr = sheetSource.getRowExpression(row);
                    if(expr && sheetSource._setRowExpression)
                        sheetSource._setRowExpression(row,this.adjustFormulaOnRemoveColumns(sheetSource,row,-1,expr,removeModel,removeColumn,removeColumnCount))
                }
                this.removeAdjustRow(this._headAdjustRows)
            }
            while(this._headAdjustColumns)
            {
                sheetSource = this._headAdjustColumns.sheetSource;
                column = this._headAdjustColumns.column;
                if(sheetSource.getColumnExpression)
                {
                    expr = sheetSource.getColumnExpression(column);
                    if(expr && sheetSource._setColumnExpression)
                        sheetSource._setColumnExpression(column,this.adjustFormulaOnRemoveColumns(sheetSource,-1,column,expr,removeModel,removeColumn,removeColumnCount))
                }
                this.removeAdjustColumn(this._headAdjustColumns)
            }
        },
        adjustFormulaOnRemoveSheet: function(baseModel, oldExpr, removeModel)
        {
            var newExpr = oldExpr,
                arg,
                fnArg,
                i;
            if(oldExpr instanceof ExternalReferenceExpression)
            {
                var refModel = oldExpr.source;
                if(baseModel !== removeModel && refModel === removeModel || baseModel === removeModel && refModel !== removeModel)
                    newExpr = new ErrorExpression(CalcErrorsReference)
            }
            else if(oldExpr instanceof ParenthesesExpression)
            {
                arg = this.adjustFormulaOnRemoveSheet(baseModel,oldExpr.argument,removeModel);
                if(arg !== oldExpr.argument)
                    newExpr = new ParenthesesExpression(arg)
            }
            else if(oldExpr instanceof UnaryOperatorExpression)
            {
                arg = this.adjustFormulaOnRemoveSheet(baseModel,oldExpr.operand,removeModel);
                if(arg !== oldExpr.operand)
                    newExpr = new UnaryOperatorExpression(oldExpr.operator,arg)
            }
            else if(oldExpr instanceof BinaryOperatorExpression)
            {
                var arg0 = this.adjustFormulaOnRemoveSheet(baseModel,oldExpr.left,removeModel);
                var arg1 = this.adjustFormulaOnRemoveSheet(baseModel,oldExpr.right,removeModel);
                if(arg0 !== oldExpr.left || arg1 !== oldExpr.right)
                    newExpr = new BinaryOperatorExpression(oldExpr.operator,arg0,arg1)
            }
            else if(oldExpr instanceof FunctionExpression)
            {
                var args = null;
                var argCount = oldExpr.argCount();
                for(i = 0; i < argCount; i++)
                {
                    fnArg = oldExpr.getArg(i);
                    arg = this.adjustFormulaOnRemoveSheet(baseModel,fnArg,removeModel);
                    if(arg !== fnArg)
                    {
                        args = [];
                        args[i] = arg;
                        break
                    }
                }
                if(args)
                {
                    for(i = 0; i < argCount; i++)
                    {
                        fnArg = oldExpr.getArg(i);
                        if(args[i] === undefined || args[i] === null)
                            args[i] = this.adjustFormulaOnRemoveSheet(baseModel,fnArg,removeModel)
                    }
                    newExpr = new FunctionExpression(oldExpr.fn,args)
                }
            }
            return newExpr
        },
        adjustFormulasOnRemoveSheet: function(removeModel)
        {
            for(var i = 0; i < this._customNames.length; i++)
            {
                var name = this._customNames[i].value;
                name.expression = this.adjustFormulaOnRemoveSheet(null,name.expression,removeModel)
            }
            var sheetSource,
                row,
                column,
                expr;
            while(this._headAdjustCells)
            {
                sheetSource = this._headAdjustCells.sheetSource;
                row = this._headAdjustCells.row;
                column = this._headAdjustCells.column;
                expr = sheetSource.getExpression(row,column);
                if(expr)
                    sheetSource.setExpression(row,column,this.adjustFormulaOnRemoveSheet(sheetSource,expr,removeModel));
                this.removeAdjustCell(this._headAdjustCells)
            }
            while(this._headAdjustRows)
            {
                sheetSource = this._headAdjustRows.sheetSource;
                row = this._headAdjustRows.row;
                if(sheetSource.getRowExpression)
                {
                    expr = sheetSource.getRowExpression(row);
                    if(expr && sheetSource.setRowExpression)
                        sheetSource.setRowExpression(row,this.adjustFormulaOnRemoveSheet(sheetSource,expr,removeModel))
                }
                this.removeAdjustRow(this._headAdjustRows)
            }
            while(this._headAdjustColumns)
            {
                sheetSource = this._headAdjustColumns.sheetSource;
                column = this._headAdjustColumns.column;
                if(sheetSource.getColumnExpression)
                {
                    expr = sheetSource.getColumnExpression(column);
                    if(expr && sheetSource.setColumnExpression)
                        sheetSource.setColumnExpression(column,this.adjustFormulaOnRemoveSheet(sheetSource,expr,removeModel))
                }
                this.removeAdjustColumn(this._headAdjustColumns)
            }
        },
        _adjustCellExpressionOnMove: function(baseModel, baseRow, baseColumn, refExpr, info)
        {
            var external = refExpr instanceof ExternalReferenceExpression;
            var refModel = external ? refExpr.source : baseModel;
            var refRowRelative = refExpr.rowRelative;
            var refColumnRelative = refExpr.columnRelative;
            var refRow,
                refColumn,
                removeExternal,
                addExternal;
            if((baseRow >= 0 || !refRowRelative) && (baseColumn >= 0 || !refColumnRelative))
            {
                refRow = refExpr.row + (refRowRelative ? baseRow : 0);
                refColumn = refExpr.column + (refColumnRelative ? baseColumn : 0);
                if(info.destinationContains(baseModel,baseRow,baseColumn,1,1))
                {
                    refRow -= refRowRelative ? info.getRowOffset() : 0;
                    refColumn -= refColumnRelative ? info.getColumnOffset() : 0
                }
                if(info.sourceContains(external ? refModel : info._fromModel,refRow,refColumn,1,1))
                {
                    refRow += info.getRowOffset();
                    refColumn += info.getColumnOffset();
                    var sourceChanged0 = info._toModel !== info._fromModel;
                    removeExternal = sourceChanged0 && info._toModel === baseModel;
                    addExternal = sourceChanged0 && info._fromModel === baseModel
                }
                else if(info.destinationContains(refModel,refRow,refColumn,1,1))
                    return new ErrorExpression(CalcErrorsReference);
                refRow -= refRowRelative ? baseRow : 0;
                refColumn -= refColumnRelative ? baseColumn : 0;
                if(refRow !== refExpr.row || refColumn !== refExpr.column || removeExternal || addExternal)
                    if(refExpr instanceof CellExpression && addExternal || refExpr instanceof ExternalCellExpression && !removeExternal)
                        return new ExternalCellExpression(addExternal ? info._toModel : refExpr.source,refRow,refColumn,refRowRelative,refColumnRelative);
                    else
                        return new CellExpression(refRow,refColumn,refRowRelative,refColumnRelative)
            }
            else if((baseRow >= 0 || !refRowRelative) && baseColumn === -1)
            {
                refRow = refExpr.row + (refRowRelative ? baseRow : 0);
                if(info.destinationContains(baseModel,baseRow,baseColumn,1,1))
                    refRow -= refRowRelative ? info.getRowOffset() : 0;
                if(info.sourceContains(refModel,refRow,-1,refRowRelative,-1))
                    refRow += info.getRowOffset();
                else if(info.destinationContains(refModel,refRow,-1,1,-1))
                    return new ErrorExpression(CalcErrorsReference);
                refRow -= refRowRelative ? baseRow : 0;
                if(refRow !== refExpr.row)
                    if(refExpr instanceof CellExpression)
                        return new CellExpression(refRow,refExpr.column,refRowRelative,refExpr.columnRelative);
                    else if(refExpr instanceof ExternalCellExpression)
                        return new ExternalCellExpression(refExpr.source,refRow,refExpr.column,refRowRelative,refExpr.columnRelative)
            }
            else if(baseRow === -1 && baseColumn >= 0 && !refColumnRelative)
            {
                refColumn = refExpr.column + (refColumnRelative ? baseColumn : 0);
                if(info.destinationContains(baseModel,baseRow,baseColumn,1,1))
                    refColumn -= refColumnRelative ? info.getColumnOffset() : 0;
                if(info.sourceContains(refModel,refRow,refColumn,1,1))
                    refColumn += info.getColumnOffset();
                else if(info.destinationContains(refModel,refRow,refColumn,1,1))
                    return new ErrorExpression(CalcErrorsReference);
                refColumn -= refColumnRelative ? baseColumn : 0;
                if(refRow !== refExpr.row || refColumn !== refExpr.column)
                    if(refExpr instanceof CellExpression)
                        return new CellExpression(refExpr.row,refColumn,refExpr.rowRelative,refColumnRelative);
                    else if(refExpr instanceof ExternalCellExpression)
                        return new ExternalCellExpression(refExpr.source,refExpr.row,refColumn,refExpr.rowRelative,refColumnRelative)
            }
            return refExpr
        },
        _adjustRangeExpressionOnMove: function(baseModel, baseRow, baseColumn, refExpr, info)
        {
            var refModel = refExpr instanceof ExternalReferenceExpression ? refExpr.source : baseModel;
            var rangeType = refExpr._getRangeType();
            var refStartRowRelative,
                refStartColumnRelative,
                refEndRowRelative,
                refEndColumnRelative;
            var refStartRow,
                refEndRow,
                refStartColumn,
                refEndColumn,
                refRowCount,
                refColumnCount;
            var ro,
                co,
                ro1,
                ro2,
                co1,
                co2,
                removeExternal,
                addExternal;
            if(rangeType === RangeType.cell)
            {
                refStartRowRelative = refExpr.startRowRelative;
                refStartColumnRelative = refExpr.startColumnRelative;
                refEndRowRelative = refExpr.endRowRelative;
                refEndColumnRelative = refExpr.endColumnRelative;
                if((baseRow >= 0 || !refStartRowRelative || !refEndRowRelative) && (baseColumn >= 0 || !refStartColumnRelative || !refEndColumnRelative))
                {
                    refStartRow = refExpr.startRow + (refStartRowRelative ? baseRow : 0);
                    refEndRow = refExpr.endRow + (refEndRowRelative ? baseRow : 0);
                    refStartColumn = refExpr.startColumn + (refStartColumnRelative ? baseColumn : 0);
                    refEndColumn = refExpr.endColumn + (refEndColumnRelative ? baseColumn : 0);
                    if(refStartRow > refEndRow || refStartColumn > refEndColumn)
                        return new ErrorExpression(CalcErrorsReference);
                    if(info.destinationContains(baseModel,baseRow,baseColumn,1,1))
                    {
                        ro1 = refStartRowRelative ? info.getRowOffset() : 0;
                        ro2 = refEndRowRelative ? info.getRowOffset() : 0;
                        co1 = refStartColumnRelative ? info.getColumnOffset() : 0;
                        co2 = refEndColumnRelative ? info.getColumnOffset() : 0;
                        refStartRow -= ro1;
                        refEndRow -= ro2;
                        refStartColumn -= co1;
                        refEndColumn -= co2
                    }
                    refRowCount = refEndRow - refStartRow + 1;
                    refColumnCount = refEndColumn - refStartColumn + 1;
                    var refRangeChanged = false;
                    if(info.sourceContains(refModel,refStartRow,refStartColumn,refRowCount,refColumnCount))
                    {
                        ro = info.getRowOffset();
                        co = info.getColumnOffset();
                        refStartRow += ro;
                        refEndRow += ro;
                        refStartColumn += co;
                        refEndColumn += co;
                        var sourceChanged1 = info._toModel !== info._fromModel;
                        removeExternal = sourceChanged1 && info._toModel === baseModel;
                        addExternal = sourceChanged1 && info._fromModel === baseModel
                    }
                    else if(info.destinationContains(refModel,refStartRow,refStartColumn,refRowCount,refColumnCount))
                    {
                        var intersectedRange = info.sourceIntersectWith(refModel,refStartRow,refStartColumn,refRowCount,refColumnCount);
                        if(intersectedRange && (intersectedRange.rowCount === refRowCount || intersectedRange.columnCount === refColumnCount))
                        {
                            refStartRow = intersectedRange.row + info.getRowOffset();
                            refStartColumn = intersectedRange.column + info.getColumnOffset();
                            refEndRow = refStartRow + intersectedRange.rowCount;
                            refEndColumn = refStartColumn + intersectedRange.columnCount;
                            refRangeChanged = true
                        }
                        else
                            return new ErrorExpression(CalcErrorsReference)
                    }
                    refStartRow -= refStartRowRelative ? baseRow : 0;
                    refEndRow -= refEndRowRelative ? baseRow : 0;
                    refStartColumn -= refStartColumnRelative ? baseColumn : 0;
                    refEndColumn -= refEndColumnRelative ? baseColumn : 0;
                    if(refStartRow !== refExpr.startRow || refStartColumn !== refExpr.startColumn || refEndRow !== refExpr.endRow || refEndColumn !== refExpr.endColumn || removeExternal || addExternal || refRangeChanged)
                        if(refExpr instanceof RangeExpression && addExternal || refExpr instanceof ExternalRangeExpression && !removeExternal)
                            return new ExternalRangeExpression(addExternal ? info._toModel : refExpr.source,refStartRow,refStartColumn,refEndRow,refEndColumn,refStartRowRelative,refStartColumnRelative,refEndRowRelative,refEndColumnRelative);
                        else
                            return new RangeExpression(refStartRow,refStartColumn,refEndRow,refEndColumn,refStartRowRelative,refStartColumnRelative,refEndRowRelative,refEndColumnRelative)
                }
                else if((baseRow >= 0 || !refStartRowRelative || !refEndRowRelative) && baseColumn === -1)
                {
                    refStartRow = refExpr.startRow + (refStartRowRelative ? baseRow : 0);
                    refEndRow = refExpr.endRow + (refEndRowRelative ? baseRow : 0);
                    if(refStartRow > refEndRow)
                        return new ErrorExpression(CalcErrorsReference);
                    if(info.destinationContains(baseModel,baseRow,baseColumn,1,1))
                    {
                        ro1 = refStartRowRelative ? info.getRowOffset() : 0;
                        ro2 = refEndRowRelative ? info.getRowOffset() : 0;
                        refStartRow -= ro1;
                        refEndRow -= ro2
                    }
                    refRowCount = refEndRow - refStartRow;
                    if(info.sourceContains(refModel,refStartRow,-1,refRowCount,-1))
                    {
                        ro = info.getRowOffset();
                        refStartRow += ro;
                        refEndRow += ro
                    }
                    else if(info.destinationContains(refModel,refStartRow,-1,refRowCount,-1))
                        return new ErrorExpression(CalcErrorsReference);
                    refStartRow -= refStartRowRelative ? baseRow : 0;
                    refEndRow -= refEndRowRelative ? baseRow : 0;
                    if(refStartRow !== refExpr.startRow || refEndRow !== refExpr.endRow)
                        if(refExpr instanceof RangeExpression)
                            return new RangeExpression(refStartRow,refExpr.startColumn,refEndRow,refExpr.endColumn,refStartRowRelative,refExpr.startColumnRelative,refEndRowRelative,refExpr.endColumnRelative);
                        else if(refExpr instanceof ExternalRangeExpression)
                            return new ExternalRangeExpression(refExpr.source,refStartRow,refExpr.startColumn,refEndRow,refExpr.endColumn,refStartRowRelative,refExpr.startColumnRelative,refEndRowRelative,refExpr.endColumnRelative)
                }
                else if(baseRow === -1 && (baseColumn >= 0 || !refStartColumnRelative || !refEndColumnRelative))
                {
                    refStartColumn = refExpr.startColumn + (refStartColumnRelative ? baseColumn : 0);
                    refEndColumn = refExpr.endColumn + (refEndColumnRelative ? baseColumn : 0);
                    if(refStartColumn > refEndColumn)
                        return new ErrorExpression(CalcErrorsReference);
                    if(info.destinationContains(baseModel,baseRow,baseColumn,1,1))
                    {
                        co1 = refStartColumnRelative ? info.getColumnOffset() : 0;
                        co2 = refEndColumnRelative ? info.getColumnOffset() : 0;
                        refStartColumn -= co1;
                        refEndColumn -= co2
                    }
                    refColumnCount = refEndColumn - refStartColumn;
                    if(info.sourceContains(refModel,-1,refStartColumn,-1,refColumnCount))
                    {
                        co = info.getColumnOffset();
                        refStartColumn += co;
                        refEndColumn += co
                    }
                    else if(info.destinationContains(refModel,-1,refStartColumn,-1,refColumnCount))
                        return new ErrorExpression(CalcErrorsReference);
                    refStartColumn -= refStartColumnRelative ? baseColumn : 0;
                    refEndColumn -= refEndColumnRelative ? baseColumn : 0;
                    if(refStartColumn !== refExpr.startColumn || refEndColumn !== refExpr.endColumn)
                        if(refExpr instanceof RangeExpression)
                            return new RangeExpression(refExpr.startRow,refStartColumn,refExpr.endRow,refEndColumn,refExpr.startRowRelative,refStartColumnRelative,refExpr.endRowRelative,refEndColumnRelative);
                        else if(refExpr instanceof ExternalRangeExpression)
                            return new ExternalRangeExpression(refExpr.source,refExpr.startRow,refStartColumn,refExpr.endRow,refEndColumn,refExpr.startRowRelative,refStartColumnRelative,refExpr.endRowRelative,refEndColumnRelative)
                }
            }
            else if(rangeType === RangeType.row)
            {
                refStartRowRelative = refExpr.startRowRelative;
                refEndRowRelative = refExpr.endRowRelative;
                if(baseRow >= 0 || !refStartRowRelative || !refEndRowRelative)
                {
                    refStartRow = refExpr.startRow + (refStartRowRelative ? baseRow : 0);
                    refEndRow = refExpr.endRow + (refEndRowRelative ? baseRow : 0);
                    if(refStartRow > refEndRow)
                        return new ErrorExpression(CalcErrorsReference);
                    if(info.destinationContains(baseModel,baseRow,baseColumn,1,1))
                    {
                        ro1 = refStartRowRelative ? info.getRowOffset() : 0;
                        ro2 = refEndRowRelative ? info.getRowOffset() : 0;
                        refStartRow -= ro1;
                        refEndRow -= ro2
                    }
                    refRowCount = refEndRow - refStartRow;
                    if(info.sourceContains(refModel,refStartRow,-1,refRowCount,-1))
                    {
                        ro = info.getRowOffset();
                        refStartRow += ro;
                        refEndRow += ro
                    }
                    else if(info.destinationContains(refModel,refStartRow,-1,refRowCount,-1))
                        return new ErrorExpression(CalcErrorsReference);
                    refStartRow -= refStartRowRelative ? baseRow : 0;
                    refEndRow -= refEndRowRelative ? baseRow : 0;
                    if(refStartRow !== refExpr.startRow || refEndRow !== refExpr.endRow)
                        if(refExpr instanceof RangeExpression)
                        {
                            var r1 = new RangeExpression;
                            r1.initBand(refStartRow,refEndRow,refStartRowRelative,refEndRowRelative,true);
                            return r1
                        }
                        else if(refExpr instanceof ExternalRangeExpression)
                        {
                            var r2 = new ExternalRangeExpression;
                            r2.initBand(refExpr.source,refStartRow,refEndRow,refStartRowRelative,refEndRowRelative,true);
                            return r2
                        }
                }
            }
            else if(rangeType === RangeType.column)
            {
                refStartColumnRelative = refExpr.startColumnRelative;
                refEndColumnRelative = refExpr.endColumnRelative;
                refStartColumn = refExpr.startColumn + (refStartColumnRelative ? baseColumn : 0);
                refEndColumn = refExpr.endColumn + (refEndColumnRelative ? baseColumn : 0);
                if(refStartColumn < refEndColumn)
                    return new ErrorExpression(CalcErrorsReference);
                if(info.destinationContains(baseModel,baseRow,baseColumn,1,1))
                {
                    co1 = refStartColumnRelative ? info.getColumnOffset() : 0;
                    co2 = refEndColumnRelative ? info.getColumnOffset() : 0;
                    refStartColumn -= co1;
                    refEndColumn -= co2
                }
                refColumnCount = refEndColumn - refStartColumn;
                if(info.sourceContains(refModel,-1,refStartColumn,-1,refColumnCount))
                {
                    co = info.getColumnOffset();
                    refStartColumn += co;
                    refEndColumn += co
                }
                else if(info.destinationContains(refModel,-1,refStartColumn,-1,refColumnCount))
                    return new ErrorExpression(CalcErrorsReference);
                refStartColumn -= refStartColumnRelative ? baseColumn : 0;
                refEndColumn -= refEndColumnRelative ? baseColumn : 0;
                if(refStartColumn !== refExpr.startColumn || refEndColumn !== refExpr.endColumn)
                    if(refExpr instanceof RangeExpression)
                    {
                        var r3 = new RangeExpression;
                        r3.initBand(refStartColumn,refEndColumn,refStartColumnRelative,refEndColumnRelative,false);
                        return r3
                    }
                    else if(refExpr instanceof ExternalRangeExpression)
                    {
                        var r4 = new ExternalRangeExpression;
                        r4.initBand(refExpr.source,refStartColumn,refEndColumn,refStartColumnRelative,refEndColumnRelative,false);
                        return r4
                    }
            }
            return refExpr
        },
        adjustFormulaOnMove: function(baseModel, baseRow, baseColumn, oldExpr, info)
        {
            var newExpr = oldExpr,
                arg;
            if(oldExpr instanceof CellExpression || oldExpr instanceof ExternalCellExpression)
                newExpr = this._adjustCellExpressionOnMove(baseModel,baseRow,baseColumn,oldExpr,info);
            else if(oldExpr instanceof RangeExpression || oldExpr instanceof ExternalRangeExpression)
                newExpr = this._adjustRangeExpressionOnMove(baseModel,baseRow,baseColumn,oldExpr,info);
            else if(oldExpr instanceof ParenthesesExpression)
            {
                arg = this.adjustFormulaOnMove(baseModel,baseRow,baseColumn,oldExpr.argument,info);
                if(arg !== oldExpr.argument)
                    newExpr = new ParenthesesExpression(arg)
            }
            else if(oldExpr instanceof UnaryOperatorExpression)
            {
                arg = this.adjustFormulaOnMove(baseModel,baseRow,baseColumn,oldExpr.operand,info);
                if(arg !== oldExpr.operand)
                    newExpr = new UnaryOperatorExpression(oldExpr.operator,arg)
            }
            else if(oldExpr instanceof BinaryOperatorExpression)
            {
                var arg0 = this.adjustFormulaOnMove(baseModel,baseRow,baseColumn,oldExpr.left,info);
                var arg1 = this.adjustFormulaOnMove(baseModel,baseRow,baseColumn,oldExpr.right,info);
                if(arg0 !== oldExpr.left || arg1 !== oldExpr.right)
                    newExpr = new BinaryOperatorExpression(oldExpr.operator,arg0,arg1)
            }
            else if(oldExpr instanceof FunctionExpression)
            {
                var args = null,
                    fnArg,
                    i;
                var argCount = oldExpr.argCount();
                for(i = 0; i < argCount; i++)
                {
                    fnArg = oldExpr.getArg(i);
                    arg = this.adjustFormulaOnMove(baseModel,baseRow,baseColumn,fnArg,info);
                    if(arg !== fnArg)
                    {
                        args = [];
                        args[i] = arg;
                        break
                    }
                }
                if(args)
                {
                    for(i = 0; i < argCount; i++)
                    {
                        fnArg = oldExpr.getArg(i);
                        if(args[i] === undefined || args[i] === null)
                            args[i] = this.adjustFormulaOnMove(baseModel,baseRow,baseColumn,fnArg,info)
                    }
                    newExpr = new FunctionExpression(oldExpr.fn,args)
                }
            }
            return newExpr
        },
        adjustFormulasOnMove: function(fromModel, fromRow, fromColumn, toModel, toRow, toColumn, rowCount, columnCount)
        {
            var info = new MoveSwapInfo(fromModel,fromRow,fromColumn,toModel,toRow,toColumn,rowCount,columnCount);
            for(var i = 0; i < this._customNames.length; i++)
            {
                var name = this._customNames[i].value;
                name.expression = this.adjustFormulaOnMove(null,-1,-1,name.expression,info)
            }
            var sheetSource,
                row,
                column,
                expr;
            while(this._headAdjustCells)
            {
                sheetSource = this._headAdjustCells.sheetSource;
                row = this._headAdjustCells.row;
                column = this._headAdjustCells.column;
                expr = sheetSource.getExpression(row,column);
                if(expr)
                    sheetSource.setExpression(row,column,this.adjustFormulaOnMove(sheetSource,row,column,expr,info));
                else if(sheetSource.getRowExpression && sheetSource.getRowExpression(row) || sheetSource.getColumnExpression && sheetSource.getColumnExpression(column))
                    sheetSource._addCellsToDirty(row,column,1,1);
                this.removeAdjustCell(this._headAdjustCells)
            }
            while(this._headAdjustRows)
            {
                sheetSource = this._headAdjustRows.sheetSource;
                row = this._headAdjustRows.row;
                if(sheetSource.getRowExpression)
                {
                    expr = sheetSource.getRowExpression(row);
                    if(expr && sheetSource.setRowExpression)
                        sheetSource.setRowExpression(row,this.adjustFormulaOnMove(sheetSource,row,-1,expr,info))
                }
                this.removeAdjustRow(this._headAdjustRows)
            }
            while(this._headAdjustColumns)
            {
                sheetSource = this._headAdjustColumns.sheetSource;
                column = this._headAdjustColumns.column;
                if(sheetSource.getColumnExpression)
                {
                    expr = sheetSource.getColumnExpression(column);
                    if(expr && sheetSource.setColumnExpression)
                        sheetSource.setColumnExpression(column,this.adjustFormulaOnMove(sheetSource,-1,column,expr,info))
                }
                this.removeAdjustColumn(this._headAdjustColumns)
            }
        },
        _adjustCellExpressionOnSwap: function(baseModel, baseRow, baseColumn, refExpr, info)
        {
            var refModel = refExpr instanceof ExternalReferenceExpression ? refExpr.source : baseModel;
            var refRowRelative = refExpr.rowRelative,
                refColumnRelative = refExpr.columnRelative,
                refRow,
                refColumn;
            if((baseRow >= 0 || !refRowRelative) && (baseColumn >= 0 || !refColumnRelative))
            {
                refRow = refExpr.row + (refRowRelative ? baseRow : 0);
                refColumn = refExpr.column + (refColumnRelative ? baseColumn : 0);
                if(info.destinationContains(baseModel,baseRow,baseColumn,1,1))
                {
                    refRow -= refRowRelative ? info.getRowOffset() : 0;
                    refColumn -= refColumnRelative ? info.getColumnOffset() : 0
                }
                else if(info.sourceContains(baseModel,refRow,refColumn,1,1))
                {
                    refRow += refRowRelative ? info.getRowOffset() : 0;
                    refColumn += refColumnRelative ? info.getColumnOffset() : 0
                }
                if(info.sourceContains(refModel,refRow,refColumn,1,1))
                {
                    refRow += info.getRowOffset();
                    refColumn += info.getColumnOffset()
                }
                else if(info.destinationContains(refModel,refRow,refColumn,1,1))
                {
                    refRow -= info.getRowOffset();
                    refColumn -= info.getColumnOffset()
                }
                refRow -= refRowRelative ? baseRow : 0;
                refColumn -= refColumnRelative ? baseColumn : 0;
                if(refRow !== refExpr.row || refColumn !== refExpr.column)
                    if(refExpr instanceof CellExpression)
                        return new CellExpression(refRow,refColumn,refRowRelative,refColumnRelative);
                    else if(refExpr instanceof ExternalCellExpression)
                        return new ExternalCellExpression(refExpr.source,refRow,refColumn,refRowRelative,refColumnRelative)
            }
            else if((baseRow >= 0 || !refRowRelative) && baseColumn === -1)
            {
                refRow = refExpr.row + (refRowRelative ? baseRow : 0);
                if(info.destinationContains(baseModel,baseRow,-1,1,-1))
                    refRow -= refRowRelative ? info.getRowOffset() : 0;
                else if(info.sourceContains(baseModel,refRow,-1,1,-1))
                    refRow += refRowRelative ? info.getRowOffset() : 0;
                if(info.sourceContains(refModel,refRow,-1,1,-1))
                    refRow += info.getRowOffset();
                else if(info.destinationContains(refModel,refRow,-1,1,-1))
                    refRow -= info.getRowOffset();
                refRow -= refRowRelative ? baseRow : 0;
                if(refRow !== refExpr.row || refColumn !== refExpr.column)
                    if(refExpr instanceof CellExpression)
                        return new CellExpression(refRow,refExpr.column,refRowRelative,refExpr.columnRelative);
                    else if(refExpr instanceof ExternalCellExpression)
                        return new ExternalCellExpression(refExpr.source,refRow,refExpr.column,refRowRelative,refExpr.columnRelative)
            }
            else if(baseRow === -1 && baseColumn >= 0 && !refColumnRelative)
            {
                refColumn = refExpr.column + (refColumnRelative ? baseColumn : 0);
                if(info.destinationContains(baseModel,-1,baseColumn,-1,1))
                    refColumn -= refColumnRelative ? info.getColumnOffset() : 0;
                else if(info.sourceContains(baseModel,-1,refColumn,-1,1))
                    refColumn += refColumnRelative ? info.getColumnOffset() : 0;
                if(info.sourceContains(refModel,-1,refColumn,-1,1))
                    refColumn += info.getColumnOffset();
                else if(info.destinationContains(refModel,-1,refColumn,-1,1))
                    refColumn -= info.getColumnOffset();
                refColumn -= refColumnRelative ? baseColumn : 0;
                if(refRow !== refExpr.row || refColumn !== refExpr.column)
                    if(refExpr instanceof CellExpression)
                        return new CellExpression(refExpr.row,refColumn,refExpr.rowRelative,refColumnRelative);
                    else if(refExpr instanceof ExternalCellExpression)
                        return new ExternalCellExpression(refExpr.source,refExpr.row,refColumn,refExpr.rowRelative,refColumnRelative)
            }
            return refExpr
        },
        _adjustRangeExpressionOnSwap: function(baseModel, baseRow, baseColumn, refExpr, info)
        {
            var refModel = refExpr instanceof ExternalReferenceExpression ? refExpr.source : baseModel;
            var rangeType = refExpr._getRangeType();
            var refStartRow,
                refEndRow,
                refStartColumn,
                refEndColumn,
                refRowCount,
                refColumnCount;
            var refStartRowRelative,
                refStartColumnRelative,
                refEndRowRelative,
                refEndColumnRelative;
            var ro,
                co,
                ro1,
                co1,
                ro2,
                co2;
            if(rangeType === RangeType.cell)
            {
                refStartRowRelative = refExpr.startRowRelative;
                refStartColumnRelative = refExpr.startColumnRelative;
                refEndRowRelative = refExpr.endRowRelative;
                refEndColumnRelative = refExpr.endColumnRelative;
                if((baseRow >= 0 || !refStartRowRelative || !refEndRowRelative) && (baseColumn >= 0 || !refStartColumnRelative || !refEndColumnRelative))
                {
                    refStartRow = refExpr.startRow + (refStartRowRelative ? baseRow : 0);
                    refEndRow = refExpr.endRow + (refEndRowRelative ? baseRow : 0);
                    refStartColumn = refExpr.startColumn + (refStartColumnRelative ? baseColumn : 0);
                    refEndColumn = refExpr.endColumn + (refEndColumnRelative ? baseColumn : 0);
                    refRowCount = refEndRow - refStartRow;
                    refColumnCount = refEndColumn - refStartColumn;
                    if(info.destinationContains(baseModel,baseRow,baseColumn,1,1))
                    {
                        ro1 = refStartRowRelative ? info.getRowOffset() : 0;
                        co1 = refStartColumnRelative ? info.getColumnOffset() : 0;
                        ro2 = refEndRowRelative ? info.getRowOffset() : 0;
                        co2 = refEndColumnRelative ? info.getColumnOffset() : 0;
                        refStartRow -= ro1;
                        refEndRow -= ro2;
                        refStartColumn -= co1;
                        refEndColumn -= co2
                    }
                    else if(info.sourceContains(baseModel,refStartRow,refStartColumn,refRowCount,refColumnCount))
                    {
                        ro1 = refStartRowRelative ? info.getRowOffset() : 0;
                        co1 = refStartColumnRelative ? info.getColumnOffset() : 0;
                        ro2 = refEndRowRelative ? info.getRowOffset() : 0;
                        co2 = refEndColumnRelative ? info.getColumnOffset() : 0;
                        refStartRow += ro1;
                        refEndRow += ro2;
                        refStartColumn += co1;
                        refEndColumn += co2
                    }
                    if(info.sourceContains(refModel,refStartRow,refStartColumn,refRowCount,refColumnCount))
                    {
                        ro = info.getRowOffset();
                        co = info.getColumnOffset();
                        refStartRow += ro;
                        refEndRow += ro;
                        refStartColumn += co;
                        refEndColumn += co
                    }
                    else if(info.destinationContains(refModel,refStartRow,refStartColumn,refRowCount,refColumnCount))
                    {
                        ro = info.getRowOffset();
                        co = info.getColumnOffset();
                        refStartRow -= ro;
                        refEndRow -= ro;
                        refStartColumn -= co;
                        refEndColumn -= co
                    }
                    refStartRow -= refStartRowRelative ? baseRow : 0;
                    refEndRow -= refEndRowRelative ? baseRow : 0;
                    refStartColumn -= refStartColumnRelative ? baseColumn : 0;
                    refEndColumn -= refEndColumnRelative ? baseColumn : 0;
                    if(refStartRow !== refExpr.startRow || refStartColumn !== refExpr.startColumn || refEndRow !== refExpr.endRow || refEndColumn !== refExpr.endColumn)
                        if(refExpr instanceof RangeExpression)
                            return new RangeExpression(refStartRow,refStartColumn,refEndRow,refEndColumn,refStartRowRelative,refStartColumnRelative,refEndRowRelative,refEndColumnRelative);
                        else if(refExpr instanceof ExternalRangeExpression)
                            return new ExternalRangeExpression(refExpr.source,refStartRow,refStartColumn,refEndRow,refEndColumn,refStartRowRelative,refStartColumnRelative,refEndRowRelative,refEndColumnRelative)
                }
                else if((baseRow >= 0 || !refStartRowRelative || !refEndRowRelative) && baseColumn === -1)
                {
                    refStartRow = refExpr.startRow + (refStartRowRelative ? baseRow : 0);
                    refEndRow = refExpr.endRow + (refEndRowRelative ? baseRow : 0);
                    if(refStartRow < refEndRow)
                        return new ErrorExpression(CalcErrorsReference);
                    if(info.destinationContains(baseModel,baseRow,-1,1,-1))
                    {
                        ro1 = refStartRowRelative ? info.getRowOffset() : 0;
                        ro2 = refStartRowRelative ? info.getRowOffset() : 0;
                        refStartRow -= ro1;
                        refEndRow -= ro2
                    }
                    else if(info.sourceContains(baseModel,baseRow,-1,1,-1))
                    {
                        ro1 = refStartRowRelative ? info.getRowOffset() : 0;
                        ro2 = refStartRowRelative ? info.getRowOffset() : 0;
                        refStartRow += ro1;
                        refEndRow += ro2
                    }
                    refRowCount = refEndRow - refStartRow;
                    if(info.sourceContains(refModel,refStartRow,-1,refRowCount,-1))
                    {
                        ro = info.getRowOffset();
                        refStartRow += ro;
                        refEndRow += ro
                    }
                    else if(info.destinationContains(refModel,refStartRow,-1,refRowCount,-1))
                    {
                        ro = info.getRowOffset();
                        refStartRow -= ro;
                        refEndRow -= ro
                    }
                    refStartRow -= refStartRowRelative ? baseRow : 0;
                    refEndRow -= refEndRowRelative ? baseRow : 0;
                    if(refStartRow !== refExpr.startRow || refEndRow !== refExpr.endRow)
                        if(refExpr instanceof RangeExpression)
                            return new RangeExpression(refStartRow,refExpr.startColumn,refEndRow,refExpr.endColumn,refStartRowRelative,refExpr.startColumnRelative,refEndRowRelative,refExpr.endColumnRelative);
                        else if(refExpr instanceof ExternalRangeExpression)
                            return new ExternalRangeExpression(refExpr.source,refStartRow,refExpr.startColumn,refEndRow,refExpr.endColumn,refStartRowRelative,refExpr.startColumnRelative,refEndRowRelative,refExpr.endColumnRelative)
                }
                else if(baseRow === -1 && (baseColumn >= 0 || !refStartColumnRelative || !refEndColumnRelative))
                {
                    refStartColumn = refExpr.startColumn + (refStartColumnRelative ? baseColumn : 0);
                    refEndColumn = refExpr.endColumn + (refEndColumnRelative ? baseColumn : 0);
                    if(refStartColumn < refEndColumn)
                        return new ErrorExpression(CalcErrorsReference);
                    if(info.destinationContains(baseModel,-1,baseColumn,-1,1))
                    {
                        co1 = refStartColumnRelative ? info.getColumnOffset() : 0;
                        co2 = refEndColumnRelative ? info.getColumnOffset() : 0;
                        refStartColumn -= co1;
                        refEndColumn -= co2
                    }
                    else if(info.sourceContains(baseModel,-1,baseColumn,-1,1))
                    {
                        co1 = refStartColumnRelative ? info.getColumnOffset() : 0;
                        co2 = refEndColumnRelative ? info.getColumnOffset() : 0;
                        refStartColumn += co1;
                        refEndColumn += co2
                    }
                    refColumnCount = refEndColumn - refStartColumn;
                    if(info.sourceContains(refModel,-1,refStartColumn,-1,refColumnCount))
                    {
                        co = info.getColumnOffset();
                        refStartColumn += co;
                        refEndColumn += co
                    }
                    else if(info.destinationContains(refModel,-1,refStartColumn,-1,refColumnCount))
                    {
                        co = info.getColumnOffset();
                        refStartColumn -= co;
                        refEndColumn -= co
                    }
                    refStartColumn -= refStartColumnRelative ? baseColumn : 0;
                    refEndColumn -= refEndColumnRelative ? baseColumn : 0;
                    if(refStartColumn !== refExpr.startColumn || refEndColumn !== refExpr.endColumn)
                        if(refExpr instanceof RangeExpression)
                            return new RangeExpression(refExpr.startRow,refStartColumn,refExpr.endRow,refEndColumn,refExpr.startRowRelative,refStartColumnRelative,refExpr.endRowRelative,refEndColumnRelative);
                        else if(refExpr instanceof ExternalRangeExpression)
                            return new ExternalRangeExpression(refExpr.source,refExpr.startRow,refStartColumn,refExpr.endRow,refEndColumn,refExpr.startRowRelative,refStartColumnRelative,refExpr.endRowRelative,refEndColumnRelative)
                }
            }
            else if(rangeType === RangeType.row)
            {
                refStartRowRelative = refExpr.startRowRelative;
                refEndRowRelative = refExpr.endRowRelative;
                if(baseRow >= 0 || !refStartRowRelative || !refEndRowRelative)
                {
                    refStartRow = refExpr.startRow + (refStartRowRelative ? baseRow : 0);
                    refEndRow = refExpr.endRow + (refEndRowRelative ? baseRow : 0);
                    if(refStartRow < refEndRow)
                        return new ErrorExpression(CalcErrorsReference);
                    if(info.destinationContains(baseModel,baseRow,-1,1,-1))
                    {
                        ro1 = refStartRowRelative ? info.getRowOffset() : 0;
                        ro2 = refEndRowRelative ? info.getRowOffset() : 0;
                        refStartRow -= ro1;
                        refEndRow -= ro2
                    }
                    else if(info.sourceContains(baseModel,baseRow,-1,1,-1))
                    {
                        ro1 = refStartRowRelative ? info.getRowOffset() : 0;
                        ro2 = refEndRowRelative ? info.getRowOffset() : 0;
                        refStartRow += ro1;
                        refEndRow += ro2
                    }
                    refRowCount = refEndRow - refStartRow;
                    if(info.sourceContains(refModel,refStartRow,-1,refRowCount,-1))
                    {
                        ro = info.getRowOffset();
                        refStartRow += ro;
                        refEndRow += ro
                    }
                    else if(info.destinationContains(refModel,refStartRow,-1,refRowCount,-1))
                    {
                        ro = info.getRowOffset();
                        refStartRow -= ro;
                        refEndRow -= ro
                    }
                    refStartRow -= refStartRowRelative ? baseRow : 0;
                    refEndRow -= refEndRowRelative ? baseRow : 0;
                    if(refStartRow !== refExpr.startRow || refEndRow !== refExpr.endRow)
                        if(refExpr instanceof RangeExpression)
                        {
                            var r1 = new RangeExpression;
                            r1.initBand(refStartRow,refEndRow,refStartRowRelative,refEndRowRelative,true);
                            return r1
                        }
                        else if(refExpr instanceof ExternalRangeExpression)
                        {
                            var r2 = new ExternalRangeExpression;
                            r2.initBand(refExpr.source,refStartRow,refEndRow,refStartRowRelative,refEndRowRelative,true);
                            return r2
                        }
                }
            }
            else if(rangeType === RangeType.column)
            {
                refStartColumnRelative = refExpr.startColumnRelative;
                refEndColumnRelative = refExpr.endColumnRelative;
                refStartColumn = refExpr.startColumn + (refStartColumnRelative ? baseColumn : 0);
                refEndColumn = refExpr.endColumn + (refEndColumnRelative ? baseColumn : 0);
                if(refStartColumn > refEndColumn)
                    return new ErrorExpression(CalcErrorsReference);
                if(info.destinationContains(baseModel,-1,baseColumn,-1,1))
                {
                    co1 = refStartColumnRelative ? info.getColumnOffset() : 0;
                    co2 = refEndColumnRelative ? info.getColumnOffset() : 0;
                    refStartColumn -= co1;
                    refEndColumn -= co2
                }
                else if(info.sourceContains(baseModel,-1,baseColumn,-1,1))
                {
                    co1 = refStartColumnRelative ? info.getColumnOffset() : 0;
                    co2 = refEndColumnRelative ? info.getColumnOffset() : 0;
                    refStartColumn += co1;
                    refEndColumn += co2
                }
                refColumnCount = refEndColumn - refStartColumn;
                if(info.sourceContains(refModel,-1,refStartColumn,-1,refColumnCount))
                {
                    co = info.getColumnOffset();
                    refStartColumn += co;
                    refEndColumn += co
                }
                else if(info.destinationContains(refModel,-1,refStartColumn,-1,refColumnCount))
                {
                    co = info.getColumnOffset();
                    refStartColumn -= co;
                    refEndColumn -= co
                }
                refStartColumn -= refStartColumnRelative ? baseColumn : 0;
                refEndColumn -= refEndColumnRelative ? baseColumn : 0;
                if(refStartColumn !== refExpr.startColumn || refEndColumn !== refExpr.endColumn)
                    if(refExpr instanceof RangeExpression)
                    {
                        var r3 = new RangeExpression;
                        r3.initBand(refStartColumn,refEndColumn,refStartColumnRelative,refEndColumnRelative,false);
                        return r3
                    }
                    else if(refExpr instanceof ExternalRangeExpression)
                    {
                        var r4 = new ExternalRangeExpression;
                        r4.initBand(refExpr.source,refStartColumn,refEndColumn,refStartColumnRelative,refEndColumnRelative,false);
                        return r4
                    }
            }
            return refExpr
        },
        adjustFormulaOnSwap: function(baseModel, baseRow, baseColumn, oldExpr, info)
        {
            var newExpr = oldExpr,
                arg;
            if(oldExpr instanceof CellExpression || oldExpr instanceof ExternalCellExpression)
                newExpr = this._adjustCellExpressionOnSwap(baseModel,baseRow,baseColumn,oldExpr,info);
            else if(oldExpr instanceof RangeExpression || oldExpr instanceof ExternalRangeExpression)
                newExpr = this._adjustRangeExpressionOnSwap(baseModel,baseRow,baseColumn,oldExpr,info);
            else if(oldExpr instanceof ParenthesesExpression)
            {
                arg = this.adjustFormulaOnSwap(baseModel,baseRow,baseColumn,oldExpr.argument,info);
                if(arg !== oldExpr.argument)
                    newExpr = new ParenthesesExpression(arg)
            }
            else if(oldExpr instanceof UnaryOperatorExpression)
            {
                arg = this.adjustFormulaOnSwap(baseModel,baseRow,baseColumn,oldExpr.operand,info);
                if(arg !== oldExpr.operand)
                    newExpr = new UnaryOperatorExpression(oldExpr.operator,arg)
            }
            else if(oldExpr instanceof BinaryOperatorExpression)
            {
                var arg0 = this.adjustFormulaOnSwap(baseModel,baseRow,baseColumn,oldExpr.left,info);
                var arg1 = this.adjustFormulaOnSwap(baseModel,baseRow,baseColumn,oldExpr.right,info);
                if(arg0 !== oldExpr.left || arg1 !== oldExpr.right)
                    newExpr = new BinaryOperatorExpression(oldExpr.operator,arg0,arg1)
            }
            else if(oldExpr instanceof FunctionExpression)
            {
                var args = null,
                    fnArg,
                    i;
                var argCount = oldExpr.argCount();
                for(i = 0; i < argCount; i++)
                {
                    fnArg = oldExpr.getArg(i);
                    arg = this.adjustFormulaOnSwap(baseModel,baseRow,baseColumn,fnArg,info);
                    if(arg !== fnArg)
                    {
                        args = [];
                        args[i] = arg;
                        break
                    }
                }
                if(args)
                {
                    for(i = 0; i < argCount; i++)
                    {
                        fnArg = oldExpr.getArg(i);
                        if(args[i] === undefined || args[i] === null)
                            args[i] = this.adjustFormulaOnSwap(baseModel,baseRow,baseColumn,fnArg,info)
                    }
                    newExpr = new FunctionExpression(oldExpr.fn,args)
                }
            }
            return newExpr
        },
        adjustFormulasOnSwap: function(fromModel, fromRow, fromColumn, toModel, toRow, toColumn, rowCount, columnCount)
        {
            var info = new MoveSwapInfo(fromModel,fromRow,fromColumn,toModel,toRow,toColumn,rowCount,columnCount);
            for(var i = 0; i < this._customNames.length; i++)
            {
                var name = this._customNames[i].value;
                name.expression = this.adjustFormulaOnSwap(null,-1,-1,name.expression,info)
            }
            var sheetSource,
                row,
                column,
                expr;
            while(this._headAdjustCells)
            {
                sheetSource = this._headAdjustCells.sheetSource;
                row = this._headAdjustCells.row;
                column = this._headAdjustCells.column;
                expr = sheetSource.getExpression(row,column);
                if(expr)
                    sheetSource.setExpression(row,column,this.adjustFormulaOnSwap(sheetSource,row,column,expr,info));
                else if(sheetSource.getRowExpression && sheetSource.getRowExpression(row) || sheetSource.getColumnExpression && sheetSource.getColumnExpression(column))
                    sheetSource._addCellsToDirty(row,column,1,1);
                this.removeAdjustCell(this._headAdjustCells)
            }
            while(this._headAdjustRows)
            {
                sheetSource = this._headAdjustRows.sheetSource;
                row = this._headAdjustRows.row;
                if(sheetSource.getRowExpression)
                {
                    expr = sheetSource.getRowExpression(row);
                    if(expr && sheetSource.setRowExpression)
                        sheetSource.setRowExpression(row,this.adjustFormulaOnSwap(sheetSource,row,-1,expr,info))
                }
                this.removeAdjustRow(this._headAdjustRows)
            }
            while(this._headAdjustColumns)
            {
                sheetSource = this._headAdjustColumns.sheetSource;
                column = this._headAdjustColumns.column;
                if(sheetSource.getColumnExpression)
                {
                    expr = sheetSource.getColumnExpression(column);
                    if(expr && sheetSource.setColumnExpression)
                        sheetSource.setColumnExpression(column,this.adjustFormulaOnSwap(sheetSource,-1,column,expr,info))
                }
                this.removeAdjustColumn(this._headAdjustColumns)
            }
        }
    };
    Calc._Controller = Controller;
    function shouldUseRange(row, col, rowCount, colCount)
    {
        return row === -1 && colCount > 127 || col === -1 && rowCount > 127 || rowCount * colCount > 127
    }
    function _SheetSource(sheet, sheetArea)
    {
        this._sheet = sheet;
        this._sheetArea = sheetArea;
        this._controller = new Controller;
        this._controller.attachDataModel(this)
    }
    _SheetSource.prototype = {
        _getSheetReference: function()
        {
            this._reference = this._reference || new SheetReference(this);
            return this._reference
        },
        getValue: function(row, column)
        {
            if(row < 0 || column < 0 || row >= this.getRowCount() || column >= this.getColumnCount())
                return CalcErrorsValue;
            var sheet = this._sheet;
            if(sheet._isValidatingCell && sheet._validatingRow === row && sheet._validatingColumn === column)
                return sheet._validatingValue;
            else
                return sheet.getValue(row,column)
        },
        getReference: function(row, column, rowCount, columnCount)
        {
            if(row === -1 && column === -1)
                return this._getSheetReference();
            else if(row === -1)
                return new ConcreteReference(this._getSheetReference(),[{
                            row: 0,
                            rowCount: this.getRowCount(),
                            col: column,
                            colCount: columnCount
                        }]);
            else if(column === -1)
                return new ConcreteReference(this._getSheetReference(),[{
                            row: row,
                            rowCount: rowCount,
                            col: 0,
                            colCount: this.getColumnCount()
                        }]);
            else
                return new ConcreteReference(this._getSheetReference(),[{
                            row: row,
                            rowCount: rowCount,
                            col: column,
                            colCount: columnCount
                        }])
        },
        getFunction: function(name)
        {
            return this._sheet ? this._sheet._findCustomFunction(name) : null
        },
        getName: function(name, row, column)
        {
            var nameInfo = this._sheet ? this._sheet._findCustomName(name) : null;
            if(nameInfo)
                return nameInfo.getExpression();
            return null
        },
        getColumnCount: function()
        {
            return this._sheet.getColumnCount()
        },
        getRowCount: function()
        {
            return this._sheet.getRowCount()
        },
        getCalcService: function()
        {
            return this._sheet ? this._sheet.getCalcService() : null
        },
        hasFormula: function(row, col)
        {
            row = row !== undefined ? row : -1;
            col = col !== undefined ? col : -1;
            return!!this.getExpression(row,col)
        },
        setFormula: function(row, col, value)
        {
            if(value && value.length === 0)
                value = null;
            var oldFormula = this.getFormula(row,col);
            if(oldFormula !== value && !(oldFormula === "" && !value || !oldFormula && value === ""))
            {
                var svc = this.getCalcService();
                if(svc)
                {
                    var expr = value ? svc.parse(value,row >= 0 ? row : 0,col >= 0 ? col : 0) : null;
                    this.setExpression(row,col,expr)
                }
                if(row >= 0 && col >= 0)
                    this._sheet._getModel(this._sheetArea).setFormula(row,col,value)
            }
        },
        getFormula: function(row, col)
        {
            var svc = this.getCalcService();
            if(svc)
            {
                var expr = this.getExpression(row,col);
                return expr ? svc.unparse(expr,row,col) : null
            }
            return null
        },
        getRowFormula: function(row)
        {
            return this.getFormula(row,-1)
        },
        setRowFormula: function(row, value)
        {
            this.setFormula(row,-1,value)
        },
        getColumnFormula: function(col)
        {
            this.getFormula(-1,col)
        },
        setColumnFormula: function(col, value)
        {
            this.setFormula(-1,col,value)
        },
        getExpression: function(row, col)
        {
            var expr = null,
                node,
                rowNode,
                colNode;
            var dataModel = this._sheet._getCalcModel(this._sheetArea);
            if(this._sheet)
            {
                node = dataModel.getNode(row,col);
                if(node)
                    expr = node.formula;
                if(!expr && row < dataModel.getRowCount())
                {
                    rowNode = dataModel.getNode(row,-1);
                    if(rowNode)
                        expr = rowNode.formula
                }
                if(!expr && col < dataModel.getColumnCount())
                {
                    colNode = dataModel.getNode(-1,col);
                    if(colNode)
                        expr = colNode.formula
                }
            }
            else if(row >= 0 && col === -1)
            {
                if(!expr && row < dataModel.getRowCount())
                {
                    rowNode = dataModel.getNode(row,-1);
                    if(rowNode)
                        expr = rowNode.formula
                }
            }
            else if(row === -1 && col >= 0)
            {
                if(expr && col < dataModel.getColumnCount())
                {
                    colNode = dataModel.getNode(-1,col);
                    if(colNode)
                        expr = colNode.formula
                }
            }
            else if(row === -1 && col === -1)
            {
                node = dataModel.getNode(-1,-1);
                if(node)
                    expr = node.formula
            }
            return expr
        },
        setExpression: function(row, col, expr)
        {
            if(row >= 0 && col >= 0)
                this._setCellExpression(row,col,expr);
            else if(row >= 0 && col === -1)
                this._setRowExpression(row,expr);
            else if(row === -1 && col >= 0)
                this._setColumnExpression(col,expr);
            if(this._controller.autoCalculation)
                this._controller.recalculate()
        },
        getRowExpression: function(row)
        {
            return this.getExpression(row,-1)
        },
        getColumnExpression: function(column)
        {
            return this.getExpression(-1,column)
        },
        _setCellExpression: function(row, col, value)
        {
            var cellCalc = this._getCellCalc(row,col,true);
            if(cellCalc)
            {
                cellCalc.stopListening();
                var node = this._sheet._getCalcModel(this._sheetArea).getNode(row,col,true);
                node.formula = value;
                cellCalc.startListening();
                this._controller.addDirtyCell(cellCalc)
            }
        },
        _setRowExpression: function(row, value)
        {
            var rowCalc = this._getRowCalc(row,true);
            if(rowCalc)
            {
                rowCalc.stopListening();
                var dataModel = this._sheet._getCalcModel(this._sheetArea);
                var node = dataModel._rowDataArray[row];
                if(!node)
                    dataModel._rowDataArray[row] = node = {};
                node.formula = value;
                rowCalc.startListening();
                this._controller.addDirtyRow(rowCalc)
            }
        },
        _setColumnExpression: function(column, value)
        {
            var columnCalc = this._getColumnCalc(column,true);
            if(columnCalc)
            {
                columnCalc.stopListening();
                var dataModel = this._sheet._getCalcModel(this._sheetArea);
                var node = dataModel._columnDataArray[column];
                if(!node)
                    dataModel._columnDataArray[column] = node = {};
                node.formula = value;
                columnCalc.startListening();
                this._controller.addDirtyRow(columnCalc)
            }
        },
        onBeforeAddRemoveRows: function(row)
        {
            var rc = this.getRowCount(),
                cc = this.getColumnCount();
            this.unlinkCellExpression(row,0,rc - row,cc);
            this.unlinkRowExpression(row,rc - row);
            this._addDependentsToAdjust(row,-1,rc - row,-1)
        },
        onAfterAddRows: function(row, count)
        {
            var rc = this.getRowCount(),
                cc = this.getColumnCount();
            this._addCellsToDirty(row,0,count,cc);
            this._addCellsToAdjust(row,0,rc - row,cc);
            this._addRowsToAdjust(row,rc - row);
            this._controller.adjustFormulasOnAddRows(this,row,count);
            if(this._controller.autoCalculation)
                this._controller.recalculate()
        },
        onAfterRemoveRows: function(row, count)
        {
            var rc = this.getRowCount(),
                cc = this.getColumnCount();
            this._addCellsToAdjust(row,0,rc - row,cc);
            this._addRowsToAdjust(row,rc - row);
            this._controller.adjustFormulasOnRemoveRows(this,row,count);
            if(this._controller.autoCalculation)
                this._controller.recalculate()
        },
        onBeforeAddRemoveColumns: function(column)
        {
            var rc = this.getRowCount(),
                cc = this.getColumnCount();
            this.unlinkCellExpression(0,column,rc,cc - column);
            this.unlinkColumnExpression(column,cc - column);
            this._addDependentsToAdjust(-1,column,-1,cc - column)
        },
        onAfterAddColumns: function(column, count)
        {
            var rc = this.getRowCount(),
                cc = this.getColumnCount();
            this._addCellsToDirty(0,column,rc,count);
            this._addCellsToAdjust(0,column,rc,cc - column);
            this._addColumnsToAdjust(column,cc - column);
            this._controller.adjustFormulasOnAddColumns(this,column,count)
        },
        onAfterRemoveColumns: function(column, count)
        {
            var rc = this.getRowCount(),
                cc = this.getColumnCount();
            this._addCellsToAdjust(0,column,rc,cc - column);
            this._addColumnsToAdjust(column,cc - column);
            this._controller.adjustFormulasOnRemoveColumns(this,column,count);
            if(this._controller.autoCalculation)
                this._controller.recalculate()
        },
        _buildAction: function(target, actionPrefix, actionTaget)
        {
            return target[actionPrefix + '' + actionTaget]
        },
        _dealWithExpression: function(row, column, expr, actionPrefix)
        {
            while(expr instanceof ParenthesesExpression)
                expr = expr.argument;
            var refRow,
                refColumn,
                refStartRow,
                refEndRow,
                refStartColumn,
                refEndColumn,
                refRowCount,
                refColumnCount,
                exprCellCalc,
                exprRowCalc,
                exprColumnCalc,
                refCellCalc,
                refRowCalc,
                refColumnCalc,
                r,
                c,
                sheetSource;
            if(expr instanceof CellExpression || expr instanceof ExternalCellExpression)
            {
                sheetSource = expr instanceof CellExpression ? this : expr.source;
                if(0 <= row && 0 <= column)
                {
                    refRow = expr.row + (expr.rowRelative ? row : 0);
                    refColumn = expr.column + (expr.columnRelative ? column : 0);
                    exprCellCalc = this._getCellCalc(row,column,true);
                    refCellCalc = sheetSource._getCellCalc(refRow,refColumn,true);
                    if(!refCellCalc)
                        return;
                    this._buildAction(refCellCalc,actionPrefix,'CellListener').call(refCellCalc,exprCellCalc)
                }
                else if(0 <= row && column === -1)
                {
                    refRow = expr.row + (expr.rowRelative ? row : 0);
                    refColumn = expr.column;
                    exprRowCalc = this._getRowCalc(row,true);
                    if(expr.columnRelative)
                    {
                        refRowCalc = sheetSource._getRowCalc(refRow,true);
                        if(!refRowCalc)
                            return;
                        this._buildAction(refRowCalc,actionPrefix,'RowListener2').call(refRowCalc,exprRowCalc,-refColumn)
                    }
                    else
                    {
                        refCellCalc = sheetSource._getCellCalc(refRow,refColumn,true);
                        if(!refCellCalc)
                            return;
                        this._buildAction(refCellCalc,actionPrefix,'RowListener').call(refCellCalc,exprRowCalc)
                    }
                }
                else if(row === -1 && 0 <= column)
                {
                    refRow = expr.row;
                    refColumn = expr.column + (expr.columnRelative ? column : 0);
                    exprColumnCalc = this._getColumnCalc(column,true);
                    if(expr.rowRelative)
                    {
                        refColumnCalc = sheetSource._getColumnCalc(refColumn,true);
                        if(!refColumnCalc)
                            return;
                        this._buildAction(refColumnCalc,actionPrefix,'ColumnListener2').call(refColumnCalc,exprColumnCalc,-refRow)
                    }
                    else
                    {
                        refCellCalc = sheetSource._getCellCalc(refRow,refColumn,true);
                        if(!refCellCalc)
                            return;
                        this._buildAction(refCellCalc,actionPrefix,'ColumnListener').call(refCellCalc,exprColumnCalc)
                    }
                }
            }
            else if(expr instanceof RangeExpression || expr instanceof ExternalRangeExpression)
            {
                sheetSource = expr instanceof RangeExpression ? this : expr.source;
                var refRangeType = expr._getRangeType();
                if(refRangeType === RangeType.cell)
                {
                    if(0 <= row && 0 <= column)
                    {
                        refStartRow = expr.startRow + (expr.startRowRelative ? row : 0);
                        refStartColumn = expr.startColumn + (expr.startColumnRelative ? column : 0);
                        refEndRow = expr.endRow + (expr.endRowRelative ? row : 0);
                        refEndColumn = expr.endColumn + (expr.endColumnRelative ? column : 0);
                        refRowCount = Math.abs(refEndRow - refStartRow) + 1;
                        refColumnCount = Math.abs(refEndColumn - refStartColumn) + 1;
                        exprCellCalc = this._getCellCalc(row,column,true);
                        if(shouldUseRange(refRow,refColumn,refRowCount,refColumnCount))
                        {
                            var rangeCalc = sheetSource._getRangeCalc(true);
                            if(!rangeCalc)
                                return;
                            this._buildAction(rangeCalc,actionPrefix,'CellListener').call(rangeCalc,refStartRow,refStartColumn,refRowCount,refColumnCount,exprCellCalc)
                        }
                        else
                            for(r = refStartRow; r <= refEndRow; r++)
                                for(c = refStartColumn; c <= refEndColumn; c++)
                                {
                                    refCellCalc = sheetSource._getCellCalc(r,c,true);
                                    if(refCellCalc)
                                        this._buildAction(refCellCalc,actionPrefix,'CellListener').call(refCellCalc,exprCellCalc)
                                }
                    }
                    else if(0 <= row && column === -1)
                    {
                        refStartRow = expr.startRow + (expr.startRowRelative ? row : 0);
                        refStartColumn = expr.startColumn + (expr.startColumnRelative ? column : 0);
                        refEndRow = expr.endRow + (expr.endRowRelative ? row : 0);
                        refEndColumn = expr.endColumn + (expr.endColumnRelative ? column : 0);
                        exprRowCalc = this._getRowCalc(row,true);
                        for(r = refStartRow; r <= refEndRow; r++)
                            for(c = refStartColumn; c <= refEndColumn; c++)
                                if(expr.startColumnRelative || expr.endColumnRelative)
                                {
                                    refRowCalc = sheetSource._getRowCalc(r,true);
                                    if(refRowCalc)
                                        this._buildAction(refRowCalc,actionPrefix,'RowListener2').call(refRowCalc,exprRowCalc,-c)
                                }
                                else
                                {
                                    refCellCalc = sheetSource._getCellCalc(r,c,true);
                                    if(refCellCalc)
                                        this._buildAction(refCellCalc,actionPrefix,'RowListener').call(refCellCalc,exprRowCalc)
                                }
                    }
                    else if(row === -1 && 0 <= column)
                    {
                        refStartRow = expr.startRow + (expr.startRowRelative ? row : 0);
                        refStartColumn = expr.startColumn + (expr.startColumnRelative ? column : 0);
                        refEndRow = expr.endRow + (expr.endRowRelative ? row : 0);
                        refEndColumn = expr.endColumn + (expr.endColumnRelative ? column : 0);
                        exprColumnCalc = this._getColumnCalc(column,true);
                        for(r = refStartRow; r <= refEndRow; r++)
                            for(c = refStartColumn; c <= refEndColumn; c++)
                                if(expr.startRowRelative || expr.endRowRelative)
                                {
                                    refColumnCalc = sheetSource._getColumnCalc(r,true);
                                    if(refColumnCalc)
                                        this._buildAction(refColumnCalc,actionPrefix,'ColumnListener2').call(refColumnCalc,exprColumnCalc,-r)
                                }
                                else
                                {
                                    refCellCalc = sheetSource._getCellCalc(r,c,true);
                                    if(refCellCalc)
                                        this._buildAction(refCellCalc,actionPrefix,'ColumnListener').call(refCellCalc,exprColumnCalc)
                                }
                    }
                }
                else if(refRangeType === RangeType.row)
                {
                    if(0 <= row && 0 <= column)
                    {
                        refStartRow = expr.startRow + (expr.startRowRelative ? row : 0);
                        refEndRow = expr.endRow + (expr.endRowRelative ? row : 0);
                        exprCellCalc = this._getCellCalc(row,column,true);
                        for(r = refStartRow; r <= refEndRow; r++)
                        {
                            refRowCalc = sheetSource._getRowCalc(r,true);
                            if(refRowCalc)
                                this._buildAction(refRowCalc,actionPrefix,'CellListener').call(refRowCalc,exprCellCalc)
                        }
                    }
                    else if(0 <= row && column === -1);
                    else if(row === -1 && 0 <= column);
                }
                else if(refRangeType === RangeType.column)
                {
                    if(0 <= row && 0 <= column)
                    {
                        refStartColumn = expr.startColumn + (expr.startColumnRelative ? column : 0);
                        refEndColumn = expr.endColumn + (expr.endColumnRelative ? column : 0);
                        exprCellCalc = this._getCellCalc(row,column,true);
                        for(c = refStartColumn; c <= refEndColumn; c++)
                        {
                            refColumnCalc = sheetSource._getColumnCalc(c,true);
                            if(refColumnCalc)
                                this._buildAction(refColumnCalc,actionPrefix,'CellListener').call(refColumnCalc,exprCellCalc)
                        }
                    }
                    else if(0 <= row && column === -1);
                    else if(row === -1 && 0 <= column);
                }
                else if(refRangeType === RangeType.sheet)
                    if(0 <= row && 0 <= column);
                    else if(0 <= row && column === -1);
                    else if(row === -1 && 0 <= column);
            }
            else if(expr instanceof UnaryOperatorExpression)
                this._dealWithExpression(row,column,expr.operand,actionPrefix);
            else if(expr instanceof BinaryOperatorExpression)
            {
                this._dealWithExpression(row,column,expr.left,actionPrefix);
                this._dealWithExpression(row,column,expr.right,actionPrefix)
            }
            else if(expr instanceof NameExpression || expr instanceof ExternalNameExpression)
            {
                sheetSource = expr instanceof NameExpression ? this : expr.source;
                exprCellCalc = this._getCellCalc(row,column,true);
                var refNameCalc = sheetSource._getNameCalc(expr.name,true);
                if(!refNameCalc)
                    return;
                this._buildAction(refNameCalc,actionPrefix,'CellListener').call(refNameCalc,exprCellCalc);
                this._dealWithExpression(row,column,sheetSource.getName(expr.name,row,column),actionPrefix)
            }
            else if(expr instanceof FunctionExpression)
            {
                var argCount = expr.argCount();
                for(var i = 0; i < argCount; i++)
                    this._dealWithExpression(row,column,expr.getArg(i),actionPrefix)
            }
        },
        linkExpression: function(row, column, expr)
        {
            this._dealWithExpression(row,column,expr,'add')
        },
        unlinkExpression: function(row, column, expr)
        {
            this._dealWithExpression(row,column,expr,'remove')
        },
        _dealWithCellExpression: function(row, column, rowCount, columnCount, action)
        {
            var dataModel = this._sheet._getCalcModel(this._sheetArea);
            for(var r = row; r !== -1 && r < row + rowCount; r = dataModel.nextNonNullRow(r))
                for(var c = column; c !== -1 && c < column + columnCount; c = dataModel.nextNonNullColumn(r,c))
                {
                    var expr = dataModel.getFormula(r,c);
                    if(expr)
                        action.call(this,r,c,expr)
                }
        },
        linkCellExpression: function(row, column, rowCount, columnCount)
        {
            this._dealWithCellExpression(row,column,rowCount,columnCount,this.linkExpression)
        },
        unlinkCellExpression: function(row, column, rowCount, columnCount)
        {
            this._dealWithCellExpression(row,column,rowCount,columnCount,this.unlinkExpression)
        },
        linkRowExpression: function(row, rowCount){},
        unlinkRowExpression: function(row, rowCount){},
        linkColumnExpression: function(col, colCount){},
        unlinkColumnExpression: function(col, colCount){},
        _addColumnsToDirty: function(col, colCount)
        {
            var dataModel = this._sheet._getCalcModel(this._sheetArea);
            if(dataModel._columnDataArray)
                for(var c = col; c !== -1 && c < col + colCount && c < dataModel._columnDataArray.length; c++)
                {
                    var node = dataModel._columnDataArray[c];
                    if(!node || !node.columnCalc && !node.formula)
                        continue;
                    var columnCalc = this._getColumnCalc(c,true);
                    this._controller.addDirtyColumn(columnCalc)
                }
        },
        _addRowsToDirty: function(row, rowCount)
        {
            var dataModel = this._sheet._getCalcModel(this._sheetArea);
            if(dataModel._rowDataArray)
                for(var r = row; r !== -1 && r < row + rowCount && r < dataModel._rowDataArray.length; r++)
                {
                    var node = dataModel._rowDataArray[r];
                    if(!node || !node.rowCalc && !node.formula)
                        continue;
                    var rowCalc = this._getRowCalc(r,true);
                    this._controller.addDirtyRow(rowCalc)
                }
        },
        _addCellsToDirty: function(row, col, rowCount, colCount)
        {
            var dataModel = this._sheet._getCalcModel(this._sheetArea);
            for(var r = row; r !== -1 && r < row + rowCount; r = dataModel.nextNonNullRow(r))
                for(var c = col; c !== -1 && c < col + colCount; c = dataModel.nextNonNullColumn(r,c))
                {
                    var node = dataModel.getNode(r,c,false);
                    if(!node || !node.cellCalc && !node.formula)
                        continue;
                    var cellCalc = this._getCellCalc(r,c,true);
                    this._controller.addDirtyCell(cellCalc)
                }
            this._addRowsToDirty(row,rowCount);
            this._addColumnsToDirty(col,colCount)
        },
        _addDependentsToAdjust: function(row, col, rowCount, colCount)
        {
            this._addDependents(row,col,rowCount,colCount,true,false)
        },
        _iterateEachCell: function(dataModel, row, column, rowCount, columnCount, setAdjust, setDirty)
        {
            for(var r = row; r !== -1 && r < row + rowCount; r = dataModel.nextNonNullRow(r))
                for(var c = column; c !== -1 && c < column + columnCount; c = dataModel.nextNonNullColumn(r,c))
                {
                    var cellCalc = this._getCellCalc(r,c,false);
                    if(cellCalc)
                    {
                        if(setAdjust)
                            cellCalc.addListenersToAdjust();
                        if(setDirty)
                            cellCalc.addListenersToDirty()
                    }
                }
        },
        _iterateEachRow: function(dataModel, row, rowCount, setAdjust, setDirty)
        {
            if(dataModel._rowDataArray)
                for(var r = row; r !== -1 && r < row + rowCount && r < dataModel._rowDataArray.length; r++)
                {
                    var node = dataModel._rowDataArray[r];
                    if(!node || !node.rowCalc)
                        continue;
                    var rowCalc = node.rowCalc;
                    if(setAdjust)
                        rowCalc.addListenersToAdjust();
                    if(setDirty)
                        rowCalc.addListenersToDirty()
                }
        },
        _iterateEachColumn: function(dataModel, column, columnCount, setAdjust, setDirty)
        {
            if(dataModel._columnDataArray)
                for(var c = column; c !== -1 && c < column + columnCount && c < dataModel._columnDataArray.length; c++)
                {
                    var node = dataModel._columnDataArray[c];
                    if(!node || !node.columnCalc)
                        continue;
                    var columnCalc = node.columnCalc;
                    if(setAdjust)
                        columnCalc.addListenersToAdjust();
                    if(setDirty)
                        columnCalc.addListenersToDirty()
                }
        },
        _addDependents: function(row, column, rowCount, columnCount, setAdjust, setDirty)
        {
            if(setAdjust || setDirty)
            {
                var dataModel = this._sheet._getCalcModel(this._sheetArea);
                if(0 <= row && 0 <= column)
                {
                    this._iterateEachCell(dataModel,row,column,rowCount,columnCount,setAdjust,setDirty);
                    this._iterateEachRow(dataModel,row,rowCount,setAdjust,setDirty);
                    this._iterateEachColumn(dataModel,column,columnCount,setAdjust,setDirty);
                    if(this._rangeCalc)
                    {
                        if(setAdjust)
                            this._rangeCalc.addListenersToAdjust(row,column,rowCount,columnCount);
                        if(setDirty)
                            this._rangeCalc.addListenersToDirty(row,column,rowCount,columnCount)
                    }
                }
                else if(0 <= row && column === -1)
                {
                    this._iterateEachCell(dataModel,row,0,rowCount,this.getColumnCount(),setAdjust,setDirty);
                    this._iterateEachRow(dataModel,row,rowCount,setAdjust,setDirty);
                    if(this._rangeCalc)
                    {
                        if(setAdjust)
                            this._rangeCalc.addListenersToAdjust(row,column,rowCount,columnCount);
                        if(setDirty)
                            this._rangeCalc.addListenersToDirty(row,column,rowCount,columnCount)
                    }
                }
                else if(row === -1 && 0 <= column)
                {
                    this._iterateEachCell(dataModel,0,column,this.getRowCount(),columnCount,setAdjust,setDirty);
                    this._iterateEachColumn(dataModel,column,columnCount,setAdjust,setDirty);
                    if(this._rangeCalc)
                    {
                        if(setAdjust)
                            this._rangeCalc.addListenersToAdjust(row,column,rowCount,columnCount);
                        if(setDirty)
                            this._rangeCalc.addListenersToDirty(row,column,rowCount,columnCount)
                    }
                }
                else if(row === -1 && column === -1)
                {
                    var rc = this.getRowCount(),
                        cc = this.getColumnCount();
                    this._iterateEachCell(dataModel,0,0,rc,cc,setAdjust,setDirty);
                    this._iterateEachRow(dataModel,0,rc,setAdjust,setDirty);
                    this._iterateEachColumn(dataModel,0,cc,setAdjust,setDirty);
                    if(this._rangeCalc)
                    {
                        if(setAdjust)
                            this._rangeCalc.addListenersToAdjust(row,column,rowCount,columnCount);
                        if(setDirty)
                            this._rangeCalc.addListenersToDirty(row,column,rowCount,columnCount)
                    }
                }
            }
        },
        _addCellsToAdjust: function(row, column, rowCount, columnCount)
        {
            var dataModel = this._sheet._getCalcModel(this._sheetArea);
            for(var r = row; r !== -1 && r < row + rowCount; r = dataModel.nextNonNullRow(r))
                for(var c = column; c !== -1 && c < column + columnCount; c = dataModel.nextNonNullColumn(r,c))
                {
                    var node = dataModel.getNode(r,c,false);
                    if(!node || !node.cellCalc && !node.formula)
                        continue;
                    if(node.cellCalc)
                        node.cellCalc = null;
                    var cellCalc = this._getCellCalc(r,c,true);
                    this._controller.addAdjustCell(cellCalc)
                }
        },
        _addColumnsToAdjust: function(col, colCount)
        {
            var dataModel = this._sheet._getCalcModel(this._sheetArea);
            if(dataModel._columnDataArray)
                for(var c = col; c !== -1 && c < col + colCount && c < dataModel._columnDataArray.length; c++)
                {
                    var node = dataModel._columnDataArray[c];
                    if(!node || !node.columnCalc && !node.formula)
                        continue;
                    var columnCalc = this._getColumnCalc(c,true);
                    this._controller.addAdjustColumn(columnCalc)
                }
        },
        _addRowsToAdjust: function(row, rowCount)
        {
            var dataModel = this._sheet._getCalcModel(this._sheetArea);
            if(dataModel._rowDataArray)
                for(var r = row; r !== -1 && r < row + rowCount && r < dataModel._rowDataArray.length; r++)
                {
                    var node = dataModel._rowDataArray[r];
                    if(!node || !node.rowCalc && !node.formula)
                        continue;
                    var rowCalc = this._getRowCalc(r,true);
                    this._controller.addAdjustRow(rowCalc)
                }
        },
        _getCellCalc: function(row, col, create)
        {
            var cell = null;
            if(row >= 0 && col >= 0)
            {
                var calcModel = this._sheet._getCalcModel(this._sheetArea);
                var node = calcModel.getNode(row,col,create);
                if(node)
                {
                    cell = node.cellCalc;
                    if(!cell && create)
                        node.cellCalc = cell = new CellCalc(this,row,col)
                }
            }
            return cell
        },
        _getRowCalc: function(row, create)
        {
            var calc = null;
            if(row >= 0)
            {
                var calcModel = this._sheet._getCalcModel(this._sheetArea);
                var node = calcModel.getNode(row,-1,create);
                if(node)
                {
                    calc = node.rowCalc;
                    if(!calc && create)
                        node.rowCalc = calc = new RowCalc(this,row)
                }
            }
            return calc
        },
        _getColumnCalc: function(column, create)
        {
            var calc = null;
            if(column >= 0)
            {
                var calcModel = this._sheet._getCalcModel(this._sheetArea);
                var node = calcModel.getNode(-1,column,create);
                if(node)
                {
                    calc = node.columnCalc;
                    if(!calc && create)
                        node.columnCalc = calc = new ColumnCalc(this,column)
                }
            }
            return calc
        },
        _getRangeCalc: function(create)
        {
            var calcModel = this._sheet._getCalcModel(this._sheetArea);
            if(!calcModel._rangeCalc && create)
                calcModel._rangeCalc = new RangeCalc(this);
            return calcModel._rangeCalc
        },
        _getNameCalc: function(name, create)
        {
            var nameCalc = null,
                calcModel;
            if(this._sheet.getCustomName(name))
            {
                calcModel = this._sheet._getCalcModel(this._sheetArea);
                if(!calcModel._names)
                    calcModel._names = {};
                nameCalc = calcModel._names[name];
                if(!nameCalc && create)
                    nameCalc = calcModel._names[name] = new NameCalc(name)
            }
            else
            {
                var calcService = this._sheet.getCalcService();
                if(calcService && calcService._getNameCalc)
                    nameCalc = calcService._getNameCalc(name,create);
                if(!nameCalc)
                {
                    calcModel = this._sheet._getCalcModel(this._sheetArea);
                    if(!calcModel._names)
                        calcModel._names = {};
                    nameCalc = calcModel._names[name];
                    if(!nameCalc && create)
                        nameCalc = calcModel._names[name] = new NameCalc(name)
                }
            }
            return nameCalc
        },
        recalculateCell: function(row, column)
        {
            var expr = this.getExpression(row,column);
            if(expr)
            {
                var oldValue = this._sheet.getValue(row,column);
                var value = this._sheet.getCalcService().evaluateParsedFormula(this,expr,row,column);
                if(oldValue === value)
                    return;
                else if(oldValue instanceof Date && value instanceof Date)
                    if(oldValue.valueOf() === value.valueOf())
                        return;
                this._sheet._getModel().setValue(row,column,value)
            }
        },
        shareCalculations: function(sheetSource)
        {
            if(this._controller === sheetSource._controller)
                return;
            this._controller.detachDataModel(this);
            if(this._controller._dataModels.length > 0)
            {
                var dm = this._controller._dataModels[0];
                if(dm)
                    dm.shareCalculations(sheetSource)
            }
            sheetSource._controller.addCustomFunctions(this._controller._customFunctions);
            sheetSource._controller.addCustomNames(this._controller._customNames);
            this._controller = sheetSource._controller;
            this._controller.attachDataModel(this)
        },
        unshareCalculations: function()
        {
            this._addCellsToAdjust(0,0,this.getRowCount(),this.getColumnCount());
            this._addRowsToAdjust(0,this.getRowCount());
            this._addColumnsToAdjust(0,this.getColumnCount());
            this._addDependentsToAdjust(-1,-1,-1,-1);
            this._controller.adjustFormulasOnRemoveSheet(this);
            if(this._controller.autoCalculation)
                this._controller.recalculate();
            this._controller.detachDataModel(this);
            this._controller = new Controller;
            this._controller.attachDataModel(this)
        }
    };
    Calc._SheetSource = _SheetSource;
    function Service(context)
    {
        this.context = context;
        this.parser = new Parser;
        this.evaluator = new Evaluator;
        this.useR1C1 = false;
        this._suspended = 0
    }
    Service.prototype = {
        contextChanged: function()
        {
            this.cachedContexts = null
        },
        _getExternalSource: function(bookName, sheetName)
        {
            if(!this.context)
                return null;
            if(bookName && (!(this.context instanceof ui.GcSpread) || this.context.name !== bookName))
                return null;
            var sc = this.getSheetContexts();
            if(sc && sc.length > 0)
                for(var i = 0; i < sc.length; i++)
                {
                    var ctx = sc[i];
                    if(compareStringIgnoreCase(ctx.name,sheetName))
                        return ctx.target._getSheetSource()
                }
            return null
        },
        _getExternalSourceToken: function(source)
        {
            return source._sheet._name
        },
        _getParserContext: function(row, col)
        {
            var parserContext = new ParserContext(this.useR1C1,row,col,null);
            var self = this;
            parserContext.getExternalSource = function(book, sheet)
            {
                return self._getExternalSource(book,sheet)
            };
            parserContext.getExternalSourceToken = function(source)
            {
                return self._getExternalSourceToken(source)
            };
            return parserContext
        },
        _getEvaluatorContext4Cell: function(sheet, row, col, arrayFormula)
        {
            return new EvaluateContext(sheet._getSheetSource(),arrayFormula,row,col)
        },
        _getEvaluatorContext4Range: function(sheet, row, col, rowCount, colCount, arrayFormula)
        {
            return new EvaluateContext(sheet._getSheetSource(),arrayFormula,row,col,rowCount,colCount)
        },
        getSheetContexts: function()
        {
            if(!this.cachedContexts)
                this.createSheetContexts();
            return this.cachedContexts
        },
        createSheetContexts: function()
        {
            if(!this.context)
                return;
            this.cachedContexts = this.context._getCalcContexts()
        },
        parse: function(formula, row, col)
        {
            var parseContext = this._getParserContext(row,col);
            parseContext.row = row;
            parseContext.column = col;
            return this.parser.parse(formula,parseContext)
        },
        unparse: function(expr, row, col)
        {
            var parseContext = this._getParserContext(row,col);
            parseContext.row = row;
            parseContext.column = col;
            return this.parser.unparse(expr,parseContext)
        },
        evaluateParsedFormula: function(sheetSource, expr, row, col, isArrayFormula)
        {
            var evalContext = this._getEvaluatorContext4Cell(sheetSource._sheet,row,col,isArrayFormula);
            var val = this.evaluator.evaluateExpression(expr,evalContext);
            if(isArrayFormula)
                return val;
            if(val instanceof CalcReference)
            {
                var rc = val.getRowCount(0),
                    cc = val.getColumnCount(0);
                if(val.getRangeCount() <= 0 || rc <= 0 || cc <= 0)
                    return CalcErrorsReference;
                else if(val.getRangeCount() !== 1 || rc > 1 && cc > 1)
                    return CalcErrorsValue;
                else
                    try
                    {
                        var ro = row - val.getRow(0),
                            co = col - val.getColumn(0);
                        if(rc === 1 && cc === 1)
                            return val.getValue(0,0,0);
                        else if(rc === 1 && cc > 1 && co >= 0 && co < cc)
                            return val.getValue(0,0,co);
                        else if(rc > 1 && cc === 1 && ro >= 0 && ro < rc)
                            return val.getValue(0,ro,0);
                        else
                            return CalcErrorsValue
                    }
                    catch(iex)
                    {
                        return CalcErrorsValue
                    }
            }
            else if(val instanceof CalcArray)
                return val.getValueByIndex(0);
            return val
        },
        evaluate: function(sourceContext, formula, row, col)
        {
            return this.evaluator.evaluateFormula(formula,this._getParserContext(row,col),this._getEvaluatorContext4Cell(sourceContext._sheet,row,col,false))
        },
        recalculate: function(context, row, col)
        {
            var sheet = context.target;
            if(sheet)
            {
                var sheetSource = sheet._getSheetSource();
                if(sheetSource)
                {
                    sheetSource._addCellsToDirty(row,col,1,1);
                    if(this._suspended === 0)
                        sheetSource._controller.recalculate()
                }
            }
        },
        recalculateAll: function(dirtyAll)
        {
            var contexts = this.getSheetContexts();
            if(contexts)
                for(var i = 0; i < contexts.length; i++)
                {
                    var sheet = contexts[i].target;
                    if(sheet)
                    {
                        var sheetSource = sheet._getSheetSource();
                        if(sheetSource)
                        {
                            if(dirtyAll !== false)
                            {
                                var colCount = sheet.getColumnCount();
                                var rowCount = sheet.getRowCount();
                                sheetSource._addColumnsToDirty(0,colCount);
                                sheetSource._addRowsToDirty(0,rowCount);
                                sheetSource._addCellsToDirty(0,0,rowCount,colCount)
                            }
                            if(this._suspended === 0)
                                sheetSource._controller.recalculate()
                        }
                    }
                }
        },
        suspend: function()
        {
            this._suspended++
        },
        resume: function()
        {
            this._suspended--;
            if(this._suspended < 0)
                this._suspended = 0;
            if(this._suspended === 0)
                this.recalculateAll(false)
        }
    };
    Calc.Service = Service
})(this,jQuery);
(function(window, $)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    var const_undefined = "undefined";
    if(typeof GrapeCity === const_undefined)
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === const_undefined)
        GrapeCity.UI = {}
})(window,jQuery);
(function(window)
{
    var prop_backColor = 'backColor',
        prop_foreColor = 'foreColor',
        prop_hAlign = 'hAlign',
        prop_vAlign = 'vAlign',
        prop_themeFont = 'themeFont',
        prop_font = 'font',
        prop_formatter = 'formatter',
        prop_borderLeft = 'borderLeft',
        prop_borderTop = 'borderTop',
        prop_borderRight = 'borderRight',
        prop_borderBottom = 'borderBottom',
        prop_locked = 'locked',
        prop_textIndent = 'textIndent',
        prop_wordWrap = 'wordWrap',
        prop_shrinkToFit = 'shrinkToFit',
        prop_backgroundImage = 'backgroundImage',
        prop_validator = "validator",
        prop_cellType = "cellType",
        prop_bindingPath = "bindingPath";
    var GrapeCity = window.GrapeCity;
    var UI = GrapeCity.UI;
    UI.Cell = function(sheet, row, col, sheetArea)
    {
        this.sheet = sheet;
        this.row = row;
        this.row2 = row;
        this.col = col;
        this.col2 = col;
        if(sheetArea === undefined || sheetArea === null)
            sheetArea = UI.SheetArea.viewport;
        this.sheetArea = sheetArea
    };
    UI.Cell.prototype = {
        value: function(value)
        {
            if(arguments.length === 0)
                return this.sheet.getValue(this.row,this.col,this.sheetArea);
            else
            {
                for(var r = this.row; r <= this.row2; r++)
                    for(var c = this.col; c <= this.col2; c++)
                        if(this._isValidIndex(r,c))
                            this.sheet.setValue(r,c,value,this.sheetArea);
                return this
            }
        },
        text: function(value)
        {
            if(arguments.length === 0)
                return this.sheet.getText(this.row,this.col,this.sheetArea);
            else
            {
                for(var r = this.row; r <= this.row2; r++)
                    for(var c = this.col; c <= this.col2; c++)
                        if(this._isValidIndex(r,c))
                            this.sheet.setText(r,c,value,this.sheetArea);
                return this
            }
        },
        formula: function(value)
        {
            if(arguments.length === 0)
                return this.sheet.getFormula(this.row,this.col,this.sheetArea);
            else
            {
                for(var r = this.row; r <= this.row2; r++)
                    for(var c = this.col; c <= this.col2; c++)
                        if(this._isValidIndex(r,c))
                            this.sheet.setFormula(r,c,value,this.sheetArea);
                return this
            }
        },
        bindingPath: function(value)
        {
            if(arguments.length === 0)
                return this.sheet.getBindingPath(this.row,this.col,this.sheetArea);
            else
            {
                for(var r = this.row; r <= this.row2; r++)
                    for(var c = this.col; c <= this.col2; c++)
                        if(this._isValidIndex(r,c))
                            this.sheet.setBindingPath(r,c,value,this.sheetArea);
                return this
            }
        },
        dataValidator: function(value)
        {
            return arguments.length == 0 ? this._getStyleProperty(prop_validator) : this._setStyleProperty(prop_validator,value)
        },
        cellType: function(value)
        {
            return arguments.length == 0 ? this._getStyleProperty(prop_cellType) : this._setStyleProperty(prop_cellType,value)
        },
        backgroundImage: function(value)
        {
            return arguments.length == 0 ? this._getStyleProperty(prop_backgroundImage) : this._setStyleProperty(prop_backgroundImage,value)
        },
        backColor: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_backColor) : this._setStyleProperty(prop_backColor,value)
        },
        foreColor: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_foreColor) : this._setStyleProperty(prop_foreColor,value)
        },
        hAlign: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_hAlign) : this._setStyleProperty(prop_hAlign,value)
        },
        vAlign: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_vAlign) : this._setStyleProperty(prop_vAlign,value)
        },
        themeFont: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_themeFont) : this._setStyleProperty(prop_themeFont,value)
        },
        font: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_font) : this._setStyleProperty(prop_font,value)
        },
        formatter: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_formatter) : this._setStyleProperty(prop_formatter,value)
        },
        borderLeft: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_borderLeft) : this._setStyleProperty(prop_borderLeft,value)
        },
        borderTop: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_borderTop) : this._setStyleProperty(prop_borderTop,value)
        },
        borderRight: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_borderRight) : this._setStyleProperty(prop_borderRight,value)
        },
        borderBottom: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_borderBottom) : this._setStyleProperty(prop_borderBottom,value)
        },
        locked: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_locked) : this._setStyleProperty(prop_locked,value)
        },
        textIndent: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_textIndent) : this._setStyleProperty(prop_textIndent,value)
        },
        wordWrap: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_wordWrap) : this._setStyleProperty(prop_wordWrap,value)
        },
        shrinkToFit: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_shrinkToFit) : this._setStyleProperty(prop_shrinkToFit,value)
        },
        clearStyleProperty: function(propertyName)
        {
            for(var r = this.row; r <= this.row2; r++)
                for(var c = this.col; c <= this.col2; c++)
                    if(this._isValidIndex(r,c))
                    {
                        var style = this.sheet.getStyle(r,c,this.sheetArea);
                        if(style)
                            style.clear(propertyName)
                    }
        },
        _getStyleProperty: function(propertyName)
        {
            var style = this.sheet.getActualStyle(this.row,this.col,this.sheetArea);
            return style ? style[propertyName] : undefined
        },
        _setStyleProperty: function(propertyName, value)
        {
            for(var r = this.row; r <= this.row2; r++)
                for(var c = this.col; c <= this.col2; c++)
                    if(this._isValidIndex(r,c))
                    {
                        var style = this.sheet.getStyle(r,c,this.sheetArea);
                        if(!style)
                            style = new UI.Style;
                        style[propertyName] = value;
                        this.sheet.setStyle(r,c,style,this.sheetArea)
                    }
            return this
        },
        _isValidIndex: function(row, col)
        {
            if(this.sheet)
            {
                var rowCount = this.sheet.getRowCount(this.sheetArea);
                var colCount = this.sheet.getColumnCount(this.sheetArea);
                if(0 <= row && row < rowCount && 0 <= col && col < colCount)
                    return true
            }
            return false
        }
    };
    UI.Row = function(sheet, index, sheetArea)
    {
        this.sheet = sheet;
        this.index = index;
        this.index2 = index;
        if(sheetArea === undefined || sheetArea === null)
            sheetArea = UI.SheetArea.viewport;
        this.sheetArea = sheetArea
    };
    UI.Row.prototype = {
        height: function(value)
        {
            if(arguments.length === 0)
                return this.sheet.getRowHeight(this.index,this.sheetArea);
            else
            {
                for(var r = this.index; r <= this.index2; r++)
                    if(this._isValidIndex(r))
                        this.sheet.setRowHeight(r,value,this.sheetArea);
                return this
            }
        },
        visible: function(value)
        {
            if(arguments.length === 0)
                return this.sheet.getRowVisible(this.index,this.sheetArea);
            else
            {
                for(var r = this.index; r <= this.index2; r++)
                    if(this._isValidIndex(r))
                        this.sheet.setRowVisible(r,value,this.sheetArea);
                return this
            }
        },
        resizable: function(value)
        {
            if(arguments.length === 0)
                return this.sheet.getRowResizable(this.index,this.sheetArea);
            else
            {
                for(var r = this.index; r <= this.index2; r++)
                    if(this._isValidIndex(r))
                        this.sheet.setRowResizable(r,value,this.sheetArea);
                return this
            }
        },
        dataValidator: function(value)
        {
            return arguments.length == 0 ? this._getStyleProperty(prop_validator) : this._setStyleProperty(prop_validator,value)
        },
        cellType: function(value)
        {
            return arguments.length == 0 ? this._getStyleProperty(prop_cellType) : this._setStyleProperty(prop_cellType,value)
        },
        backgroundImage: function(value)
        {
            return arguments.length == 0 ? this._getStyleProperty(prop_backgroundImage) : this._setStyleProperty(prop_backgroundImage,value)
        },
        backColor: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_backColor) : this._setStyleProperty(prop_backColor,value)
        },
        foreColor: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_foreColor) : this._setStyleProperty(prop_foreColor,value)
        },
        hAlign: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_hAlign) : this._setStyleProperty(prop_hAlign,value)
        },
        vAlign: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_vAlign) : this._setStyleProperty(prop_vAlign,value)
        },
        themeFont: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_themeFont) : this._setStyleProperty(prop_themeFont,value)
        },
        font: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_font) : this._setStyleProperty(prop_font,value)
        },
        formatter: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_formatter) : this._setStyleProperty(prop_formatter,value)
        },
        borderLeft: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_borderLeft) : this._setStyleProperty(prop_borderLeft,value)
        },
        borderTop: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_borderTop) : this._setStyleProperty(prop_borderTop,value)
        },
        borderRight: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_borderRight) : this._setStyleProperty(prop_borderRight,value)
        },
        borderBottom: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_borderBottom) : this._setStyleProperty(prop_borderBottom,value)
        },
        locked: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_locked) : this._setStyleProperty(prop_locked,value)
        },
        textIndent: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_textIndent) : this._setStyleProperty(prop_textIndent,value)
        },
        wordWrap: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_wordWrap) : this._setStyleProperty(prop_wordWrap,value)
        },
        shrinkToFit: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_shrinkToFit) : this._setStyleProperty(prop_shrinkToFit,value)
        },
        clearStyleProperty: function(propertyName)
        {
            for(var r = this.index; r <= this.index2; r++)
                if(this._isValidIndex(r))
                {
                    var style = this.sheet.getStyle(r,-1,this.sheetArea);
                    if(style)
                    {
                        style.clear(propertyName);
                        this.sheet._trigger(UI.Events.RowChanged,{
                            sheet: this.sheet,
                            sheetName: this.sheet._name,
                            row: r,
                            sheetArea: this.sheetArea,
                            propertyName: propertyName
                        })
                    }
                }
        },
        _getStyleProperty: function(propertyName)
        {
            var style = this.sheet.getStyle(this.index,-1,this.sheetArea);
            return style ? style[propertyName] : undefined
        },
        _setStyleProperty: function(propertyName, value)
        {
            for(var r = this.index; r <= this.index2; r++)
                if(this._isValidIndex(r))
                {
                    var style = this.sheet.getStyle(r,-1,this.sheetArea);
                    if(!style)
                        style = new UI.Style;
                    style[propertyName] = value;
                    this.sheet.setStyle(r,-1,style,this.sheetArea)
                }
            return this
        },
        _isValidIndex: function(row)
        {
            if(this.sheet)
            {
                var rowCount = this.sheet.getRowCount(this.sheetArea);
                if(0 <= row && row < rowCount)
                    return true
            }
            return false
        }
    };
    UI.Col = function(sheet, index, sheetArea)
    {
        this.sheet = sheet;
        this.index = index;
        this.index2 = index;
        if(sheetArea === undefined || sheetArea === null)
            sheetArea = UI.SheetArea.viewport;
        this.sheetArea = sheetArea
    };
    UI.Col.prototype = {
        width: function(value)
        {
            if(arguments.length === 0)
                return this.sheet.getColumnWidth(this.index,this.sheetArea);
            else
            {
                for(var c = this.index; c <= this.index2; c++)
                    if(this._isValidIndex(c))
                        this.sheet.setColumnWidth(c,value,this.sheetArea);
                return this
            }
        },
        visible: function(value)
        {
            if(arguments.length === 0)
                return this.sheet.getColumnVisible(this.index,this.sheetArea);
            else
            {
                for(var c = this.index; c <= this.index2; c++)
                    if(this._isValidIndex(c))
                        this.sheet.setColumnVisible(c,value,this.sheetArea);
                return this
            }
        },
        resizable: function(value)
        {
            if(arguments.length === 0)
                return this.sheet.getColumnResizable(this.index,this.sheetArea);
            else
            {
                for(var c = this.index; c <= this.index2; c++)
                    if(this._isValidIndex(c))
                        this.sheet.setColumnResizable(c,value,this.sheetArea);
                return this
            }
        },
        dataValidator: function(value)
        {
            return arguments.length == 0 ? this._getStyleProperty(prop_validator) : this._setStyleProperty(prop_validator,value)
        },
        cellType: function(value)
        {
            return arguments.length == 0 ? this._getStyleProperty(prop_cellType) : this._setStyleProperty(prop_cellType,value)
        },
        backgroundImage: function(value)
        {
            return arguments.length == 0 ? this._getStyleProperty(prop_backgroundImage) : this._setStyleProperty(prop_backgroundImage,value)
        },
        backColor: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_backColor) : this._setStyleProperty(prop_backColor,value)
        },
        foreColor: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_foreColor) : this._setStyleProperty(prop_foreColor,value)
        },
        hAlign: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_hAlign) : this._setStyleProperty(prop_hAlign,value)
        },
        vAlign: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_vAlign) : this._setStyleProperty(prop_vAlign,value)
        },
        themeFont: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_themeFont) : this._setStyleProperty(prop_themeFont,value)
        },
        font: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_font) : this._setStyleProperty(prop_font,value)
        },
        formatter: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_formatter) : this._setStyleProperty(prop_formatter,value)
        },
        borderLeft: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_borderLeft) : this._setStyleProperty(prop_borderLeft,value)
        },
        borderTop: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_borderTop) : this._setStyleProperty(prop_borderTop,value)
        },
        borderRight: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_borderRight) : this._setStyleProperty(prop_borderRight,value)
        },
        borderBottom: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_borderBottom) : this._setStyleProperty(prop_borderBottom,value)
        },
        locked: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_locked) : this._setStyleProperty(prop_locked,value)
        },
        textIndent: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_textIndent) : this._setStyleProperty(prop_textIndent,value)
        },
        wordWrap: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_wordWrap) : this._setStyleProperty(prop_wordWrap,value)
        },
        shrinkToFit: function(value)
        {
            return arguments.length === 0 ? this._getStyleProperty(prop_shrinkToFit) : this._setStyleProperty(prop_shrinkToFit,value)
        },
        clearStyleProperty: function(propertyName)
        {
            for(var c = this.index; c <= this.index2; c++)
                if(this._isValidIndex(c))
                {
                    var style = this.sheet.getStyle(-1,c,this.sheetArea);
                    if(style)
                    {
                        style.clear(propertyName);
                        this.sheet._trigger(UI.Events.ColumnChanged,{
                            sheet: this.sheet,
                            sheetName: this.sheet._name,
                            col: c,
                            sheetArea: this.sheetArea,
                            propertyName: propertyName
                        })
                    }
                }
        },
        _getStyleProperty: function(propertyName)
        {
            var style = this.sheet.getStyle(-1,this.index,this.sheetArea);
            return style ? style[propertyName] : undefined
        },
        _setStyleProperty: function(propertyName, value)
        {
            for(var c = this.index; c <= this.index2; c++)
                if(this._isValidIndex(c))
                {
                    var style = this.sheet.getStyle(-1,c,this.sheetArea);
                    if(!style)
                        style = new UI.Style;
                    style[propertyName] = value;
                    this.sheet.setStyle(-1,c,style,this.sheetArea)
                }
            return this
        },
        _isValidIndex: function(col)
        {
            if(this.sheet)
            {
                var colCount = this.sheet.getColumnCount(this.sheetArea);
                if(0 <= col && col < colCount)
                    return true
            }
            return false
        }
    }
})(window);
(function($)
{
    "use strict";;
    var uiSuperPanelClasses = "wijmo-wijsuperpanel " + "ui-widget " + "ui-widget-content",
        rounderClass = "ui-corner-all",
        uiStateDisabled = "ui-state-disabled",
        uiStateHover = "ui-state-hover",
        uiStateActive = "ui-state-active",
        uiStateDefault = "ui-state-default",
        scrollerHandle = "wijmo-wijsuperpanel-handle",
        hbarContainerCSS = "wijmo-wijsuperpanel-hbarcontainer",
        vbarContainerCSS = "wijmo-wijsuperpanel-vbarcontainer",
        innerElementHtml = "<div class='wijmo-wijsuperpanel-statecontainer'>" + "<div class='wijmo-wijsuperpanel-contentwrapper'>" + "<div class='wijmo-wijsuperpanel-templateouterwrapper'></div>" + "</div>" + "</div>",
        hbarHtml = "<div class='wijmo-wijsuperpanel-hbarcontainer ui-widget-header'>" + "<div class='wijmo-wijsuperpanel-handle ui-state-default ui-corner-" + "all'><span class='ui-icon ui-icon-grip-solid-vertical'></span></div>" + "<div class='wijmo-wijsuperpanel-hbar-buttonleft ui-state-default " + "ui-corner-bl'><span class='ui-icon ui-icon-triangle-1-w'></span></div>" + "<div class='wijmo-wijsuperpanel-hbar-buttonright ui-state-default " + "ui-corner-br'><span class='ui-icon ui-icon-triangle-1-e'></span></div>" + "</div>",
        vbarHtml = "<div class='wijmo-wijsuperpanel-vbarcontainer ui-widget-header'>" + "<div class='wijmo-wijsuperpanel-handle ui-state-default ui-corner-all'" + "><span class='ui-icon ui-icon-grip-solid-horizontal'></span></div>" + "<div class='wijmo-wijsuperpanel-vbar-buttontop ui-state-default " + "ui-corner-tr'><span class='ui-icon ui-icon-triangle-1-n'></span></div>" + "<div class='wijmo-wijsuperpanel-vbar-buttonbottom ui-state-default " + "ui-corner-br'><span class='ui-icon ui-icon-triangle-1-s'></span></div>" + "</div>",
        hButtons = "<div class='ui-state-default wijmo-wijsuperpanel-button " + "wijmo-wijsuperpanel-buttonleft'><span class='ui-icon " + "ui-icon-carat-1-w'></span></div><div class='ui-state-default" + " wijmo-wijsuperpanel-button wijmo-wijsuperpanel-buttonright'>" + "<span class='ui-icon ui-icon-carat-1-e'></span></div>",
        vButtons = "<div class='ui-state-default wijmo-wijsuperpanel-button" + " wijmo-wijsuperpanel-buttontop'><span class='ui-icon ui-icon-carat-1-n'>" + "</span></div><div class='ui-state-default wijmo-wijsuperpanel-button" + " wijmo-wijsuperpanel-buttonbottom'><span class='ui-icon" + " ui-icon-carat-1-s'></span></div>";
    $.widget("wijmo.wijspreadpanelex",{
        options: {
            allowResize: false,
            autoRefresh: false,
            animationOptions: {
                queue: false,
                disabled: false,
                duration: 250,
                easing: undefined
            },
            hScrollerActivating: null,
            hScroller: {
                scrollBarPosition: "bottom",
                scrollBarVisibility: "auto",
                scrollMode: "scrollBar",
                scrollValue: null,
                scrollMax: 100,
                scrollMin: 0,
                scrollLargeChange: null,
                scrollSmallChange: null,
                scrollMinDragLength: 6,
                increaseButtonPosition: null,
                decreaseButtonPosition: null,
                hoverEdgeSpan: 20,
                firstStepChangeFix: 0
            },
            keyboardSupport: false,
            keyDownInterval: 100,
            mouseWheelSupport: true,
            bubbleScrollingEvent: true,
            resizableOptions: {
                handles: "all",
                helper: "ui-widget-content wijmo-wijsuperpanel-helper"
            },
            resized: null,
            dragStop: null,
            painted: null,
            scrolling: null,
            scroll: null,
            scrolled: null,
            showRounder: true,
            vScrollerActivating: null,
            vScroller: {
                scrollBarPosition: "right",
                scrollBarVisibility: "auto",
                scrollMode: "scrollBar",
                scrollValue: null,
                scrollMax: 100,
                scrollMin: 0,
                scrollLargeChange: null,
                scrollSmallChange: null,
                scrollMinDragLength: 6,
                increaseButtonPosition: null,
                decreaseButtonPosition: null,
                hoverEdgeSpan: 20,
                firstStepChangeFix: 0
            }
        },
        _setOption: function(key, value)
        {
            var self = this,
                o = self.options,
                f = self._fields(),
                hd = f.hbarDrag,
                vd = f.vbarDrag,
                r = f.resizer;
            if(key === "animationOptions")
                value = $.extend(o.animationOptions,value);
            else if(key === "hScroller")
            {
                if(value.scrollLargeChange !== undefined && value.scrollLargeChange !== null)
                    self._autoHLarge = false;
                value = $.extend(o.hScroller,value)
            }
            else if(key === "vScroller")
            {
                if(value.scrollLargeChange !== undefined && value.scrollLargeChange !== null)
                    self._autoVLarge = false;
                value = $.extend(o.vScroller,value)
            }
            else if(key === "resizableOptions")
                value = $.extend(self.resizableOptions,value);
            $.Widget.prototype._setOption.apply(self,arguments);
            if($.isPlainObject(value))
                self.options[key] = value;
            switch(key)
            {
                case"allowResize":
                    self._initResizer();
                    break;
                case"disabled":
                    if(value)
                    {
                        if(hd !== undefined)
                            hd.draggable("disable");
                        if(vd !== undefined)
                            vd.draggable("disable");
                        if(r !== undefined)
                            r.resizable("disable")
                    }
                    else
                    {
                        if(hd !== undefined)
                            hd.draggable("enable");
                        if(vd !== undefined)
                            vd.draggable("enable");
                        if(r !== undefined)
                            r.resizable("enable")
                    }
                    break;
                case"mouseWheelSupport":
                case"keyboardSupport":
                    self._bindElementEvents(self,f,self.element,o);
                    break
            }
            return self
        },
        _create: function()
        {
            var self = this,
                o = self.options;
            o.vScroller.dir = "v";
            o.hScroller.dir = "h";
            self.paintPanel();
            self._initResizer();
            if(self.options.disabled)
                self.disable();
            self._detectAutoRefresh()
        },
        _detectAutoRefresh: function()
        {
            var self = this,
                panels = $.wijmo.wijspreadpanelex.panels;
            if(panels === undefined)
            {
                panels = [];
                $.wijmo.wijspreadpanelex.panels = panels
            }
            panels.push(self);
            if(self.options.autoRefresh)
                if(!$.wijmo.wijspreadpanelex.setAutoRefreshInterval)
                {
                    $.wijmo.wijspreadpanelex.setAutoRefreshInterval = self._setAutoRefreshInterval;
                    $.wijmo.wijspreadpanelex.setAutoRefreshInterval()
                }
        },
        _setAutoRefreshInterval: function()
        {
            var interval = $.wijmo.wijspreadpanelex.autoRereshInterval,
                panels = $.wijmo.wijspreadpanelex.panels,
                intervalID = window.setInterval(function()
                {
                    window.clearInterval(intervalID);
                    var count = panels.length,
                        toContinue = false,
                        i,
                        panel,
                        mainElement,
                        autoRefresh,
                        ele,
                        mark;
                    for(i = 0; i < count; i++)
                    {
                        panel = panels[i];
                        mainElement = panel.element[0];
                        autoRefresh = panel.options.autoRefresh;
                        if(autoRefresh)
                            toContinue = true;
                        ele = panel.getContentElement();
                        mark = panel._paintedMark;
                        if(panel.options.autoRefresh && ele.is(":visible") && (mark === undefined || mark.width !== ele[0].offsetWidth || mark.height !== ele[0].offsetHeight || mark.mainWidth !== mainElement.offsetWidth || mark.mainHeight !== mainElement.offsetHeight))
                            panel.paintPanel()
                    }
                    if(toContinue)
                        window.setTimeout($.wijmo.wijspreadpanelex.setAutoRefreshInterval,0)
                },interval === undefined ? 500 : interval)
        },
        destroy: function()
        {
            var self = this,
                f = self._fields(),
                ele = self.element,
                buttons,
                templateWrapper;
            $.wijmo.wijspreadpanelex.panels = $.grep($.wijmo.wijspreadpanelex.panels,function(value)
            {
                return value !== self
            });
            if(!f.initialized)
                return;
            if(self._radiusKey)
                self.element.css(self._radiusKey,"");
            if(f.intervalID !== undefined)
            {
                window.clearInterval(f.intervalID);
                f.intervalID = undefined
            }
            if(f.resizer !== undefined)
                f.resizer.resizable("destroy");
            if(f.hbarContainer !== undefined)
            {
                f.hbarDrag.remove();
                f.hbarContainer.unbind("." + self.widgetName)
            }
            if(f.vbarContainer !== undefined)
            {
                f.vbarDrag.remove();
                f.vbarContainer.unbind("." + self.widgetName)
            }
            ele.unbind("." + self.widgetName);
            f.contentWrapper.unbind("." + self.widgetName);
            buttons = f.stateContainer.find(">.wijmo-wijsuperpanel-button");
            buttons.unbind("." + self.widgetName);
            templateWrapper = f.templateWrapper;
            templateWrapper.contents().each(function(index, e)
            {
                ele.append(e)
            });
            f.stateContainer.remove();
            if(f.tabindex)
                ele.removeAttr("tabindex");
            ele.removeClass(uiSuperPanelClasses + " " + rounderClass);
            $.Widget.prototype.destroy.apply(self,arguments)
        },
        _fields: function()
        {
            var self = this,
                ele = self.element,
                key = self.widgetName + "-fields",
                d = self._fieldsStore;
            if(d === undefined)
            {
                d = {};
                ele.data(key,d);
                self._fieldsStore = d
            }
            return d
        },
        _hasMode: function(scroller, mode)
        {
            var modes = scroller.scrollMode.split(",");
            modes = $.map(modes,function(n)
            {
                return $.trim(n).toLowerCase()
            });
            return $.inArray(mode.toLowerCase(),modes) > -1
        },
        _bindElementEvents: function(self, f, ele, o)
        {
            var hEdge = self._hasMode(o.hScroller,"edge"),
                vEdge = self._hasMode(o.vScroller,"edge"),
                wn = self.widgetName;
            if(hEdge || vEdge)
            {
                if(self._mousemoveBind === undefined)
                {
                    self._mousemoveBind = true;
                    ele.bind("mousemove." + wn,self,self._contentMouseMove);
                    ele.bind("mouseleave." + wn,null,function()
                    {
                        self._clearInterval()
                    })
                }
            }
            else
            {
                ele.unbind("mousemove",self._contentMouseMove);
                self._mousemoveBind = undefined
            }
            if(o.mouseWheelSupport)
            {
                if(self._mouseWheelBind === undefined)
                {
                    self._mouseWheelBind = true;
                    ele.bind("mousewheel." + wn,self,self._panelMouseWheel)
                }
            }
            else
            {
                self.element.unbind("mousewheel",self._panelMouseWheel);
                self._mouseWheelBind = undefined
            }
            if(o.keyboardSupport)
            {
                if(self._keyboardBind === undefined)
                {
                    self._keyboardBind = true;
                    ele.bind("keydown." + wn,self,self._panelKeyDown)
                }
            }
            else
            {
                ele.unbind("keydown",self._panelKeyDown);
                self._keyboardBind = undefined
            }
        },
        _dragStop: function(e, self, dir)
        {
            var data = {dragHandle: dir};
            self._trigger("dragStop",e,data)
        },
        _contentMouseMove: function(e)
        {
            var self = e.data,
                o = self.options,
                hScroller,
                vScroller,
                contentWrapper,
                f,
                hMode,
                vMode,
                mousePagePosition,
                off,
                left,
                top,
                hEdge,
                vEdge,
                innerHeight,
                innerWidth,
                dir;
            if(o.disabled)
                return;
            hScroller = o.hScroller;
            vScroller = o.vScroller;
            contentWrapper = $(e.currentTarget);
            f = self._fields();
            hMode = self._hasMode(hScroller,"edge");
            vMode = self._hasMode(vScroller,"edge");
            self._clearInterval();
            mousePagePosition = {
                X: e.pageX,
                Y: e.pageY
            };
            off = contentWrapper.offset();
            left = off.left;
            top = off.top;
            left = mousePagePosition.X - left;
            top = mousePagePosition.Y - top;
            hEdge = hScroller.hoverEdgeSpan;
            vEdge = vScroller.hoverEdgeSpan;
            innerHeight = contentWrapper.innerHeight();
            innerWidth = contentWrapper.innerWidth();
            dir = "";
            if(hMode)
            {
                if(left < hEdge)
                    dir = "left";
                if(left > innerWidth - hEdge)
                    dir = "right"
            }
            if(vMode)
            {
                if(top < vEdge)
                    dir = "top";
                if(top > innerHeight - vEdge)
                    dir = "bottom"
            }
            self._setScrollingInterval(f,dir,self,false)
        },
        _setScrollingInterval: function(f, dir, self, large)
        {
            var o = self.options;
            if(dir.length > 0)
                f.internalFuncID = window.setInterval(function()
                {
                    self._doScrolling(dir,self,large)
                },o.keyDownInterval)
        },
        _scrollButtonMouseOver: function(e)
        {
            var self = e.data,
                button;
            if(self.options.disabled)
                return;
            button = $(e.currentTarget);
            if(!button.hasClass(uiStateDisabled))
            {
                button.bind("mouseout." + self.widgetName,self,self._buttonMouseOut).bind("mousedown." + self.widgetName,self,self._buttonMouseDown).bind("mouseup." + self.widgetName,self,self._buttonMouseUp).addClass(uiStateHover);
                self._buttonScroll(button,self,"buttonshover")
            }
        },
        _buttonScroll: function(button, self, mode)
        {
            var dir = "",
                o = self.options,
                f = self._fields(),
                hMode = self._hasMode(o.hScroller,mode),
                vMode = self._hasMode(o.vScroller,mode);
            if(button.hasClass("wijmo-wijsuperpanel-buttonleft") && hMode)
                dir = "left";
            else if(button.hasClass("wijmo-wijsuperpanel-buttonright") && hMode)
                dir = "right";
            else if(button.hasClass("wijmo-wijsuperpanel-buttontop") && vMode)
                dir = "top";
            else if(button.hasClass("wijmo-wijsuperpanel-buttonbottom") && vMode)
                dir = "bottom";
            if(dir.length > 0)
            {
                self._clearInterval();
                self._doScrolling(dir,self,true);
                self._setScrollingInterval(f,dir,self,true)
            }
        },
        _buttonMouseDown: function(e)
        {
            var self = e.data,
                button;
            if(self.options.disabled)
                return;
            button = $(e.currentTarget);
            if(!button.hasClass(uiStateDisabled))
            {
                button.addClass(uiStateActive);
                self._buttonScroll(button,self,"buttons")
            }
        },
        _buttonMouseUp: function(e)
        {
            var self = e.data,
                button = $(e.currentTarget);
            button.removeClass("ui-state-active");
            self._clearInterval()
        },
        _buttonMouseOut: function(e)
        {
            var self = e.data,
                button = $(e.currentTarget);
            button.unbind("mouseout",self._buttonMouseOut).unbind("mousedown",self._buttonMouseDown).unbind("mouseup",self._buttonMouseUp).removeClass(uiStateHover).removeClass(uiStateActive);
            self._clearInterval()
        },
        _panelKeyDown: function(e)
        {
            var self = e.data,
                o = self.options,
                shift,
                keycode;
            if(!o.keyboardSupport || o.disabled)
                return;
            shift = e.shiftKey;
            keycode = e.keyCode;
            if(keycode === $.ui.keyCode.LEFT)
                self._doScrolling("left",self,shift);
            else if(keycode === $.ui.keyCode.RIGHT)
                self._doScrolling("right",self,shift);
            else if(keycode === $.ui.keyCode.UP)
                self._doScrolling("top",self,shift);
            else if(keycode === $.ui.keyCode.DOWN)
                self._doScrolling("bottom",self,shift);
            e.stopPropagation();
            e.preventDefault()
        },
        _draggingInternal: function(data, self, scroller, originalElement)
        {
            var dir = scroller.dir,
                h = dir === "h",
                key = h ? "left" : "top",
                left = data.position[key] - self._getScrollContainerPadding(key),
                track = self._getTrackLen(dir) - originalElement[h ? "outerWidth" : "outerHeight"](true),
                proportion = left / track,
                topValue = scroller.scrollMax - scroller.scrollLargeChange + 1,
                v = proportion * topValue,
                arg;
            if(v < scroller.scrollMin)
                v = scroller.scrollMin;
            if(v > topValue)
                v = topValue;
            arg = {
                oldValue: scroller.scrollValue,
                newValue: v,
                dir: dir
            };
            if(!self._scrolling(true,self,arg))
                return;
            scroller.scrollValue = v;
            self._setDragAndContentPosition(true,false,dir,"dragging")
        },
        _dragging: function(e, data, self)
        {
            var o = self.options,
                originalElement = $(e.target),
                p = originalElement.parent();
            if(p.hasClass(hbarContainerCSS))
                self._draggingInternal(data,self,o.hScroller,originalElement);
            else
                self._draggingInternal(data,self,o.vScroller,originalElement)
        },
        _panelMouseWheel: function(e, delta)
        {
            var self = e.data,
                o = self.options,
                originalElement,
                dir,
                onHbar,
                hScroller,
                vScroller,
                scrollEnd;
            if(!o.mouseWheelSupport || o.disabled)
                return;
            originalElement = $(e.srcElement || e.originalEvent.target);
            dir = "";
            onHbar = originalElement.closest("." + hbarContainerCSS,self.element).size() > 0;
            hScroller = o.hScroller;
            vScroller = o.vScroller;
            if((e.wheelDelta === undefined || e.wheelDelta === null) && (e.detail === undefined || e.detail === null))
            {
                e.wheelDelta = e.originalEvent.wheelDelta;
                e.detail = e.originalEvent.detail
            }
            if(e.wheelDelta)
                delta = e.wheelDelta / 120;
            if(e.detail)
                delta = -e.detail / 3;
            if(delta > 0)
                dir = onHbar ? "left" : "top";
            else
                dir = onHbar ? "right" : "bottom";
            if(dir.length > 0)
                self._doScrolling(dir,self);
            scrollEnd = false;
            if(dir === "left")
                scrollEnd = !self.hNeedScrollBar || Math.abs(hScroller.scrollValue - hScroller.scrollMin) < 0.001;
            if(dir === "right")
                scrollEnd = !self.hNeedScrollBar || Math.abs(hScroller.scrollValue - (hScroller.scrollMax - self._getHScrollBarLargeChange() + 1)) < 0.001;
            if(dir === "top")
                scrollEnd = !self.vNeedScrollBar || Math.abs(vScroller.scrollValue - vScroller.scrollMin) < 0.001;
            if(dir === "bottom")
                scrollEnd = !self.vNeedScrollBar || Math.abs(vScroller.scrollValue - (vScroller.scrollMax - self._getVScrollBarLargeChange() + 1)) < 0.001;
            if(!scrollEnd || !o.bubbleScrollingEvent || dir === "left" || dir === "right")
            {
                e.stopPropagation();
                e.preventDefault()
            }
        },
        _documentMouseUp: function(e)
        {
            var self = e.data.self,
                ele = e.data.ele;
            ele.removeClass(uiStateActive);
            self._clearInterval();
            $(document).unbind("mouseup",self._documentMouseUp)
        },
        _scrollerMouseOver: function(e)
        {
            var self = e.data,
                originalElement,
                ele,
                addhover;
            if(self.options.disabled)
                return;
            originalElement = $(e.srcElement || e.originalEvent.target);
            ele = null;
            addhover = false;
            if(originalElement.hasClass(uiStateDefault))
            {
                ele = originalElement;
                addhover = true
            }
            else if(originalElement.parent().hasClass(uiStateDefault))
            {
                ele = originalElement.parent();
                addhover = true
            }
            else if(originalElement.hasClass(vbarContainerCSS) || originalElement.hasClass(hbarContainerCSS))
                ele = originalElement;
            if(ele !== undefined)
            {
                if(addhover)
                    ele.addClass(uiStateHover);
                ele.bind("mouseout." + self.widgetName,self,self._elementMouseOut);
                ele.bind("mousedown." + self.widgetName,self,self._elementMouseDown);
                ele.bind("mouseup." + self.widgetName,self,self._elementMouseUp)
            }
        },
        _elementMouseUp: function(e)
        {
            var ele = $(e.currentTarget);
            ele.removeClass("ui-state-active")
        },
        _elementMouseDown: function(e)
        {
            var ele = $(e.currentTarget),
                self = e.data,
                scrollDirection,
                large,
                active,
                hbarDrag,
                pos,
                vbarDrag,
                pos2,
                f;
            if(self.options.disabled || e.which !== 1)
                return;
            scrollDirection = "";
            large = false;
            active = false;
            if(ele.hasClass("wijmo-wijsuperpanel-vbar-buttontop"))
            {
                scrollDirection = "top";
                active = true
            }
            else if(ele.hasClass("wijmo-wijsuperpanel-vbar-buttonbottom"))
            {
                scrollDirection = "bottom";
                active = true
            }
            else if(ele.hasClass("wijmo-wijsuperpanel-hbar-buttonleft"))
            {
                scrollDirection = "left";
                active = true
            }
            else if(ele.hasClass("wijmo-wijsuperpanel-hbar-buttonright"))
            {
                scrollDirection = "right";
                active = true
            }
            else if(ele.hasClass(scrollerHandle))
            {
                ele.addClass("ui-state-active");
                return
            }
            else if(ele.hasClass(hbarContainerCSS))
            {
                hbarDrag = ele.find("." + scrollerHandle);
                pos = hbarDrag.offset();
                if(e.pageX < pos.left)
                    scrollDirection = "left";
                else
                    scrollDirection = "right";
                large = true
            }
            else if(ele.hasClass(vbarContainerCSS))
            {
                vbarDrag = ele.find("." + scrollerHandle);
                pos2 = vbarDrag.offset();
                if(e.pageY < pos2.top)
                    scrollDirection = "top";
                else
                    scrollDirection = "bottom";
                large = true
            }
            self._clearInterval();
            self._doScrolling(scrollDirection,self,large);
            f = self._fields();
            self._setScrollingInterval(f,scrollDirection,self,large);
            if(active)
                ele.addClass("ui-state-active");
            $(document).bind("mouseup." + self.widgetName,{
                self: self,
                ele: ele
            },self._documentMouseUp)
        },
        doScrolling: function(dir, large)
        {
            this._doScrolling(dir,this,large)
        },
        _setScrollerValue: function(dir, scroller, smallChange, largeChange, isAdd, isLarge, self)
        {
            var vMin = scroller.scrollMin,
                change = isLarge ? largeChange : smallChange,
                value = scroller.scrollValue,
                t,
                vTopValue,
                firstStepChangeFix,
                data;
            if(!value)
                value = vMin;
            t = 0;
            if(isAdd)
            {
                vTopValue = scroller.scrollMax - largeChange + 1;
                if(Math.abs(value - vTopValue) < 0.001)
                {
                    self._clearInterval();
                    return false
                }
                firstStepChangeFix = scroller.firstStepChangeFix;
                t = value + change;
                if(!isLarge && Math.abs(value - vMin) < 0.0001 && !isNaN(firstStepChangeFix))
                    t += firstStepChangeFix;
                if(t > vTopValue)
                    t = vTopValue
            }
            else
            {
                if(Math.abs(value - vMin) < 0.001)
                {
                    self._clearInterval();
                    return false
                }
                t = value - change;
                if(t < 0)
                    t = vMin
            }
            data = {
                oldValue: scroller.scrollValue,
                newValue: t,
                direction: dir,
                dir: scroller.dir
            };
            if(!self._scrolling(true,self,data))
                return false;
            scroller.scrollValue = data.newValue;
            return true
        },
        _doScrolling: function(dir, self, large)
        {
            var o = self.options,
                vScroller = o.vScroller,
                hScroller = o.hScroller,
                vSmall = self._getVScrollBarSmallChange(),
                vLarge = self._getVScrollBarLargeChange(),
                hLarge = self._getHScrollBarLargeChange(),
                hSmall = self._getHScrollBarSmallChange();
            if(dir === "top" || dir === "bottom")
            {
                if(!self._setScrollerValue(dir,vScroller,vSmall,vLarge,dir === "bottom",large,self))
                    return;
                dir = "v"
            }
            else if(dir === "left" || dir === "right")
            {
                if(!self._setScrollerValue(dir,hScroller,hSmall,hLarge,dir === "right",large,self))
                    return;
                dir = "h"
            }
            self._setDragAndContentPosition(true,true,dir)
        },
        _disableButtonIfNeeded: function(self)
        {
            var f = self._fields(),
                o,
                buttonLeft,
                buttonRight,
                buttonTop,
                buttonBottom,
                hLargeChange,
                hMax,
                hValue,
                hScrollMin,
                vLargeChange,
                vMax,
                vValue,
                vScrollMin;
            if(f.intervalID > 0)
                window.clearInterval(f.intervalID);
            o = self.options;
            buttonLeft = f.buttonLeft;
            buttonRight = f.buttonRight;
            buttonTop = f.buttonTop;
            buttonBottom = f.buttonBottom;
            if(buttonLeft !== undefined)
            {
                hLargeChange = self._getHScrollBarLargeChange();
                hMax = o.hScroller.scrollMax - hLargeChange + 1;
                hValue = o.hScroller.scrollValue;
                hScrollMin = o.hScroller.scrollMin;
                if(hValue === undefined)
                    hValue = hScrollMin;
                if(Math.abs(hValue - hScrollMin) < 0.001 || !f.hScrolling)
                    buttonLeft.addClass(uiStateDisabled);
                else
                    buttonLeft.removeClass(uiStateDisabled);
                if(Math.abs(hValue - hMax) < 0.001 || !f.hScrolling)
                    buttonRight.addClass(uiStateDisabled);
                else
                    buttonRight.removeClass(uiStateDisabled)
            }
            if(buttonTop !== undefined)
            {
                vLargeChange = self._getVScrollBarLargeChange();
                vMax = o.vScroller.scrollMax - vLargeChange + 1;
                vValue = o.vScroller.scrollValue;
                vScrollMin = o.vScroller.scrollMin;
                if(vValue === undefined)
                    vValue = vScrollMin;
                if(Math.abs(vValue - vScrollMin) < 0.001 || !f.vScrolling)
                    buttonTop.addClass(uiStateDisabled);
                else
                    buttonTop.removeClass(uiStateDisabled);
                if(Math.abs(vValue - vMax) < 0.001 || !f.vScrolling)
                    buttonBottom.addClass(uiStateDisabled);
                else
                    buttonBottom.removeClass(uiStateDisabled)
            }
        },
        _clearInterval: function()
        {
            var f = this._fields(),
                intervalID = f.internalFuncID;
            if(intervalID > 0)
            {
                window.clearInterval(intervalID);
                f.internalFuncID = -1
            }
        },
        _elementMouseOut: function(event)
        {
            var ele = $(event.currentTarget),
                self = event.data;
            ele.unbind("mouseout",self._elementMouseOut);
            ele.unbind("mousedown",self._elementMouseDown);
            ele.unbind("mouseup",self._elementMouseUp);
            ele.removeClass(uiStateHover)
        },
        _getScorllOffset: function(child1)
        {
            var child = $(child1),
                f,
                cWrapper,
                tempWrapper,
                childOffset,
                templateOffset,
                cWrapperOffset,
                tDistance,
                bDistance,
                lDistance,
                rDistance,
                result = {
                    left: null,
                    top: null
                };
            if(child.size() === 0)
                return result;
            f = this._fields();
            cWrapper = f.contentWrapper;
            tempWrapper = f.templateWrapper;
            childOffset = child.offset();
            templateOffset = tempWrapper.offset();
            childOffset.leftWidth = childOffset.left + child.outerWidth();
            childOffset.topHeight = childOffset.top + child.outerHeight();
            cWrapperOffset = cWrapper.offset();
            cWrapperOffset.leftWidth = cWrapperOffset.left + cWrapper.outerWidth();
            cWrapperOffset.topHeight = cWrapperOffset.top + cWrapper.outerHeight();
            lDistance = childOffset.left - templateOffset.left;
            if(childOffset.left < cWrapperOffset.left)
                result.left = lDistance;
            else if(childOffset.leftWidth > cWrapperOffset.leftWidth)
            {
                rDistance = childOffset.leftWidth - templateOffset.left - cWrapper.innerWidth();
                if(lDistance < rDistance)
                    result.left = lDistance;
                else
                    result.left = rDistance
            }
            tDistance = childOffset.top - templateOffset.top;
            if(childOffset.top < cWrapperOffset.top)
                result.top = tDistance;
            else if(childOffset.topHeight > cWrapperOffset.topHeight)
            {
                bDistance = childOffset.topHeight - templateOffset.top - cWrapper.innerHeight();
                if(tDistance < bDistance)
                    result.top = tDistance;
                else
                    result.top = bDistance
            }
            return result
        },
        needToScroll: function(child1)
        {
            var offset = this._getScorllOffset(child1);
            return offset.top !== null || offset.left !== null
        },
        scrollChildIntoView: function(child1)
        {
            var offset = this._getScorllOffset(child1),
                left = offset.left,
                top = offset.top;
            if(left !== null)
                this.hScrollTo(left);
            if(top !== null)
                this.vScrollTo(top)
        },
        hScrollTo: function(x)
        {
            var o = this.options;
            o.hScroller.scrollValue = this.scrollPxToValue(x,"h");
            this._setDragAndContentPosition(true,true,"h","nonestop")
        },
        vScrollTo: function(y)
        {
            var o = this.options;
            o.vScroller.scrollValue = this.scrollPxToValue(y,"v");
            this._setDragAndContentPosition(true,true,"v","nonestop")
        },
        scrollPxToValue: function(px, dir)
        {
            var o = this.options,
                m = dir === "h" ? "outerWidth" : "outerHeight",
                m1 = dir === "h" ? "contentWidth" : "contentHeight",
                scroller = dir === "h" ? "hScroller" : "vScroller",
                f = this._fields(),
                cWrapper = f.contentWrapper,
                size = f[m1],
                contentHeight = cWrapper[m](),
                vMin = o[scroller].scrollMin,
                vMax = o[scroller].scrollMax,
                vRange = vMax - vMin,
                vLargeChange = dir === "h" ? this._getHScrollBarLargeChange() : this._getVScrollBarLargeChange(),
                maxv = vRange - vLargeChange + 1,
                ret = maxv * (px / (size - contentHeight));
            if(ret < vMin)
                ret = vMin;
            if(ret > maxv)
                ret = maxv;
            return ret
        },
        scrollTo: function(x, y)
        {
            this.hScrollTo(x);
            this.vScrollTo(y)
        },
        refresh: function()
        {
            this.paintPanel()
        },
        paintPanel: function(unfocus)
        {
            var self = this,
                ele = self.element,
                focused,
                o,
                f,
                templateWrapper;
            if(ele.is(":visible"))
            {
                focused = typeof document.activeElement != 'unknown' ? document.activeElement : undefined;
                o = self.options;
                f = self._fields();
                if(!f.initialized)
                    self._initialize(f,ele,self);
                self._resetLargeChange(self,f,o);
                self._bindElementEvents(self,f,ele,o);
                templateWrapper = f.templateWrapper;
                templateWrapper.css({
                    float: "left",
                    left: "0px",
                    top: "0px",
                    width: "auto",
                    height: "auto"
                });
                templateWrapper.hide();
                templateWrapper.show();
                f.contentWidth = templateWrapper.width();
                f.contentHeight = templateWrapper.height();
                templateWrapper.css("float","");
                self._setRounder(self,ele);
                self._setInnerElementsSize(f,ele);
                if(self._testScroll(self,f,o) === false)
                    return false;
                self._initScrollBars(self,f,o);
                self._initScrollButtons(self,f,o);
                self._trigger("painted");
                self._paintedMark = {
                    date: new Date,
                    mainWidth: ele[0].offsetWidth,
                    mainHeight: ele[0].offsetHeight,
                    width: f.contentWidth,
                    height: f.contentWidth
                };
                if(focused !== undefined && !unfocus)
                    $(focused).focus();
                return true
            }
            return false
        },
        _resetLargeChange: function(self, f, o)
        {
            var handle;
            if(self._autoVLarge)
                o.vScroller.scrollLargeChange = null;
            if(self._autoHLarge)
                o.hScroller.scrollLargeChange = null;
            f.vTrackLen = undefined;
            f.hTrackLen = undefined;
            if(f.vbarContainer)
            {
                handle = f.vbarContainer.children("." + scrollerHandle + ":eq(0)");
                handle.detach();
                f.vbarContainer.remove();
                f.vbarContainer = undefined
            }
            if(f.hbarContainer)
            {
                handle = f.hbarContainer.children("." + scrollerHandle + ":eq(0)");
                handle.detach();
                f.hbarContainer.remove();
                f.hbarContainer = undefined
            }
        },
        _initialize: function(f, ele, self)
        {
            f.initialized = true;
            ele.addClass(uiSuperPanelClasses);
            f.oldHeight = ele.css("height");
            var old = ele.css("overflow");
            ele.css("overflow","");
            ele.height(ele.height());
            ele.css("overflow",old);
            self._createAdditionalDom(self,f,ele)
        },
        getContentElement: function()
        {
            return this._fields().templateWrapper
        },
        _setButtonPosition: function(self, o, scroller, dir, target, f, state)
        {
            var h = dir === "h",
                mouseoverkey = "mouseover." + self.widgetName,
                decKey = h ? "buttonLeft" : "buttonTop",
                incKey = h ? "buttonRight" : "buttonBottom",
                decButton = f[decKey],
                incButton = f[incKey],
                html,
                buttons,
                defaultPosition;
            if(self._hasMode(scroller,"buttons") || self._hasMode(scroller,"buttonshover"))
            {
                html = h ? hButtons : vButtons;
                if(decButton === undefined)
                {
                    buttons = $(html).appendTo(state);
                    buttons.bind(mouseoverkey,self,self._scrollButtonMouseOver);
                    f[decKey] = decButton = state.children(h ? ".wijmo-wijsuperpanel-buttonleft" : ".wijmo-wijsuperpanel-buttontop");
                    f[incKey] = incButton = state.children(h ? ".wijmo-wijsuperpanel-buttonright" : ".wijmo-wijsuperpanel-buttonbottom")
                }
                defaultPosition = {
                    my: h ? "left" : "top",
                    of: target,
                    at: h ? "left" : "top",
                    collision: "none"
                };
                $.extend(defaultPosition,scroller.decreaseButtonPosition);
                decButton.position(defaultPosition);
                defaultPosition = {
                    my: h ? "right" : "bottom",
                    of: target,
                    at: h ? "right" : "bottom",
                    collision: "none"
                };
                $.extend(defaultPosition,scroller.increaseButtonPosition);
                incButton.position(defaultPosition)
            }
            else if(decButton !== undefined)
            {
                decButton.remove();
                incButton.remove();
                f[decKey] = f[incKey] = undefined
            }
        },
        _initScrollButtons: function(self, f, o)
        {
            var a = f.contentWrapper,
                state = f.stateContainer;
            self._setButtonPosition(self,o,o.hScroller,"h",a,f,state);
            self._setButtonPosition(self,o,o.vScroller,"v",a,f,state)
        },
        _getVScrollBarSmallChange: function()
        {
            var o = this.options,
                va;
            if(!o.vScroller.scrollSmallChange)
            {
                va = this._getVScrollBarLargeChange();
                o.vScroller.scrollSmallChange = va / 2
            }
            return o.vScroller.scrollSmallChange
        },
        _getVScrollBarLargeChange: function()
        {
            return this._getLargeChange("v")
        },
        _getLargeChange: function(dir)
        {
            var self = this,
                o = self.options,
                f = self._fields(),
                v = dir === "v",
                scroller = v ? o.vScroller : o.hScroller,
                clientKey = v ? "innerHeight" : "innerWidth",
                offsetKey = v ? "contentHeight" : "contentWidth",
                autoKey = v ? "_autoVLarge" : "_autoHLarge",
                hMax,
                hMin,
                hRange,
                content,
                contentWidth,
                wrapperWidth,
                percent,
                large;
            if(scroller.scrollLargeChange)
                return scroller.scrollLargeChange;
            hMax = scroller.scrollMax;
            hMin = scroller.scrollMin;
            hRange = hMax - hMin;
            content = f.contentWrapper;
            contentWidth = content[clientKey]();
            wrapperWidth = f[offsetKey];
            percent = contentWidth / (wrapperWidth - contentWidth);
            large = (hRange + 1) * percent / (1 + percent);
            if(isNaN(large))
                large = 0;
            scroller.scrollLargeChange = large;
            self[autoKey] = true;
            return scroller.scrollLargeChange
        },
        _getHScrollBarSmallChange: function()
        {
            var o = this.options,
                va;
            if(!o.hScroller.scrollSmallChange)
            {
                va = this._getHScrollBarLargeChange();
                o.hScroller.scrollSmallChange = va / 2
            }
            return o.hScroller.scrollSmallChange
        },
        _getHScrollBarLargeChange: function()
        {
            return this._getLargeChange("h")
        },
        _initScrollBars: function(self, f, o)
        {
            var hScroller = o.hScroller,
                hMax = hScroller.scrollMax,
                hMin = hScroller.scrollMin,
                hRange = hMax - hMin,
                vScroller = o.vScroller,
                vMax = vScroller.scrollMax,
                vMin = vScroller.scrollMin,
                vRange = vMax - vMin,
                hbarDrag = f.hbarDrag,
                vbarDrag = f.vbarDrag,
                hLargeChange,
                track,
                dragLen,
                difference,
                icon,
                vLargeChange,
                track1,
                dragLen1,
                difference1,
                icon1;
            if(self.hNeedScrollBar && hbarDrag.is(":visible"))
            {
                hLargeChange = self._getHScrollBarLargeChange();
                track = self._getTrackLen("h");
                dragLen = self._getDragLength(hRange,hLargeChange,track,o.hScroller.scrollMinDragLength);
                hbarDrag.width(dragLen);
                difference = hbarDrag.outerWidth(true) - hbarDrag.width();
                hbarDrag.width(dragLen - difference);
                icon = hbarDrag.children("span");
                icon.css("margin-left",(hbarDrag.width() - icon[0].offsetWidth) / 2);
                if(track <= hbarDrag.outerWidth(true))
                    hbarDrag.hide();
                else
                    hbarDrag.show()
            }
            if(self.vNeedScrollBar && vbarDrag.is(":visible"))
            {
                vLargeChange = self._getVScrollBarLargeChange();
                track1 = self._getTrackLen("v");
                dragLen1 = self._getDragLength(vRange,vLargeChange,track1,o.vScroller.scrollMinDragLength);
                vbarDrag.height(dragLen1);
                difference1 = vbarDrag.outerHeight(true) - vbarDrag.height();
                vbarDrag.height(dragLen1 - difference1);
                icon1 = vbarDrag.children("span");
                icon1.css("margin-top",(vbarDrag.height() - icon1[0].offsetHeight) / 2);
                if(track1 <= vbarDrag.outerHeight(true))
                    vbarDrag.hide();
                else
                    vbarDrag.show()
            }
            self._setDragAndContentPosition(false,false,"both")
        },
        _getTrackLen: function(dir)
        {
            var self = this,
                f = self._fields(),
                key = dir + "TrackLen",
                hbarContainer,
                vbarContainer,
                track,
                padding;
            if(f[key] !== undefined)
                return f[key];
            hbarContainer = f.hbarContainer;
            vbarContainer = f.vbarContainer;
            track = 0;
            padding = 0;
            if(dir === "h")
            {
                padding = self._getScrollContainerPadding("h");
                track = hbarContainer.innerWidth()
            }
            if(dir === "v")
            {
                padding = self._getScrollContainerPadding("v");
                track = vbarContainer.innerHeight()
            }
            f[key] = track - padding;
            return f[key]
        },
        _getScrollContainerPadding: function(paddingType)
        {
            var self = this,
                f = self._fields(),
                padding = 0,
                container,
                key;
            if(paddingType === "h")
                padding = self._getScrollContainerPadding("left") + self._getScrollContainerPadding("right");
            else if(paddingType === "v")
                padding = self._getScrollContainerPadding("top") + self._getScrollContainerPadding("bottom");
            else
            {
                if(paddingType === "left" || paddingType === "right")
                    container = f.hbarContainer;
                else
                    container = f.vbarContainer;
                key = paddingType + "Padding";
                if(f[key] !== undefined)
                {
                    padding = f[key];
                    return padding
                }
                if(container && container.css)
                    padding = parseFloat(container.css("padding-" + paddingType));
                f[key] = padding
            }
            return padding
        },
        _triggerScroll: function(contentLeft, dir, contentAnimationOptions)
        {
            var data = {
                    position: contentLeft,
                    dir: dir,
                    animationOptions: contentAnimationOptions
                };
            this._trigger("scroll",null,data)
        },
        _contentDragAnimate: function(dir, animated, hbarContainer, hbarDrag, stop, fireScrollEvent, dragging)
        {
            var self = this,
                o = self.options,
                v = dir === "v",
                scroller = v ? o.vScroller : o.hScroller,
                tempKey = v ? "outerHeight" : "outerWidth",
                wrapKey = v ? "innerHeight" : "innerWidth",
                contentKey = v ? "contentHeight" : "contentWidth",
                paddingKey = v ? "top" : "left",
                hMin = scroller.scrollMin,
                hMax = scroller.scrollMax,
                hRange = hMax - hMin,
                hValue = scroller.scrollValue === undefined ? hMin : scroller.scrollValue - hMin,
                hLargeChange = self._getLargeChange(dir),
                max = hRange - hLargeChange + 1,
                f = self._fields(),
                cWrapper = f.contentWrapper,
                tempWrapper = f.templateWrapper,
                contentLeft,
                dragleft,
                track,
                drag,
                r,
                padding,
                dragAnimationOptions,
                properties,
                contentAnimationOptions,
                userComplete,
                properties1,
                key;
            if(hValue > max)
                hValue = max;
            contentLeft = (f[contentKey] - cWrapper[wrapKey]()) * (hValue / max);
            if(Math.abs(contentLeft) < 0.001)
                contentLeft = 0;
            contentLeft = Math.round(contentLeft);
            dragleft = -1;
            if(hbarContainer !== undefined)
            {
                if(animated && hbarDrag.is(":animated") && stop !== "nonestop")
                    hbarDrag.stop(true,false);
                track = self._getTrackLen(dir);
                drag = hbarDrag[tempKey](true);
                r = track - drag;
                padding = self._getScrollContainerPadding(paddingKey);
                dragleft = hValue / max * r + padding
            }
            if(animated && o.animationOptions && !o.animationOptions.disabled)
            {
                if(dragleft >= 0 && dragging !== "dragging")
                {
                    dragAnimationOptions = $.extend({},o.animationOptions);
                    dragAnimationOptions.complete = undefined;
                    properties = v ? {top: dragleft} : {left: dragleft};
                    hbarDrag.animate(properties,dragAnimationOptions)
                }
                contentAnimationOptions = $.extend({},o.animationOptions);
                userComplete = o.animationOptions.complete;
                contentAnimationOptions.complete = function()
                {
                    self._scrollEnd(fireScrollEvent,self,dir);
                    if($.isFunction(userComplete))
                        userComplete(arguments)
                };
                if(animated && tempWrapper.is(":animated") && stop !== "nonestop")
                    tempWrapper.stop(true,false);
                properties1 = v ? {top: -contentLeft} : {left: -contentLeft};
                tempWrapper.animate(properties1,contentAnimationOptions);
                self._triggerScroll(contentLeft,dir,contentAnimationOptions)
            }
            else
            {
                key = v ? "top" : "left";
                if(dragleft >= 0 && dragging !== "dragging")
                    hbarDrag[0].style[key] = dragleft + "px";
                tempWrapper[0].style[key] = -contentLeft + "px";
                self._triggerScroll(contentLeft,dir);
                self._scrollEnd(fireScrollEvent,self,dir)
            }
        },
        _setDragAndContentPosition: function(fireScrollEvent, animated, dir, stop, dragging)
        {
            var self = this,
                f = self._fields(),
                hbarContainer = f.hbarContainer,
                hbarDrag = f.hbarDrag,
                vbarContainer = f.vbarContainer,
                vbarDrag = f.vbarDrag;
            if((dir === "both" || dir === "h") && f.hScrolling)
                self._contentDragAnimate("h",animated,hbarContainer,hbarDrag,stop,fireScrollEvent,dragging);
            if((dir === "both" || dir === "v") && f.vScrolling)
                self._contentDragAnimate("v",animated,vbarContainer,vbarDrag,stop,fireScrollEvent,dragging);
            if(f.intervalID > 0)
                window.clearInterval(f.intervalID);
            f.intervalID = window.setInterval(function()
            {
                self._disableButtonIfNeeded(self)
            },500)
        },
        _scrolling: function(fireEvent, self, d)
        {
            var r = true;
            if(fireEvent)
            {
                d.beforePosition = self.getContentElement().position();
                self._beforePosition = d.beforePosition;
                r = self._trigger("scrolling",null,d)
            }
            return r
        },
        _scrollEnd: function(fireEvent, self, dir)
        {
            if(fireEvent)
                window.setTimeout(function()
                {
                    var content = self.getContentElement(),
                        after,
                        d;
                    if(!content.is(":visible"))
                        return;
                    after = self.getContentElement().position();
                    d = {
                        dir: dir,
                        beforePosition: self._beforePosition,
                        afterPosition: after
                    };
                    self._trigger("scrolled",null,d)
                },0)
        },
        _getDragLength: function(range, largeChange, track, min)
        {
            var divide = range / largeChange,
                dragLength = track / divide,
                minidrag = min;
            if(dragLength < minidrag)
                dragLength = minidrag;
            else if(dragLength + 1 >= track)
                dragLength = track - 1;
            return Math.round(dragLength)
        },
        _needScrollbar: function(scroller, needscroll)
        {
            var scrollbarMode = this._hasMode(scroller,"scrollbar"),
                barVisibility = scroller.scrollBarVisibility,
                needScrollBar = scrollbarMode && (barVisibility === "visible" || barVisibility === "auto" && needscroll);
            return needScrollBar
        },
        _bindBarEvent: function(barContainer, barDrag, dir)
        {
            var self = this;
            self._isDragging = false;
            barContainer.bind("mouseover." + self.widgetName,self,self._scrollerMouseOver);
            barDrag.draggable({
                axis: dir === "h" ? "x" : "y",
                start: function(e, data)
                {
                    self._isDragging = true
                },
                drag: function(e, data)
                {
                    self._dragging(e,data,self)
                },
                containment: "parent",
                stop: function(e)
                {
                    self._dragStop(e,self,dir);
                    $(e.target).removeClass("ui-state-active");
                    self._isDragging = false
                }
            })
        },
        _createBarIfNeeded: function(hNeedScrollBar, scrollerWrapper, dir, html, content)
        {
            if(hNeedScrollBar)
            {
                var self = this,
                    data,
                    f = self._fields(),
                    strBarContainer = dir + "barContainer",
                    strBarDrag = dir + "barDrag",
                    hbar = dir === "h",
                    contentLength = content[hbar ? "innerHeight" : "innerWidth"](),
                    c = f[strBarContainer] = $(html),
                    targetBarLen,
                    d;
                scrollerWrapper.append(c);
                targetBarLen = c[0][hbar ? "offsetHeight" : "offsetWidth"];
                contentLength = contentLength - targetBarLen;
                data = {
                    direction: hbar ? "horizontal" : "vertical",
                    targetBarLen: targetBarLen,
                    contentLength: contentLength
                };
                if(self._trigger(hbar ? "hScrollerActivating" : "vScrollerActivating",null,data) === false)
                    return false;
                d = f[strBarDrag] = c.find("." + scrollerHandle);
                self._bindBarEvent(c,d,dir);
                content[hbar ? "height" : "width"](contentLength)
            }
        },
        _setScrollbarPosition: function(wrapper, self, content, targetBarContainer, referBarContainer, targetNeedScrollBar, referNeedScrollBar, targetScrollBarPosition, referScrollBarPosition, dir, scrollingNeed)
        {
            var hbar = dir === "h",
                targetBarLen,
                targetPadding,
                targetBarPosition,
                barPosition1,
                contentPosition1,
                barPosition2,
                contentPosition2,
                contentLength2,
                referBarWidth;
            if(targetNeedScrollBar)
            {
                targetBarLen = targetBarContainer[0][hbar ? "offsetHeight" : "offsetWidth"];
                targetPadding = self._getScrollContainerPadding(dir);
                targetBarPosition = hbar ? "top" : "left";
                barPosition1 = hbar ? {
                    top: "0px",
                    bottom: "auto",
                    left: "auto",
                    right: "auto"
                } : {
                    left: "0px",
                    right: "auto",
                    top: "auto",
                    bottom: "auto"
                };
                contentPosition1 = hbar ? {top: targetBarLen + "px"} : {left: targetBarLen + "px"};
                barPosition2 = hbar ? {
                    top: "auto",
                    right: "auto",
                    left: "auto",
                    bottom: "0px"
                } : {
                    left: "auto",
                    right: "0px",
                    top: "auto",
                    bottom: "auto"
                };
                contentPosition2 = hbar ? {top: ""} : {left: ""};
                contentLength2 = content[hbar ? "innerWidth" : "innerHeight"]();
                if(targetScrollBarPosition === targetBarPosition)
                {
                    targetBarContainer.css(barPosition1);
                    content.css(contentPosition1);
                    if(hbar)
                    {
                        targetBarContainer.children(".wijmo-wijsuperpanel-hbar-buttonleft").removeClass("ui-corner-bl").addClass("ui-corner-tl");
                        targetBarContainer.children(".wijmo-wijsuperpanel-hbar-buttonright").removeClass("ui-corner-br").addClass("ui-corner-tr");
                        targetBarContainer.removeClass("ui-corner-bottom").addClass("ui-corner-top")
                    }
                    else
                    {
                        targetBarContainer.children(".wijmo-wijsuperpanel-vbar-buttontop").removeClass("ui-corner-tr").addClass("ui-corner-tl");
                        targetBarContainer.children(".wijmo-wijsuperpanel-vbar-buttonbottom").removeClass("ui-corner-br").addClass("ui-corner-bl");
                        targetBarContainer.removeClass("ui-corner-right").addClass("ui-corner-left")
                    }
                }
                else
                {
                    targetBarContainer.css(barPosition2);
                    content.css(contentPosition2);
                    if(hbar)
                    {
                        targetBarContainer.children(".wijmo-wijsuperpanel-hbar-buttonleft").removeClass("ui-corner-tl").addClass("ui-corner-bl");
                        targetBarContainer.children(".wijmo-wijsuperpanel-hbar-buttonright").removeClass("ui-corner-bl").addClass("ui-corner-br");
                        targetBarContainer.removeClass("ui-corner-top").addClass("ui-corner-bottom")
                    }
                    else
                    {
                        targetBarContainer.children(".wijmo-wijsuperpanel-vbar-buttontop").removeClass("ui-corner-tl").addClass("ui-corner-tr");
                        targetBarContainer.children(".wijmo-wijsuperpanel-vbar-buttonbottom").removeClass("ui-corner-bl").addClass("ui-corner-br");
                        targetBarContainer.removeClass("ui-corner-left").addClass("ui-corner-right")
                    }
                }
                referBarWidth = 0;
                if(referNeedScrollBar)
                {
                    referBarWidth = referBarContainer[0][hbar ? "offsetWidth" : "offsetHeight"];
                    if(referScrollBarPosition === "left")
                        targetBarContainer.css("right","0px");
                    else if(referScrollBarPosition === "right")
                        targetBarContainer.css("left","0px");
                    else if(referScrollBarPosition === "top")
                        targetBarContainer.css("bottom","0px");
                    else if(referScrollBarPosition === "bottom")
                        targetBarContainer.css("top","0px")
                }
                if(!hbar && referNeedScrollBar)
                    referBarWidth = 0;
                targetBarContainer[hbar ? "width" : "height"](contentLength2 - targetPadding);
                self._enableDisableScrollBar(dir,targetBarContainer,!scrollingNeed)
            }
            else
                wrapper.css(hbar ? "left" : "top","")
        },
        _testScroll: function(self, f, o)
        {
            var wrapper = f.templateWrapper,
                content = f.contentWrapper,
                scrollerWrapper = f.stateContainer,
                contentWidth = content.innerWidth(),
                contentHeight = content.innerHeight(),
                wrapperWidth = f.contentWidth,
                wrapperHeight = f.contentHeight,
                hNeedScrollBar,
                vNeedScrollBar,
                hbarContainer,
                vbarContainer,
                hbarPosition,
                vbarPosition;
            f.hScrolling = wrapperWidth > contentWidth;
            f.vScrolling = wrapperHeight > contentHeight;
            hNeedScrollBar = self.hNeedScrollBar = self._needScrollbar(o.hScroller,f.hScrolling);
            if(self._createBarIfNeeded(hNeedScrollBar,scrollerWrapper,"h",hbarHtml,content) === false)
                return false;
            if(hNeedScrollBar && !f.vScrolling)
            {
                wrapper.css("float","left");
                f.contentHeight = wrapper.height();
                f.vScrolling = f.contentHeight > contentHeight - f.hbarContainer[0].offsetHeight;
                wrapper.css("float","")
            }
            vNeedScrollBar = self.vNeedScrollBar = self._needScrollbar(o.vScroller,f.vScrolling);
            if(self._createBarIfNeeded(vNeedScrollBar,scrollerWrapper,"v",vbarHtml,content) === false)
                return false;
            if(vNeedScrollBar && !f.hScrolling)
            {
                wrapper.css("float","left");
                f.contentWidth = wrapper.width();
                f.hScrolling = f.contentWidth > contentWidth - f.vbarContainer[0].offsetWidth;
                wrapper.css("float","");
                if(f.hScrolling && !hNeedScrollBar)
                {
                    hNeedScrollBar = self.hNeedScrollBar = self._needScrollbar(o.hScroller,f.hScrolling);
                    if(self._createBarIfNeeded(hNeedScrollBar,scrollerWrapper,"h",hbarHtml,content) === false)
                        return false
                }
            }
            hbarContainer = f.hbarContainer;
            vbarContainer = f.vbarContainer;
            hbarPosition = o.hScroller.scrollBarPosition;
            vbarPosition = o.vScroller.scrollBarPosition;
            self._setScrollbarPosition(wrapper,self,content,hbarContainer,vbarContainer,hNeedScrollBar,vNeedScrollBar,hbarPosition,vbarPosition,"h",f.hScrolling);
            self._setScrollbarPosition(wrapper,self,content,vbarContainer,hbarContainer,vNeedScrollBar,hNeedScrollBar,vbarPosition,hbarPosition,"v",f.vScrolling)
        },
        _enableDisableScrollBar: function(bar, barContainer, disable)
        {
            if(bar === "v")
            {
                barContainer[disable ? "addClass" : "removeClass"]("wijmo-wijsuperpanel-vbarcontainer-disabled");
                barContainer.find("." + uiStateDefault)[disable ? "addClass" : "removeClass"](uiStateDisabled)
            }
            else if(bar === "h")
            {
                barContainer[disable ? "addClass" : "removeClass"]("wijmo-wijsuperpanel-hbarcontainer-disabled");
                barContainer.find("." + uiStateDefault)[disable ? "addClass" : "removeClass"](uiStateDisabled)
            }
            barContainer.children("." + scrollerHandle)[disable ? "hide" : "show"]()
        },
        _initResizer: function()
        {
            var self = this,
                o = self.options,
                f = self._fields(),
                resizer = f.resizer,
                resizableOptions,
                oldstop;
            if(!resizer && o.allowResize)
            {
                resizableOptions = o.resizableOptions;
                oldstop = resizableOptions.stop;
                resizableOptions.stop = function(e)
                {
                    self._resizeStop(e,self);
                    if($.isFunction(oldstop))
                        oldstop(e)
                };
                f.resizer = resizer = self.element.resizable(resizableOptions)
            }
            if(!o.allowResize && f.resizer)
            {
                resizer.resizable("destroy");
                f.resizer = null
            }
        },
        _resizeStop: function(e, self)
        {
            if(!this.options.autoRefresh)
                self.paintPanel(true);
            self._trigger("resized")
        },
        _createAdditionalDom: function(self, f, ele)
        {
            if(!ele.attr("tabindex"))
            {
                ele.attr("tabindex","-1");
                f.tabindex = true
            }
            var stateContainer = f.stateContainer = $(innerElementHtml),
                templateW;
            f.contentWrapper = stateContainer.children();
            templateW = f.templateWrapper = f.contentWrapper.children();
            ele.contents().each(function(index, el)
            {
                var jel = $(el);
                if(jel.hasClass("wijmo-wijsuperpanel-header"))
                {
                    f.header = jel;
                    return
                }
                if(jel.hasClass("wijmo-wijsuperpanel-footer"))
                {
                    f.footer = jel;
                    return
                }
                templateW[0].appendChild(el)
            });
            if(f.header !== undefined)
                ele.prepend(f.header);
            ele[0].appendChild(stateContainer[0]);
            if(f.footer !== undefined)
                f.footer.insertAfter(stateContainer)
        },
        _setRounder: function(self, ele)
        {
            if(this.options.showRounder)
            {
                ele.addClass(rounderClass);
                if(self._rounderAdded)
                    return;
                if($.browser.msie)
                    return;
                var key1,
                    key,
                    value,
                    border;
                key1 = key = "";
                if($.browser.webkit)
                {
                    key = "WebkitBorderTopLeftRadius";
                    key1 = "WebkitBorderRadius"
                }
                else if($.browser.mozilla)
                {
                    key = "MozBorderRadiusBottomleft";
                    key1 = "MozBorderRadius"
                }
                else
                {
                    key = "border-top-left-radius";
                    key1 = "border-radius"
                }
                value = ele.css(key);
                border = parseInt(value,10);
                ele.css(key1,border + 1);
                self._rounderAdded = true;
                self._radiusKey = key1
            }
            else
                ele.removeClass(rounderClass)
        },
        _setInnerElementsSize: function(f, ele)
        {
            var state = f.stateContainer,
                content = f.contentWrapper,
                height = 0,
                style,
                clientHeight,
                clientWidth,
                style2;
            if(f.header !== undefined)
                height += f.header.outerHeight();
            if(f.footer !== undefined)
                height += f.footer.outerHeight();
            style = state[0].style;
            clientHeight = ele.innerHeight() - height;
            clientWidth = ele.innerWidth();
            style.display = "none";
            style.height = clientHeight + "px";
            style.width = clientWidth + "px";
            style2 = content[0].style;
            style2.height = clientHeight + "px";
            style2.width = clientWidth + "px";
            style.display = ""
        }
    })
})(jQuery);
(function(window, $)
{
    "use strict";;
    var const_undefined = "undefined";
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === const_undefined)
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === const_undefined)
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    var document = window.document;
    var cmd_refresh = "refresh",
        cmd_scroll = "scroll",
        cmd_resize = "resize",
        dir_horizontal = "horizontal",
        dir_vertical = "vertical",
        tag_input = "input",
        tag_div = "div",
        pixel = "px",
        zero = "0",
        thousands = "3000",
        cssHidden = "hidden",
        cssVisible = "visible",
        cssTop = "top",
        cssNone = "none",
        e_wijspreadpanelexscrolling = "wijspreadpanelexscrolling",
        e_wijspreadpanelexdragstop = "wijspreadpanelexdragstop",
        e_wijspreadpanelex = "wijmo-wijspreadpanelex",
        ns_spread = ".gcSpread",
        data_postfix = "_data";
    UI.GcSpread = function(name, count, host)
    {
        this._init(name,count,host)
    };
    UI.GcSpread.prototype = {
        _scrollbarSize: 18,
        _host: null,
        _allowUndo: true,
        _paintSuspended: false,
        _availableSheetIndex: -1,
        sheets: null,
        name: null,
        _tabStripRatio: 0.5,
        _activeSheetIndex: 0,
        _allowUserZoom: true,
        _allowUserResize: true,
        _tabStripVisible: true,
        _tabEditable: true,
        _newTabVisible: true,
        useWijmoTheme: false,
        _canUserEditFormula: true,
        _highlightInvalidData: false,
        _init: function(name, count, host)
        {
            this.name = name;
            this.calcService = new GrapeCity.Calc.Service(this);
            this._userEvents = [];
            this._userEventsElem = document.createElement(tag_input);
            this._eventSuspended = 0;
            this._undoManager = new UI._UndoManager(this,-1,this.allowUndo());
            this._clipboardHelper = {
                fromSheet: null,
                range: null,
                isCutting: false
            };
            this.sheets = [];
            var i;
            for(i = 0; i < count; i++)
                this.sheets.push(this._createSheet(this._getDefaultSheetName(i)));
            if(this.sheets.length > 0)
            {
                var defSource = null;
                for(i = 0; !defSource && i < this.sheets.length; i++)
                    defSource = this.sheets[i]._getSheetSource();
                if(defSource)
                    for(i = 0; i < this.sheets.length; i++)
                    {
                        var sheetSource = this.sheets[i]._getSheetSource();
                        if(sheetSource)
                            sheetSource.shareCalculations(defSource)
                    }
            }
            if(host)
                this._setHost(host)
        },
        _getDefaultSheetName: function(sheetIndex)
        {
            if(this._availableSheetIndex < this.getSheetCount())
                this._availableSheetIndex = this.getSheetCount();
            else
                this._availableSheetIndex++;
            if(sheetIndex === undefined || sheetIndex === null || sheetIndex < this._availableSheetIndex)
                sheetIndex = this._availableSheetIndex;
            var conflict = false,
                name;
            do
            {
                name = "Φύλλο" + (sheetIndex + 1);
                var len = this.sheets.length;
                if(len > 0)
                    for(var i = 0; i < len; i++)
                        if(i in this.sheets)
                        {
                            var sheet = this.sheets[i];
                            if(sheet._name === name)
                            {
                                sheetIndex++;
                                conflict = true;
                                break
                            }
                            else if(conflict)
                                conflict = false
                        }
            } while(conflict);
            return name
        },
        _createSheet: function(sheetName, rowCount, columnCount)
        {
            var sheet = new UI.GcSheet(sheetName);
            sheet.borderWidth = 0;
            sheet.setRowCount(rowCount ? rowCount : 200);
            sheet.setColumnCount(columnCount ? columnCount : 20);
            sheet.calcService = this.calcService;
            sheet.parent = this;
            sheet.borderWidth = 0;
            return sheet
        },
        _setHost: function(host)
        {
            this._host = host;
            if(host)
            {
                var tmp = [],
                    i,
                    j,
                    self = this;
                for(i = 0; i < host.children.length; i++)
                    tmp.push(host.children[i]);
                host.innerHTML = "<table cellspacing=0 cellpadding=0 border=0>" + "<tr><td></td><td></td></tr>" + "<tr><td></td><td></td></tr></table>";
                var table = host.firstChild;
                while(tmp.length > 0)
                    host.insertBefore(tmp.pop(),table);
                this._vp = document.createElement(tag_div);
                this._vp.id = host.id + "vp";
                table.rows[0].cells[0].insertBefore(this._vp,null);
                var full = "100%";
                table.style.width = full;
                table.style.height = full;
                table.style.border = zero;
                table.style.margin = zero;
                for(i = 0; i < table.rows.length; i++)
                    for(j = 0; j < table.rows[i].cells.length; j++)
                    {
                        table.rows[i].cells[j].style.padding = zero;
                        table.rows[i].cells[j].style.border = zero
                    }
                this._vs = document.createElement(tag_div);
                this._vs.id = host.id + "vs";
                this._vs.style.overflowY = cmd_scroll;
                this._vs.style.overflowX = cssHidden;
                this._vs.style.border = zero;
                table.rows[0].cells[1].insertBefore(this._vs,null);
                this._vscrollDelegate = function(event, data)
                {
                    var sheet = self.getActiveSheet();
                    if(sheet && data)
                    {
                        var v = parseInt(data.newValue,10);
                        if(data.oldValue === undefined || data.oldValue === null)
                            data.oldValue = 0;
                        data.oldValue = parseInt(data.oldValue,10);
                        if(data.direction === cssTop)
                        {
                            if(data.oldValue - data.newValue === 10)
                                v = sheet._getPrevPageTopRow()
                        }
                        else if(data.direction === "bottom")
                            if(data.newValue - data.oldValue === 10)
                                v = sheet._getNextPageTopRow();
                        if(data.newValue !== v)
                            data.newValue = v;
                        if(data.oldValue !== v)
                        {
                            sheet._eventHandler.doVScroll(v);
                            sheet._trigger(UI.Events.TopRowChanged,{
                                sheet: sheet,
                                sheetName: sheet._name,
                                oldTopRow: data.oldValue,
                                newTopRow: v
                            })
                        }
                    }
                };
                this.initPaint = function()
                {
                    var isStandardCanvas = GrapeCity.UI._isStandardCanvas();
                    if(isStandardCanvas)
                        return;
                    var sheet = this.getActiveSheet();
                    if(!sheet)
                        return;
                    var control = sheet._getCanvas();
                    if(control && control.firstChild && control.firstChild.loaded)
                    {
                        this.invalidateLayout();
                        this.repaint()
                    }
                    else
                    {
                        var self = this;
                        window.setTimeout(function()
                        {
                            self.initPaint.call(self)
                        },10)
                    }
                };
                this.vScrollStop = function(e, d)
                {
                    var sheet = self.getActiveSheet();
                    if(sheet)
                    {
                        var vbar = $(self._vs);
                        var o = vbar.wijspreadpanelex().data(e_wijspreadpanelex).options;
                        var v = o.vScroller.scrollValue;
                        var f = false;
                        if(v < sheet._getFirstPageTopRow())
                        {
                            o.vScroller.scrollValue = sheet._getFirstPageTopRow();
                            f = true
                        }
                        else if(v > sheet._getLastPageTopRow())
                        {
                            o.vScroller.scrollValue = sheet._getLastPageTopRow();
                            f = true
                        }
                        if(f)
                        {
                            vbar.wijspreadpanelex(cmd_refresh);
                            self._refreshScrollbar()
                        }
                    }
                };
                $(this._vs).bind(cmd_scroll + ns_spread,this._vscrollDelegate);
                $(this._vs).bind(e_wijspreadpanelexdragstop + ns_spread,this.vScrollStop);
                var vsDummy = document.createElement(tag_div);
                vsDummy.id = host.id + "vsDummy";
                this._vs.appendChild(vsDummy);
                vsDummy.style.height = thousands + pixel;
                vsDummy.style.width = "1" + pixel;
                table.rows[1].cells[0].innerHTML = "<table cellspacing=0 cellpadding=0 border=0><tr><td></td><td></td></tr></table>";
                var t1 = table.rows[1].cells[0].firstChild;
                t1.style.border = zero;
                t1.style.margin = zero;
                for(i = 0; i < t1.rows.length; i++)
                    for(j = 0; j < t1.rows[i].cells.length; j++)
                    {
                        t1.rows[i].cells[j].style.padding = zero;
                        t1.rows[i].cells[j].style.border = zero
                    }
                this._tabs = document.createElement(tag_div);
                this._tabs.style.width = "" + this._getActualTabStripRatio() * this._host.clientWidth + pixel;
                this._tabs.style.height = "" + this._scrollbarSize + pixel;
                t1.rows[0].cells[0].insertBefore(this._tabs,null);
                this._tab = new UI._GcTab(host.getAttribute("id") + "_tabStrip");
                this._tab.setOwner(this);
                this._tab._setHost(this._tabs);
                this._hs = document.createElement(tag_div);
                this._hs.id = host.id + "hs";
                this._hs.style.overflowX = cmd_scroll;
                this._hs.style.overflowY = cssHidden;
                this._hs.style.border = zero;
                t1.rows[0].cells[1].insertBefore(this._hs,null);
                this._hscrollDelegate = function(event, data)
                {
                    var sheet = self.getActiveSheet();
                    if(sheet)
                    {
                        var v = parseInt(data.newValue,10);
                        if(data.oldValue === undefined || data.oldValue === null)
                            data.oldValue = 0;
                        data.oldValue = parseInt(data.oldValue,10);
                        if(data.direction === "left")
                        {
                            if(data.oldValue - data.newValue === 10)
                                v = sheet._getPrevPageLeftColumn()
                        }
                        else if(data.direction === "right")
                            if(data.newValue - data.oldValue === 10)
                                v = sheet._getPageRightColumn();
                        if(data.newValue !== v)
                            data.newValue = v;
                        if(data.oldValue !== data.newValue)
                        {
                            sheet._eventHandler.doHScroll(v);
                            sheet._trigger(UI.Events.LeftColumnChanged,{
                                sheet: sheet,
                                sheetName: sheet._name,
                                oldLeftCol: data.oldValue,
                                newLeftCol: data.newValue
                            })
                        }
                    }
                };
                this.hScrollStop = function(e, d)
                {
                    var sheet = self.getActiveSheet();
                    if(sheet)
                    {
                        var hbar = $(self._hs);
                        var o = hbar.wijspreadpanelex().data(e_wijspreadpanelex).options;
                        var v = o.hScroller.scrollValue;
                        var f = false;
                        if(v < sheet._getFirstPageLeftColumn())
                        {
                            o.hScroller.scrollValue = sheet._getFirstPageLeftColumn();
                            f = true
                        }
                        if(f)
                        {
                            hbar.wijspreadpanelex(cmd_refresh);
                            self._refreshScrollbar()
                        }
                    }
                };
                $(this._hs).bind(cmd_scroll + ns_spread,this._hscrollDelegate);
                $(this._hs).bind(e_wijspreadpanelexdragstop + ns_spread,this.hScrollStop);
                var hsDummy = document.createElement(tag_div);
                hsDummy.id = host.id + "hsDummy";
                this._hs.appendChild(hsDummy);
                hsDummy.style.width = thousands + pixel;
                hsDummy.style.height = "1" + pixel;
                $(window).bind(cmd_resize + ns_spread,UI.createEventHandler(this,this._doResize));
                if($.browser.chrome || $.browser.safari)
                {
                    var vs = document.createElement(tag_div);
                    vs.id = host.id + "vsTest";
                    vs.style.overflowY = cmd_scroll;
                    vs.style.overflowX = cssHidden;
                    vs.style.border = zero;
                    vs.style.height = "200" + pixel;
                    vs.style.width = "26" + pixel;
                    vs.style.position = "absolute";
                    vs.style.left = "-1000" + pixel;
                    this._dummyScrollbar = vs;
                    document.body.insertBefore(vs,null);
                    var dummy = document.createElement(tag_div);
                    dummy.id = host.id + "dummyPanel";
                    vs.appendChild(dummy);
                    dummy.style.height = thousands + pixel;
                    dummy.style.width = "1" + pixel;
                    var spOptions = {
                            allowResize: false,
                            keyboardSupport: false,
                            bubbleScrollingEvent: false,
                            hScroller: {scrollBarVisibility: cssHidden},
                            vScroller: {
                                scrollSmallChange: 1,
                                scrollLargeChange: 10,
                                scrollMin: 0,
                                scrollMax: 100,
                                scrollBarVisibility: cssVisible
                            }
                        };
                    var ssDummyVbar = $(this._dummyScrollbar);
                    ssDummyVbar.css("overflowY","");
                    ssDummyVbar.wijspreadpanelex(spOptions)
                }
                var sheet = this.getActiveSheet();
                if(sheet)
                {
                    sheet._setHost(this._vp);
                    $(window).unbind('resize.gcSheet');
                    var self = this;
                    sheet._scrollCallback = function(e)
                    {
                        if(self.getActiveSheet() !== sheet)
                            return;
                        if(e.action === cmd_scroll)
                        {
                            if(e.orientation === dir_vertical)
                            {
                                var vbar = $(self._vs);
                                var o1 = vbar.wijspreadpanelex().data(e_wijspreadpanelex).options;
                                o1.vScroller.scrollValue = sheet._scrollTopRow;
                                vbar.wijspreadpanelex(cmd_refresh);
                                self._refreshScrollbar()
                            }
                            else if(e.orientation === dir_horizontal)
                            {
                                var hbar = $(self._hs);
                                var o2 = hbar.wijspreadpanelex().data(e_wijspreadpanelex).options;
                                o2.hScroller.scrollValue = sheet._scrollLeftCol;
                                hbar.wijspreadpanelex(cmd_refresh);
                                self._refreshScrollbar()
                            }
                        }
                        else if(e.action === cmd_resize)
                            self._doResize()
                    }
                }
                this._doResize();
                this._loadData();
                var fn = this._getFunction(this._host.id + "_onLoad");
                if(fn)
                    fn(this);
                this.submitDelegate = function(form)
                {
                    var old = form.onsubmit;
                    form.onsubmit = function()
                    {
                        self.saveData();
                        if(typeof old === "function")
                            old.apply()
                    }
                };
                var form = $(document.forms).get(0);
                if(form)
                    this.submitDelegate(form)
            }
        },
        _refreshScrollbar: function()
        {
            if(this._dummyScrollbar)
            {
                var bar = $(this._dummyScrollbar);
                bar.wijspreadpanelex(cmd_refresh)
            }
        },
        _loadData: function(callBackData)
        {
            var id = this._host.id;
            var dataField = document.getElementById(id + data_postfix);
            if(!dataField || dataField.value === "")
                return;
            var data = typeof JSON !== "undefined" && JSON.parse ? JSON.parse(dataField.value) : null;
            if(data === undefined || data === null)
                return;
            this.suspendEvent();
            this.isPaintSuspended(true);
            var spreadData = null;
            if(data.length > 0)
                spreadData = JSON.parse(data[0]);
            this.calcService.suspend();
            this.setSheetCount(spreadData.sheetCount);
            if(data.length > 0)
                if(spreadData.referenceStyle !== null && spreadData.referenceStyle !== undefined)
                    this.referenceStyle(spreadData.referenceStyle);
            var sheetData,
                sheet,
                i,
                j,
                t,
                c;
            if(data.length > 1)
            {
                var sheetDatas = [],
                    rowCount,
                    columnCount;
                for(i = 1; i < data.length; i++)
                {
                    var sheetSettings = JSON.parse(data[i]);
                    sheet = this.sheets[i - 1];
                    var sheetName = sheetSettings.name;
                    rowCount = sheetSettings.rowCount;
                    columnCount = sheetSettings.columnCount;
                    sheet._allowCellOverflow = spreadData.allowCellOverflow;
                    sheetData = sheetSettings.data;
                    if(sheetData)
                    {
                        if(callBackData)
                        {
                            for(j in sheetData)
                                if(typeof j !== "function")
                                    sheet._dataModel.dataTable[j] = sheetData[j]
                        }
                        else
                            sheet._dataModel.dataTable = sheetData;
                        sheet._dataModel.dataTable.length = rowCount
                    }
                    if(sheetSettings.dataSource && sheetSettings.dataSource.length > 0)
                    {
                        var dataSource = JSON.parse(sheetSettings.dataSource);
                        if(dataSource)
                            sheet._dataSource = dataSource
                    }
                    if(sheetSettings.rows)
                        sheet._rowInfos = sheetSettings.rows;
                    if(sheetSettings.columns)
                    {
                        sheet._colInfos = sheetSettings.columns;
                        sheet._colInfos.length = columnCount
                    }
                    if(sheetSettings.spans)
                        for(j = 0; j < sheetSettings.spans.length; j++)
                        {
                            var rg = sheetSettings.spans[j];
                            sheet._addSpanImp(rg.Row,rg.Column,rg.RowCount,rg.ColumnCount)
                        }
                    if(sheetSettings.colStyles)
                        for(t in sheetSettings.colStyles)
                            if(!isNaN(t))
                            {
                                c = parseInt(t,10);
                                sheet.setStyle(-1,c,sheetSettings.colStyles[t])
                            }
                    if(sheetSettings.rowStyles)
                        for(t in sheetSettings.rowStyles)
                            if(!isNaN(t))
                            {
                                var r = parseInt(t,10);
                                sheet.setStyle(r,-1,sheetSettings.rowStyles[t],UI.SheetArea.viewport)
                            }
                    if(sheetSettings.rowHeaderAutoText !== null && sheetSettings.rowHeaderAutoText !== undefined)
                        sheet.rowHeaderAutoText = sheetSettings.rowHeaderAutoText;
                    if(sheetSettings.colHeaderAutoText !== null && sheetSettings.colHeaderAutoText !== undefined)
                        sheet.colHeaderAutoText = sheetSettings.colHeaderAutoText;
                    if(sheetSettings.referenceStyle !== null && sheetSettings.referenceStyle !== undefined)
                        sheet.referenceStyle(sheetSettings.referenceStyle);
                    if(sheetSettings.gridline !== null && sheetSettings.gridline !== undefined)
                        sheet.gridline = sheetSettings.gridline;
                    sheet._dataModel.name = sheetName;
                    sheet._name = sheetName;
                    sheet.setRowCount(rowCount);
                    sheet.setColumnCount(columnCount);
                    sheet.setActiveCell(sheetSettings.ar,sheetSettings.ac);
                    sheet.setFrozenCount(sheetSettings.frozenRowCount,sheetSettings.frozenColCount);
                    sheetDatas[i] = sheetData
                }
            }
            if(data.length > 0)
            {
                this.setActiveSheetIndex(spreadData.activeSheetIndex);
                if(typeof spreadData.tabStripRatio !== "undefined")
                    this.setTabStripRatio(spreadData.tabStripRatio);
                if(typeof spreadData.tabStripVisible !== "undefined")
                    this._tabStripVisible = spreadData.tabStripVisible;
                if(typeof spreadData.tabEditable !== "undefined")
                    this._tabEditable = spreadData.tabEditable;
                if(typeof spreadData.newTabVisible !== "undefined")
                    this._newTabVisible = spreadData.newTabVisible;
                this.gcSpreadsheet = spreadData.gcSpreadsheet;
                sheet = this.getActiveSheet();
                if(sheet)
                {
                    var st = spreadData.theme;
                    if(typeof st === "string" || typeof st === const_undefined)
                        sheet.currentTheme(st);
                    else
                    {
                        var tc = st.themeColor;
                        var themeColor = new UI.ThemeColor(st.name,tc.text1,tc.text2,tc.background1,tc.background2,tc.accent1,tc.accent2,tc.accent3,tc.accent4,tc.accent5,tc.accent6,tc.link,tc.followedLink);
                        var theme = new UI.SpreadTheme(st.name,themeColor,st.headingFont,st.bodyFont);
                        sheet.currentTheme(theme)
                    }
                }
                if(spreadData.useWijmoTheme)
                    this.useWijmoTheme = spreadData.useWijmoTheme;
                if(spreadData.names)
                    for(var n = 0; n < spreadData.names.length; n++)
                    {
                        var ni = spreadData.names[n];
                        if(ni.sn)
                        {
                            var sheet = this.getSheetFromName(ni.sn);
                            if(sheet)
                                sheet.addCustomName(ni.name,ni.formula,ni.row,ni.col)
                        }
                        else
                            this.addCustomName(ni.name,ni.formula,ni.row,ni.col)
                    }
                for(i = 1; i < data.length; i++)
                {
                    sheetData = sheetDatas[i];
                    sheet = this.sheets[i - 1];
                    rowCount = sheet.getRowCount();
                    columnCount = sheet.getColumnCount();
                    for(j in sheetData)
                        if(typeof j !== "function")
                        {
                            var rowData = sheetData[j];
                            if(rowData)
                                for(c = 0; c < columnCount; c++)
                                {
                                    var formula = null;
                                    if(rowData[c])
                                        formula = rowData[c].formula;
                                    if(formula)
                                    {
                                        rowData[c].formula = null;
                                        sheet.setFormula(parseInt(j,10),c,formula)
                                    }
                                }
                        }
                }
            }
            this.calcService.resume();
            this._recalcAll();
            for(i = 0; i < this.sheets.length; i++)
            {
                sheet = this.sheets[i];
                sheet.clearPendingChanges()
            }
            this.resumeEvent();
            this.isPaintSuspended(false);
            dataField.value = ""
        },
        _getFunction: function(fn)
        {
            if(fn === undefined || fn === null || fn === "")
                return null;
            try
            {
                return window[fn]
            }
            catch(e){}
            return null
        },
        _dispose: function()
        {
            $(window).unbind(cmd_resize + ns_spread);
            $(this._vs).unbind(cmd_scroll + ns_spread);
            $(this._hs).unbind(cmd_scroll + ns_spread);
            this._disposeUserEvents();
            var sheet = this.getActiveSheet();
            if(sheet)
                sheet._dispose();
            if(this._host)
            {
                this._host.innerHTML = "";
                this._host.removeAttribute("gcUIElement")
            }
            if($.browser.chrome || $.browser.safari)
                $("#" + this._host.id + "vsTest").remove()
        },
        _disposeUserEvents: function()
        {
            $(this._userEventsElem).unbind(ns_spread);
            for(var i = 0; i < this.sheets.length; i++)
            {
                var sheet = this.sheets[i];
                sheet._disposeUserEvents()
            }
        },
        _getCalcContexts: function()
        {
            var t = [];
            for(var i = 0; i < this.sheets.length; i++)
            {
                var e = this.sheets[i];
                var ct = e._getCalcContexts();
                if(ct)
                    while(ct.length > 0)
                        t.push(ct.pop())
            }
            return t
        },
        _doResize: function()
        {
            if($.browser.msie && parseInt($.browser.version,10) < 9)
            {
                var canvas = this.canvas;
                if(canvas)
                {
                    var ssContainer = canvas.parentElement;
                    var spContainer = ssContainer.parentElement;
                    var control = canvas.firstChild;
                    if(control && spContainer)
                    {
                        var isAutoSize = function(w)
                            {
                                return w === "" || w === "auto" || /%$/ig.test(w)
                            };
                        if(isAutoSize(ssContainer.style.width) && spContainer.offsetWidth > 12)
                            control.width = spContainer.offsetWidth - 12;
                        if(isAutoSize(ssContainer.style.height && spContainer.offsetHeight > 10))
                            control.height = spContainer.offsetHeight - 10
                    }
                }
            }
            var scrollbarSize = this._scrollbarSize;
            var host = this._host;
            if(!host)
                return;
            var availableWidth = $(this._vp).width();
            var w = host.clientWidth;
            var h = host.clientHeight;
            var tabStripWidth = this._getActualTabStripRatio() * availableWidth;
            this._tabs.style.width = "" + (tabStripWidth - 1) + pixel;
            this._vp.style.width = "" + (w - scrollbarSize) + pixel;
            this._vp.style.height = "" + (h - scrollbarSize) + pixel;
            this._vs.style.width = "" + scrollbarSize + pixel;
            this._vs.style.height = this._vp.style.height;
            var hsWidth = w - (this._tabStripVisible ? tabStripWidth : 0) - scrollbarSize;
            this._hs.style.width = "" + (hsWidth > 0 ? hsWidth : 1) + pixel;
            this._hs.style.height = "" + scrollbarSize + pixel;
            var sheet = this.getActiveSheet();
            if(sheet)
            {
                if(sheet._scrollTopRow === 0)
                    sheet._scrollTopRow = sheet._getFirstPageTopRow();
                if(sheet._scrollLeftCol === 0)
                    sheet._scrollLeftCol = sheet._getFirstPageLeftColumn()
            }
            var ssVbar = $(this._vs);
            var spOptions = ssVbar.data(e_wijspreadpanelex) ? ssVbar.data(e_wijspreadpanelex).options : null;
            if(!spOptions)
                spOptions = {
                    allowResize: false,
                    keyboardSupport: true,
                    bubbleScrollingEvent: true,
                    hScroller: {scrollBarVisibility: cssHidden},
                    vScroller: {
                        scrollSmallChange: 1,
                        scrollLargeChange: 10,
                        scrollMin: 0,
                        scrollBarVisibility: cssVisible
                    }
                };
            if(sheet)
            {
                spOptions.vScroller.scrollMin = sheet._getFirstPageTopRow();
                spOptions.vScroller.scrollMax = sheet.getRowCount() + spOptions.vScroller.scrollLargeChange - 2;
                if(spOptions.vScroller.scrollValue < sheet._scrollTopRow)
                    spOptions.vScroller.scrollValue = sheet._scrollTopRow
            }
            ssVbar.css("overflowY","");
            ssVbar.wijspreadpanelex(spOptions);
            ssVbar.unbind(e_wijspreadpanelexscrolling,this._vscrollDelegate);
            ssVbar.bind(e_wijspreadpanelexscrolling,this._vscrollDelegate);
            ssVbar.wijspreadpanelex(cmd_refresh);
            var ssHbar = $(this._hs);
            spOptions = ssHbar.data(e_wijspreadpanelex) ? ssHbar.data(e_wijspreadpanelex).options : null;
            if(!spOptions)
                spOptions = {
                    allowResize: false,
                    keyboardSupport: true,
                    bubbleScrollingEvent: true,
                    vScroller: {scrollBarVisibility: cssHidden},
                    hScroller: {
                        scrollSmallChange: 1,
                        scrollLargeChange: 10,
                        scrollMin: 0,
                        scrollBarVisibility: cssVisible
                    }
                };
            if(sheet)
            {
                spOptions.hScroller.scrollMin = sheet._getFirstPageLeftColumn();
                spOptions.hScroller.scrollMax = sheet.getColumnCount() + spOptions.hScroller.scrollLargeChange - 2;
                if(spOptions.hScroller.scrollValue < sheet._scrollLeftCol)
                    spOptions.hScroller.scrollValue = sheet._scrollLeftCol
            }
            ssHbar.css("overflowX","");
            ssHbar.wijspreadpanelex(spOptions);
            ssHbar.unbind(e_wijspreadpanelexscrolling,this._hscrollDelegate);
            ssHbar.bind(e_wijspreadpanelexscrolling,this._hscrollDelegate);
            ssHbar.wijspreadpanelex(cmd_refresh);
            if(sheet)
                sheet._eventHandler.doResize();
            if(this._tabStripVisible === true)
                $(this._tabs).show();
            else
                $(this._tabs).hide();
            this._tab.doResize();
            var fixWidth = $(this._vp).width();
            var fixTabWidth = $(this._tabs).width();
            if(fixTabWidth > fixWidth + 1 && !this._haveLayoutFixing)
            {
                this._haveLayoutFixing = true;
                var self = this;
                setTimeout(function()
                {
                    delete self._haveLayoutFixing;
                    self._doResize()
                },1)
            }
        },
        _doTabHSResize: function()
        {
            var scrollbarSize = this._scrollbarSize;
            var host = this._host;
            if(!host)
                return;
            var availableWidth = this._vp.clientWidth;
            var w = host.clientWidth;
            var tabStripWidth = this._getActualTabStripRatio() * availableWidth;
            this._tabs.style.width = "" + (tabStripWidth - 1) + pixel;
            var hsWidth = w - (this._tabStripVisible ? tabStripWidth : 0) - scrollbarSize;
            this._hs.style.width = "" + (hsWidth > 0 ? hsWidth : 1) + pixel;
            this._hs.style.height = "" + scrollbarSize + pixel;
            if(this._tabStripVisible === true)
                $(this._tabs).show();
            else
                $(this._tabs).hide();
            this._tab.doResize()
        },
        _trigger: function(type, data)
        {
            if(this._eventSuspended === 0)
                $(this._userEventsElem).trigger(type,data)
        },
        _copyUserEvents: function(sheet)
        {
            sheet._eventHandler._eventSuspended = this._eventSuspended;
            for(var e = 0; e < this._userEvents.length; e++)
            {
                var item = this._userEvents[e];
                sheet.bind(item.type,item.data,item.fn)
            }
        },
        _suspendInvalidate: function()
        {
            var sheet = this.getActiveSheet();
            sheet._suspendInvalidate()
        },
        _resumeInvalidate: function()
        {
            var sheet = this.getActiveSheet();
            sheet._resumeInvalidate()
        },
        allowUserZoom: function(value)
        {
            if(arguments.length === 0)
                return this._allowUserZoom;
            this._allowUserZoom = value;
            return this
        },
        allowUserResize: function(value)
        {
            if(arguments.length === 0)
                return this._allowUserResize;
            this._allowUserResize = value;
            return this
        },
        tabStripVisible: function(value)
        {
            if(arguments.length === 0)
                return this._tabStripVisible;
            this._tabStripVisible = value;
            this._doResize();
            return this
        },
        tabEditable: function(value)
        {
            if(arguments.length === 0)
                return this._tabEditable;
            this._tabEditable = value;
            return this
        },
        newTabVisible: function(value)
        {
            if(arguments.length === 0)
                return this._newTabVisible;
            this._newTabVisible = value;
            this._doTabHSResize();
            return this
        },
        canUserEditFormula: function(value)
        {
            if(arguments.length === 0)
                return this._canUserEditFormula;
            this._canUserEditFormula = value;
            return this
        },
        startSheetIndex: function(value)
        {
            if(arguments.length === 0)
                return this._tab ? this._tab._firstTab : 0;
            if(this._tab && 0 <= value && value < this.getSheetCount())
                this._tab._firstTab = value;
            this._doTabHSResize();
            return this
        },
        highlightInvalidData: function(value)
        {
            if(arguments.length === 0)
                return this._highlightInvalidData;
            var self = this;
            var sheet = this.getActiveSheet();
            if(sheet)
                return sheet._bindToAutoRefresh(function(value)
                    {
                        self._highlightInvalidData = value;
                        return self
                    })(value);
            else
                self._highlightInvalidData = value
        },
        isPaintSuspended: function(value)
        {
            if(arguments.length === 0)
                return this._paintSuspended;
            else
            {
                if(this._paintSuspended !== value)
                {
                    this._paintSuspended = value;
                    for(var i = 0; i < this.sheets.length; i++)
                        this.sheets[i].isPaintSuspended(value)
                }
                return this
            }
        },
        repaint: function()
        {
            if(this._tab)
                this._tab.repaint();
            var sheet = this.getActiveSheet();
            if(sheet)
            {
                sheet.invalidateLayout();
                sheet.repaint()
            }
        },
        invalidateLayout: function()
        {
            this._doResize();
            var sheet = this.getActiveSheet();
            if(sheet)
                sheet.invalidateLayout()
        },
        getActiveSheet: function()
        {
            if(this.sheets.length > 0)
                return this.sheets[this._activeSheetIndex];
            else
                return null
        },
        getActiveSheetIndex: function()
        {
            return this._activeSheetIndex
        },
        setActiveSheetIndex: function(value)
        {
            if(typeof value !== "number")
                return;
            if(value === this._activeSheetIndex)
                return;
            var sheet = this.getActiveSheet();
            if(sheet)
                sheet._dispose();
            this._activeSheetIndex = value;
            sheet = this.getActiveSheet();
            if(sheet)
                this._setActiveSheetImp(sheet);
            this._doResize()
        },
        _setActiveSheetImp: function(sheet)
        {
            sheet._setHost(this._vp);
            var self = this;
            sheet._scrollCallback = function(e)
            {
                if(self.getActiveSheet() !== sheet)
                    return;
                if(e.action === cmd_scroll)
                {
                    if(e.orientation === dir_vertical)
                    {
                        var vbar = $(self._vs);
                        var o1 = vbar.wijspreadpanelex().data(e_wijspreadpanelex).options;
                        o1.vScroller.scrollValue = sheet._scrollTopRow;
                        vbar.wijspreadpanelex(cmd_refresh);
                        self._refreshScrollbar()
                    }
                    else if(e.orientation === dir_horizontal)
                    {
                        var hbar = $(self._hs);
                        var o2 = hbar.wijspreadpanelex().data(e_wijspreadpanelex).options;
                        o2.hScroller.scrollValue = sheet._scrollLeftCol;
                        hbar.wijspreadpanelex(cmd_refresh);
                        self._refreshScrollbar()
                    }
                }
                else if(e.action === cmd_resize)
                    self._doResize()
            };
            window.gcGlobal.activeElement = sheet;
            sheet._syncHScollbarPosition();
            sheet._syncVScrollbarPosition()
        },
        addSheet: function(index, sheet)
        {
            if(sheet === undefined || sheet === null)
                sheet = this._createSheet(this._getDefaultSheetName(index),200,20);
            if(sheet._name === undefined || sheet._name === null || sheet._name === "")
                sheet._name = this._getDefaultSheetName(index);
            var i,
                length = this.sheets.length;
            for(i = 0; i < length; i++)
                if(this.sheets[i]._name === sheet._name)
                    throw'NotSupportedException';
            this._copyUserEvents(sheet);
            var defSheetSource = null;
            if(this.sheets.length > 0)
                for(i = 0; !defSheetSource && i < this.sheets.length; i++)
                    defSheetSource = this.sheets[i]._getSheetSource();
            var oldActiveSheet = this.getActiveSheet();
            var oldActiveSheetIndex = this._activeSheetIndex;
            var p = this.sheets.length - index;
            var tmp = [];
            for(i = 0; i < p && this.sheets.length > 0; i++)
                tmp.push(this.sheets.pop());
            if(this.isPaintSuspended() !== sheet.isPaintSuspended())
                sheet.isPaintSuspended(this.isPaintSuspended());
            this.sheets.push(sheet);
            sheet.parent = this;
            sheet.borderWidth = 0;
            sheet.calcService = this.calcService;
            if(defSheetSource)
            {
                var sheetSource = sheet._getSheetSource();
                if(sheetSource)
                    sheetSource.shareCalculations(defSheetSource)
            }
            while(tmp.length > 0)
                this.sheets.push(tmp.pop());
            if(this.sheets.length === 1)
            {
                if(this._vp)
                    sheet._setHost(this._vp);
                this._activeSheetIndex = 0
            }
            else
                this._activeSheetIndex = oldActiveSheetIndex;
            var newActiveSheet = this.getActiveSheet();
            if(newActiveSheet !== oldActiveSheet)
            {
                if(oldActiveSheet)
                    oldActiveSheet._dispose();
                if(newActiveSheet)
                    this._setActiveSheetImp(newActiveSheet);
                this._doResize()
            }
            if(this._tab)
                this._tab.repaint()
        },
        removeSheet: function(index)
        {
            if(isNaN(index) || index < 0 || index >= this.sheets.length)
                throw new Error("index is out of range");
            var activeSheetChanged = index <= this._activeSheetIndex || index === this.sheets.length - 1;
            var oldActiveSheet = this.getActiveSheet();
            if(index < this._activeSheetIndex)
                this._activeSheetIndex--;
            var removedSheet = this.sheets[index];
            removedSheet._disposeUserEvents();
            this.sheets.splice(index,1);
            if(this.sheets.length === 0)
                this._activeSheetIndex = -1;
            else if(this._activeSheetIndex >= this.sheets.length)
                this._activeSheetIndex = this.sheets.length - 1;
            if(activeSheetChanged)
            {
                if(oldActiveSheet)
                    oldActiveSheet._dispose();
                if(this._activeSheetIndex > -1)
                    this._setActiveSheetImp(this.sheets[this._activeSheetIndex])
            }
            if(this._tab)
            {
                if(this._tab._firstTab >= this.sheets.length)
                    this._tab._firstTab = this.sheets.length - 1;
                if(this._tab._firstTab < 0)
                    this._tab._firstTab = 0;
                this._tab.repaint()
            }
        },
        clearSheets: function()
        {
            for(var i = 0; i < this.sheets.length; i++)
            {
                this.sheets[i]._disposeUserEvents();
                this.sheets[i]._dispose()
            }
            this.sheets.splice(0,this.sheets.length);
            this._activeSheetIndex = -1;
            if(this._tab)
            {
                this._tab._firstTab = 0;
                this._tab.repaint()
            }
        },
        getSheet: function(index)
        {
            if(index >= 0 && index < this.sheets.length)
                return this.sheets[index];
            else
                return null
        },
        getSheetFromName: function(name)
        {
            for(var i = 0; i < this.sheets.length; i++)
                if(this.sheets[i].getName() === name)
                    return this.sheets[i];
            return null
        },
        getSheetCount: function()
        {
            return this.sheets.length
        },
        setSheetCount: function(count)
        {
            if(count < 0)
                throw'ArgumentOutOfRangeException';
            else if(count === 0)
                this.clearSheets();
            var length = this.sheets.length,
                i;
            if(count < length)
                for(i = length - 1; i >= count; i--)
                    this.removeSheet(i);
            else if(count > length)
                for(i = length; i < count; i++)
                    this.addSheet(i)
        },
        search: function(searchCondition)
        {
            if(!searchCondition)
                return null;
            var SearchFoundFlags_None = UI.SearchFoundFlags.None,
                SearchResult = UI.SearchResult;
            if(!searchCondition.searchString || searchCondition.searchTarget === SearchFoundFlags_None || this.getSheetCount() <= 0)
                return new SearchResult;
            if(searchCondition.startSheetIndex === -1)
                searchCondition.startSheetIndex = 0;
            if(searchCondition.endSheetIndex === -1)
                searchCondition.endSheetIndex = this.getSheetCount() - 1;
            if(searchCondition.endSheetIndex >= searchCondition.startSheetIndex && 0 <= searchCondition.startSheetIndex && searchCondition.startSheetIndex < this.getSheetCount() && 0 <= searchCondition.endSheetIndex && searchCondition.endSheetIndex < this.getSheetCount())
                for(var sheetIndex = searchCondition.startSheetIndex; sheetIndex <= searchCondition.endSheetIndex; sheetIndex++)
                {
                    var sheet = this.getSheet(sheetIndex);
                    var searchResult = sheet.search(searchCondition);
                    if(searchResult && searchResult.searchFoundFlag !== SearchFoundFlags_None)
                    {
                        searchResult.foundSheetIndex = sheetIndex;
                        return searchResult
                    }
                }
            return new SearchResult
        },
        showCell: function(row, col, verticalPosition, horizontalPosition)
        {
            var sheet = this.getActiveSheet();
            if(sheet)
                sheet.showCell(row,col,verticalPosition,horizontalPosition)
        },
        showColumn: function(col, horizontalPosition)
        {
            var sheet = this.getActiveSheet();
            if(sheet)
                sheet.showColumn(col,horizontalPosition)
        },
        showRow: function(row, verticalPosition)
        {
            var sheet = this.getActiveSheet();
            if(sheet)
                sheet.showRow(row,verticalPosition)
        },
        showActiveCell: function(verticalPosition, horizontalPosition)
        {
            var sheet = this.getActiveSheet();
            if(sheet)
                sheet.showCell(sheet._activeRowIndex,sheet._activeColIndex,verticalPosition,horizontalPosition)
        },
        getCustomName: function(name)
        {
            return this._names ? this._names[name] : null
        },
        addCustomName: function(name, formula, baseRow, baseCol)
        {
            if(name === undefined || name === null || name === '' || formula === undefined || formula === null || formula === '')
                throw new Error("invalid custom name");
            if(!this._names)
                this._names = {};
            var expr = this.calcService.parse(formula,baseRow,baseCol);
            this._names[name] = new UI.NameInfo(name,expr,baseRow,baseCol);
            this._recalcAll()
        },
        removeCustomName: function(name)
        {
            if(this._names && name !== undefined && name !== null && name !== '' && this._names.hasOwnProperty(name))
            {
                delete this._names[name];
                this._recalcAll()
            }
        },
        clearCustomNames: function()
        {
            if(this._names)
            {
                delete this._names;
                this._recalcAll()
            }
        },
        _findCustomName: function(name)
        {
            if(name === undefined || name === null || name === '')
                return null;
            var fn = this.getCustomName(name);
            if((fn === undefined || fn === null) && this.parent && this.parent.getCustomName)
                fn = this.parent.getCustomName(name);
            return fn
        },
        addCustomFunction: function(fn)
        {
            if(fn === undefined || fn === null || !(fn instanceof GrapeCity.Calc.Functions.Function))
                throw new Error("invalid custom function");
            var fnName = fn.name.toUpperCase();
            if(!this._functions)
                this._functions = {};
            this._functions[fnName] = fn;
            this._recalcAll()
        },
        getCustomFunction: function(name)
        {
            if(this._functions && name !== undefined && name !== null && name !== '')
                return this._functions[name.toUpperCase()];
            return null
        },
        removeCustomFunction: function(name)
        {
            if(this._functions && name !== undefined && name !== null && name !== '')
            {
                name = name.toUpperCase();
                if(this._functions.hasOwnProperty(name))
                {
                    delete this._functions[name];
                    this._recalcAll()
                }
            }
        },
        clearCustomFunctions: function()
        {
            if(this._functions)
            {
                var names = [];
                for(var i in this._functions)
                    if(this._functions.hasOwnProperty(i))
                        names.push(i);
                delete this._functions;
                this._recalcAll()
            }
        },
        _recalcAll: function()
        {
            if(this.calcService)
                this.calcService.recalculateAll()
        },
        referenceStyle: function(value)
        {
            var R1C1 = UI.ReferenceStyle.R1C1,
                A1 = UI.ReferenceStyle.A1;
            if(arguments.length === 0)
                return this.calcService.useR1C1 ? R1C1 : A1;
            this.calcService.useR1C1 = value === R1C1;
            return this
        },
        bind: function(type, data, fn)
        {
            this._userEvents.push({
                type: type,
                data: data,
                fn: fn
            });
            $(this._userEventsElem).bind(type + ns_spread,data,fn);
            for(var i = 0; i < this.sheets.length; i++)
            {
                var sheet = this.sheets[i];
                sheet.bind(type,data,fn)
            }
        },
        unbind: function(type, fn)
        {
            for(var e = 0; e < this._userEvents.length; e++)
            {
                var item = this._userEvents[e];
                if(item.type === type)
                    this._userEvents.splice(e,1)
            }
            $(this._userEventsElem).unbind(type + ns_spread,fn);
            for(var i = 0; i < this.sheets.length; i++)
            {
                var sheet = this.sheets[i];
                sheet.unbind(type,fn)
            }
        },
        unbindAll: function()
        {
            this._userEvents.length = 0;
            $(this._userEventsElem).unbind(ns_spread);
            for(var i = 0; i < this.sheets.length; i++)
            {
                var sheet = this.sheets[i];
                sheet.unbindAll()
            }
        },
        suspendEvent: function()
        {
            this._eventSuspended++;
            for(var i = 0; i < this.sheets.length; i++)
            {
                var sheet = this.sheets[i];
                sheet.suspendEvent()
            }
        },
        resumeEvent: function()
        {
            this._eventSuspended--;
            if(this._eventSuspended < 0)
                this._eventSuspended = 0;
            for(var i = 0; i < this.sheets.length; i++)
            {
                var sheet = this.sheets[i];
                sheet.resumeEvent()
            }
        },
        saveData: function()
        {
            if(!this._host)
                return;
            var id = this._host.id;
            var dataField = document.getElementById(id + data_postfix);
            if(dataField === undefined || dataField === null)
                return;
            var sheetCount = this.getSheetCount();
            var t = [];
            var sdata = {
                    activeSheetIndex: this.getActiveSheetIndex(),
                    sheetCount: sheetCount,
                    tabStripRatio: this.getTabStripRatio(),
                    tabStripVisible: this._tabStripVisible,
                    tabEditable: this._tabEditable,
                    newTabVisible: this._newTabVisible,
                    allowCellOverflow: this.getActiveSheet() ? this.getActiveSheet()._allowCellOverflow : false,
                    theme: this.getActiveSheet().currentTheme().name()
                };
            t.push(JSON.stringify(sdata));
            for(var i = 0; i < sheetCount; i++)
            {
                var sheet = this.getSheet(i);
                var deletedRowsString = "";
                if(sheet.deletedRows)
                    deletedRowsString = JSON.stringify(sheet.deletedRows);
                var rInfos = {};
                for(var j in sheet._rowInfos)
                    if(typeof j !== "function")
                    {
                        var rx = sheet._rowInfos[j];
                        if(rx && rx.dirty)
                            rInfos[j] = rx
                    }
                var cInfos = {};
                for(j in sheet._colInfos)
                    if(typeof j !== "function")
                    {
                        var cx = sheet._colInfos[j];
                        if(cx && cx.dirty)
                            cInfos[j] = cx
                    }
                var state = {
                        name: sheet._name,
                        ar: sheet._activeRowIndex,
                        ac: sheet._activeColIndex,
                        columns: cInfos,
                        rows: rInfos,
                        rowCount: sheet.getRowCount(),
                        columnCount: sheet.getColumnCount(),
                        data: sheet._dataModel.dirtyNodes,
                        deletedRows: deletedRowsString,
                        spans: sheet._getSpanModel(),
                        rowHeaderAutoText: sheet.rowHeaderAutoText,
                        colHeaderAutoText: sheet.colHeaderAutoText,
                        referenceStyle: sheet.referenceStyle(),
                        gridline: sheet.gridline,
                        frozenRowCount: sheet.frozenRowCount,
                        frozenColCount: sheet.frozenColCount
                    };
                t.push(JSON.stringify(state))
            }
            dataField.value = JSON.stringify(t)
        },
        doCommand: function(action)
        {
            this._undoManager.doAction(action)
        },
        allowUndo: function(value)
        {
            if(arguments.length === 0)
                return this._allowUndo;
            this._allowUndo = value;
            if(this._undoManager)
                this._undoManager._allowUndo = value;
            return this
        },
        undoManager: function()
        {
            if(!this._undoManager)
                this._undoManager = new UI._UndoManager(this,-1,this.allowUndo());
            return this._undoManager
        },
        canUserDragDrop: function(value)
        {
            if(arguments.length === 0)
            {
                if(this._allowDragDrop === undefined || this._allowDragDrop === null)
                    this._allowDragDrop = true;
                return this._allowDragDrop
            }
            else
            {
                this._allowDragDrop = value;
                return this
            }
        },
        canUserDragFill: function(value)
        {
            if(arguments.length === 0)
            {
                if(this._allowDragFill === undefined || this._allowDragFill === null)
                    this._allowDragFill = true;
                return this._allowDragFill
            }
            else
            {
                if(this._allowDragFill !== value)
                {
                    this._allowDragFill = value;
                    var sheet = this.getActiveSheet();
                    if(sheet)
                        if(sheet.getSelections().length === 1)
                            sheet._render.repaintSelection(sheet.getSelections().toArray()[0])
                }
                return this
            }
        },
        toJSON: function()
        {
            var sheetCount = this.getSheetCount();
            var sdata = {
                    activeSheetIndex: this.getActiveSheetIndex(),
                    sheetCount: sheetCount,
                    tabStripRatio: this.getTabStripRatio(),
                    tabStripVisible: this._tabStripVisible,
                    tabEditable: this._tabEditable,
                    newTabVisible: this._newTabVisible,
                    referenceStyle: this.referenceStyle(),
                    useWijmoTheme: this.useWijmoTheme,
                    canUserEditFormula: this._canUserEditFormula,
                    startSheetIndex: this.startSheetIndex(),
                    allowUndo: this._allowUndo,
                    allowUserZoom: this._allowUserZoom,
                    allowUserResize: this._allowUserResize,
                    allowDragDrop: this._allowDragDrop,
                    allowDragFill: this._allowDragFill,
                    highlightInvalidData: this._highlightInvalidData
                };
            var sheets = {};
            for(var i = 0; i < sheetCount; i++)
            {
                var sheet = this.getSheet(i);
                sheets[sheet._name] = sheet.toJSON()
            }
            sdata.sheets = sheets;
            var names = [];
            if(this._names && this.calcService)
                for(var n in this._names)
                    if(this._names.hasOwnProperty(n))
                    {
                        var ni = this._names[n];
                        var name = ni.getName(),
                            row = ni.getRow(),
                            col = ni.getColumn(),
                            expr = ni.getExpression();
                        var f = this.calcService.unparse(expr,row,col);
                        names.push({
                            name: name,
                            formula: f,
                            row: row,
                            col: col
                        })
                    }
            sdata.names = names;
            return sdata
        },
        fromJSON: function(spreadData)
        {
            if(!spreadData)
                return;
            var oldState = this.isPaintSuspended();
            this.isPaintSuspended(true);
            try
            {
                this.calcService = new GrapeCity.Calc.Service(this);
                this._availableSheetIndex = -1;
                this.clearSheets();
                this.setSheetCount(spreadData.sheetCount);
                if(spreadData.referenceStyle !== null && spreadData.referenceStyle !== undefined)
                    this.referenceStyle(spreadData.referenceStyle);
                var i = 0,
                    sheet;
                if(spreadData.sheets)
                    for(var s in spreadData.sheets)
                        if(typeof s === "string")
                        {
                            var sheetData = spreadData.sheets[s];
                            sheet = this.sheets[i];
                            sheet.fromJSON(sheetData);
                            i++
                        }
                this.setActiveSheetIndex(spreadData.activeSheetIndex);
                if(typeof spreadData.tabStripRatio !== const_undefined)
                    this.setTabStripRatio(spreadData.tabStripRatio);
                if(typeof spreadData.tabStripVisible !== const_undefined)
                    this._tabStripVisible = spreadData.tabStripVisible;
                if(typeof spreadData.tabEditable !== const_undefined)
                    this._tabEditable = spreadData.tabEditable;
                if(typeof spreadData.newTabVisible !== const_undefined)
                    this._newTabVisible = spreadData.newTabVisible;
                this.gcSpreadsheet = spreadData.gcSpreadsheet;
                if(typeof spreadData.useWijmoTheme !== const_undefined)
                    this.useWijmoTheme = spreadData.useWijmoTheme;
                if(typeof spreadData.canUserEditFormula !== const_undefined)
                    this._canUserEditFormula = spreadData.canUserEditFormula;
                if(typeof spreadData.startSheetIndex !== const_undefined)
                    this.startSheetIndex(spreadData.startSheetIndex);
                if(typeof spreadData.allowUndo !== const_undefined)
                    this.allowUndo(spreadData.allowUndo);
                if(typeof spreadData.allowUserZoom !== const_undefined)
                    this._allowUserZoom = spreadData.allowUserZoom;
                if(typeof spreadData.allowUserResize !== const_undefined)
                    this._allowUserResize = spreadData.allowUserResize;
                if(typeof spreadData.allowDragDrop !== const_undefined)
                    this._allowDragDrop = spreadData.allowDragDrop;
                if(typeof spreadData.allowDragFill !== const_undefined)
                    this._allowDragFill = spreadData.allowDragFill;
                if(typeof spreadData.highlightInvalidData !== const_undefined)
                    this._highlightInvalidData = spreadData.highlightInvalidData;
                if(spreadData.names)
                {
                    for(var n = 0; n < spreadData.names.length; n++)
                    {
                        var ni = spreadData.names[n];
                        this.addCustomName(ni.name,ni.formula,ni.row,ni.col)
                    }
                    for(var i = 0; i < spreadData.sheetCount; i++)
                        this.sheets[i]._rebuildCalcNodes()
                }
            }
            finally
            {
                this.isPaintSuspended(oldState)
            }
        },
        setTabStripRatio: function(tabStripRatio, skipRefreshScrollbar)
        {
            var ratio;
            if(isNaN(ratio = parseFloat(tabStripRatio)))
                throw"ArgumentException";
            var totalWidth = this._vp.clientWidth;
            var minRatio = this._tab._resizeBarWidth / totalWidth;
            if(ratio < minRatio)
                this._tabStripRatio = minRatio;
            else if(ratio > 1)
                this._tabStripRatio = 1;
            else
                this._tabStripRatio = ratio;
            if(ratio < 0)
                this._tabStripRatioUserSet = 0;
            else if(ratio > 1)
                this._tabStripRatioUserSet = 1;
            else
                this._tabStripRatioUserSet = ratio;
            this._doTabHSResize();
            if(!skipRefreshScrollbar)
            {
                var hsb = $(this._hs);
                if(hsb.wijspreadpanelex)
                    hsb.wijspreadpanelex(cmd_refresh);
                this._refreshScrollbar()
            }
        },
        getTabStripRatio: function()
        {
            if(this._tabStripRatioUserSet === undefined || this._tabStripRatioUserSet === null)
                this._tabStripRatioUserSet = this._tabStripRatio;
            return this._tabStripRatioUserSet
        },
        _getActualTabStripRatio: function()
        {
            return this._tabStripRatio
        }
    };
    var gcTab_ns = ".gcTab",
        tabNameEditor_ns = ".tabNameEditor",
        spliter_ns = ".spliter";
    var mouse = "mouse",
        e_down = "down",
        e_move = "move",
        e_up = "up",
        e_out = "out",
        e_focusin = "focusin",
        e_focusout = "focusout",
        e_keydown = "keydown";
    var spliter_mousemove = mouse + e_move + spliter_ns,
        spliter_mouseup = mouse + e_up + spliter_ns;
    var tabNameEditor_keydown = e_keydown + tabNameEditor_ns,
        tabNameEditor_focusin = e_focusin + tabNameEditor_ns,
        tabNameEditor_focusout = e_focusout + tabNameEditor_ns;
    var gcTab_mousedown = mouse + e_down + gcTab_ns,
        gcTab_mousemove = mouse + e_move + gcTab_ns,
        gcTab_mouseup = mouse + e_up + gcTab_ns,
        gcTab_mouseout = mouse + e_out + gcTab_ns,
        gcTable_dbclick = "dblclick" + gcTab_ns;
    var tag_canvas = "canvas",
        dc_2d = "2d";
    var hit_element_resizebar = "resizeBar",
        hit_element_navbutton = "navButton",
        hit_element_tab = "tab",
        hit_element_newTab = "newTab";
    var color_FFFFFF = "#FFFFFF",
        color_D8E7FA = "#D8E7FA",
        color_D5E5F9 = "#D5E5F9",
        color_B6D2F5 = "#B6D2F5",
        color_D9E7F9 = "#D9E7F9",
        color_688CAF = "#688CAF",
        color_92A5C7 = "#92A5C7",
        color_black = "black",
        color_white = "white";
    UI._GcTab = function(name)
    {
        this._init(name)
    };
    UI._GcTab.prototype = {
        _init: function(name)
        {
            this._bounds = new UI.Rect(0,0,200,20);
            this.name = name
        },
        _spread: null,
        _activeIndex: 0,
        _firstTab: 0,
        _tabStartPos: 74,
        _tabLeftPadding: 12,
        _tabRightPadding: 8,
        _tabSpace: 3,
        _activePos: 70,
        _hoverNavButton: -1,
        _hoverTab: -1,
        _navButtonSize: 18,
        _newTabSize: 48,
        _firstTabSpace: 5,
        _font: "10pt Arial",
        _resizeBarWidth: 8,
        _tabSizes: [],
        _setHost: function(host)
        {
            var canvas = document.createElement(tag_canvas);
            canvas.setAttribute("id",this.name);
            host.appendChild(canvas);
            canvas.gcObject = true;
            var $canvas;
            if(this.canvas)
            {
                $canvas = $(this.canvas);
                $canvas.unbind(gcTab_mousedown);
                $canvas.unbind(gcTab_mousemove);
                $canvas.unbind(gcTab_mouseup);
                $canvas.unbind(gcTab_mouseout);
                $canvas.unbind(gcTable_dbclick);
                this.canvas.parentNode.removeChild(this.canvas)
            }
            this.canvas = canvas;
            if(!GrapeCity.UI._isStandardCanvas() && GrapeCity.UI._isSilverlightCanvas())
            {
                canvas.setAttribute('renderMethod','auto');
                var self = this;
                this.test = function linkSilverlight(tab)
                {
                    tab._spread.initPaint.call(tab._spread)
                };
                canvas.setAttribute('onload',this.test(self))
            }
            var createEventHandler = UI.createEventHandler;
            $canvas = $(canvas);
            $canvas.bind(gcTab_mousedown,createEventHandler(this,this.doMouseDown));
            $canvas.bind(gcTab_mousemove,createEventHandler(this,this.doMouseMove));
            $canvas.bind(gcTab_mouseup,createEventHandler(this,this.doMouseUp));
            $canvas.bind(gcTab_mouseout,createEventHandler(this,this.doMouseOut));
            $canvas.bind(gcTable_dbclick,createEventHandler(this,this.doMouseDbClick));
            this.doResize()
        },
        setOwner: function(owner)
        {
            this._spread = owner;
            if(owner && owner._font && owner._font.length > 0)
                this._font = owner._font
        },
        _dispose: function()
        {
            var canvas = this.canvas;
            if(canvas)
            {
                $(canvas).unbind(gcTab_mousedown);
                $(canvas).unbind(gcTab_mousemove);
                $(canvas).unbind(gcTab_mouseup);
                $(canvas).unbind(gcTable_dbclick);
                $(canvas).unbind(gcTab_mouseout);
                canvas.parentNode.removeChild(canvas)
            }
        },
        getBounds: function()
        {
            return this._bounds
        },
        setBounds: function(rect)
        {
            this._bounds.x = rect.x;
            this._bounds.y = rect.y;
            this._bounds.width = rect.width;
            this._bounds.height = rect.height
        },
        doNavButtonClick: function(btnClicked)
        {
            var t = this._firstTab;
            var self = this,
                repeatClickIntevel = 200;
            switch(btnClicked)
            {
                case 0:
                    t = 0;
                    break;
                case 1:
                    t -= 1;
                    if(t > 0)
                        this._repeatDown = window.setTimeout(function()
                        {
                            self.doNavButtonClick(1)
                        },repeatClickIntevel);
                    if(t < 0)
                        t = 0;
                    break;
                case 2:
                    t += 1;
                    if(t < this._spread.sheets.length - 1)
                        this._repeatDown = window.setTimeout(function()
                        {
                            self.doNavButtonClick(2)
                        },repeatClickIntevel);
                    if(t >= this._spread.sheets.length)
                        t = this._spread.sheets.length - 1;
                    if(t < 0)
                        t = 0;
                    break;
                case 3:
                    t = this._spread.sheets.length - 1;
                    if(t < 0)
                        t = 0;
                    break;
                default:
                    return
            }
            if(t !== this._firstTab)
            {
                this._firstTab = t;
                this.repaint()
            }
        },
        _hitTest: function(left, top)
        {
            var rect = this.getBounds();
            if(rect.x + rect.width - this._resizeBarWidth < left && left < rect.x + rect.width)
                return{element: hit_element_resizebar};
            var navButton = -1;
            var x = 0,
                i;
            for(i = 0; i < 4; i++)
            {
                if(x <= left && left < x + this._navButtonSize)
                {
                    navButton = i;
                    break
                }
                x += this._navButtonSize
            }
            if(navButton !== -1)
                return{
                        element: hit_element_navbutton,
                        index: i
                    };
            x = this._tabStartPos;
            if(this._firstTab > 0)
            {
                if(x < left && left < x + this._firstTabSpace + this._tabSpace)
                    return{
                            element: hit_element_tab,
                            index: this._firstTab - 1,
                            position: x
                        };
                x += this._firstTabSpace
            }
            var tabSize = 0;
            if(this._firstTab > -1)
                for(i = this._firstTab; i < this._spread.sheets.length && i < this._tabSizes.length; i++)
                {
                    tabSize = this._tabSizes[i];
                    if(x < left && left < x + tabSize + this._tabSpace)
                        return{
                                element: hit_element_tab,
                                index: i,
                                position: x
                            };
                    x += tabSize + this._tabSpace
                }
            if(this._spread._newTabVisible)
                if(left > x && left < x + this._newTabSize)
                    return{
                            element: hit_element_newTab,
                            position: x
                        };
            return{element: ""}
        },
        doMouseDown: function(e)
        {
            var t = $(this._getCanvas()).offset();
            var left = e.pageX - t.left;
            var top = e.pageY - t.top;
            var activeSheet = this._spread.getActiveSheet();
            var sheet,
                args;
            if(this._tabNameEditor)
                this.endSheetTabEditing(activeSheet,false);
            var hitTestInfo = this._hitTest(left,top);
            var E = UI.Events;
            if(e.button === 0)
            {
                if(hitTestInfo.element === hit_element_resizebar)
                {
                    this.resizeTab = true;
                    this.activeX = e.pageX;
                    this.handleDocumentMouseMove()
                }
                else if(hitTestInfo.element === hit_element_navbutton)
                    this.doNavButtonClick(hitTestInfo.index);
                else if(hitTestInfo.element === hit_element_tab)
                {
                    var i = hitTestInfo.index;
                    var x = hitTestInfo.position;
                    this._activeIndex = i;
                    this._activePos = x;
                    sheet = this._spread.getSheet(i);
                    this._spread._trigger(E.SheetTabClick,{
                        sheet: sheet,
                        sheetName: sheet._name,
                        sheetTabIndex: i
                    });
                    if(i !== this._spread._activeSheetIndex)
                    {
                        args = {
                            oldSheet: activeSheet,
                            newSheet: sheet,
                            cancel: false
                        };
                        this._spread._trigger(E.ActiveSheetChanging,args);
                        if(args && args.cancel === false)
                        {
                            this._spread.setActiveSheetIndex(i);
                            this._spread._trigger(E.ActiveSheetChanged,{
                                oldSheet: activeSheet,
                                newSheet: sheet
                            });
                            if(this._activeIndex < this._firstTab)
                                this._firstTab--;
                            else if(this._activeIndex > this._firstTab && this._activePos + this._tabSizes[this._activeIndex] > this.getBounds().width - this._resizeBarWidth)
                                this._firstTab++;
                            this.repaint()
                        }
                    }
                }
                else if(hitTestInfo.element === hit_element_newTab)
                {
                    var spread = this._spread;
                    spread._trigger(E.SheetTabClick,{
                        sheet: null,
                        sheetName: null,
                        sheetTabIndex: -1
                    });
                    var index = this._spread.sheets.length;
                    sheet = this._spread._createSheet(this._spread._getDefaultSheetName(index));
                    this._spread.addSheet(index,sheet);
                    this._activeIndex = index;
                    this._activePos = hitTestInfo.position;
                    args = {
                        oldSheet: activeSheet,
                        newSheet: sheet,
                        cancel: false
                    };
                    this._spread._trigger(E.ActiveSheetChanging,args);
                    if(args && args.cancel === false)
                    {
                        if(sheet._eventHandler)
                            sheet._eventHandler._doCallBack("activeSheetChanged," + this._spread._activeSheetIndex + "," + index,{
                                oldValue: this._spread._activeSheetIndex,
                                newValue: index
                            });
                        this._spread.setActiveSheetIndex(index);
                        this._spread._trigger(E.ActiveSheetChanged,{
                            oldSheet: activeSheet,
                            newSheet: sheet
                        });
                        while(this._activeIndex > this._firstTab && this._activePos + this._tabSizes[this._activeIndex] + this._newTabSize > this.getBounds().width - this._resizeBarWidth)
                        {
                            this._activePos -= this._tabSizes[this._firstTab];
                            this._firstTab++
                        }
                        this.repaint()
                    }
                }
                activeSheet = this._spread.getActiveSheet();
                if(activeSheet && !activeSheet.isEditing() && activeSheet._eventHandler)
                    activeSheet._eventHandler._setFocus()
            }
            return false
        },
        doMouseMove: function(e)
        {
            var cursor_default = "default",
                cursor_wresize = "w-resize";
            if(this.resizeTab)
            {
                this.canvas.style.cursor = cursor_wresize;
                var d = e.pageX - this.activeX;
                var totalWidth = this._spread._vp.clientWidth;
                var ss = this._spread;
                ss.setTabStripRatio(ss._getActualTabStripRatio() + d / totalWidth,true);
                var minRatio = this._resizeBarWidth / totalWidth;
                var maxRatio = 1;
                if(ss._getActualTabStripRatio() < minRatio)
                {
                    ss.setTabStripRatio(minRatio,true);
                    this.activeX = this._resizeBarWidth
                }
                else if(ss._getActualTabStripRatio() >= maxRatio)
                {
                    ss.setTabStripRatio(maxRatio,true);
                    this.activeX = totalWidth
                }
                else
                    this.activeX = e.pageX
            }
            else
            {
                this._hoverNavButton = -1;
                this._hoverTab = -1;
                var t = $(this._getCanvas()).offset();
                var left = e.pageX - t.left;
                var top = e.pageY - t.top;
                var hitTestInfo = this._hitTest(left,top);
                if(hitTestInfo.element === "")
                {
                    this.canvas.style.cursor = cursor_default;
                    this.repaint();
                    return false
                }
                else if(hitTestInfo.element === hit_element_resizebar)
                    this.canvas.style.cursor = cursor_wresize;
                else
                {
                    this.canvas.style.cursor = cursor_default;
                    if(hitTestInfo.element === hit_element_navbutton)
                        this._hoverNavButton = hitTestInfo.index;
                    else if(hitTestInfo.element === hit_element_tab)
                        this._hoverTab = hitTestInfo.index;
                    else if(hitTestInfo.element === hit_element_newTab)
                        this._hoverTab = -2
                }
                this.repaint()
            }
            return false
        },
        doMouseUp: function(e)
        {
            if(this.resizeTab)
            {
                this.resizeTab = false;
                var hsb = $(this._spread._hs);
                if(hsb.wijspreadpanelex)
                    hsb.wijspreadpanelex(cmd_refresh);
                this._spread._refreshScrollbar()
            }
            if(this._repeatDown)
            {
                window.clearTimeout(this._repeatDown);
                this._repeatDown = null
            }
            this.unhandleDocumentMouseMove()
        },
        doMouseOut: function(e)
        {
            var oldNav = this._hoverNavButton;
            this._hoverNavButton = -1;
            var oldTab = this._hoverTab;
            this._hoverTab = -1;
            if(this._hoverNavButton !== oldNav || this._hoverTab !== oldTab)
                this.repaint();
            return false
        },
        doMouseDbClick: function(e)
        {
            if(!this._spread._tabEditable)
                return false;
            var offset = $(this._getCanvas()).offset();
            var left = e.pageX - offset.left;
            var top = e.pageY - offset.top;
            var hitTestInfo = this._hitTest(left,top);
            if(hitTestInfo.element === hit_element_tab)
            {
                var element = window.gcGlobal.activeElement;
                if(element && element.endEdit)
                    element.endEdit();
                window.gcGlobal.activeElement = null;
                var tabIndex = this._activeIndex;
                var currentSheet = this._spread.sheets[tabIndex];
                var currentTabSize = this._tabSizes[tabIndex];
                var canvasOffset = $(this._getCanvas()).offset();
                this._spread._trigger(UI.Events.SheetTabDoubleClick,{
                    sheet: currentSheet,
                    sheetName: currentSheet._name,
                    sheetTabIndex: tabIndex
                });
                var t = document.createElement(tag_input);
                t.type = "text";
                t.value = currentSheet._name;
                t.setAttribute("contentEditable","true");
                t.setAttribute("autocomplete","off");
                t.style.position = "absolute";
                t.style.margin = zero;
                t.style.padding = zero;
                t.style.margin = zero;
                t.style.left = canvasOffset.left + this._activePos + this._tabLeftPadding + pixel;
                t.style.top = canvasOffset.top + 1 + pixel;
                t.style.width = currentTabSize - this._tabLeftPadding - this._tabRightPadding + 2 + pixel;
                t.style.backgroundColor = color_white;
                t.style.borderWidth = 0;
                t.style.outline = cssNone;
                document.body.insertBefore(t,null);
                this._tabNameEditor = t;
                var self = this;
                $(t).addClass("sheetTabEditor");
                $(t).bind(tabNameEditor_keydown,function(e)
                {
                    var Key = UI.Key;
                    if(e.keyCode === Key.enter)
                    {
                        self.endSheetTabEditing(currentSheet,false);
                        return false
                    }
                    else if(e.keyCode === Key.esc)
                    {
                        self.endSheetTabEditing(currentSheet,true);
                        return false
                    }
                });
                $(t).bind(tabNameEditor_focusin,function(e)
                {
                    t.selectionStart = 0;
                    t.selectionEnd = t.value.length
                });
                $(t).bind(tabNameEditor_focusout,function(e)
                {
                    self.endSheetTabEditing(currentSheet,false)
                });
                t.focus()
            }
        },
        _isValidSheetName: function(sheetName)
        {
            if(sheetName === undefined || sheetName === null || sheetName === "")
                return false;
            var length = this._spread.sheets.length;
            for(var i = 0; i < length; i++)
            {
                var sheet = this._spread.sheets[i];
                if(sheet !== this._spread.getActiveSheet())
                    if(sheetName === sheet._name)
                        return false
            }
            return true
        },
        endSheetTabEditing: function(sheet, cancel)
        {
            if(this._tabNameEditor)
            {
                var newName = this._tabNameEditor.value;
                if(cancel === false && newName !== undefined && newName !== null && newName !== "" && this._isValidSheetName(newName))
                {
                    var action = new UI.UndoRedo.SheetRenameUndoAction(sheet,newName);
                    sheet._doCommand(action)
                }
                $(this._tabNameEditor).unbind(tabNameEditor_keydown);
                $(this._tabNameEditor).unbind(tabNameEditor_focusin);
                $(this._tabNameEditor).unbind(tabNameEditor_focusout);
                this._tabNameEditor.parentNode.removeChild(this._tabNameEditor);
                delete this._tabNameEditor
            }
        },
        handleDocumentMouseMove: function()
        {
            if(!this._isCapture)
            {
                var self = this;
                $(document).bind(spliter_mousemove,function(e)
                {
                    self.doMouseMove(e)
                });
                $(document).bind(spliter_mouseup,function(e)
                {
                    self.doMouseUp(e)
                });
                var sheet = this._spread.getActiveSheet();
                if(sheet)
                    sheet._continueMouseUpBubble = true;
                this._isCapture = true
            }
        },
        unhandleDocumentMouseMove: function()
        {
            if(this._isCapture)
            {
                this._isCapture = false;
                $(document).unbind(spliter_mousemove);
                $(document).unbind(spliter_mouseup);
                var sheet = this._spread.getActiveSheet();
                if(sheet)
                    delete sheet._continueMouseUpBubble
            }
        },
        repaint: function(clipRect)
        {
            var c = this._getCanvas();
            if(c)
            {
                if(!c.getContext && GrapeCity.UI._isSilverlightCanvas())
                    if(!c.getContext)
                        return;
                if(!c.getContext && typeof window.FlashCanvas !== const_undefined)
                    window.FlashCanvas.initElement(c);
                if(c.getContext)
                    this.paint(c.getContext(dc_2d),clipRect)
            }
        },
        doResize: function()
        {
            var canvas = this._getCanvas();
            if(!canvas || !canvas.parentNode)
                return;
            if(canvas.parentNode.clientWidth === 0 || canvas.parentNode.clientHeight === 0)
                return;
            canvas.style.display = cssNone;
            canvas.width = Math.max(canvas.parentNode.clientWidth,0);
            canvas.height = Math.max(canvas.parentNode.clientHeight,0);
            canvas.style.display = "";
            this._bounds.width = canvas.clientWidth;
            this._bounds.height = canvas.clientHeight;
            this.repaint()
        },
        paint: function(ctx, clipRect)
        {
            var rect = this.getBounds();
            if(clipRect)
            {
                if(clipRect.x >= rect.x + rect.width)
                    return;
                if(clipRect.y >= rect.y + rect.height)
                    return;
                if(clipRect.x + clipRect.width > rect.width)
                {
                    clipRect.width = rect.width - clipRect.x;
                    if(clipRect.width <= 0)
                        return
                }
                if(clipRect.y + clipRect.height > rect.height)
                {
                    clipRect.height = rect.height - clipRect.y;
                    if(clipRect.height <= 0)
                        return
                }
                if(clipRect.width <= 0 || clipRect.height <= 0)
                    return
            }
            var bufferCtx,
                useDoubleBuffer = GrapeCity.UI._useDoubleBuffer();
            if(useDoubleBuffer)
            {
                var self = this,
                    buffer = this.buffer;
                if(!buffer || buffer.width !== rect.width || buffer.height !== rect.height)
                {
                    this.buffer = buffer = document.createElement(tag_canvas);
                    buffer.width = rect.width;
                    buffer.height = rect.height
                }
                if(!buffer.getContext)
                    if(GrapeCity.UI._isSilverlightCanvas())
                    {
                        if(!buffer.getContext)
                            return
                    }
                    else if(typeof window.FlashCanvas !== "undefined")
                    {
                        window.FlashCanvas.initElement(c);
                        if(!buffer.getContext)
                            return
                    }
            }
            bufferCtx = useDoubleBuffer ? this.buffer.getContext(dc_2d) : ctx;
            bufferCtx.clearRect(0,0,rect.width,rect.height);
            bufferCtx.translate(-rect.x,-rect.y);
            this.paintTabs(bufferCtx,clipRect);
            bufferCtx.translate(rect.x,rect.y);
            if(useDoubleBuffer)
            {
                var x = rect.x >= 0 ? 0 : -rect.x;
                var y = rect.y >= 0 ? 0 : -rect.y;
                if(clipRect)
                {
                    x = rect.x + clipRect.x;
                    y = rect.y + clipRect.y;
                    rect = new UI.Rect(rect.x + clipRect.x,rect.y + clipRect.y,clipRect.width,clipRect.height)
                }
                bufferCtx = this.buffer.getContext(dc_2d);
                var imgData = null;
                try
                {
                    if(!clipRect)
                    {
                        var canvas = this._getCanvas();
                        if(canvas)
                            imgData = bufferCtx.getImageData(x,y,Math.min(rect.width - x,Math.max(canvas.width - rect.x,3)),Math.min(rect.height - y,Math.max(canvas.height - rect.y,3)));
                        else
                            imgData = bufferCtx.getImageData(x,y,Math.max(rect.width - x,0),Math.max(rect.height - y,0))
                    }
                    else
                        imgData = bufferCtx.getImageData(clipRect.x,clipRect.y,clipRect.width,clipRect.height)
                }
                catch(ex)
                {
                    return
                }
                x = rect.x >= 0 ? rect.x : 0;
                y = rect.y >= 0 ? rect.y : 0;
                if(imgData && rect.width > 0 && rect.height > 0)
                    ctx.putImageData(imgData,x,y)
            }
        },
        _getCanvas: function()
        {
            var c = this.canvas;
            if(!c && this.parent)
                c = this.parent.canvas;
            if(c && !c.getContext && c.firstChild)
                c.getContext = c.firstChild.getContext;
            return c
        },
        paintTabs: function(ctx, clipRect)
        {
            if(!ctx || !this._spread)
                return;
            var rect = this.getBounds();
            ctx.save();
            ctx.font = this._font;
            ctx.rect(rect.x,rect.y,rect.width,rect.height);
            ctx.clip();
            ctx.beginPath();
            var gradFill = null,
                headerStyle,
                i;
            if(this._spread.useWijmoTheme)
            {
                gradFill = ctx.createLinearGradient(rect.x,rect.y,rect.width,rect.height);
                headerStyle = UI.Global.prototype.getWijmoThemeStyle(UI.VisualState.Normal);
                gradFill.addColorStop(0,"#DDDDDD");
                gradFill.addColorStop(1,headerStyle.backgroundColor)
            }
            else
            {
                gradFill = ctx.createLinearGradient(rect.x + 0.7 * rect.height,rect.y + 0,0.7 * rect.height,rect.height);
                gradFill.addColorStop(0,"#7FA1D1");
                gradFill.addColorStop(0.3,"#A1BADD");
                gradFill.addColorStop(1,"#9AB5DB")
            }
            ctx.fillStyle = gradFill;
            ctx.fillRect(rect.x,rect.y,rect.width,rect.height);
            var tabSize = 60;
            var pos = this._tabStartPos;
            this._tabSizes = [];
            for(i = 0; i < this._spread.sheets.length; i++)
            {
                tabSize = ctx.measureText(this._spread.sheets[i]._name).width + this._tabRightPadding + this._tabLeftPadding;
                this._tabSizes.push(tabSize)
            }
            var tabs = [];
            if(this._firstTab > 0)
                pos += this._firstTabSpace;
            if(this._firstTab >= 0)
                for(i = this._firstTab; i < this._spread.sheets.length; i++)
                {
                    tabSize = this._tabSizes[i];
                    tabs.push({
                        i: i,
                        x: pos,
                        w: tabSize,
                        t: this._spread.sheets[i]._name
                    });
                    pos += tabSize + this._tabSpace
                }
            if(this._spread._newTabVisible)
                this.drawTab(ctx,pos,0,this._newTabSize,rect.height,5,false,this._hoverTab === -2,"Νέο\u2026");
            this._activeIndex = this._spread.getActiveSheetIndex();
            if(this._spread.sheets.length > 0)
            {
                for(i = tabs.length - 1; i >= 0; i--)
                {
                    var t = tabs[i];
                    if(t.i !== this._activeIndex)
                        this.drawTab(ctx,t.x,0,t.w,rect.height,5,false,t.i === this._hoverTab,t.t)
                }
                if(this._firstTab > 0)
                {
                    var index = this._firstTab - 1;
                    tabSize = this._tabSizes[index];
                    this.drawTab(ctx,this._tabStartPos + this._firstTabSpace - tabSize - this._tabSpace,0,tabSize,rect.height,5,this._activeIndex === index,this._hoverTab === index,"")
                }
                if(this._activeIndex >= this._firstTab && this._activeIndex < this._spread.sheets.length)
                {
                    var at = tabs[this._activeIndex - this._firstTab];
                    this.drawTab(ctx,at.x,0,at.w,rect.height,5,true,false,at.t)
                }
            }
            ctx.fillStyle = color_92A5C7;
            ctx.fillRect(rect.x,rect.y,this._tabStartPos - 1,rect.height);
            var tSize = 5;
            var x = 9;
            var y = 9;
            this.drawNavButton(ctx,x,y,x + tSize,y + tSize,x + tSize,y - tSize,true,x - tSize,this._hoverNavButton === 0);
            x += this._navButtonSize;
            this.drawNavButton(ctx,x,y,x + tSize,y + tSize,x + tSize,y - tSize,true,null,this._hoverNavButton === 1);
            x += this._navButtonSize;
            this.drawNavButton(ctx,x,y,x - tSize,y + tSize,x - tSize,y - tSize,false,null,this._hoverNavButton === 2);
            x += this._navButtonSize;
            this.drawNavButton(ctx,x,y,x - tSize,y + tSize,x - tSize,y - tSize,false,x + 4,this._hoverNavButton === 3);
            if(this._spread.useWijmoTheme)
            {
                var getWijmoThemeStyle = UI.Global.prototype.getWijmoThemeStyle,
                    VisualState = UI.VisualState;
                var hoverStyle = getWijmoThemeStyle(VisualState.Hover);
                headerStyle = getWijmoThemeStyle(VisualState.Normal);
                ctx.fillStyle = headerStyle.backgroundColor;
                ctx.fillRect(rect.x + rect.width - this._resizeBarWidth,rect.y,this._resizeBarWidth,rect.height);
                ctx.fillStyle = hoverStyle.backgroundColor;
                ctx.fillRect(rect.x + rect.width - 7,rect.y + 1,6,rect.height - 2);
                ctx.fillStyle = hoverStyle.color;
                ctx.fillRect(rect.x + rect.width - 5,rect.y + 4,2,rect.height - 8)
            }
            else
            {
                ctx.fillStyle = color_688CAF;
                ctx.fillRect(rect.x + rect.width - this._resizeBarWidth,rect.y,this._resizeBarWidth,rect.height);
                ctx.fillStyle = "#D7E6F7";
                ctx.fillRect(rect.x + rect.width - 7,rect.y + 1,6,rect.height - 2);
                ctx.fillStyle = color_white;
                ctx.fillRect(rect.x + rect.width - 5,rect.y + 4,4,rect.height - 8);
                ctx.fillStyle = color_688CAF;
                ctx.fillRect(rect.x + rect.width - 5,rect.y + 4,1,rect.height - 8)
            }
            ctx.beginPath();
            ctx.restore()
        },
        drawNavButton: function(ctx, x1, y1, x2, y2, x3, y3, left, linePosition, highlight)
        {
            ctx.save();
            var hoverStyle = null;
            var tmpStyle = null;
            var rect = this.getBounds();
            if(this._spread.useWijmoTheme)
            {
                var getWijmoThemeStyle = UI.Global.prototype.getWijmoThemeStyle,
                    VisualState = UI.VisualState;
                hoverStyle = getWijmoThemeStyle(VisualState.Hover);
                tmpStyle = getWijmoThemeStyle(VisualState.Normal);
                if(highlight)
                {
                    ctx.fillStyle = hoverStyle.backgroundColor;
                    ctx.fillRect(x1 - 7,rect.y + 2,14,rect.height - 4)
                }
                else
                {
                    ctx.fillStyle = tmpStyle.backgroundColor;
                    ctx.fillRect(x1 - 9,rect.y + 1,this._navButtonSize,rect.height - 2)
                }
            }
            else if(highlight)
            {
                ctx.fillStyle = "orange";
                ctx.fillRect(x1 - 8,rect.y + 1,16,rect.height - 2);
                ctx.fillStyle = "lightyellow";
                ctx.fillRect(x1 - 7,rect.y + 2,14,rect.height - 4)
            }
            else
            {
                var fill = ctx.createLinearGradient(rect.x + 0.7 * rect.height,rect.y + 0,0.7 * rect.height,rect.height);
                fill.addColorStop(0,color_FFFFFF);
                fill.addColorStop(0.1,color_D8E7FA);
                fill.addColorStop(0.45,color_D5E5F9);
                fill.addColorStop(0.45,color_B6D2F5);
                fill.addColorStop(0.9,color_D9E7F9);
                fill.addColorStop(1,color_FFFFFF);
                ctx.fillStyle = fill;
                ctx.fillRect(x1 - 9,rect.y + 1,this._navButtonSize,rect.height - 2)
            }
            if(this._spread.useWijmoTheme)
                if(highlight)
                    ctx.fillStyle = hoverStyle.color;
                else
                    ctx.fillStyle = tmpStyle.color;
            else
                ctx.fillStyle = color_black;
            if(linePosition !== undefined && linePosition !== null)
                ctx.fillRect(linePosition,Math.min(y2,y3),1,Math.max(y2,y3) - Math.min(y2,y3));
            if(left)
            {
                x1 -= 2;
                x2 -= 2;
                x3 -= 2
            }
            else
            {
                x1 += 2;
                x2 += 2;
                x3 += 2
            }
            ctx.beginPath();
            ctx.moveTo(x1,y1);
            ctx.lineTo(x2,y2);
            ctx.lineTo(x3,y3);
            ctx.lineTo(x1,y1);
            ctx.fill();
            ctx.restore()
        },
        drawTab: function(ctx, x, y, width, height, cornerRadius, isActive, isHover, text)
        {
            ctx.save();
            ctx.beginPath();
            ctx.moveTo(x,y);
            ctx.lineTo(x,y + height - cornerRadius);
            ctx.arcTo(x,y + height,x + width,y + height,cornerRadius);
            ctx.lineTo(x + width,y + height);
            ctx.lineTo(x + width + height,y);
            if(!isActive)
                ctx.lineTo(x,y);
            var tmpStyle = null;
            var rect = this.getBounds();
            var fillStyle = ctx.createLinearGradient(rect.x + 0.7 * rect.height,rect.y + 0,0.7 * rect.height,rect.height);
            var sheet = this._spread.getSheetFromName(text);
            if(text === "" && this._firstTab > 0)
                sheet = this._spread.getSheet(this._firstTab - 1);
            var fillStyleSet = false;
            if(this._spread.useWijmoTheme)
            {
                var visualStata = UI.VisualState.Normal;
                if(isActive || isHover)
                    visualStata = UI.VisualState.Hover;
                tmpStyle = UI.Global.prototype.getWijmoThemeStyle(visualStata);
                fillStyle.addColorStop(0,tmpStyle.backgroundColor);
                fillStyleSet = true
            }
            else if(sheet && sheet._sheetTabColor)
            {
                var transparentColor = "rgba(0, 0, 0, 0)";
                ctx.fillStyle = transparentColor;
                ctx.fillStyle = new UI._ThemeContext(sheet).getColor(sheet._sheetTabColor);
                var color = ctx.fillStyle;
                if(color === transparentColor)
                    fillStyleSet = false;
                else
                {
                    if(isHover || isActive)
                    {
                        fillStyle.addColorStop(0,color_FFFFFF);
                        fillStyle.addColorStop(0.45,"#F1F6FD");
                        fillStyle.addColorStop(0.9,color);
                        fillStyle.addColorStop(1,color_FFFFFF)
                    }
                    else
                        fillStyle = color;
                    fillStyleSet = true
                }
            }
            if(!fillStyleSet)
                if(isHover || isActive)
                {
                    fillStyle.addColorStop(0,color_FFFFFF);
                    fillStyle.addColorStop(0.6,"#F1F6FD");
                    fillStyle.addColorStop(0.9,"#BCD5F6");
                    fillStyle.addColorStop(1,color_FFFFFF)
                }
                else
                {
                    fillStyle.addColorStop(0,color_D8E7FA);
                    fillStyle.addColorStop(0.45,color_D5E5F9);
                    fillStyle.addColorStop(0.6,color_B6D2F5);
                    fillStyle.addColorStop(1,color_D9E7F9)
                }
            ctx.fillStyle = fillStyle;
            ctx.fill();
            ctx.strokeStyle = color_92A5C7;
            ctx.stroke();
            ctx.textBaseline = cssTop;
            if(this._spread.useWijmoTheme && tmpStyle)
                ctx.fillStyle = tmpStyle.color;
            else
                ctx.fillStyle = color_black;
            var adjX = this._tabLeftPadding;
            var adjY = 1;
            ctx.fillText(text,x + adjX,y + adjY);
            ctx.restore()
        }
    }
})(window,jQuery);
(function(window, $)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    var const_string = "string",
        const_boolean = "boolean",
        const_number = "number";
    var wijspreadOptions = {
            sheetCount: 1,
            name: "",
            font: "10pt Arial",
            _allowUserZoom: true,
            _allowUserResize: true,
            tabStripVisible: true,
            tabEditable: true,
            newTabVisible: true,
            useWijmoTheme: false,
            tabStripRatio: 0.5,
            activeSheetIndex: 0,
            sheets: []
        };
    $.wijmo._wijspreadNS = $.wijmo.wijspread;
    $.wijmo.wijspread = undefined;
    $.widget("wijmo.wijspread",{
        _init: function()
        {
            var host = this.element.context;
            if(host)
            {
                var ss = new GrapeCity.UI.GcSpread(host.id,this.options.sheetCount);
                this.element.data("spread",ss);
                host.setAttribute("gcUIElement","gcSpread");
                GrapeCity.UI.Global.prototype._createDummyObjects();
                var options = this.options;
                if(typeof options.name === const_string && options.name.length > 0)
                    ss.name = options.name;
                if(typeof options.font === const_string && options.font.length > 0)
                    ss._font = options.font;
                if(typeof options._allowUserZoom === const_boolean)
                    ss._allowUserZoom = options._allowUserZoom;
                if(typeof options._allowUserResize === const_boolean)
                    ss._allowUserResize = options._allowUserResize;
                if(typeof options.tabStripVisible === const_boolean)
                    ss._tabStripVisible = options.tabStripVisible;
                if(typeof options.tabEditable === const_boolean)
                    ss._tabEditable = options.tabEditable;
                if(typeof options.newTabVisible === const_boolean)
                    ss._newTabVisible = options.newTabVisible;
                if(typeof options.useWijmoTheme === const_boolean)
                    ss.useWijmoTheme = options.useWijmoTheme;
                if(typeof options.tabStripRatio === const_number)
                    ss._tabStripRatio = options.tabStripRatio;
                if(typeof options.activeSheetIndex === const_number)
                    ss._activeSheetIndex = options.activeSheetIndex;
                if(!GrapeCity.UI._isStandardCanvas() && GrapeCity.UI._isSilverlightCanvas())
                {
                    var canvas = document.createElement("canvas");
                    canvas.setAttribute("width","" + host.clientWidth);
                    canvas.setAttribute("height","" + host.clientHeight);
                    host.appendChild(canvas);
                    ss.canvas = canvas;
                    canvas.setAttribute('renderMethod','auto');
                    canvas.setAttribute('gcSpreadsheet','true');
                    canvas.setAttribute('onload','$("#' + host.id + '").wijspread("initPaint");');
                    $(window).bind("resize.gcSpread",GrapeCity.UI.createEventHandler(ss,ss._doResize))
                }
                else
                    ss._setHost(host);
                if(options.sheets && options.sheets.length > 0)
                {
                    var sheet;
                    if(ss.getSheetCount() < options.sheets.length)
                    {
                        var n = ss.getSheetCount();
                        while(n < options.sheets.length)
                        {
                            sheet = ss._createSheet(ss._getDefaultSheetName(n));
                            ss.addSheet(n,sheet);
                            n = ss.getSheetCount()
                        }
                    }
                    for(var i = 0; i < options.sheets.length; i++)
                    {
                        sheet = ss.getSheet(i);
                        sheet.applyOptions(options.sheets[i])
                    }
                }
                if(GrapeCity.UI._isStandardCanvas())
                {
                    ss.invalidateLayout();
                    ss.repaint()
                }
                else if(GrapeCity.UI._isSilverlightCanvas())
                    if(slcanvas && slcanvas.autoLoad)
                        slcanvas.autoLoad()
            }
        },
        initPaint: function()
        {
            var isStandardCanvas = GrapeCity.UI._isStandardCanvas();
            if(isStandardCanvas)
                return;
            var self = this;
            if(self._initPaintTimeout !== undefined && self._initPaintTimeout !== null)
                window.clearTimeout(self._initPaintTimeout);
            var control = self.spread().canvas.firstChild;
            if(control && control.loaded)
            {
                var spreadsheetObject = control.Content.SpreadsheetObject;
                self.spread().attachSpreadsheetObject(spreadsheetObject)
            }
            else
                self._initPaintTimeout = window.setTimeout(function()
                {
                    self.initPaint.call(self)
                },10)
        },
        spread: function()
        {
            return this.element.data("spread")
        },
        destroy: function()
        {
            $(window).unbind("resize.gcSpread");
            this.spread()._dispose()
        },
        refresh: function()
        {
            var spread = this.spread();
            spread._doResize();
            spread.repaint()
        },
        repaint: function()
        {
            this.spread().repaint()
        },
        options: wijspreadOptions
    });
    if($.wijmo._wijspreadNS)
        $.wijmo.wijspread = $.wijmo._wijspreadNS;
    var ko = window.ko;
    if(ko)
    {
        ko.wijmo = ko.wijmo || {};
        ko.wijmo.wijspreadBinding = {
            init: function(element, valueAccessor, allBindingsAccessor, viewModel)
            {
                $("#" + element.id).wijspread(valueAccessor())
            },
            update: function(element, valueAccessor, allBindingsAccessor, viewModel){}
        };
        ko.bindingHandlers["wijspread"] = ko.wijmo.wijspreadBinding
    }
})(window,jQuery);
(function(window, $)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    function getColorUnitString(unit)
    {
        var s = unit.toString(16);
        if(s.length === 1)
            s = '0' + s;
        return s
    }
    function Color(a, r, g, b)
    {
        this.a = a;
        this.r = r;
        this.g = g;
        this.b = b
    }
    Color.prototype = {
        a: 1,
        r: 0,
        g: 0,
        b: 0,
        toString: function()
        {
            if(this.a === 255)
                return'#' + getColorUnitString(this.r) + getColorUnitString(this.g) + getColorUnitString(this.b);
            return"rgba(" + this.r + "," + this.g + "," + this.b + "," + this.a + ")"
        },
        getBrightness: function()
        {
            return(this.r * 299 + this.g * 587 + this.b * 114) / 1000
        }
    };
    GrapeCity.UI._Color = Color;
    function hueToRGB(n1, n2, hue)
    {
        if(hue < 0)
            hue += 240;
        if(hue > 240)
            hue -= 240;
        if(hue < 40)
            return n1 + ((n2 - n1) * hue + 20) / 40;
        if(hue < 120)
            return n2;
        if(hue < 160)
            return n1 + ((n2 - n1) * (160 - hue) + 20) / 40;
        return n1
    }
    function colorFromHLS(hue, luminosity, saturation)
    {
        var r,
            g,
            b;
        if(saturation === 0)
            r = g = b = parseInt(luminosity * 0xff / 240,10);
        else
        {
            var n1,
                n2;
            if(luminosity <= 120)
                n2 = (luminosity * (240 + saturation) + 120) / 240;
            else
                n2 = luminosity + saturation - (luminosity * saturation + 120) / 240;
            n1 = 2 * luminosity - n2;
            r = parseInt((hueToRGB(n1,n2,hue + 80) * 0xff + 120) / 240,10);
            g = parseInt((hueToRGB(n1,n2,hue) * 0xff + 120) / 240,10);
            b = parseInt((hueToRGB(n1,n2,hue - 80) * 0xff + 120) / 240,10)
        }
        return new Color(0xff,r,g,b)
    }
    function HLSColor(rgbColor)
    {
        var r = rgbColor.r,
            g = rgbColor.g,
            b = rgbColor.b;
        var maxUnit = Math.max(Math.max(r,g),b);
        var minUnit = Math.min(Math.min(r,g),b);
        var sum = maxUnit + minUnit;
        this.luminosity = parseInt((sum * 240 + 0xff) / 510,10);
        var diff = maxUnit - minUnit;
        if(diff === 0)
        {
            this.saturation = 0;
            this.hue = 160
        }
        else
        {
            if(this.luminosity <= 120)
                this.saturation = parseInt((diff * 240 + sum / 2) / sum,10);
            else
                this.saturation = parseInt((diff * 240 + (510 - sum) / 2) / (510 - sum),10);
            var partR = ((maxUnit - r) * 40 + diff / 2) / diff;
            var partG = ((maxUnit - g) * 40 + diff / 2) / diff;
            var partB = ((maxUnit - b) * 40 + diff / 2) / diff;
            if(r === maxUnit)
                this.hue = parseInt(partB - partG,10);
            else if(g === maxUnit)
                this.hue = parseInt(80 + partR - partB,10);
            else
                this.hue = parseInt(160 + partG - partR,10);
            if(this.hue < 0)
                this.hue += 240;
            if(this.hue > 240)
                this.hue -= 240
        }
    }
    HLSColor.prototype = {
        hue: 160,
        luminosity: 0,
        saturation: 0,
        toColor: function()
        {
            return colorFromHLS(this.hue,this.luminosity,this.saturation)
        },
        getLighterColor: function(percentLighter)
        {
            var luminosity = this.luminosity;
            var newLuma = this.newLuma(this.luminosity,500,true);
            return colorFromHLS(this.hue,luminosity + (newLuma - luminosity) * percentLighter,this.saturation)
        },
        getDrakerColor: function(percentDarker)
        {
            var newLuma = this.newLuma(this.luminosity,-333,true);
            return colorFromHLS(this.hue,newLuma * (1 - percentDarker),this.saturation)
        },
        newLuma: function(luminosity, n, scale)
        {
            if(n === 0)
                return luminosity;
            if(scale)
            {
                if(n > 0)
                    return(luminosity * (0x3e8 - n) + 0xf1 * n) / 0x3e8;
                return luminosity * (n + 0x3e8) / 0x3e8
            }
            luminosity += n * 240 / 0x3e8;
            if(luminosity < 0)
                luminosity = 0;
            if(luminosity > 240)
                luminosity = 240;
            return luminosity
        }
    };
    var rrggbbPattern = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i;
    var rgbPattern = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i;
    var rgbFunctionPattern = /^rgb\(([\s\d]*),([\s\d]*),([\s\d]*)\)$/i;
    var rgbaFunctionPattern = /^rgba\(([\s\d]*),([\s\d]*),([\s\d]*),([\s\d]*)\)$/i;
    var normalizeColorString = function(useCanvas)
        {
            var _dummyColorSpan,
                _canvasContext;
            return useCanvas ? function(strColor1)
                {
                    if(!_canvasContext)
                    {
                        var c = window.document.createElement('canvas');
                        if(c && c.getContext)
                            _canvasContext = c.getContext('2d')
                    }
                    if(!_canvasContext)
                        return strColor1;
                    _canvasContext.fillStyle = strColor1;
                    strColor1 = _canvasContext.fillStyle;
                    return strColor1
                } : function(strColor2)
                {
                    if(typeof _dummyColorSpan === "undefined")
                        _dummyColorSpan = $("<span></span>");
                    _dummyColorSpan.css("color",strColor2);
                    return _dummyColorSpan.css("color")
                }
        }(GrapeCity.UI._isStandardCanvas());
    function hexToColorUnit(x)
    {
        return parseInt(x,16)
    }
    function hex2ToColorUnit2(x)
    {
        return parseInt(x + x,16)
    }
    function decOrPercentToColorUnit(x)
    {
        return x.indexOf("%") > 0 ? parseFloat(x) * 2.55 : x | 0
    }
    function parseColorString(value)
    {
        var sColor = normalizeColorString(value);
        var re = RegExp;
        if(rrggbbPattern.test(sColor))
            return[re.$1,re.$2,re.$3].map(hexToColorUnit);
        else if(rgbaFunctionPattern.test(sColor))
        {
            var v = [re.$1,re.$2,re.$3].map(decOrPercentToColorUnit);
            v.splice(0,0,parseFloat(re.$4) * 255);
            return v
        }
        else if(rgbFunctionPattern.test(sColor))
            return[re.$1,re.$2,re.$3].map(decOrPercentToColorUnit);
        else if(rgbPattern.test(sColor))
            return[re.$1,re.$2,re.$3].map(hex2ToColorUnit2);
        return null
    }
    function parseToColor(value)
    {
        if(value instanceof Color)
            return value;
        var a = 0,
            r = 0,
            g = 0,
            b = 0;
        if(value && value !== "")
        {
            var val = parseColorString(value);
            if(val)
                if(val.length === 3)
                {
                    a = 255;
                    r = val[0];
                    g = val[1];
                    b = val[2]
                }
                else if(val.length === 4)
                {
                    a = val[0];
                    r = val[1];
                    g = val[2];
                    b = val[3]
                }
        }
        return new Color(a,r,g,b)
    }
    Color.parse = function(value)
    {
        return parseToColor(value)
    };
    var updateTint = function(color, tint)
        {
            if(tint === 0)
                return color;
            var hls = new HLSColor(color);
            var lumDiff = parseInt(tint > 0 ? (240 - hls.luminosity) * tint : hls.luminosity * tint,10);
            return colorFromHLS(hls.hue,hls.luminosity + lumDiff,hls.saturation)
        };
    var BACKCOLOR1 = 0,
        BACKCOLOR2 = 1,
        TEXTCOLOR1 = 2,
        TEXTCOLOR2 = 3,
        ACCENT1 = 4,
        ACCENT2 = 5,
        ACCENT3 = 6,
        ACCENT4 = 7,
        ACCENT5 = 8,
        ACCENT6 = 9,
        HYPERLINK = 10,
        FHYPERLINK = 11;
    function ThemeColor(name, text1, text2, background1, background2, accent1, accent2, accent3, accent4, accent5, accent6, link, followedLink)
    {
        this._name = name;
        this._colorList = [parseToColor(background1),parseToColor(background2),parseToColor(text1),parseToColor(text2),parseToColor(accent1),parseToColor(accent2),parseToColor(accent3),parseToColor(accent4),parseToColor(accent5),parseToColor(accent6),parseToColor(link),parseToColor(followedLink)]
    }
    ThemeColor.prototype = {
        name: function(value)
        {
            if(arguments.length === 0)
                return this._name;
            this._name = value;
            return this
        },
        background1: function(value)
        {
            return this._property(BACKCOLOR1,arguments.length === 0,value)
        },
        background2: function(value)
        {
            return this._property(BACKCOLOR2,arguments.length === 0,value)
        },
        textColor1: function(value)
        {
            return this._property(TEXTCOLOR1,arguments.length === 0,value)
        },
        textColor2: function(value)
        {
            return this._property(TEXTCOLOR2,arguments.length === 0,value)
        },
        accent1: function(value)
        {
            return this._property(ACCENT1,arguments.length === 0,value)
        },
        accent2: function(value)
        {
            return this._property(ACCENT2,arguments.length === 0,value)
        },
        accent3: function(value)
        {
            return this._property(ACCENT3,arguments.length === 0,value)
        },
        accent4: function(value)
        {
            return this._property(ACCENT4,arguments.length === 0,value)
        },
        accent5: function(value)
        {
            return this._property(ACCENT5,arguments.length === 0,value)
        },
        accent6: function(value)
        {
            return this._property(ACCENT6,arguments.length === 0,value)
        },
        hyperline: function(value)
        {
            return this._property(HYPERLINK,arguments.length === 0,value)
        },
        followedHyperline: function(value)
        {
            return this._property(FHYPERLINK,arguments.length === 0,value)
        },
        _property: function(index, get, value)
        {
            if(get)
                return this._colorList[index];
            this._colorList[index] = parseToColor(value);
            return this
        },
        getColor: function(name)
        {
            if(name && name !== "")
            {
                var index = -1;
                var t = name.split(' ');
                if(t)
                {
                    if(t.length > 1)
                    {
                        if(t[0] === undefined || t[0] === null)
                            return name;
                        var cn1 = t[0].toLowerCase();
                        if(cn1 === "background")
                            index = parseInt(t[1],10) - 1;
                        else if(cn1 === "text")
                            index = parseInt(t[1],10) + 1;
                        else if(cn1 === "accent")
                            index = parseInt(t[1],10) + 3
                    }
                    else if(t.length === 1)
                    {
                        if(t[0] === undefined || t[0] === null)
                            return name;
                        var cn2 = t[0].toLowerCase();
                        if(cn2 === "hyperlink")
                            index = HYPERLINK;
                        else if(cn2 === "followedHyperlink")
                            index = FHYPERLINK
                    }
                    var tint = 0;
                    if(t.length > 2)
                        tint = parseInt(t[2],10);
                    if(index >= 0 && index <= 11)
                    {
                        var color = updateTint(this._colorList[index],tint / 100.0);
                        return color.toString()
                    }
                }
            }
            return name
        }
    };
    UI.ThemeColor = ThemeColor;
    var ThemeColors = {
            Default: new ThemeColor("Default","#000000","#1F497D","#FFFFFF","#EEECE1","#4F81BD","#C0504D","#9BBB59","#8064A2","#4BACC6","#F79646","#0000FF","#800080"),
            Office: new ThemeColor("Office","#000000","#1F497D","#FFFFFF","#EEECE1","#4F81BD","#C0504D","#9BBB59","#8064A2","#4BACC6","#F79646","#0000FF","#800080"),
            Apex: new ThemeColor("Apex","#000000","#69676D","#FFFFFF","#C9C2D1","#CEB966","#9CB084","#6BB1C9","#6585CF","#7E6BC9","#A379BB","#410082","#932968"),
            Aspect: new ThemeColor("Aspect","#000000","#323232","#FFFFFF","#E3DED1","#F07F09","#9F2936","#1B587C","#4E8542","#604878","#C19859","#6B9F25","#B26B02"),
            Concourse: new ThemeColor("Concourse","#000000","#464646","#FFFFFF","#DEF5FA","#2DA2BF","#DA1F28","#EB641B","#39639D","#474B78","#7D3C4A","#FF8119","#44B9E8"),
            Civic: new ThemeColor("Civic","#000000","#646B86","#FFFFFF","#C5D1D7","#D16349","#CCB400","#8CADAE","#8C7B70","#8FB08C","#D19049","#00A3D6","#694F07"),
            Oriel: new ThemeColor("Oriel","#000000","#575F6D","#FFFFFF","#FFF39D","#FE8637","#7598D9","#B32C16","#F5CD2D","#AEBAD5","#777C84","#D2611C","#3B435B"),
            Origin: new ThemeColor("Origin","#000000","#464653","#FFFFFF","#DDE9EC","#727CA3","#9FB8CD","#D2DA7A","#FADA7A","#B88472","#8E736A","#B292CA","#6B5680"),
            Paper: new ThemeColor("Paper","#000000","#444D26","#FFFFFF","#FEFAC9","#A5B592","#F3A447","#E7BC29","#D092A7","#9C85C0","#809EC2","#8E58B6","#7F6F6F"),
            Solstice: new ThemeColor("Solstice","#000000","#4F271C","#FFFFFF","#E7DEC9","#3891A7","#FEB80A","#C32D2E","#84AA33","#964305","#475A8D","#8DC765","#AA8A14"),
            Technic: new ThemeColor("Technic","#000000","#3B3B3B","#FFFFFF","#D4D2D0","#6EA0B0","#CCAF0A","#8D89A4","#748560","#9E9273","#7E848D","#00C8C3","#A116E0"),
            Trek: new ThemeColor("Trek","#000000","#4E3B30","#FFFFFF","#FBEEC9","#F0A22E","#A5644E","#B58B80","#C3986D","#A19574","#C17529","#AD1F1F","#FFC42F"),
            Urban: new ThemeColor("Urban","#000000","#424456","#FFFFFF","#DEDEDE","#53548A","#438086","#A04DA3","#C4652D","#8B5D3D","#5C92B5","#67AFBD","#C2A874"),
            Verve: new ThemeColor("Verve","#000000","#666666","#FFFFFF","#D2D2D2","#FF388C","#E40059","#9C007F","#68007F","#005BD3","#00349E","#17BBFD","#FF79C2"),
            Equity: new ThemeColor("Equity","#000000","#696464","#FFFFFF","#E9E5DC","#D34817","#9B2D1F","#A28E6A","#956251","#918485","#855D5D","#CC9900","#96A9A9"),
            Flow: new ThemeColor("Flow","#000000","#04617B","#FFFFFF","#DBF5F9","#0F6FC6","#009DD9","#0BD0D9","#10CF9B","#7CCA62","#A5C249","#E2D700","#85DFD0"),
            Foundry: new ThemeColor("Foundry","#000000","#676A55","#FFFFFF","#EAEBDE","#72A376","#B0CCB0","#A8CDD7","#C0BEAF","#CEC597","#E8B7B7","#DB5353","#903638"),
            Median: new ThemeColor("Median","#000000","#775F55","#FFFFFF","#EBDDC3","#94B6D2","#DD8047","#A5AB81","#D8B25C","#7BA79D","#968C8C","#F7B615","#704404"),
            Metro: new ThemeColor("Metro","#000000","#4E5B6F","#FFFFFF","#D6ECFF","#7FD13B","#EA157A","#FEB80A","#00ADDC","#738AC8","#1AB39F","#EB8803","#5F7791"),
            Module: new ThemeColor("Module","#000000","#5A6378","#FFFFFF","#D4D4D6","#F0AD00","#60B5CC","#E66C7D","#6BB76D","#E88651","#C64847","#168BBA","#680000"),
            Opulent: new ThemeColor("Opulent","#000000","#B13F9A","#FFFFFF","#F4E7ED","#B83D68","#AC66BB","#DE6C36","#F9B639","#CF6DA4","#FA8D3D","#FFDE66","#D490C5")
        };
    GrapeCity.UI.ThemeColors = ThemeColors;
    function SpreadTheme(name, themeColor, headingFont, bodyFont)
    {
        this._name = name;
        this._themeColor = themeColor ? themeColor : new ThemeColor(name);
        this._headingFont = headingFont;
        this._bodyFont = bodyFont
    }
    SpreadTheme.prototype = {
        name: function(value)
        {
            if(arguments.length === 0)
                return this._name;
            this._name = value;
            return this
        },
        colors: function(value)
        {
            if(arguments.length === 0)
                return this._themeColor;
            this._themeColor = value;
            return this
        },
        headerFont: function(value)
        {
            if(arguments.length === 0)
                return this._headingFont;
            this._headingFont = value;
            return this
        },
        bodyFont: function(value)
        {
            if(arguments.length === 0)
                return this._bodyFont;
            this._bodyFont = value;
            return this
        }
    };
    GrapeCity.UI.SpreadTheme = SpreadTheme;
    UI.SpreadThemes = {
        Default: new SpreadTheme("Default",ThemeColors.Default,"Cambria","Calibri"),
        Office: new SpreadTheme("Office",ThemeColors.Office,"Cambria","Calibri"),
        Apex: new SpreadTheme("Apex",ThemeColors.Apex,"Lucida Sans","Book Antiqua"),
        Aspect: new SpreadTheme("Aspect",ThemeColors.Aspect,"Verdana","Verdana"),
        Concourse: new SpreadTheme("Concourse",ThemeColors.Concourse,"Lucida Sans Unicode","Lucida Sans Unicode"),
        Civic: new SpreadTheme("Civic",ThemeColors.Civic,"Georgia","Georgia"),
        Oriel: new SpreadTheme("Oriel",ThemeColors.Oriel,"Century Schoolbook","Century Schoolbook"),
        Origin: new SpreadTheme("Origin",ThemeColors.Origin,"Bookman Old Style","Gill Sans MT"),
        Paper: new SpreadTheme("Paper",ThemeColors.Paper,"Constantia","Constantia"),
        Solstice: new SpreadTheme("Solstice",ThemeColors.Solstice,"Gill Sans MT","Gill Sans MT"),
        Technic: new SpreadTheme("Technic",ThemeColors.Technic,"Franklin Gothic Book","Arial"),
        Trek: new SpreadTheme("Trek",ThemeColors.Trek,"Franklin Gothic Medium","Franklin Gothic Book"),
        Urban: new SpreadTheme("Urban",ThemeColors.Urban,"Trebuchet MS","Georgia"),
        Verve: new SpreadTheme("Verve",ThemeColors.Verve,"Century Gothic","Century Gothic"),
        Equity: new SpreadTheme("Equity",ThemeColors.Equity,"Franklin Gothic Book","Perpetua"),
        Flow: new SpreadTheme("Flow",ThemeColors.Flow,"Calibri","Constantia"),
        Foundry: new SpreadTheme("Foundry",ThemeColors.Foundry,"Rockwell","Rockwell"),
        Median: new SpreadTheme("Median",ThemeColors.Median,"Tw Cen MT","Tw Cen MT"),
        Metro: new SpreadTheme("Metro",ThemeColors.Metro,"Consolas","Corbel"),
        Module: new SpreadTheme("Module",ThemeColors.Module,"Corbel","Corbel"),
        Opulent: new SpreadTheme("Opulent",ThemeColors.Opulent,"Trebuchet MS","Trebuchet MS")
    }
})(window,jQuery);
(function(window)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    var Colors = {
            Black: "black",
            Blue: 'Blue',
            Brown: 'Brown'
        };
    var Color = {FromArgb: function(a, r, g, b)
            {
                return new UI._Color(a,r,g,b).toString()
            }};
    var string = {Empty: ''};
    var __invalidValuePlaceHolder = {};
    function prop(readonly, obj, p, val, extra)
    {
        if(readonly)
            return!extra ? obj[p] : extra.call(obj,obj[p]);
        var old = obj[p];
        obj[p] = val;
        if(extra)
            extra.call(obj,old !== val);
        return obj
    }
    function normalizeColor(color, themeContext)
    {
        if(themeContext && color && color !== '')
            if(themeContext.getColor)
            {
                var c = themeContext.getColor(color);
                if(c)
                    return c
            }
        return color
    }
    UI.EmptyValueStyle = {
        Gaps: 0,
        Zero: 1,
        Connect: 2
    };
    UI.SparklineAxisMinMax = {
        individual: 0,
        group: 1,
        custom: 2
    };
    UI.SparklineSetting = function(setting)
    {
        if(!setting)
        {
            this._axisColor = Colors.Black;
            this._firstMarkerColor = Color.FromArgb(255,149,179,215);
            this._highMarkerColor = Colors.Blue;
            this._lastMarkerColor = Color.FromArgb(255,149,179,215);
            this._lowMarkerColor = Colors.Blue;
            this._markersColor = Color.FromArgb(255,36,64,98);
            this._negativeColor = Colors.Brown;
            this._seriesColor = Color.FromArgb(255,36,64,98);
            this.displayEmptyCellsAs = UI.EmptyValueStyle.Gaps;
            this.rightToLeft = false;
            this.displayHidden = false;
            this.displayXAxis = false;
            this._showFirst = false;
            this._showHigh = false;
            this._showLast = false;
            this._showLow = false;
            this._showNegative = false;
            this._showMarkers = false;
            this.manualMax = 0.0;
            this.manualMin = 0.0;
            this.maxAxisType = UI.SparklineAxisMinMax.individual;
            this.minAxisType = UI.SparklineAxisMinMax.individual;
            this.groupMaxValue = 0;
            this.groupMinValue = 0;
            this.lineWeight = 1.0
        }
        else
            for(var x in setting)
                if(x)
                    this[x] = setting[x];
        this._worksheet = null
    };
    UI.SparklineSetting.prototype = {
        axisColor: function(value)
        {
            return prop(arguments.length === 0,this,'_axisColor',value)
        },
        firstMarkerColor: function(value)
        {
            return prop(arguments.length === 0,this,'_firstMarkerColor',value)
        },
        highMarkerColor: function(value)
        {
            return prop(arguments.length === 0,this,'_highMarkerColor',value)
        },
        lastMarkerColor: function(value)
        {
            return prop(arguments.length === 0,this,'_lastMarkerColor',value)
        },
        lowMarkerColor: function(value)
        {
            return prop(arguments.length === 0,this,'_lowMarkerColor',value)
        },
        markersColor: function(value)
        {
            return prop(arguments.length === 0,this,'_markersColor',value)
        },
        negativeColor: function(value)
        {
            return prop(arguments.length === 0,this,'_negativeColor',value)
        },
        seriesColor: function(value)
        {
            return prop(arguments.length === 0,this,'_seriesColor',value)
        },
        showFirst: function(value)
        {
            return prop(arguments.length === 0,this,'_showFirst',value)
        },
        showHigh: function(value)
        {
            return prop(arguments.length === 0,this,'_showHigh',value)
        },
        showLast: function(value)
        {
            return prop(arguments.length === 0,this,'_showLast',value)
        },
        showLow: function(value)
        {
            return prop(arguments.length === 0,this,'_showLow',value)
        },
        showNegative: function(value)
        {
            return prop(arguments.length === 0,this,'_showNegative',value)
        },
        showMarkers: function(value)
        {
            return prop(arguments.length === 0,this,'_showMarkers',value)
        },
        clone: function()
        {
            var s = new UI.SparklineSetting;
            s._axisColor = this._axisColor;
            s._firstMarkerColor = this._firstMarkerColor;
            s._highMarkerColor = this._highMarkerColor;
            s._lastMarkerColor = this._lastMarkerColor;
            s._lowMarkerColor = this._lowMarkerColor;
            s._markersColor = this._markersColor;
            s._negativeColor = this._negativeColor;
            s._seriesColor = this._seriesColor;
            s.displayEmptyCellsAs = this.displayEmptyCellsAs;
            s.rightToLeft = this.rightToLeft;
            s.displayHidden = this.displayHidden;
            s.displayXAxis = this.displayXAxis;
            s._showFirst = this._showFirst;
            s._showHigh = this._showHigh;
            s._showLast = this._showLast;
            s._showLow = this._showLow;
            s._showNegative = this._showNegative;
            s._showMarkers = this._showMarkers;
            s.manualMax = this.manualMax;
            s.manualMin = this.manualMin;
            s.maxAxisType = this.maxAxisType;
            s.minAxisType = this.minAxisType;
            s.groupMaxValue = this.groupMaxValue;
            s.groupMinValue = this.groupMinValue;
            s.lineWeight = this.lineWeight;
            return s
        },
        _setThemeContext: function(worksheet)
        {
            this._worksheet = worksheet
        },
        _getActualColor: function(colorType)
        {
            return normalizeColor(this[colorType](),new UI._ThemeContext(this._worksheet))
        },
        toJSON: function()
        {
            return{
                    _axisColor: this._axisColor,
                    _firstMarkerColor: this._firstMarkerColor,
                    _highMarkerColor: this._highMarkerColor,
                    _lastMarkerColor: this._lastMarkerColor,
                    _lowMarkerColor: this._lowMarkerColor,
                    _markersColor: this._markersColor,
                    _negativeColor: this._negativeColor,
                    _seriesColor: this._seriesColor,
                    displayEmptyCellsAs: this.displayEmptyCellsAs,
                    rightToLeft: this.rightToLeft,
                    displayHidden: this.displayHidden,
                    displayXAxis: this.displayXAxis,
                    _showFirst: this._showFirst,
                    _showHigh: this._showHigh,
                    _showLast: this._showLast,
                    _showLow: this._showLow,
                    _showNegative: this._showNegative,
                    _showMarkers: this._showMarkers,
                    manualMax: this.manualMax,
                    manualMin: this.manualMin,
                    maxAxisType: this.maxAxisType,
                    minAxisType: this.minAxisType,
                    groupMaxValue: this.groupMaxValue,
                    groupMinValue: this.groupMinValue,
                    lineWeight: this.lineWeight
                }
        }
    };
    UI.SparklineType = {
        line: 0,
        column: 1,
        winloss: 2
    };
    UI.DataOrientation = {
        Vertical: 0,
        Horizontal: 1
    };
    UI.SparklineGroup = function(type, setting)
    {
        this.setting = setting;
        this.displayDateAxis = false;
        this.sparklineType = type;
        this._sparklineGroupManager = null;
        this._innerList = [];
        this._axisReference = null;
        this._axisOrientation = UI.DataOrientation.Horizontal
    };
    UI.SparklineGroup.prototype = {
        add: function(item)
        {
            if(item)
            {
                this._innerList.push(item);
                item.group(this);
                this._adjustGroupMaxMinValue();
                this.onGroupChanged()
            }
        },
        remove: function(item)
        {
            var ret = this._innerList.remove(item);
            item.onSparklineChanged();
            item._group = this.clone();
            this._adjustGroupMaxMinValue();
            this.onGroupChanged();
            return ret
        },
        contains: function(item)
        {
            return this._innerList.contains(item)
        },
        onGroupChanged: function()
        {
            if(this._innerList)
                for(var i = 0; i < this._innerList.length; i++)
                {
                    var item = this._innerList[i];
                    if(item)
                        item.onSparklineChanged()
                }
        },
        clone: function()
        {
            var setting = !this.setting ? null : this.setting.clone();
            var g = new UI.SparklineGroup(this.sparklineType,setting);
            g.displayDateAxis = this.displayDateAxis;
            g._axisReference = this._axisReference;
            g._axisOrientation = this._axisOrientation;
            return g
        },
        dateAxisData: function(value)
        {
            if(arguments.length === 0)
                return prop(true,this,'_axisReference',value);
            else
                return prop(false,this,'_axisReference',value,function(changed)
                    {
                        if(changed)
                            this.onGroupChanged()
                    })
        },
        dateAxisOrientation: function(value)
        {
            return prop(arguments.length === 0,this,'_axisOrientation',value,function(changed)
                {
                    if(changed)
                        this.onGroupChanged()
                })
        },
        count: function()
        {
            return this._innerList.length
        },
        _adjustGroupMaxMinValue: function()
        {
            if(!this.setting)
                return;
            this.setting.groupMaxValue = Number.MIN_VALUE;
            this.setting.groupMinValue = Number.MAX_VALUE;
            var useGroupMax = this.setting.maxAxisType === UI.SparklineAxisMinMax.group;
            var useGroupMin = this.setting.minAxisType === UI.SparklineAxisMinMax.group;
            var isNeedUpdateGroup = false;
            if(useGroupMax || useGroupMin)
                for(var i = 0; i < this._innerList.length; i++)
                {
                    var sparkline = this._innerList[i];
                    var res = this._getMaxMinValues(sparkline);
                    var min = res.min;
                    var max = res.max;
                    if(useGroupMax && this.setting.groupMaxValue < max)
                    {
                        this.setting.groupMaxValue = max;
                        isNeedUpdateGroup = true
                    }
                    if(useGroupMin && this.setting.groupMinValue > min)
                    {
                        this.setting.groupMinValue = min;
                        isNeedUpdateGroup = true
                    }
                }
            return isNeedUpdateGroup
        },
        _getMaxMinValues: function(sparkline)
        {
            var max = Number.MIN_VALUE;
            var min = Number.MAX_VALUE;
            var data = sparkline.data();
            if(data)
            {
                var values = sparkline._provideValues(data,sparkline.dataOrientation(),false);
                for(var i = 0; i < values.length; i++)
                {
                    var val = values[i];
                    if(val === __invalidValuePlaceHolder)
                        val = 0;
                    if(typeof val === 'number')
                    {
                        var check = val;
                        max = max < check ? check : max;
                        min = min > check ? check : min
                    }
                }
            }
            return{
                    min: min,
                    max: max
                }
        },
        toJSON: function()
        {
            return{
                    setting: this.setting ? this.setting.toJSON() : null,
                    displayDateAxis: this.displayDateAxis,
                    sparklineType: this.sparklineType,
                    axisReference: this._axisReference,
                    axisOrientation: this._axisOrientation,
                    sparklines: this._innerList
                }
        },
        fromJSON: function(jsonData)
        {
            if(!jsonData)
                return;
            if(jsonData.setting)
                this.setting = new UI.SparklineSetting(jsonData.setting);
            if(jsonData.displayDateAxis !== undefined && jsonData.displayDateAxis !== null)
                this.displayDateAxis = jsonData.displayDateAxis;
            if(jsonData.sparklineType !== undefined && jsonData.sparklineType !== null)
                this.sparklineType = jsonData.sparklineType;
            if(jsonData.axisReference !== undefined && jsonData.axisReference !== null)
                this._axisReference = jsonData.axisReference;
            if(jsonData.axisOrientation !== undefined && jsonData.axisOrientation !== null)
                this._axisOrientation = jsonData.axisOrientation;
            if(jsonData.sparklines)
            {
                this._innerList = [];
                var sparklinesData = jsonData.sparklines,
                    count = sparklinesData.length;
                for(var i = 0; i < count; i++)
                {
                    var sp = new UI.Sparkline;
                    sp.fromJSON(sparklinesData[i]);
                    this.add(sp)
                }
            }
        }
    };
    function Rect(x, y, w, h)
    {
        this.X = x;
        this.Y = y;
        this.Width = w;
        this.Height = h;
        this.Left = this.X;
        this.Right = this.Left + this.Width;
        this.Top = this.Y;
        this.Bottom = this.Y + this.Height
    }
    function SparklineRender(sparkline)
    {
        this._minItemHeight = 2;
        this.info = sparkline;
        this._clearCache()
    }
    SparklineRender.prototype = {
        sparklineInfo: function(value)
        {
            return prop(arguments.length === 0,this,'info')
        },
        paint: function(ctx, x, y, w, h)
        {
            this._clearCache();
            if(this.info.sparklineType() === UI.SparklineType.line)
                this._paintLines(ctx,x,y,w,h);
            this._paintDataPoints(ctx,x,y,w,h);
            this._paintAxis(ctx,x,y,w,h)
        },
        _clearCache: function()
        {
            this._cachedMinDatetime = Number.MAX_VALUE;
            this._cachedMaxDatetime = Number.MIN_VALUE;
            this._cachedMinValue = Number.MAX_VALUE;
            this._cachedMaxValue = Number.MIN_VALUE;
            this._cachedIndexMapping = null;
            this._cachedValues = null;
            this._cachedDatetimes = null
        },
        _getWorksheet: function()
        {
            if(this.info && this.info.group() && this.info.group()._sparklineGroupManager)
                return this.info.group()._sparklineGroupManager._sheet
        },
        _getSpace: function(anchor)
        {
            if(this.info.sparklineType() === UI.SparklineType.line)
                return 3 + this._getLineWeight() + 1;
            return 3
        },
        _leftSpace: function()
        {
            return this._getSpace('left')
        },
        _rightSpace: function()
        {
            return this._getSpace('right')
        },
        _topSpace: function()
        {
            return this._getSpace('top')
        },
        _bottomSpace: function()
        {
            return this._getSpace('bottom')
        },
        _getCachedValues: function()
        {
            if(!this._cachedValues)
            {
                var range = this.info.data();
                var orientation = this.info.dataOrientation();
                this._cachedValues = this.info._provideValues(range,orientation)
            }
            return this._cachedValues
        },
        _getCachedDatetimes: function()
        {
            if(!this._cachedDatetimes)
            {
                var range = this.info.group().dateAxisData();
                var orientation = this.info.group().dateAxisOrientation();
                this._cachedDatetimes = this.info._provideValues(range,orientation,true)
            }
            return this._cachedDatetimes
        },
        _getCachedIndexMaping: function()
        {
            if(this._cachedIndexMapping)
                return this._cachedIndexMapping;
            var i,
                v;
            if(this.sparklineInfo().displayDateAxis())
            {
                this._cachedIndexMapping = [];
                var valueCount = this._getCachedValues().length;
                var dateAxisCount = this._getCachedDatetimes().length;
                var count = Math.min(valueCount,dateAxisCount);
                var sorted = [];
                if(count > 0)
                    sorted = this._getCachedDatetimes().slice(0,count);
                sorted.sort(function(a, b)
                {
                    if(a === b)
                        return 0;
                    if(a === __invalidValuePlaceHolder)
                        a = 0;
                    if(b === __invalidValuePlaceHolder)
                        b = 0;
                    return a - b
                });
                for(i = 0; i < sorted.length; i++)
                {
                    var datetime = sorted[i];
                    if(datetime === undefined || datetime === null)
                        continue;
                    var valueIndex = this._getCachedDatetimes().indexOf(datetime);
                    while(this._cachedIndexMapping.contains(valueIndex))
                        valueIndex = this._getCachedDatetimes().indexOf(datetime,valueIndex + 1);
                    if(!isNaN(datetime))
                    {
                        v = this._getCachedValues()[valueIndex];
                        if(!(v !== undefined && v !== null && isNaN(v) && v !== __invalidValuePlaceHolder))
                            this._cachedIndexMapping.push(valueIndex)
                    }
                }
            }
            else
            {
                this._cachedIndexMapping = [];
                for(i = 0; i < this._getCachedValues().length; i++)
                {
                    v = this._getCachedValues()[i];
                    if(!(v !== undefined && v !== null && isNaN(v) && v !== __invalidValuePlaceHolder))
                        this._cachedIndexMapping.push(i)
                }
            }
            return this._cachedIndexMapping
        },
        _getValue: function(valueIndex)
        {
            var values = this._getCachedValues();
            var item = values[valueIndex];
            if(item === undefined || item === null)
            {
                if(this.sparklineInfo().setting().displayEmptyCellsAs === UI.EmptyValueStyle.Zero)
                    item = 0
            }
            else if(item === __invalidValuePlaceHolder)
                item = 0;
            return item
        },
        _paintLines: function(ctx, x, y, w, h)
        {
            var i,
                p1,
                p2;
            var count = this._getCachedIndexMaping().length - 1;
            if(count < 0)
                count = 0;
            this.linePos = [];
            for(i = 0; i < count; i++)
            {
                var start = this._getValue(this._getCachedIndexMaping()[i]);
                if(start !== undefined && start !== null)
                {
                    var endIndex = i + 1;
                    var end = this._getValue(this._getCachedIndexMaping()[endIndex]);
                    if(end === undefined || end === null)
                    {
                        var displayEmptyCellsAs = this.sparklineInfo().setting().displayEmptyCellsAs;
                        if(displayEmptyCellsAs === UI.EmptyValueStyle.Zero)
                            end = 0;
                        else if(displayEmptyCellsAs === UI.EmptyValueStyle.Connect)
                            for(endIndex = i + 2; endIndex <= count; endIndex++)
                            {
                                var temp = this._getCachedValues()[this._getCachedIndexMaping()[endIndex]];
                                if(temp !== undefined && temp !== null)
                                {
                                    end = temp;
                                    break
                                }
                            }
                    }
                    if(end !== undefined && end !== null)
                    {
                        var startRec = this._getDataPointPosition(this._getCachedIndexMaping()[i],{
                                Width: w,
                                Height: h
                            });
                        var endRec = this._getDataPointPosition(this._getCachedIndexMaping()[endIndex],{
                                Width: w,
                                Height: h
                            });
                        var d = startRec.Width / 2;
                        p1 = {
                            X: startRec.X + d,
                            Y: startRec.Y + d
                        };
                        p2 = {
                            X: endRec.X + d,
                            Y: endRec.Y + d
                        };
                        this.linePos[i] = {
                            P1: p1,
                            P2: p2
                        }
                    }
                    else
                        i++
                }
            }
            if(this.linePos.length > 0)
            {
                ctx.strokeStyle = this.info.setting().seriesColor();
                ctx.lineCap = 'round';
                ctx.lineWidth = this._getLineWeight();
                for(i = 0; i < this.linePos.length; i++)
                {
                    var line = this.linePos[i];
                    if(!line)
                        continue;
                    ctx.beginPath();
                    p1 = line.P1;
                    p2 = line.P2;
                    ctx.moveTo(x + p1.X,y + p1.Y);
                    ctx.lineTo(x + p2.X,y + p2.Y);
                    ctx.stroke();
                    ctx.closePath()
                }
            }
        },
        _getDataPointColor: function(indexInValueCache)
        {
            var ret = null;
            var value = this._getValue(indexInValueCache);
            var worksheet = this._getWorksheet();
            if(value !== undefined && value !== null)
            {
                this.info.setting()._setThemeContext(worksheet);
                if(this._cachedMinValue === Number.MAX_VALUE || this._cachedMaxValue === Number.MIN_VALUE)
                    this._getMaxMinValue();
                var min = this._cachedMinValue;
                if(value === min && this.info.setting().showLow())
                    ret = this.info.setting()._getActualColor("lowMarkerColor");
                if(ret === undefined || ret === null)
                {
                    var max = this._cachedMaxValue;
                    if(value === max && this.info.setting().showHigh())
                        ret = this.info.setting()._getActualColor("highMarkerColor")
                }
                if(ret === undefined || ret === null)
                    if(this.info.group().displayDateAxis)
                    {
                        var dateIndex1 = this._getCachedIndexMaping().indexOf(indexInValueCache);
                        if(dateIndex1 === 0 && this.info.setting().showFirst())
                            ret = this.info.setting()._getActualColor("firstMarkerColor")
                    }
                    else if(indexInValueCache === 0 && this.info.setting().showFirst())
                        ret = this.info.setting()._getActualColor("firstMarkerColor");
                if(ret === undefined || ret === null)
                    if(this.info.group().displayDateAxis)
                    {
                        var dateIndex2 = this._getCachedIndexMaping().indexOf(indexInValueCache);
                        if(dateIndex2 === this._getCachedIndexMaping().length - 1 && this.info.setting().showLast())
                            ret = this.info.setting()._getActualColor("lastMarkerColor")
                    }
                    else if(indexInValueCache === this._getCachedIndexMaping().length - 1 && this.info.setting().showLast())
                        ret = this.info.setting()._getActualColor("lastMarkerColor");
                if(ret === undefined || ret === null)
                    if(value < 0 && this.info.setting().showNegative())
                        ret = this.info.setting()._getActualColor("negativeColor");
                if(ret === undefined || ret === null)
                {
                    var sparklineType = this.info.sparklineType();
                    if(sparklineType === UI.SparklineType.line)
                    {
                        if(this.info.setting().showMarkers())
                            ret = this.info.setting()._getActualColor("markersColor")
                    }
                    else if(sparklineType === UI.SparklineType.column)
                        ret = this.info.setting()._getActualColor("seriesColor");
                    else if(sparklineType === UI.SparklineType.winloss)
                        ret = this.info.setting()._getActualColor("seriesColor")
                }
            }
            if(ret === undefined || ret === null)
                return'Transparent';
            else
                return ret
        },
        _paintDataPoints: function(ctx, x, y, w, h)
        {
            var finalSize = {
                    Width: w,
                    Height: h
                };
            for(var i = 0; i < this._getCachedIndexMaping().length; i++)
            {
                var index = this._getCachedIndexMaping()[i];
                var color = this._getDataPointColor(index);
                var rec = this._getDataPointPosition(index,finalSize);
                ctx.fillStyle = color;
                if(this.info.sparklineType() === UI.SparklineType.line)
                {
                    ctx.save();
                    var centerX = x + rec.X + rec.Width / 2;
                    var centerY = y + rec.Y + rec.Height / 2;
                    ctx.translate(centerX,centerY);
                    ctx.rotate(45 * Math.PI / 180);
                    ctx.fillRect(0 - rec.Width / 2,0 - rec.Height / 2,rec.Width,rec.Height);
                    ctx.restore()
                }
                else
                {
                    var newX = x + rec.X + rec.Width / 4;
                    newX = Math.floor(newX);
                    var newY = y + rec.Y;
                    var newWidth = rec.Width / 2;
                    var newHeight = rec.Height;
                    ctx.fillRect(newX,newY,newWidth,newHeight)
                }
            }
        },
        _paintAxis: function(ctx, x, y, w, h)
        {
            if(!this.info.setting().displayXAxis || !this._hasAxis())
                return;
            var avalibleSize = {
                    Width: w,
                    Height: h
                };
            var x1 = this._leftSpace();
            var x2 = avalibleSize.Width - this._rightSpace();
            var y1 = this._getAxisY(avalibleSize);
            y1 = Math.floor(y1) + 0.5;
            var y2 = y1;
            var color = this.info.setting()._getActualColor('axisColor');
            var lineWidth = this._getWorksheet()._zoomFactor;
            if(lineWidth < 1)
                lineWidth = 1;
            ctx.strokeStyle = color;
            ctx.lineWidth = lineWidth;
            ctx.beginPath();
            ctx.moveTo(x + x1,y + y1);
            ctx.lineTo(x + x2,y + y2);
            ctx.stroke()
        },
        _hasAxisNormal: function()
        {
            var max = this._getActualMaxValue();
            if(max !== Number.MIN_VALUE)
            {
                var min = this._getActualMinValue();
                if(min !== Number.MAX_VALUE)
                    return max === min || max * min <= 0
            }
            return true
        },
        _hasAxis: function()
        {
            var b = this._hasAxisNormal();
            if(this.info.sparklineType() !== UI.SparklineType.winloss)
                return b;
            if(!b && this._getCachedIndexMaping().length > 0)
                for(var i in this._getCachedIndexMaping())
                    if(i)
                    {
                        var index = this._getCachedIndexMaping()[i];
                        var item = this._getCachedValues()[index];
                        if(item !== undefined && item !== null)
                            return true
                    }
            return b
        },
        _getMinDatetime: function()
        {
            if(isNaN(this._cachedMinDatetime) || this._cachedMinDatetime === Number.MAX_VALUE)
                this._getMaxMindatetimes();
            return this._cachedMinDatetime
        },
        _getMaxDatetime: function()
        {
            if(isNaN(this._cachedMaxDatetime) || this._cachedMaxDatetime === Number.MIN_VALUE)
                this._getMaxMindatetimes();
            return this._cachedMaxDatetime
        },
        _getMaxMindatetimes: function()
        {
            for(var i = 0; i < this._getCachedIndexMaping().length; i++)
            {
                var index = this._getCachedIndexMaping()[i];
                var datetime = this._getCachedDatetimes()[index];
                if(isNaN(datetime))
                    continue;
                var v = this._getValue(index);
                if(v !== null && v === undefined || isNaN(v))
                    continue;
                if(datetime === undefined || datetime === null)
                    continue;
                if(datetime > this._cachedMaxDatetime)
                    this._cachedMaxDatetime = datetime;
                if(datetime < this._cachedMinDatetime)
                    this._cachedMinDatetime = datetime
            }
        },
        _calcItemWidth: function(availableSize)
        {
            var min = this._getMinDatetime();
            var max = this._getMaxDatetime();
            var datetimeValues = [],
                i,
                d;
            for(i = 0; i < this._getCachedIndexMaping().length; i++)
            {
                var index = this._getCachedIndexMaping()[i];
                d = this._getCachedDatetimes()[index];
                if(d === undefined || d === null || isNaN(d))
                    continue;
                if(!d)
                    continue;
                datetimeValues.push(d)
            }
            datetimeValues.sort(function(x, y)
            {
                return x - y
            });
            if(datetimeValues.length > 1 && min !== max)
            {
                var minDValue = Number.MAX_VALUE;
                var sumD = 0;
                for(i = 1; i < datetimeValues.length; i++)
                {
                    var oa = datetimeValues[i];
                    d = oa - datetimeValues[i - 1];
                    if(d < minDValue && d > 0)
                        minDValue = d;
                    sumD += d
                }
                var width = (availableSize.Width - this._leftSpace() - this._rightSpace()) * minDValue / sumD / 2;
                if(width < 2)
                    width = 2;
                return width
            }
            else
                return(availableSize.Width - this._leftSpace() - this._rightSpace()) / 2
        },
        _getItemWidth: function(availableSize)
        {
            if(this.info.displayDateAxis())
                return this._calcItemWidth(availableSize);
            else
            {
                var count = this._getCachedIndexMaping().length;
                var itemWidth = (availableSize.Width - this._leftSpace() - this._rightSpace()) / count;
                return itemWidth
            }
        },
        _getItemX: function(availableSize, index)
        {
            var itemWidth;
            if(this.sparklineInfo().displayDateAxis())
            {
                itemWidth = this._getItemWidth(availableSize);
                var max = this._getMaxDatetime();
                var min = this._getMinDatetime();
                if(max === min)
                    return this._leftSpace() + itemWidth / 2;
                var datetime = this._getCachedDatetimes()[index];
                if(!datetime)
                    return 0;
                var canvasWidth = availableSize.Width - this._leftSpace() - this._rightSpace();
                canvasWidth -= itemWidth;
                var range = max - min;
                return this._leftSpace() + Math.floor((datetime - min) / range * canvasWidth)
            }
            else
            {
                itemWidth = this._getItemWidth(availableSize);
                var valueIndex = this._getCachedIndexMaping().indexOf(index);
                var x = this._leftSpace() + itemWidth * valueIndex;
                x = Math.floor(x);
                return x
            }
        },
        _getCanvasSize: function(availableSize)
        {
            var w = availableSize.Width - this._leftSpace() - this._rightSpace();
            w = Math.max(w,0);
            var h = availableSize.Height - this._topSpace() - this._bottomSpace();
            h = Math.max(h,0);
            return{
                    Width: w,
                    Height: h
                }
        },
        _getMaxMinValue: function()
        {
            for(var i = 0; i < this._getCachedValues().length; i++)
            {
                var item = this._getCachedValues()[i];
                if(item === undefined)
                    continue;
                if(item !== undefined && item !== null)
                {
                    if(typeof item !== 'number')
                        item = 0;
                    if(item < this._cachedMinValue)
                        this._cachedMinValue = item;
                    if(item > this._cachedMaxValue)
                        this._cachedMaxValue = item
                }
            }
        },
        _getActualMaxValue: function()
        {
            if(this._cachedMaxValue === Number.MIN_VALUE || !this._cachedMaxValue)
                this._getMaxMinValue();
            var maxAxisType = this.info.setting().maxAxisType;
            if(maxAxisType === UI.SparklineAxisMinMax.individual)
                return this._cachedMaxValue;
            else if(maxAxisType === UI.SparklineAxisMinMax.group)
                return this.info.setting().groupMaxValue;
            else if(maxAxisType === UI.SparklineAxisMinMax.custom)
                return this.info.setting().manualMax;
            return this._cachedMaxValue
        },
        _getActualMinValue: function()
        {
            if(this._cachedMinValue === Number.MAX_VALUE || !this._cachedMinValue)
                this._getMaxMinValue();
            var maxAxisType = this.info.setting().minAxisType;
            if(maxAxisType === UI.SparklineAxisMinMax.individual)
                return this._cachedMinValue;
            else if(maxAxisType === UI.SparklineAxisMinMax.group)
                return this.info.setting().groupMinValue;
            else if(maxAxisType === UI.SparklineAxisMinMax.custom)
                return this.info.setting().manualMin
        },
        _getItemHeightNormal: function(availableSize, index)
        {
            var size = this._getCanvasSize(availableSize);
            var max = this._getActualMaxValue();
            var min = this._getActualMinValue();
            var range = max - min;
            if(max === min)
            {
                if(max === 0)
                    return 0;
                range = Math.abs(max)
            }
            var value = this._getCachedValues()[index];
            if(!value)
                value = 0;
            var d = size.Height / range;
            return value * d
        },
        _getItemHeight: function(availableSize, index)
        {
            var sparklineType = this.info.sparklineType();
            var value;
            if(sparklineType === UI.SparklineType.line)
                return this._getItemHeightNormal(availableSize,index);
            else if(sparklineType === UI.SparklineType.column)
            {
                value = this._getCachedValues()[index];
                if(value === undefined || value === null)
                    if(this.info.setting().displayEmptyCellsAs === UI.EmptyValueStyle.Zero)
                        return 0;
                var h = this._getItemHeightNormal(availableSize,index);
                if(h > -this._minItemHeight && h < this._minItemHeight)
                    if(value > 0)
                        return h + this._minItemHeight;
                    else if(value < 0)
                        return h - this._minItemHeight;
                return h
            }
            else if(sparklineType === UI.SparklineType.winloss)
            {
                value = this._getCachedValues()[index];
                if(value === undefined || value === null || value === 0 || isNaN(value))
                    return 0;
                var size = this._getCanvasSize(availableSize);
                if(value >= 0)
                    return size.Height / 2;
                else
                    return-size.Height / 2
            }
        },
        _getAxisYNormal: function(availableSize)
        {
            var size = this._getCanvasSize(availableSize);
            var max = this._getActualMaxValue();
            var min = this._getActualMinValue();
            if(max === Number.MIN_VALUE || min === Number.MAX_VALUE)
                return availableSize.Height / 2;
            var range = max - min;
            if(max === min)
            {
                if(max === 0)
                    return availableSize.Height / 2;
                range = max;
                if(max < 0)
                    max = 0
            }
            var d = size.Height / range;
            return this._topSpace() + max * d
        },
        _getAxisY: function(availableSize)
        {
            if(this.info.sparklineType() === UI.SparklineType.winloss)
                return availableSize.Height / 2;
            return this._getAxisYNormal(availableSize)
        },
        _getItemVisibleHeightNormal: function(availableSize, index)
        {
            var size = this._getCanvasSize(availableSize);
            var max = this._getActualMaxValue();
            var min = this._getActualMinValue();
            var range = max - min;
            if(max === min)
            {
                if(max === 0)
                    return 0;
                range = max
            }
            var d = size.Height / range;
            var value = this._getValue(index);
            if(value === undefined || value === null)
                value = 0;
            if(max !== min && max * min > 0)
            {
                var visibleHeight = 0;
                if(value >= 0)
                    visibleHeight = (value - min) * d;
                else
                    visibleHeight = (value - max) * d;
                return visibleHeight
            }
            else
                return value * d
        },
        _getItemVisibleHeight: function(availableSize, index)
        {
            var sparklineType = this.info.sparklineType();
            if(sparklineType === UI.SparklineType.line)
                return this._getItemVisibleHeightNormal(availableSize,index);
            else if(sparklineType === UI.SparklineType.column)
            {
                var h = this._getItemVisibleHeightNormal(availableSize,index);
                if(h > -this._minItemHeight && h < this._minItemHeight)
                {
                    var value = this._getValue(index);
                    if(value === undefined || value === null)
                        value = 0;
                    if(value !== 0)
                        if(value > 0)
                            return h + this._minItemHeight;
                        else
                            return h - this._minItemHeight
                }
                return h
            }
            else if(sparklineType === UI.SparklineType.winloss)
                return this._getItemHeight(availableSize,index)
        },
        _getDataPointPositionNormal: function(index, availableSize)
        {
            var itemWidth = this._getItemWidth(availableSize);
            var x = this._getItemX(availableSize,index);
            if(itemWidth < 0)
                itemWidth = 0;
            itemWidth = Math.floor(itemWidth);
            if(itemWidth % 2 === 1)
                itemWidth += 1;
            var height = this._getItemHeight(availableSize,index);
            var axis = this._getAxisY(availableSize);
            var max = this._getActualMaxValue();
            var min = this._getActualMinValue();
            var y = 0;
            if(max < 0 && min < 0)
                y = Math.max(this.info._topSpace(),axis);
            else
            {
                y = axis;
                if(height >= 0)
                    y = axis - height
            }
            var visibleHeight = this._getItemVisibleHeight(availableSize,index);
            var rect = new Rect(x,y,itemWidth,Math.abs(visibleHeight));
            if(height !== 0)
                if(rect.Y < this._topSpace() && rect.Bottom < this._topSpace() + 1)
                    rect.Height = Math.floor(rect.Height + 1);
                else
                {
                    var bottomLine = availableSize.Height - this._bottomSpace();
                    if(rect.Bottom > bottomLine && rect.Y > bottomLine - 1)
                    {
                        rect.Y = bottomLine - visibleHeight;
                        rect.Height = visibleHeight
                    }
                }
            return rect
        },
        _getLineWeight: function()
        {
            var lineWeight = this.info.setting().lineWeight * this._getWorksheet()._zoomFactor;
            if(lineWeight < 1)
                lineWeight = 1;
            return lineWeight
        },
        _getDataPointPosition: function(index, availableSize)
        {
            var lineWeight = this._getLineWeight();
            lineWeight++;
            if(lineWeight < 2)
                lineWeight = 2;
            var rec = this._getDataPointPositionNormal(index,availableSize);
            if(this.info.sparklineType() === UI.SparklineType.line)
            {
                rec.X = rec.X + (rec.Width - lineWeight) / 2;
                var value = this._getValue(index);
                if(value !== undefined && value !== null)
                {
                    if(value >= 0)
                        rec.Y -= lineWeight / 2;
                    else
                        rec.Y = rec.Bottom - lineWeight / 2;
                    rec.Width = lineWeight;
                    rec.Height = lineWeight
                }
                else
                {
                    rec.Width = 0;
                    rec.Height = 0
                }
            }
            if(this.info.group().setting.rightToLeft)
            {
                var left = rec.X;
                var reverseLeft = availableSize.Width - left;
                var newLeft = reverseLeft - rec.Width;
                rec = new Rect(newLeft,rec.Y,rec.Width,rec.Height)
            }
            return rec
        }
    };
    function _getCount(range, orientation)
    {
        if(orientation === UI.DataOrientation.Horizontal)
            return range.colCount;
        else
            return range.rowCount
    }
    function _getValue(worksheet, i, range, orientation)
    {
        if(orientation === UI.DataOrientation.Horizontal)
            return worksheet.getValue(range.row,range.col + i);
        else
            return worksheet.getValue(range.row + i,range.col)
    }
    function _isHidden(worksheet, i, range, orientation)
    {
        if(orientation === UI.DataOrientation.Horizontal)
            return worksheet.getColumnWidth(range.col + i) <= 0 || worksheet.getRowHeight(range.row) <= 0;
        else
            return worksheet.getRowHeight(range.row + i) <= 0 || worksheet.getColumnWidth(range.col) <= 0
    }
    UI.Sparkline = function(row, column, dataReference, dataOrientation, type, setting)
    {
        this.row = row;
        this.column = column;
        this._dataOrientation = dataOrientation;
        this._data = dataReference;
        this._group = new UI.SparklineGroup(type,setting);
        this._group.add(this);
        this._renderer = new SparklineRender(this)
    };
    UI.Sparkline.prototype = {
        group: function(value)
        {
            if(arguments.length === 0)
            {
                if(this._group === undefined || this._group === null)
                {
                    var g = new UI.SparklineGroup;
                    g.add(this);
                    this._group = g
                }
                return this._group
            }
            else
            {
                if(value !== this._group)
                {
                    if(this._group)
                        this._group.remove(this);
                    this._group = value;
                    if(this._group)
                        if(!this._group.contains(this))
                            this._group.add(this);
                    this.onSparklineChanged()
                }
                return this
            }
        },
        sparklineType: function(value)
        {
            if(arguments.length === 0)
                return this.group().sparklineType;
            else
            {
                if(this.group().sparklineType !== value)
                {
                    this.group().sparklineType = value;
                    this.onSparklineChanged()
                }
                return this
            }
        },
        onSparklineChanged: function()
        {
            var sheet = this._getWorksheet();
            if(sheet)
            {
                var self = this;
                sheet._trigger(UI.Events.SparklineChanged,{
                    sheet: sheet,
                    sheetName: sheet._name,
                    sparkline: self
                })
            }
        },
        setting: function(value)
        {
            if(arguments.length === 0)
                return this.group().setting;
            else
            {
                this.group().setting = value;
                return this
            }
        },
        data: function(value)
        {
            if(arguments.length === 0)
                return this._data;
            else
            {
                if(this._data !== value)
                {
                    this._data = value;
                    this.onSparklineChanged()
                }
                return this
            }
        },
        dataOrientation: function(value)
        {
            if(arguments.length === 0)
                return this._dataOrientation;
            else
            {
                if(this._dataOrientation !== value)
                {
                    this._dataOrientation = value;
                    this.onSparklineChanged()
                }
                return this
            }
        },
        dateAxisData: function(value)
        {
            if(arguments.length === 0)
                return this.group().dateAxisData();
            else
            {
                this.group().dateAxisData(value);
                return this
            }
        },
        dateAxisOrientation: function(value)
        {
            if(arguments.length === 0)
                return this.group().dateAxisOrientation();
            else
            {
                this.group().dateAxisOrientation(value);
                return this
            }
        },
        displayDateAxis: function(value)
        {
            if(arguments.length === 0)
                return this.group().displayDateAxis;
            else
            {
                this.group().displayDateAxis = value;
                return this
            }
        },
        clone: function()
        {
            var s = new UI.Sparkline;
            s.row = this.row;
            s.column = this.column;
            s.data(this.data());
            s.dataOrientation(this.dataOrientation());
            s.group(this.group().clone());
            return s
        },
        paintSparkline: function(ctx, x, y, w, h)
        {
            this._renderer.paint(ctx,x,y,w,h)
        },
        _provideValues: function(range, orientation, isDatetime)
        {
            var worksheet = this._getWorksheet();
            var ret = [];
            if(!worksheet)
                return ret;
            var count = _getCount(range,orientation);
            for(var i = 0; i < count; i++)
            {
                var value = _getValue(worksheet,i,range,orientation);
                if(!this.setting().displayHidden && _isHidden(worksheet,i,range,orientation))
                    value = NaN;
                else if(value !== null && value !== undefined)
                    if(isDatetime)
                        value = Date.parse(value);
                    else if(typeof value !== 'number')
                        value = __invalidValuePlaceHolder;
                ret.push(value)
            }
            return ret
        },
        _getWorksheet: function()
        {
            return this.group() && this.group()._sparklineGroupManager ? this.group()._sparklineGroupManager._sheet : null
        },
        toJSON: function()
        {
            return{
                    row: this.row,
                    col: this.column,
                    orientation: this._dataOrientation,
                    data: this._data,
                    type: this._group.sparklineType,
                    setting: this._group.setting,
                    axisReference: this._group._axisReference,
                    axisOrientation: this._group._axisOrientation
                }
        },
        fromJSON: function(jsonData)
        {
            if(!jsonData)
                return;
            if(jsonData.row !== undefined && jsonData.row !== null)
                this.row = jsonData.row;
            if(jsonData.col !== undefined && jsonData.col !== null)
                this.column = jsonData.col;
            if(jsonData.orientation !== undefined && jsonData.orientation !== null)
                this._dataOrientation = jsonData.orientation;
            if(jsonData.data)
            {
                var cr = jsonData.data;
                this._data = new UI.Range(cr.row,cr.col,cr.rowCount,cr.colCount)
            }
        }
    };
    UI.WorksheetSparklineGroupManager = function(sheet, calcEvaluator)
    {
        this._groups = [];
        this._sheet = sheet;
        this.evaluator = calcEvaluator
    };
    UI.WorksheetSparklineGroupManager.prototype = {
        groups: function(value)
        {
            return prop(arguments.length === 0,this,'_groups')
        },
        add: function(group)
        {
            this.groups().push(group);
            group._sparklineGroupManager = this
        },
        remove: function(group)
        {
            this.groups().remove(group)
        },
        contains: function(group)
        {
            return this.groups().contains(group)
        },
        count: function()
        {
            return this.groups().length
        },
        _addRows: function(row, count)
        {
            for(var i = 0; i < this._groups.length; i++)
            {
                var g = this._groups[i];
                if(g.displayDateAxis)
                {
                    var dateAxis = this._addRowRange(row,count,g.dateAxisData());
                    g.dateAxisData(dateAxis)
                }
                for(var j = 0; j < g._innerList.length; j++)
                {
                    var sparkline = g._innerList[j];
                    if(row <= sparkline.row)
                        sparkline.row += count;
                    var data = this._addRowRange(row,count,sparkline.data());
                    sparkline.data(data)
                }
            }
        },
        _addColumns: function(column, count)
        {
            for(var i = 0; i < this._groups.length; i++)
            {
                var g = this._groups[i];
                if(g.displayDateAxis)
                {
                    var dateAxis = this._addColumnRange(column,count,g.dateAxisData());
                    g.dateAxisData(dateAxis)
                }
                for(var j = 0; j < g._innerList.length; j++)
                {
                    var sparkline = g._innerList[j];
                    if(column <= sparkline.column)
                        sparkline.column += count;
                    var data = this._addColumnRange(column,count,sparkline.data());
                    sparkline.data(data)
                }
            }
        },
        _removeRows: function(row, count)
        {
            for(var i = 0; i < this._groups.length; i++)
            {
                var g = this._groups[i];
                if(g.displayDateAxis)
                {
                    var dateAxis = this._removeRowRange(row,count,g.dateAxisData());
                    if(dateAxis !== null && dateAxis !== undefined)
                        g.dateAxisData(dateAxis);
                    else
                    {
                        g.clear();
                        this.remove(g);
                        continue
                    }
                }
                var lines = [];
                lines = lines.concat(g._innerList);
                for(var j = 0; j < lines.length; j++)
                {
                    var sparkline = lines[j];
                    if(sparkline.row >= row && sparkline.row < row + count)
                        g.remove(sparkline);
                    else
                    {
                        if(row <= sparkline.row)
                            sparkline.row -= count;
                        var data = this._removeRowRange(row,count,sparkline.data());
                        if(data !== null && data !== undefined)
                            sparkline.data(data);
                        else
                            g.remove(sparkline)
                    }
                }
                if(g.count() <= 0)
                    this.remove(g)
            }
        },
        _removeColumns: function(column, count)
        {
            for(var i = 0; i < this._groups.length; i++)
            {
                var g = this._groups[i];
                if(g.displayDateAxis)
                {
                    var dateAxis = this._removeColumnRange(column,count,g.dateAxisData());
                    if(dateAxis !== null && dateAxis !== undefined)
                        g.dateAxisData(dateAxis);
                    else
                    {
                        g.clear();
                        this.remove(g);
                        continue
                    }
                }
                var lines = [];
                lines = lines.concat(g._innerList);
                for(var j = 0; j < lines.length; j++)
                {
                    var sparkline = lines[j];
                    if(sparkline.column >= column && sparkline.column < column + count)
                        g.remove(sparkline);
                    else
                    {
                        if(column <= sparkline.column)
                            sparkline.column -= count;
                        var data = this._removeColumnRange(column,count,sparkline.data());
                        if(data !== null && data !== undefined)
                            sparkline.data(data);
                        else
                            g.remove(sparkline)
                    }
                }
                if(g.count() <= 0)
                    this.remove(g)
            }
        },
        clear: function(row, column, rowCount, columnCount)
        {
            if(arguments.length === 0)
            {
                for(var i = 0; i < this._groups.length; i++)
                {
                    var item = this._groups[i];
                    if(item)
                        item.SparklineGroupManager = null
                }
                this._groups.clear();
                return
            }
            for(var r = row; r < row + rowCount; r++)
                for(var c = column; c < column + columnCount; c++)
                {
                    var sparkline = this._find(r,c);
                    if(sparkline)
                    {
                        var g = sparkline.group();
                        g.remove(sparkline);
                        if(g.count() === 0)
                            this.remove(g)
                    }
                }
        },
        _find: function(row, column)
        {
            for(var i = 0; i < this._groups.length; i++)
            {
                var g = this._groups[i];
                for(var j = 0; j < g.count(); j++)
                {
                    var si = g[j];
                    if(si && si.row === row && si.column === column)
                        return si
                }
            }
            return null
        },
        _copy: function(fromRow, fromColumn, toRow, toColumn, rowCount, columnCount)
        {
            var savedSparkline = new UI._GcSheetModel(rowCount,columnCount,null);
            var offsetRow = toRow - fromRow;
            var offsetColumn = toColumn - fromColumn;
            var m = this._sheet._getModel(),
                i,
                j;
            for(i = 0; i < rowCount; i++)
                for(j = 0; j < columnCount; j++)
                {
                    var sparkline = m.getSparkline(fromRow + i,fromColumn + j);
                    if(sparkline)
                    {
                        var newSparkline = sparkline.clone();
                        newSparkline.row = toRow + i;
                        newSparkline.column = toColumn + j;
                        var dateAxisRef = newSparkline.dateAxisData();
                        if(dateAxisRef !== null && dateAxisRef !== undefined)
                            if(this._canOffset(dateAxisRef,offsetRow,offsetColumn,this._sheet.getRowCount(),this._sheet.getColumnCount()))
                                newSparkline.dateAxisData(dateAxisRef.offset(offsetColumn,offsetRow));
                        var dataRef = newSparkline.data();
                        if(dataRef !== undefined && dataRef !== null)
                            if(this._canOffset(dataRef,offsetRow,offsetColumn,this._sheet.getRowCount(),this._sheet.getColumnCount()))
                                newSparkline.data(dataRef.offset(offsetColumn,offsetRow));
                        this.add(newSparkline.group());
                        savedSparkline.setValue(i,j,newSparkline)
                    }
                }
            for(i = 0; i < rowCount; i++)
                for(j = 0; j < columnCount; j++)
                    m.setSparkline(toRow + i,toColumn + j,savedSparkline.getValue(i,j))
        },
        _canOffset: function(exp, offsetRow, offsetColumn, MAX_ROW_COUNT, MAX_COLUMN_COUNT)
        {
            var dataRange = this._getExpressionRange(exp);
            if(dataRange)
            {
                var row = dataRange.row < 0 ? 0 : dataRange.row;
                var column = dataRange.col < 0 ? 0 : dataRange.col;
                var rowCount = dataRange.row < 0 ? MAX_ROW_COUNT : dataRange.rowCount;
                var columnCount = dataRange.col < 0 ? MAX_COLUMN_COUNT : dataRange.colCount;
                return!(row + offsetRow < 0 || column + offsetColumn < 0 || row + rowCount + offsetRow > MAX_ROW_COUNT || column + columnCount + offsetColumn > MAX_COLUMN_COUNT)
            }
            return false
        },
        _move: function(fromRow, fromColumn, toRow, toColumn, rowCount, columnCount)
        {
            this._moveDataRange(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount);
            var m = this._sheet._getModel();
            var savedSparkline = new UI._GcSheetModel(rowCount,columnCount,null);
            var i,
                j;
            for(i = 0; i < rowCount; i++)
                for(j = 0; j < columnCount; j++)
                {
                    var sparkline = m.getSparkline(fromRow + i,fromColumn + j);
                    if(sparkline)
                    {
                        sparkline.row = toRow + i;
                        sparkline.column = toColumn + j;
                        savedSparkline.setValue(i,j,sparkline)
                    }
                    m.setSparkline(fromRow + i,fromColumn + j,null)
                }
            for(i = 0; i < rowCount; i++)
                for(j = 0; j < columnCount; j++)
                    m.setSparkline(toRow + i,toColumn + j,savedSparkline.getValue(i,j))
        },
        _moveDataRange: function(fromRow, fromColumn, toRow, toColumn, rowCount, columnCount)
        {
            var moveRange = new UI.Range(fromRow,fromColumn,rowCount,columnCount);
            var offsetRow = toRow - fromRow;
            var offsetColumn = toColumn - fromColumn;
            for(var i = 0; i < this._groups.length; i++)
            {
                var g = this._groups[i];
                for(var j = 0; j < g._innerList.length; j++)
                {
                    var sparkline = g._innerList[j];
                    if(!sparkline)
                        continue;
                    var dateAxisRef = sparkline.dateAxisData();
                    var dateAxisRange = this._getExpressionRange(dateAxisRef);
                    if(dateAxisRange)
                        if(moveRange.containsRange(dateAxisRange) && moveRange.contains(sparkline.row,sparkline.column))
                            sparkline.dateAxisData(dateAxisRef.offset(offsetColumn,offsetRow));
                    var dataRef = sparkline.data();
                    var dataRange = this._getExpressionRange(dataRef);
                    if(dataRange)
                        if(moveRange.containsRange(dataRange) && moveRange.contains(sparkline.row,sparkline.column))
                            sparkline.data(dataRef.offset(offsetColumn,offsetRow))
                }
            }
        },
        _getExpressionRange: function(exp)
        {
            return exp
        },
        _swap: function(fromRow, fromColumn, toRow, toColumn, rowCount, columnCount){},
        _exCopy: function(src, fromRow, fromColumn, toRow, toColumn, rowCount, columnCount)
        {
            if(src === this._sheet)
            {
                this._copy(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount);
                return
            }
            var offsetRow = toRow - fromRow;
            var offsetColumn = toColumn - fromColumn;
            var srcModel = src._getModel();
            var destModel = this._sheet._getModel();
            for(var i = 0; i < rowCount; i++)
                for(var j = 0; j < columnCount; j++)
                {
                    var sparkline = srcModel.getSparkline(fromRow + i,fromColumn + j);
                    if(sparkline)
                    {
                        var newSparkline = sparkline.clone();
                        newSparkline.row = toRow + i;
                        newSparkline.column = toColumn + j;
                        var dateAxisRef = newSparkline.dateAxisData();
                        if(dateAxisRef !== undefined && dateAxisRef !== null)
                            if(this._canOffset(dateAxisRef,offsetRow,offsetColumn,this._sheet.getRowCount(),this._sheet.getColumnCount()))
                                newSparkline.dateAxisData(dateAxisRef.offset(offsetColumn,offsetRow));
                        var dataRef = newSparkline.data();
                        if(dataRef !== undefined && dataRef !== null)
                            if(this._canOffset(dataRef,offsetRow,offsetColumn,this._sheet.getRowCount(),this._sheet.getColumnCount()))
                                newSparkline.data(dataRef.offset(offsetColumn,offsetRow));
                        this.add(newSparkline.group());
                        destModel.setSparkline(toRow + i,toColumn + j,newSparkline)
                    }
                    else
                        destModel.setSparkline(toRow + i,toColumn + j,null)
                }
        },
        _exMove: function(src, fromRow, fromColumn, toRow, toColumn, rowCount, columnCount)
        {
            if(src === this._sheet)
            {
                this._move(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount);
                return
            }
            var srcModel = src._getModel();
            var destModel = this._sheet._getModel();
            this._exMoveDataRange(src,fromRow,fromColumn,toRow,toColumn,rowCount,columnCount);
            for(var i = 0; i < rowCount; i++)
                for(var j = 0; j < columnCount; j++)
                {
                    var sparkline = srcModel.getSparkline(fromRow + i,fromColumn + j);
                    if(sparkline)
                    {
                        sparkline.row = toRow + i;
                        sparkline.column = toColumn + j;
                        var oldGroup = sparkline.group();
                        var newGroup = oldGroup.clone();
                        oldGroup.remove(sparkline);
                        if(oldGroup.length <= 0)
                            src._sparklineGroupManager.remove(oldGroup);
                        newGroup.add(sparkline);
                        this.add(newGroup);
                        destModel.setSparkline(toRow + i,toColumn + j,sparkline)
                    }
                    else
                        destModel.setSparkline(toRow + i,toColumn + j,null);
                    srcModel.setSparkline(fromRow + i,fromColumn + j,null)
                }
        },
        _exMoveDataRange: function(src, fromRow, fromColumn, toRow, toColumn, rowCount, columnCount)
        {
            if(src === this._sheet)
            {
                this._moveDataRange(fromRow,fromColumn,toRow,toColumn,rowCount,columnCount);
                return
            }
            var moveRange = new UI.Range(fromRow,fromColumn,rowCount,columnCount);
            var offsetRow = toRow - fromRow;
            var offsetColumn = toColumn - fromColumn;
            for(var i = 0; i < src._sparklineGroupManager._groups.length; i++)
            {
                var g = src._sparklineGroupManager._groups[i];
                for(var j = 0; j < g._innerList.length; j++)
                {
                    var sparkline = g._innerList[j];
                    if(!sparkline)
                        continue;
                    var dateAxisRef = sparkline.dateAxisData();
                    var dateAxisRange = this._getExpressionRange(dateAxisRef);
                    if(dateAxisRange && this._sameSource(src,dateAxisRef))
                        if(moveRange.containsRange(dateAxisRange) && moveRange.contains(sparkline.row,sparkline.column))
                            sparkline.dateAxisData(dateAxisRef.offset(offsetColumn,offsetRow));
                    var dataRef = sparkline.data();
                    var dataRange = this._getExpressionRange(dataRef);
                    if(dataRange && this._sameSource(src,dataRef))
                        if(moveRange.containsRange(dataRange) && moveRange.contains(sparkline.row,sparkline.column))
                            sparkline.data(dataRef.offset(offsetColumn,offsetRow))
                }
            }
        },
        _sameSource: function(source, exp)
        {
            return true
        },
        _addRowRange: function(row, rowCount, range)
        {
            if(range)
                if(row > range.row + range.rowCount - 1)
                    return range;
                else if(row > range.row)
                    return new UI.Range(range.row,range.col,range.rowCount + rowCount,range.colCount);
                else
                    return new UI.Range(range.row + rowCount,range.col,range.rowCount,range.colCount);
            return null
        },
        _addColumnRange: function(column, columnCount, range)
        {
            if(range)
                if(column > range.col + range.colCount - 1)
                    return range;
                else if(column > range.col)
                    return new UI.Range(range.row,range.col,range.rowCount,range.colCount + columnCount);
                else
                    return new UI.Range(range.row,range.col + columnCount,range.rowCount,range.colCount);
            return null
        },
        _removeColumnRange: function(column, columnCount, range)
        {
            var ret = this._rangeSubCat(range.col,range.col + range.colCount - 1,column,column + columnCount - 1);
            if(!ret)
                return null;
            return new UI.Range(range.row,ret.start,range.rowCount,ret.end - ret.start + 1)
        },
        _removeRowRange: function(row, rowCount, range)
        {
            var ret = this._rangeSubCat(range.row,range.row + range.rowCount - 1,row,row + rowCount - 1);
            if(!ret)
                return null;
            return new UI.Range(ret.start,range.col,ret.end - ret.start + 1,range.colCount)
        },
        _rangeSubCat: function(sourceStart, sourceEnd, targetStart, targetEnd)
        {
            var resultStart = -1;
            var resultEnd,
                i;
            if(targetEnd < sourceStart)
            {
                var removeCount = targetEnd - targetStart + 1;
                resultStart = sourceStart - removeCount;
                resultEnd = sourceEnd - removeCount;
                return{
                        start: resultStart,
                        end: resultEnd
                    }
            }
            if(targetStart > sourceEnd)
            {
                resultStart = sourceStart;
                resultEnd = sourceEnd;
                return{
                        start: resultStart,
                        end: resultEnd
                    }
            }
            if(targetStart <= sourceStart)
            {
                var sourceCount = sourceEnd - sourceStart + 1;
                var removedCount = 0;
                for(i = sourceStart; i <= targetEnd; i++)
                    if(i <= sourceEnd)
                        removedCount++;
                    else
                        break;
                resultStart = targetStart;
                resultEnd = resultStart + sourceCount - removedCount - 1;
                return{
                        start: resultStart,
                        end: resultEnd
                    }
            }
            var remainedCount = 0;
            for(i = sourceStart; i <= sourceEnd; i++)
            {
                if(resultStart === -1)
                    if(i < targetStart || i > targetEnd)
                        resultStart = i;
                if(i < targetStart || i > targetEnd)
                    remainedCount++
            }
            if(resultStart !== -1 && remainedCount > 0)
            {
                resultEnd = resultStart + remainedCount - 1;
                return{
                        start: resultStart,
                        end: resultEnd
                    }
            }
            return null
        },
        toJSON: function()
        {
            return{groups: this._groups ? this._groups : null}
        },
        fromJSON: function(jsonData)
        {
            if(!jsonData)
                return;
            if(jsonData.groups)
            {
                this._groups = [];
                var groupsData = jsonData.groups,
                    count = groupsData.length;
                for(var i = 0; i < count; i++)
                {
                    var g = new UI.SparklineGroup;
                    g.fromJSON(groupsData[i]);
                    this.add(g)
                }
            }
        }
    }
})(window);
(function(window)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    Array.prototype.nextNonEmptyIndex = function(index)
    {
        if(index < 0)
            index = -1;
        var n = index + 1;
        for(var i = n; i < this.length; i++)
            if(this[i] !== undefined && this[i] !== null)
                return i;
        return-1
    };
    Array.prototype.clear = function(index, count)
    {
        if(index < 0)
            return;
        var n = 0;
        for(var i = 0; n < count && i < this.length; i++)
        {
            this[index + i] = null;
            n++
        }
    };
    UI.GroupState = {
        Expanded: 0,
        Collapsed: 1
    };
    UI.RangeGroupDirection = {
        Backward: 0,
        Forward: 1
    };
    UI.RangeGroupInfo = function(model, start, end, level)
    {
        this.model = model;
        this.start = start;
        this.end = end;
        this.level = level
    };
    UI.RangeGroupInfo.prototype = {
        model: null,
        children: null,
        parent: null,
        start: 0,
        end: 0,
        level: 0,
        getState: function()
        {
            if(this.model)
                return this.model.getState(this);
            return GrapeCity.UI.GroupState.Expanded
        },
        setState: function(value)
        {
            if(this.model)
                this.model.expand(this.level,value === GrapeCity.UI.GroupState.Expanded)
        },
        contains: function(index)
        {
            return this.start <= index && index <= this.end
        },
        addChild: function(child)
        {
            if(child)
            {
                if(!this.children)
                    this.children = [];
                this.children.push(child);
                child.parent = this
            }
        }
    };
    GrapeCity.UI.WorkingState = function()
    {
        this.count = 0
    };
    GrapeCity.UI.WorkingState.prototype = {
        addRef: function()
        {
            this.count++
        },
        release: function()
        {
            this.count--
        },
        isWorking: function()
        {
            return this.count > 0
        }
    };
    UI.RangeGroup = function(count)
    {
        this.items = new Array(count);
        this.suspendAddingGroup = new GrapeCity.UI.WorkingState
    };
    UI.RangeGroup.prototype = {
        head: null,
        tail: null,
        items: null,
        _rootCached: null,
        direction: GrapeCity.UI.RangeGroupDirection.Forward,
        suspendAddingGroup: null,
        setDirection: function(direction)
        {
            this.direction = direction
        },
        getDirection: function()
        {
            return this.direction
        },
        _getCount: function()
        {
            if(!this.items)
                return 0;
            return this.items.length
        },
        group: function(index, count)
        {
            if(!this._isIndexValid(index))
                throw new Error("index");
            if(!this._isIndexValid(index + count - 1))
                throw new Error("count");
            this._rootCached = null;
            for(var n = 0; n < count; n++)
            {
                var currentN = index + n;
                var modelCurrentN = this._getModelIndexFromViewIndex(currentN);
                if(!this.items[modelCurrentN])
                    this.items[modelCurrentN] = new GrapeCity.UI.RangeGroupItemInfo;
                else
                    this.items[modelCurrentN].level++
            }
            this._rootCached = this.createRangeGroup()
        },
        ungroupRange: function(index, count)
        {
            if(!this._isIndexValid(index))
                throw new Error("index");
            if(!this._isIndexValid(index + count - 1))
                throw new Error("count");
            var shouldClearSummaryItemInfo = false;
            var group = null;
            var summaryItemIndex = -1;
            if(this.direction === GrapeCity.UI.RangeGroupDirection.Forward)
            {
                summaryItemIndex = index + count;
                shouldClearSummaryItemInfo = this._isIndexValid(summaryItemIndex);
                if(shouldClearSummaryItemInfo)
                {
                    var si = index + count - 1;
                    var lastN = si;
                    var modelLast = null;
                    do
                    {
                        var modelLastN = this._getModelIndexFromViewIndex(si);
                        modelLast = this.items[modelLastN];
                        if(modelLast !== undefined && modelLast !== null)
                            lastN = si;
                        si--
                    } while((modelLast === undefined || modelLast === null) && si >= index);
                    if(modelLast !== undefined && modelLast !== null)
                    {
                        group = this._findImp(this._rootCached,lastN,modelLast.level);
                        if(group)
                            summaryItemIndex = group.end + 1;
                        shouldClearSummaryItemInfo = group && (group.end === index + count - 1 || this.getState(group) === GrapeCity.UI.GroupState.Collapsed)
                    }
                }
            }
            else if(this.direction === GrapeCity.UI.RangeGroupDirection.Backward)
            {
                summaryItemIndex = index - 1;
                shouldClearSummaryItemInfo = this._isIndexValid(summaryItemIndex);
                if(shouldClearSummaryItemInfo)
                {
                    var si1 = index;
                    var firstN = si1;
                    var modelFirst = null;
                    do
                    {
                        var modelFirstN = this._getModelIndexFromViewIndex(si1);
                        modelFirst = this.items[modelFirstN];
                        if(modelFirst !== undefined && modelFirst !== null)
                            firstN = si1;
                        si1++
                    } while((modelFirst === undefined || modelFirst === null) && si1 < index + count);
                    if(modelFirst !== undefined && modelFirst !== null)
                    {
                        group = this._findImp(this._rootCached,firstN,modelFirst.level);
                        if(group)
                            summaryItemIndex = group.start - 1;
                        shouldClearSummaryItemInfo = group && (group.start === index || this.getState(group) === GrapeCity.UI.GroupState.Collapsed)
                    }
                }
            }
            this._rootCached = null;
            for(var n = 0; n < count; n++)
            {
                var currentN = index + n;
                var modelCurrentN = this._getModelIndexFromViewIndex(currentN);
                if(this.items[modelCurrentN])
                    if(this.items[modelCurrentN].level > -1)
                        this.items[modelCurrentN].level--
            }
            if(shouldClearSummaryItemInfo)
            {
                var modelSearchN = this._getModelIndexFromViewIndex(summaryItemIndex);
                var itemSearch = this.items[modelSearchN];
                if(itemSearch)
                    if(itemSearch.level === group.level)
                        this.items[modelSearchN] = null
            }
            this._rootCached = this.createRangeGroup()
        },
        ungroup: function()
        {
            this._rootCached = null;
            var count = this._getCount();
            this.items = new Array(count);
            this._rootCached = this.createRangeGroup()
        },
        _expand: function(index, level, expand)
        {
            if(!this._isIndexValid(index))
                throw new Error("index");
            if(level < -1)
                throw new Error("level");
            var group = this.find(index,level);
            if(group)
                this.expandGroup(group,expand)
        },
        expand: function(level, expand)
        {
            if(level < -1)
                throw new Error("level");
            var e = new GrapeCity.UI.GroupedItemIndexEnumerator(this);
            while(e.moveNext())
                this._expand(e.current,level,expand)
        },
        expandGroup: function(groupInfo, expand)
        {
            if(!groupInfo)
                throw new Error("groupInfo");
            var summaryItemIndex = -1;
            switch(this.direction)
            {
                case GrapeCity.UI.RangeGroupDirection.Backward:
                    summaryItemIndex = groupInfo.start - 1;
                    break;
                case GrapeCity.UI.RangeGroupDirection.Forward:
                    summaryItemIndex = groupInfo.end + 1;
                    break
            }
            this.setCollapsed(summaryItemIndex,!expand)
        },
        isCollapsed: function(index)
        {
            if(!this._isIndexValid(index))
                throw new Error("index");
            var level = this.getLevel(index);
            if(level > -1)
            {
                var group = this.find(index,level);
                for(var n = -1; n <= level; n++)
                    if(group)
                    {
                        if(group.getState() === GrapeCity.UI.GroupState.Collapsed)
                            return true;
                        group = group.parent;
                        if(!group)
                            break
                    }
            }
            return false
        },
        find: function(index, level)
        {
            if(this._rootCached)
            {
                if(level === -1)
                    return this._rootCached;
                if(!this._isIndexValid(index))
                    throw new Error("index");
                return this._findImp(this._rootCached,index,level)
            }
            return null
        },
        getLevel: function(index)
        {
            if(!this._isIndexValid(index))
                throw new Error("index");
            var modelIndex = this._getModelIndexFromViewIndex(index);
            var item = this.items[modelIndex];
            if(item)
                return item.level;
            return-1
        },
        getCollapsed: function(index)
        {
            var modelIndex = this._getModelIndexFromViewIndex(index);
            var item = this.items[modelIndex];
            if(item)
                return!!item.collapsed;
            return false
        },
        setCollapsed: function(index, collapsed)
        {
            if(index < 0)
            {
                if(!this.head)
                    this.head = new GrapeCity.UI.RangeGroupItemInfo;
                this.head.collapsed = collapsed
            }
            else if(index > -1 && index < this._getCount())
            {
                var modelIndex = this._getModelIndexFromViewIndex(index);
                if(!this.items[modelIndex])
                {
                    this.items[modelIndex] = new GrapeCity.UI.RangeGroupItemInfo;
                    this.items[modelIndex].level = -1
                }
                this.items[modelIndex].collapsed = collapsed
            }
            else if(index >= this._getCount())
            {
                if(!this.tail)
                    this.tail = new GrapeCity.UI.RangeGroupItemInfo;
                this.tail.collapsed = collapsed
            }
            this._rootCached = null;
            this.refresh()
        },
        getMaxLevel: function()
        {
            var maxLevel = -1;
            var e = new GrapeCity.UI.GroupedItemIndexEnumerator(this);
            while(e.moveNext())
            {
                var level = this.getLevel(e.current);
                if(maxLevel < level)
                    maxLevel = level
            }
            return maxLevel
        },
        _move: function(from, to, count)
        {
            if(count > 0)
            {
                if(from < 0)
                    from = 0;
                if(to < 0)
                    to = 0;
                if(from === to)
                    return;
                var copiedItems = [];
                var index = this.items.nextNonEmptyIndex(from - 1);
                while(index >= 0 && index < from + count)
                {
                    var copied = new GrapeCity.UI.RangeGroupItemInfo(this.items[index]);
                    copiedItems.push({
                        index: index - from,
                        value: copied
                    });
                    index = this.items.nextNonEmptyIndex(index)
                }
                this.items.clear(from,count);
                this.items.clear(to,count);
                if(copiedItems.length > 0)
                    for(var i = 0; i < copiedItems.length; i++)
                    {
                        var item = copiedItems[i];
                        this.items[to + item.index] = item.value
                    }
                this._rootCached = null;
                this.refresh()
            }
        },
        _copy: function(from, to, count)
        {
            if(count > 0)
            {
                if(from < 0)
                    from = 0;
                if(to < 0)
                    to = 0;
                if(from === to)
                    return;
                var copiedItems = [];
                var index = this.items.nextNonEmptyIndex(from - 1);
                while(index >= 0 && index < from + count)
                {
                    var copied = new GrapeCity.UI.RangeGroupItemInfo(this.items[index]);
                    copiedItems.push({
                        index: index - from,
                        value: copied
                    });
                    index = this.items.nextNonEmptyIndex(index)
                }
                this.items.clear(to,count);
                if(copiedItems.length > 0)
                    for(var i = 0; i < copiedItems.length; i++)
                    {
                        var item = copiedItems[i];
                        this.items[to + item.index] = item.value
                    }
                this._rootCached = null;
                this.refresh()
            }
        },
        _add: function(index, count)
        {
            if(count > 0)
            {
                var modelIndex = this._getModelIndexFromViewIndex(index);
                var addedGroupItem = modelIndex > 1 ? this.items[modelIndex - 1] : null;
                for(var i = 0; i < count; i++)
                    this.items.splice(modelIndex,0,!!addedGroupItem ? new GrapeCity.UI.RangeGroupItemInfo(addedGroupItem) : null);
                if(!this.suspendAddingGroup.isWorking)
                    if(index > 0)
                    {
                        var modelIndexPrevious = this._getModelIndexFromViewIndex(index - 1);
                        var groupItemInfo = null;
                        if(modelIndexPrevious < 0)
                            groupItemInfo = this.head;
                        else
                            groupItemInfo = this.items[modelIndexPrevious];
                        if(groupItemInfo)
                            for(var i1 = 0; i1 < count; i1++)
                                this.items[index + i1] = new GrapeCity.UI.RangeGroupItemInfo({
                                    collapsed: groupItemInfo.collapsed,
                                    level: groupItemInfo.level
                                })
                    }
                this._rootCached = null;
                this.refresh()
            }
        },
        _remove: function(index, count)
        {
            if(count > 0)
            {
                this.items.splice(index,count);
                this._rootCached = null;
                this.refresh()
            }
        },
        equals: function(obj)
        {
            var group = obj;
            if(group)
            {
                if(!this.items && group.items && group.items.length > 0)
                    return false;
                if(!group.items && this.items && this.items.length > 0)
                    return false;
                if(!this.rangeGroupItemInfoEquals(this.head,group.head))
                    return false;
                if(!this.rangeGroupItemInfoEquals(this.tail,group.tail))
                    return false;
                if(this.items && group.items)
                {
                    if(this.direction !== group.direction)
                        return false;
                    if(this.items.length !== group.items.length)
                        return false;
                    for(var i = 0; i < this.items.length; i++)
                        if(!this.items[i] && group.items[i] || this.items[i] && !group.items[i])
                            return false
                }
                return true
            }
            return false
        },
        _isEmpty: function()
        {
            if(this.items && this.items.length > 0)
                return false;
            return true
        },
        refresh: function()
        {
            if(!this._isEmpty())
                this._rootCached = this.createRangeGroup()
        },
        _setCount: function(count)
        {
            if(!this.items)
                this.items = new Array(count);
            this._rootCached = null;
            var currentCount = this._getCount();
            if(count < currentCount)
                this.items.splice(count,currentCount - count);
            else if(count > currentCount)
            {
                var lastItem = this.items[this.items.length - 1];
                if(lastItem)
                    for(var i = 0; i < count; i++)
                        this.items.push(new GrapeCity.UI.RangeGroupItemInfo(lastItem));
                else
                    this.items = this.items.concat(new Array(count))
            }
        },
        getState: function(group)
        {
            var summaryItemIndex = -1;
            switch(this.direction)
            {
                case GrapeCity.UI.RangeGroupDirection.Forward:
                    summaryItemIndex = group.end + 1;
                    break;
                case GrapeCity.UI.RangeGroupDirection.Backward:
                    summaryItemIndex = group.start - 1;
                    break
            }
            var item = null;
            if(summaryItemIndex < 0)
                item = this.head;
            if(summaryItemIndex > -1 && summaryItemIndex < this._getCount())
            {
                var viewIndex = this._getViewIndexFromModelIndex(summaryItemIndex);
                item = this.items[viewIndex]
            }
            else if(summaryItemIndex >= this._getCount())
                item = this.tail;
            if(item && item.collapsed)
                return GrapeCity.UI.GroupState.Collapsed;
            else
                return GrapeCity.UI.GroupState.Expanded
        },
        _setLevel: function(index, level)
        {
            if(index < 0)
            {
                if(!this.head)
                    this.head = new GrapeCity.UI.RangeGroupItemInfo;
                this.head.level = level
            }
            else if(index > -1 && index < this._getCount())
            {
                var modelIndex = this._getModelIndexFromViewIndex(index);
                if(!this.items[modelIndex])
                {
                    this.items[modelIndex] = new GrapeCity.UI.RangeGroupItemInfo;
                    this.items[modelIndex].level = -1
                }
                this.items[modelIndex].level = level
            }
            else if(index >= this._getCount())
            {
                if(!this.tail)
                    this.tail = new GrapeCity.UI.RangeGroupItemInfo;
                this.tail.level = level
            }
            this._rootCached = null;
            this.refresh()
        },
        suspendAdding: function()
        {
            this.suspendAddingGroup.addRef()
        },
        resumeAdding: function()
        {
            this.suspendAddingGroup.release()
        },
        _getViewIndexFromModelIndex: function(index)
        {
            return index
        },
        _getModelIndexFromViewIndex: function(index)
        {
            return index
        },
        rangeGroupItemInfoEquals: function(item1, item2)
        {
            if(!item1)
                return!item2;
            else
            {
                if(!item2)
                    return false;
                return item1.level === item2.level && item1.collapsed === item2.collapsed
            }
        },
        createRangeGroup: function()
        {
            var root = new GrapeCity.UI.RangeGroupInfo(this,0,this._getCount() - 1,-1);
            var e = new GrapeCity.UI.GroupedItemIndexEnumerator(this);
            while(e.moveNext())
            {
                var group = this._createRangeGroup(e,0);
                if(group && group.level > -1)
                    root.addChild(group)
            }
            return root
        },
        _createRangeGroup: function(e, level)
        {
            var currentGroup = null;
            do
            {
                var index = e.current;
                var modelIndex = this._getModelIndexFromViewIndex(index);
                var item = this.items[modelIndex];
                var indexNext = e.nextToCurrent();
                if(item.level < level)
                    continue;
                else if(item.level >= level)
                    if(!currentGroup)
                        currentGroup = new GrapeCity.UI.RangeGroupInfo(this,index,index,level);
                if(item.level > level)
                {
                    var childGroup = this._createRangeGroup(e,level + 1);
                    if(e.current > -1)
                    {
                        index = e.current;
                        indexNext = e.nextToCurrent()
                    }
                    else
                    {
                        index = childGroup.end;
                        indexNext = -1
                    }
                    currentGroup.addChild(childGroup)
                }
                if(index > currentGroup.end)
                    currentGroup.end = index;
                if(this.isGroupEnd(index,indexNext,level))
                    return currentGroup
            } while(e.moveNext());
            return currentGroup
        },
        isGroupEnd: function(index, indexNext, processLevel)
        {
            var modelIndex = this._getModelIndexFromViewIndex(index);
            var item = this.items[modelIndex];
            if(!this._isIndexValid(indexNext))
                return true;
            var modelIndexNext = this._getModelIndexFromViewIndex(indexNext);
            var itemNext = this.items[modelIndexNext];
            if(!itemNext)
                return true;
            if(itemNext.level < item.level)
            {
                var offset1 = item.level - itemNext.level;
                var offset2 = item.level - processLevel;
                if(offset1 === offset2)
                    return false;
                if(offset2 >= 0 && offset2 < offset1)
                    return true
            }
            return false
        },
        _isIndexValid: function(index)
        {
            return index >= -1 && index < this._getCount()
        },
        _findImp: function(group, index, level)
        {
            if(group)
                if(group.contains(index) && group.level === level)
                    return group;
                else if(group.children && group.children.length > 0)
                    for(var i = 0; i < group.children.length; i++)
                    {
                        var child = group.children[i];
                        var temp = this._findImp(child,index,level);
                        if(temp)
                            return temp
                    }
            return null
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.itemsCount !== null && settings.itemsCount !== undefined)
            {
                this.items = new Array(settings.itemsCount);
                if(settings.itemsData)
                {
                    var length = settings.itemsData.length;
                    for(var i = 0; i < length; i++)
                    {
                        var item = settings.itemsData[i];
                        if(item.count > 0 && item.index >= 0 && item.info)
                            for(var k = 0; k < item.count; k++)
                            {
                                if(item.index + k >= this.items.length)
                                    break;
                                this.items[item.index + k] = new GrapeCity.UI.RangeGroupItemInfo(item.info)
                            }
                    }
                }
            }
            if(settings.direction !== null && settings.direction !== undefined)
                this.direction = settings.direction;
            if(settings.head)
                this.head = settings.head;
            if(settings.tail)
                this.tail = settings.tail;
            this.refresh()
        },
        toJSON: function()
        {
            var itemsData = [];
            var itemsCount = this.items.length;
            var k = -1;
            for(var i = 0; i < itemsCount; i++)
                if(this.items[i])
                    if(k >= 0 && i === itemsData[k].count + itemsData[k].index && this.items[i].level === itemsData[k].info.level && this.items[i].collapsed === itemsData[k].info.collapsed)
                        itemsData[k].count++;
                    else
                    {
                        k++;
                        itemsData[k] = {
                            index: i,
                            count: 1,
                            info: this.items[i]
                        }
                    }
            return{
                    itemsCount: itemsCount,
                    itemsData: itemsData,
                    direction: this.direction,
                    head: this.head,
                    tail: this.tail
                }
        }
    };
    UI.RangeGroupItemInfo = function(info)
    {
        if(this !== info && info)
        {
            this.level = info.level;
            this.collapsed = info.collapsed
        }
        if(!info)
        {
            this.level = 0;
            this.collapsed = null
        }
    };
    UI.GroupedItemIndexEnumerator = function(rangeGroup)
    {
        this.rangeGroup = rangeGroup
    };
    UI.GroupedItemIndexEnumerator.prototype = {
        isEOF: false,
        rangeGroup: null,
        current: -1,
        nextToCurrent: function()
        {
            return this.current + 1
        },
        moveNext: function()
        {
            if(this.isEOF || !this.rangeGroup || !this.rangeGroup.items)
                return false;
            var found = false;
            for(var n = this.current + 1; n < this.rangeGroup.items.length; n++)
            {
                var modelIndex = this.rangeGroup._getModelIndexFromViewIndex(n);
                if(this.rangeGroup.items[modelIndex])
                {
                    found = true;
                    this.current = n;
                    break
                }
            }
            if(!found)
                this.current = -1;
            if(this.current > -1)
                return true;
            else
            {
                this.isEOF = true;
                return false
            }
        },
        reset: function()
        {
            this.isEOF = false;
            this.current = -1
        }
    };
    var ThemeHelper = {
            getThemeBackgroundColor: function(sheet, defaultColor)
            {
                var useWijmoTheme = sheet.parent ? sheet.parent.useWijmoTheme : sheet.useWijmoTheme;
                if(useWijmoTheme === true)
                {
                    var themeStyle = GrapeCity.UI.Global.prototype.getWijmoThemeStyle(GrapeCity.UI.VisualState.Normal);
                    if(themeStyle && themeStyle.backgroundColor && themeStyle.backgroundColor !== "")
                        defaultColor = themeStyle.backgroundColor
                }
                return defaultColor
            },
            getThemeForeColor: function(sheet, defaultColor)
            {
                var useWijmoTheme = sheet.parent ? sheet.parent.useWijmoTheme : sheet.useWijmoTheme;
                if(useWijmoTheme === true)
                {
                    var themeStyle = GrapeCity.UI.Global.prototype.getWijmoThemeStyle(GrapeCity.UI.VisualState.Normal);
                    if(themeStyle && themeStyle.color && themeStyle.color !== "")
                        defaultColor = themeStyle.color
                }
                return defaultColor
            }
        };
    GrapeCity.UI._RangeGroupPresenter = function(sheet, rowGroup, viewportIndex)
    {
        this._sheet = sheet;
        this._rowGroup = rowGroup;
        this._viewportIndex = viewportIndex
    };
    GrapeCity.UI._RangeGroupPresenter.prototype = {
        _sheet: null,
        _rowGroup: null,
        _viewportIndex: 1,
        PADDING: 2.0,
        LINE_SIZE: 2.0,
        STARTLINE_SIZE: 6.0,
        createGroupInfo: function()
        {
            this._groupLineInfos = [];
            this._groupDotInfos = [];
            this._groupButtonInfos = [];
            var rowGroup = this._rowGroup;
            var maxLevel = this.getMaxLevel(rowGroup);
            if(maxLevel !== -1)
            {
                var groupLayout = this._sheet._getGroupLayout();
                var buttonSize = this._calcMinWidthOrHeight({
                        width: groupLayout.width,
                        height: groupLayout.height
                    },rowGroup);
                var groups = this.getGroupsByLevel(0,rowGroup);
                if(groups && groups.length > 0)
                    for(var i = 0; i < groups.length; i++)
                    {
                        var group = groups[i];
                        this._measureGroups(group,buttonSize)
                    }
            }
        },
        paintGroups: function(ctx)
        {
            if(this._rowGroup)
                this._paintRowGroups(ctx);
            else
                this._paintColumnGroups(ctx)
        },
        _calcMinWidthOrHeight: function(finalSize, rowGroup)
        {
            var size = 0;
            var maxLevel = this.getMaxLevel(rowGroup);
            if(rowGroup)
                size = (finalSize.width - this.PADDING * 2) / (maxLevel + 2);
            else
                size = (finalSize.height - this.PADDING * 2) / (maxLevel + 2);
            size = Math.max(0,size);
            return size
        },
        _measureGroups: function(group, buttonSize)
        {
            var rowGroup = this._rowGroup;
            var groupDirection = this.getGroupDirection(rowGroup);
            var viewportStart = this.getViewportStartIndex(rowGroup);
            var viewportEnd = this.getViewportEndIndex(rowGroup);
            var startRow = group.start;
            var endRow = group.end;
            var button,
                buttonRowIndex,
                i;
            if(group.getState() === GrapeCity.UI.GroupState.Expanded)
            {
                var needPaintingLine = true;
                var parent = group.parent;
                if(parent && (groupDirection === GrapeCity.UI.RangeGroupDirection.Backward && group.start === parent.start || groupDirection === GrapeCity.UI.RangeGroupDirection.Forward && group.end === parent.end))
                    needPaintingLine = false;
                if(needPaintingLine)
                {
                    button = {
                        isExpanded: true,
                        level: group.level,
                        paintLine: true
                    };
                    if(groupDirection === GrapeCity.UI.RangeGroupDirection.Forward)
                    {
                        buttonRowIndex = endRow + 1;
                        if(buttonRowIndex >= viewportStart && buttonRowIndex <= viewportEnd)
                        {
                            button.index = buttonRowIndex;
                            button.lineDirection = GrapeCity.UI.RangeGroupDirection.Forward;
                            this._groupButtonInfos.push(button)
                        }
                    }
                    else if(groupDirection === GrapeCity.UI.RangeGroupDirection.Backward)
                    {
                        buttonRowIndex = startRow - 1;
                        if(buttonRowIndex >= viewportStart && buttonRowIndex <= viewportEnd)
                        {
                            button.index = buttonRowIndex;
                            button.lineDirection = GrapeCity.UI.RangeGroupDirection.Backward;
                            this._groupButtonInfos.push(button)
                        }
                    }
                }
                if(startRow <= viewportEnd && endRow >= viewportStart)
                {
                    var start = Math.max(viewportStart,startRow);
                    var end = Math.min(viewportEnd,endRow);
                    if(needPaintingLine)
                    {
                        var groupLineInfo = {
                                start: start,
                                end: end,
                                level: group.level
                            };
                        if(groupDirection === GrapeCity.UI.RangeGroupDirection.Forward && start === startRow || groupDirection === GrapeCity.UI.RangeGroupDirection.Backward && end === endRow)
                            groupLineInfo.startLine = true;
                        this._groupLineInfos.push(groupLineInfo)
                    }
                    var dots = [];
                    for(i = start; i <= end; i++)
                        dots.push(i);
                    if(group.children && group.children.length > 0)
                        for(i = 0; i < group.children.length; i++)
                        {
                            var childGroup = group.children[i];
                            if(childGroup.getState() === GrapeCity.UI.GroupState.Collapsed)
                                for(var j = childGroup.start; j <= childGroup.end; j++)
                                    dots.remove(j);
                            if(groupDirection === GrapeCity.UI.RangeGroupDirection.Forward)
                                dots.remove(childGroup.end + 1);
                            else if(groupDirection === GrapeCity.UI.RangeGroupDirection.Backward)
                                dots.remove(childGroup.start - 1);
                            this._measureGroups(childGroup,buttonSize)
                        }
                    var needPaintingDot = true;
                    if(group.children)
                        for(var ci = 0; ci < group.children.length; ci++)
                        {
                            var child = group.children[ci];
                            if(child)
                                if(child.start === group.start && child.end === group.end)
                                {
                                    needPaintingDot = false;
                                    break
                                }
                                else if(groupDirection === GrapeCity.UI.RangeGroupDirection.Forward && child.end === group.end || groupDirection === GrapeCity.UI.RangeGroupDirection.Backward && child.start === group.start)
                                    for(var ic = child.start; ic <= child.end; ic++)
                                        dots.remove(ic)
                        }
                    if(needPaintingDot && dots.length > 0)
                        for(i = 0; i < dots.length; i++)
                        {
                            var index = dots[i];
                            var groupDotInfo = {
                                    index: index,
                                    level: group.level + 1
                                };
                            this._groupDotInfos.push(groupDotInfo)
                        }
                }
            }
            else if(group.getState() === GrapeCity.UI.GroupState.Collapsed)
            {
                button = {
                    isExpanded: false,
                    level: group.level
                };
                if(groupDirection === GrapeCity.UI.RangeGroupDirection.Forward)
                {
                    buttonRowIndex = endRow + 1;
                    if(buttonRowIndex >= viewportStart && buttonRowIndex <= viewportEnd)
                    {
                        button.index = buttonRowIndex;
                        button.lineDirection = GrapeCity.UI.RangeGroupDirection.Forward;
                        this._groupButtonInfos.push(button)
                    }
                }
                else if(groupDirection === GrapeCity.UI.RangeGroupDirection.Backward)
                {
                    buttonRowIndex = startRow - 1;
                    if(buttonRowIndex >= viewportStart && buttonRowIndex <= viewportEnd)
                    {
                        button.index = buttonRowIndex;
                        button.lineDirection = GrapeCity.UI.RangeGroupDirection.Backward;
                        this._groupButtonInfos.push(button)
                    }
                }
            }
        },
        _paintRowGroups: function(ctx)
        {
            var maxLevel = this.getMaxLevel(true);
            if(maxLevel === -1)
                return;
            var groupLayout = this._sheet._getGroupLayout();
            var itemWidth = this._calcMinWidthOrHeight({
                    width: groupLayout.width,
                    height: groupLayout.height
                },true);
            if(itemWidth === 0)
                return;
            ctx.save();
            var offsetX = Math.max(0,(itemWidth - this.STARTLINE_SIZE) / 2) + this.PADDING;
            var columnX,
                rowY,
                i,
                rowLayout;
            var rowLayoutModel = this._sheet._getRowLayout(this._viewportIndex,GrapeCity.UI.SheetArea.viewport);
            if(this._groupDotInfos && this._groupDotInfos.length > 0)
                for(i = 0; i < this._groupDotInfos.length; i++)
                {
                    var dot = this._groupDotInfos[i];
                    rowLayout = rowLayoutModel.findRow(dot.index);
                    if(rowLayout && rowLayout.height >= this.LINE_SIZE)
                    {
                        columnX = dot.level * itemWidth + offsetX;
                        rowY = rowLayout.y + Math.max(0,(rowLayout.height - this.LINE_SIZE) / 2);
                        var dotWidth = this.LINE_SIZE;
                        var dotHeight = this.LINE_SIZE;
                        ctx.fillStyle = ThemeHelper.getThemeForeColor(this._sheet,"black");
                        ctx.fillRect(groupLayout.x + columnX + 0.5,groupLayout.y + rowY,dotWidth,dotHeight)
                    }
                }
            var rowGroupDirection = this._sheet.rowRangeGroup.direction;
            if(this._groupLineInfos && this._groupLineInfos.length > 0)
                for(i = 0; i < this._groupLineInfos.length; i++)
                {
                    var groupLineInfo = this._groupLineInfos[i];
                    var start = groupLineInfo.start,
                        end = groupLineInfo.end;
                    var startRowLayout = null,
                        endRowLayout = null;
                    do
                    {
                        startRowLayout = rowLayoutModel.findRow(start);
                        start++
                    } while(!startRowLayout && start <= end);
                    do
                    {
                        endRowLayout = rowLayoutModel.findRow(end);
                        end--
                    } while(!endRowLayout && end >= start);
                    if(!startRowLayout && !endRowLayout)
                        continue;
                    else if(!startRowLayout && endRowLayout)
                        startRowLayout = endRowLayout;
                    else if(startRowLayout && !endRowLayout)
                        endRowLayout = startRowLayout;
                    columnX = groupLineInfo.level * itemWidth + offsetX;
                    rowY = startRowLayout.y;
                    if(rowGroupDirection === GrapeCity.UI.RangeGroupDirection.Forward)
                        rowY += 1;
                    var lineWidth = this.LINE_SIZE;
                    var lineHeight = Math.max(0,endRowLayout.y + endRowLayout.height - startRowLayout.y);
                    ctx.fillStyle = ThemeHelper.getThemeForeColor(this._sheet,"black");
                    ctx.fillRect(groupLayout.x + columnX + 0.5,groupLayout.y + rowY,lineWidth,lineHeight);
                    var startLine = groupLineInfo.startLine;
                    if(startLine)
                    {
                        var startLineWidth = Math.min(this.STARTLINE_SIZE,itemWidth - this.LINE_SIZE);
                        if(startLineWidth > 0)
                        {
                            if(rowGroupDirection === GrapeCity.UI.RangeGroupDirection.Backward)
                                rowY = rowY + lineHeight - this.LINE_SIZE;
                            if(rowY >= startRowLayout.y && rowY < endRowLayout.y + endRowLayout.height)
                            {
                                ctx.fillStyle = ThemeHelper.getThemeForeColor(this._sheet,"black");
                                ctx.fillRect(groupLayout.x + columnX + 0.5,groupLayout.y + rowY,startLineWidth,this.LINE_SIZE)
                            }
                        }
                    }
                }
            if(this._groupButtonInfos && this._groupButtonInfos.length > 0)
                for(i = 0; i < this._groupButtonInfos.length; i++)
                {
                    var groupButtonInfo = this._groupButtonInfos[i];
                    rowLayout = rowLayoutModel.findRow(groupButtonInfo.index);
                    if(!rowLayout || rowLayout && rowLayout.height <= 0)
                        continue;
                    var heightOffSet = Math.max(0,(rowLayout.height - itemWidth) / 2);
                    columnX = groupButtonInfo.level * itemWidth + this.PADDING;
                    rowY = rowLayout.y + heightOffSet;
                    var btnWidth = itemWidth;
                    var btnHeight = Math.min(itemWidth,rowLayout.height);
                    ctx.strokeStyle = "gray";
                    ctx.lineWidth = 1;
                    ctx.strokeRect(groupLayout.x + columnX + 0.5,groupLayout.y + rowY,btnWidth,btnHeight);
                    if(!groupButtonInfo.isExpanded)
                        ctx.fillRect(groupLayout.x + columnX + btnWidth / 2 - 1 + 0.5,groupLayout.y + rowY + 4,2,btnHeight - 8);
                    ctx.fillRect(groupLayout.x + columnX + 4 + 0.5,groupLayout.y + rowY + btnHeight / 2 - 1,btnWidth - 8,2);
                    var btnLine = groupButtonInfo.paintLine;
                    if(btnLine && btnHeight < rowLayout.height)
                    {
                        columnX = groupButtonInfo.level * itemWidth + offsetX;
                        rowY = rowLayout.y;
                        var btnLineWidth = this.LINE_SIZE;
                        var btnLineHeight = heightOffSet;
                        if(groupButtonInfo.lineDirection === GrapeCity.UI.RangeGroupDirection.Backward)
                        {
                            rowY += heightOffSet + btnHeight;
                            btnLineHeight = rowLayout.height - btnHeight - heightOffSet
                        }
                        ctx.fillStyle = ThemeHelper.getThemeForeColor(this._sheet,"black");
                        ctx.fillRect(groupLayout.x + columnX + 0.5,groupLayout.y + rowY,btnLineWidth,btnLineHeight)
                    }
                }
            ctx.stroke();
            ctx.beginPath();
            ctx.restore()
        },
        _paintColumnGroups: function(ctx)
        {
            var maxLevel = this.getMaxLevel(false);
            if(maxLevel === -1)
                return;
            var groupLayout = this._sheet._getGroupLayout();
            var itemHeight = this._calcMinWidthOrHeight({
                    width: groupLayout.width,
                    height: groupLayout.height
                },false);
            if(itemHeight === 0)
                return;
            var offsetY = Math.max(0,(itemHeight - this.STARTLINE_SIZE) / 2) + this.PADDING;
            var columnX,
                rowY,
                i,
                columnLayout;
            var columnLayoutModel = this._sheet._getColumnLayout(this._viewportIndex,GrapeCity.UI.SheetArea.viewport);
            ctx.save();
            for(i = 0; i < this._groupDotInfos.length; i++)
            {
                var dot = this._groupDotInfos[i];
                columnLayout = columnLayoutModel.findCol(dot.index);
                if(columnLayout && columnLayout.width >= this.LINE_SIZE)
                {
                    columnX = columnLayout.x + Math.max(0,(columnLayout.width - this.LINE_SIZE) / 2);
                    rowY = dot.level * itemHeight + offsetY;
                    var dotWidth = this.LINE_SIZE;
                    var dotHeight = this.LINE_SIZE;
                    ctx.fillStyle = ThemeHelper.getThemeForeColor(this._sheet,"black");
                    ctx.fillRect(groupLayout.x + columnX,groupLayout.y + rowY + 0.5,dotWidth,dotHeight)
                }
            }
            var columnGroupDirection = this._sheet.colRangeGroup.direction;
            for(i = 0; i < this._groupLineInfos.length; i++)
            {
                var groupLineInfo = this._groupLineInfos[i];
                var start = groupLineInfo.start,
                    end = groupLineInfo.end;
                var startColumnLayout = null,
                    endColumnLayout = null;
                do
                {
                    startColumnLayout = columnLayoutModel.findCol(start);
                    start++
                } while(!startColumnLayout && start <= end);
                do
                {
                    endColumnLayout = columnLayoutModel.findCol(end);
                    end--
                } while(!endColumnLayout && end >= start);
                if(!startColumnLayout && !endColumnLayout)
                    continue;
                else if(!startColumnLayout && endColumnLayout)
                    startColumnLayout = endColumnLayout;
                else if(startColumnLayout && !endColumnLayout)
                    endColumnLayout = startColumnLayout;
                columnX = startColumnLayout.x;
                if(columnGroupDirection === GrapeCity.UI.RangeGroupDirection.Forward)
                    columnX += 1;
                rowY = groupLineInfo.level * itemHeight + offsetY;
                var lineWidth = Math.max(0,endColumnLayout.x + endColumnLayout.width - startColumnLayout.x);
                var lineHeight = this.LINE_SIZE;
                ctx.fillStyle = ThemeHelper.getThemeForeColor(this._sheet,"black");
                ctx.fillRect(groupLayout.x + columnX,groupLayout.y + rowY + 0.5,lineWidth,lineHeight);
                var startLine = groupLineInfo.startLine;
                if(startLine)
                {
                    var startLineHeight = Math.min(this.STARTLINE_SIZE,itemHeight - this.LINE_SIZE);
                    if(startLineHeight > 0)
                    {
                        if(columnGroupDirection === GrapeCity.UI.RangeGroupDirection.Backward)
                            columnX = columnX + lineWidth - this.LINE_SIZE;
                        if(columnX >= startColumnLayout.x && columnX < endColumnLayout.x + endColumnLayout.width)
                        {
                            ctx.fillStyle = ThemeHelper.getThemeForeColor(this._sheet,"black");
                            ctx.fillRect(groupLayout.x + columnX,groupLayout.y + rowY + 0.5,this.LINE_SIZE,startLineHeight)
                        }
                    }
                }
            }
            for(i = 0; i < this._groupButtonInfos.length; i++)
            {
                var groupButtonInfo = this._groupButtonInfos[i];
                columnLayout = columnLayoutModel.findCol(groupButtonInfo.index);
                if(!columnLayout || columnLayout && columnLayout.width <= 0)
                    continue;
                var widthOffSet = Math.max(0,(columnLayout.width - itemHeight) / 2);
                columnX = columnLayout.x + widthOffSet;
                rowY = groupButtonInfo.level * itemHeight + this.PADDING;
                var btnWidth = Math.min(itemHeight,columnLayout.width);
                var btnHeight = itemHeight;
                ctx.strokeStyle = "gray";
                ctx.lineWidth = 1;
                ctx.strokeRect(groupLayout.x + columnX,groupLayout.y + rowY + 0.5,btnWidth,btnHeight);
                if(!groupButtonInfo.isExpanded)
                    ctx.fillRect(groupLayout.x + columnX + btnWidth / 2 - 1,groupLayout.y + rowY + 4.5,2,btnHeight - 8);
                ctx.fillRect(groupLayout.x + columnX + 4,groupLayout.y + rowY + btnHeight / 2 - 0.5,btnWidth - 8,2);
                var paintLine = groupButtonInfo.paintLine;
                if(paintLine && btnWidth < columnLayout.width)
                {
                    columnX = columnLayout.x;
                    rowY = groupButtonInfo.level * itemHeight + offsetY;
                    var btnLineWidth = widthOffSet;
                    var btnLineHeight = this.LINE_SIZE;
                    if(groupButtonInfo.lineDirection === GrapeCity.UI.RangeGroupDirection.Backward)
                    {
                        columnX += widthOffSet + btnWidth;
                        btnLineWidth = columnLayout.width - btnWidth - widthOffSet
                    }
                    ctx.fillStyle = ThemeHelper.getThemeForeColor(this._sheet,"black");
                    ctx.fillRect(groupLayout.x + columnX,groupLayout.y + rowY + 0.5,btnLineWidth,btnLineHeight)
                }
            }
            ctx.beginPath();
            ctx.restore()
        },
        getRowOrColumnStartIndex: function(rowRange)
        {
            if(rowRange)
            {
                var topIndex = this._sheet.getViewportTopRow(this._viewportIndex);
                return Math.max(0,topIndex - 1)
            }
            else
            {
                var leftIndex = this._sheet.getViewportLeftColumn(this._viewportIndex);
                return Math.min(0,leftIndex - 1)
            }
        },
        getRowOrColumnCount: function(rowRange)
        {
            if(rowRange)
            {
                var bottomIndex = this._sheet.getViewportBottomRow(this._viewportIndex);
                return Math.min(this._sheet.getRowCount(),bottomIndex + 2)
            }
            else
            {
                var rightIndex = this._sheet.getViewportRightColumn(this._viewportIndex);
                return Math.min(this._sheet.getColumnCount(),rightIndex + 2)
            }
        },
        getGroupsByLevel: function(level, rowRange)
        {
            var groups = [];
            var index = this.getRowOrColumnStartIndex(rowRange);
            var count = this.getRowOrColumnCount(rowRange);
            while(index < count)
            {
                var group = null;
                if(rowRange)
                    group = this._sheet.rowRangeGroup.find(index,level);
                else
                    group = this._sheet.colRangeGroup.find(index,level);
                if(group)
                {
                    index = group.end + 1;
                    groups.push(group)
                }
                else
                    index++
            }
            return groups
        },
        getMaxLevel: function(rowGroup)
        {
            var maxLevel = -1;
            if(rowGroup)
                maxLevel = this._sheet.rowRangeGroup.getMaxLevel();
            else
                maxLevel = this._sheet.colRangeGroup.getMaxLevel();
            return maxLevel
        },
        getGroupDirection: function(rowGroup)
        {
            if(rowGroup)
                return this._sheet.rowRangeGroup.direction;
            else
                return this._sheet.colRangeGroup.direction
        },
        getViewportStartIndex: function(rowGroup)
        {
            if(rowGroup)
                return this._sheet.getViewportTopRow(this._viewportIndex);
            else
                return this._sheet.getViewportLeftColumn(this._viewportIndex)
        },
        getViewportEndIndex: function(rowGroup)
        {
            if(rowGroup)
                return this._sheet.getViewportBottomRow(this._viewportIndex);
            else
                return this._sheet.getViewportRightColumn(this._viewportIndex)
        },
        getRowGroupButton: function(x, y)
        {
            var maxLevel = this.getMaxLevel(true);
            if(maxLevel === -1)
                return null;
            var groupLayout = this._sheet._getGroupLayout();
            var itemWidth = this._calcMinWidthOrHeight({
                    width: groupLayout.width,
                    height: groupLayout.height
                },true);
            if(itemWidth === 0)
                return null;
            var columnX,
                rowY;
            var rowLayoutModel = this._sheet._getRowLayout(this._viewportIndex,GrapeCity.UI.SheetArea.viewport);
            if(this._groupButtonInfos && this._groupButtonInfos.length > 0)
                for(var i = 0; i < this._groupButtonInfos.length; i++)
                {
                    var groupButtonInfo = this._groupButtonInfos[i];
                    var rowLayout = rowLayoutModel.findRow(groupButtonInfo.index);
                    if(!rowLayout || rowLayout && rowLayout.height <= 0)
                        continue;
                    var heightOffSet = Math.max(0,(rowLayout.height - itemWidth) / 2);
                    columnX = groupButtonInfo.level * itemWidth + this.PADDING;
                    rowY = rowLayout.y + heightOffSet;
                    var btnWidth = itemWidth;
                    var btnHeight = Math.min(itemWidth,rowLayout.height);
                    var btnRect = new GrapeCity.UI.Rect(groupLayout.x + columnX + 0.5,groupLayout.y + rowY + 0.5,btnWidth,btnHeight);
                    if(btnRect.contains(x,y))
                        return groupButtonInfo
                }
            return null
        },
        getColGroupButton: function(x, y)
        {
            var maxLevel = this.getMaxLevel(false);
            if(maxLevel === -1)
                return null;
            var groupLayout = this._sheet._getGroupLayout();
            var itemHeight = this._calcMinWidthOrHeight({
                    width: groupLayout.width,
                    height: groupLayout.height
                },false);
            if(itemHeight === 0)
                return null;
            var columnX,
                rowY;
            var columnLayoutModel = this._sheet._getColumnLayout(this._viewportIndex,GrapeCity.UI.SheetArea.viewport);
            if(this._groupButtonInfos && this._groupButtonInfos.length > 0)
                for(var i = 0; i < this._groupButtonInfos.length; i++)
                {
                    var groupButtonInfo = this._groupButtonInfos[i];
                    var columnLayout = columnLayoutModel.findCol(groupButtonInfo.index);
                    if(!columnLayout || columnLayout && columnLayout.width <= 0)
                        continue;
                    var widthOffSet = Math.max(0,(columnLayout.width - itemHeight) / 2);
                    columnX = columnLayout.x + widthOffSet;
                    rowY = groupButtonInfo.level * itemHeight + this.PADDING;
                    var btnWidth = Math.min(itemHeight,columnLayout.width);
                    var btnHeight = itemHeight;
                    var btnRect = new GrapeCity.UI.Rect(groupLayout.x + columnX + 0.5,groupLayout.y + rowY + 0.5,btnWidth,btnHeight);
                    if(btnRect.contains(x,y))
                        return groupButtonInfo
                }
            return null
        }
    };
    GrapeCity.UI._RangeGroupHeaderPresenter = function(sheet, rowGroup)
    {
        this._sheet = sheet;
        this._rowGroup = rowGroup
    };
    GrapeCity.UI._RangeGroupHeaderPresenter.prototype = {
        _sheet: null,
        _rowGroup: null,
        PADDING: 2.0,
        paintGroupHeader: function(ctx)
        {
            if(!this._sheet)
                return;
            var rowGroup = this._rowGroup;
            var maxLevel = this.getMaxLevel(rowGroup);
            if(maxLevel !== -1)
            {
                var groupLayout = this._sheet._getGroupLayout();
                var minSize = this._calcMinWidthOrHeight({
                        width: groupLayout.width,
                        height: groupLayout.height
                    },rowGroup);
                if(minSize === 0)
                    return;
                var buttonCount = maxLevel + 2;
                var columnX,
                    rowY,
                    i,
                    rectSize,
                    rectX,
                    rectY,
                    text,
                    textWidth,
                    textHeight;
                var layout = this._sheet._getSheetLayout();
                ctx.fillStyle = ThemeHelper.getThemeBackgroundColor(this._sheet,"#D7E6F7");
                ctx.strokeStyle = "gray";
                ctx.font = "8.25pt Arial";
                ctx.lineWidth = 1;
                if(rowGroup)
                {
                    ctx.fillRect(groupLayout.x,groupLayout.y,groupLayout.width,layout.height);
                    ctx.strokeRect(groupLayout.x - 0.5,groupLayout.y - 0.5,groupLayout.width,layout.height + 1);
                    ctx.strokeRect(groupLayout.x - 0.5,layout.headerY - 0.5,groupLayout.width,layout.colHeaderHeight);
                    ctx.fillStyle = ThemeHelper.getThemeForeColor(this._sheet,"black");
                    if(this._sheet.colHeaderVisible && layout.colHeaderHeight >= minSize)
                    {
                        columnX = this.PADDING;
                        rowY = Math.max(0,layout.headerY + (layout.colHeaderHeight - minSize) / 2);
                        for(i = 0; i < buttonCount; i++)
                        {
                            rectSize = minSize - 1;
                            rectX = groupLayout.x + columnX + 0.5;
                            rectY = groupLayout.y + rowY;
                            ctx.strokeRect(rectX,rectY,rectSize,rectSize);
                            text = "" + (i + 1);
                            textWidth = ctx.measureText(text).width;
                            textHeight = this._sheet._getFontHeight(ctx.font);
                            if(rectSize >= textWidth && minSize >= textHeight)
                                ctx.fillText(text,rectX + (rectSize - textWidth) / 2,rectY + 11);
                            columnX += minSize
                        }
                    }
                }
                else
                {
                    ctx.fillRect(groupLayout.x,groupLayout.y,layout.width,groupLayout.height);
                    ctx.strokeRect(groupLayout.x - 0.5,groupLayout.y - 0.5,layout.width + 1,groupLayout.height);
                    ctx.strokeRect(layout.headerX - 0.5,groupLayout.y - 0.5,layout.rowHeaderWidth,groupLayout.height);
                    ctx.fillStyle = ThemeHelper.getThemeForeColor(this._sheet,"black");
                    if(this._sheet.rowHeaderVisible && layout.rowHeaderWidth >= minSize)
                    {
                        columnX = Math.max(0,layout.headerX + (layout.rowHeaderWidth - minSize) / 2);
                        rowY = this.PADDING;
                        for(i = 0; i < buttonCount; i++)
                        {
                            rectSize = minSize - 1;
                            rectX = groupLayout.x + columnX;
                            rectY = groupLayout.y + rowY + 0.5;
                            ctx.strokeRect(rectX,rectY,rectSize,rectSize);
                            text = "" + (i + 1);
                            textWidth = ctx.measureText(text).width;
                            textHeight = this._sheet._getFontHeight(ctx.font);
                            if(rectSize >= textWidth && minSize >= textHeight)
                                ctx.fillText(text,rectX + (rectSize - textWidth) / 2,rectY - 0.5 + 11);
                            rowY += minSize
                        }
                    }
                }
            }
        },
        getGroupButton: function(x, y)
        {
            if(!this._sheet)
                return null;
            var rowGroup = this._rowGroup;
            var maxLevel = this.getMaxLevel(rowGroup);
            if(maxLevel !== -1)
            {
                var groupLayout = this._sheet._getGroupLayout();
                var minSize = this._calcMinWidthOrHeight({
                        width: groupLayout.width,
                        height: groupLayout.height
                    },rowGroup);
                if(minSize === 0)
                    return null;
                var buttonCount = maxLevel + 2;
                var columnX,
                    rowY,
                    i,
                    btnRect;
                var layout = this._sheet._getSheetLayout();
                if(rowGroup)
                {
                    columnX = this.PADDING;
                    rowY = Math.max(0,layout.headerY + (layout.colHeaderHeight - minSize) / 2);
                    for(i = 0; i < buttonCount; i++)
                    {
                        btnRect = new GrapeCity.UI.Rect(groupLayout.x + columnX,groupLayout.y + rowY,minSize,minSize);
                        if(btnRect.contains(x,y))
                            return{index: i};
                        columnX += minSize
                    }
                }
                else
                {
                    columnX = Math.max(0,layout.headerX + (layout.rowHeaderWidth - minSize) / 2);
                    rowY = this.PADDING;
                    for(i = 0; i < buttonCount; i++)
                    {
                        btnRect = new GrapeCity.UI.Rect(groupLayout.x + columnX,groupLayout.y + rowY,minSize,minSize);
                        if(btnRect.contains(x,y))
                            return{index: i};
                        rowY += minSize
                    }
                }
            }
            return null
        }
    };
    GrapeCity.UI._RangeGroupHeaderPresenter.prototype._calcMinWidthOrHeight = GrapeCity.UI._RangeGroupPresenter.prototype._calcMinWidthOrHeight;
    GrapeCity.UI._RangeGroupHeaderPresenter.prototype.getMaxLevel = GrapeCity.UI._RangeGroupPresenter.prototype.getMaxLevel
})(window);
(function(window)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    var EnumeratorOption = {
            All: 0,
            HasValue: 1,
            HasStyle: 2
        };
    GrapeCity.UI.CellsEnumerator = function(sheet, searchCondition)
    {
        if(!sheet)
            throw new Error("sheet is null.");
        this.actualEndRow = -1;
        this.isActualEndRowSet = false;
        this.isBlockRange = false;
        this.options = EnumeratorOption.HasValue;
        this.worksheet = sheet;
        this.sheetArea = searchCondition.sheetArea;
        this.searchOrder = searchCondition.searchOrder;
        this.rowStart = searchCondition.rowStart;
        this.columnStart = searchCondition.columnStart;
        this.rowEnd = searchCondition.rowEnd;
        this.columnEnd = searchCondition.columnEnd;
        this.findBeginRow = searchCondition.findBeginRow;
        this.findBeginColumn = searchCondition.findBeginColumn;
        this.init();
        this.block = this.worksheet._getModel(this.sheetArea)
    };
    GrapeCity.UI.CellsEnumerator.prototype = {
        init: function()
        {
            this.currentRow = -1;
            this.currentColumn = -1
        },
        moveNext: function()
        {
            if(this.currentRow === -1 && this.currentColumn === -1)
                if(this.rowStart <= this.rowEnd && this.columnStart <= this.columnEnd)
                {
                    this.currentRow = this.findBeginRow;
                    this.currentColumn = this.findBeginColumn;
                    if(this.isIndexAcceptable(this.currentRow,this.currentColumn))
                        if(!this.skipCurrent())
                            return true
                }
            if(this.rowStart <= this.rowEnd && this.columnStart <= this.columnEnd)
                while(this.tryMoveNext())
                    if(!this.skipCurrent())
                        return true;
            this.currentRow = -1;
            this.currentColumn = -1;
            return false
        },
        isIndexAcceptable: function(row, column)
        {
            if((this.options & EnumeratorOption.HasValue) > 0)
                if(this.block)
                    if(this.block.getValue(row,column) !== null || this.block.getFormula(row,column) !== null)
                        return true;
            return this.options === EnumeratorOption.All
        },
        skipCurrent: function()
        {
            return false
        },
        tryMoveNext: function()
        {
            var r1 = this.currentRow;
            var c1 = this.currentColumn;
            var isValueFound = false;
            if((this.options & EnumeratorOption.HasValue) > 0)
            {
                var r1Temp = {value: r1};
                var c1Temp = {value: c1};
                if(this.nextValue(r1Temp,c1Temp))
                {
                    r1 = r1Temp.value;
                    c1 = c1Temp.value;
                    isValueFound = true
                }
            }
            if(this.options === EnumeratorOption.HasValue)
                if(isValueFound)
                {
                    this.currentRow = r1;
                    this.currentColumn = c1
                }
                else
                {
                    this.currentRow = -1;
                    this.currentColumn = -1
                }
            return!(this.currentRow === -1 && this.currentColumn === -1)
        },
        nextValue: function(refRow, refColumn)
        {
            while(this.next(refRow,refColumn))
                if(this.isIndexAcceptable(refRow.value,refColumn.value))
                    return true;
            return false
        },
        nextZOrder: function(model, refRow, refColumn)
        {
            if(model)
            {
                var c0 = this.getNextNonEmptyColumnInRow(model,refRow.value,refColumn.value + 1);
                if(c0 !== -1)
                {
                    refColumn.value = c0;
                    if(this.isZOrderOver(refRow.value,refColumn.value))
                        return true
                }
                do
                {
                    var r1 = model.nextNonNullRow(refRow.value);
                    if(r1 === -1 || r1 > this.rowEnd)
                        refRow.value = -1;
                    else
                    {
                        if(r1 !== -1)
                            refRow.value = r1;
                        if(r1 !== -1 && r1 < refRow.value)
                            refRow.value = r1
                    }
                    if(refRow.value !== -1)
                    {
                        if(refRow.value === this.rowStart || this.isBlockRange)
                            refColumn.value = this.columnStart - 1;
                        else
                            refColumn.value = -1;
                        do
                        {
                            var c1 = this.getNextNonEmptyColumnInRow(model,refRow.value,refColumn.value + 1);
                            if(c1 === -1 || c1 > this.columnEnd)
                                refColumn.value = -1;
                            else
                            {
                                if(c1 !== -1)
                                    refColumn.value = c1;
                                if(c1 !== -1 && c1 < refColumn.value)
                                    refColumn.value = c1
                            }
                            if(refColumn.value !== -1)
                                return this.isZOrderOver(refRow.value,refColumn.value)
                        } while(refColumn.value !== -1)
                    }
                } while(refRow.value !== -1)
            }
            return false
        },
        getNextNonEmptyColumnInRow: function(model, row, column)
        {
            for(var i = column; i <= this.columnEnd; i++)
                if(model.getValue(row,i) !== null)
                    return i;
            return-1
        },
        isZOrderOver: function(row, column)
        {
            if(this.isBlockRange)
                return row >= this.rowStart && row <= this.getActualEndRow() && column >= this.columnStart && column <= this.actualEndColumn();
            else
            {
                if(row > this.getActualEndRow())
                    return false;
                if(row === this.getActualEndRow())
                    if(column < 0 || column > this.actualEndColumn())
                        return false;
                if(row < this.rowStart)
                    return false;
                if(row === this.rowStart)
                    if(column < this.columnStart)
                        return false
            }
            return true
        },
        getActualEndRow: function()
        {
            if(this.isActualEndRowSet)
                return this.actualEndRow;
            else
            {
                var endRowTemp = -1;
                var isSet = false;
                if((this.options & EnumeratorOption.HasValue) > 0)
                    if(this.block)
                    {
                        var index = this.block.getRowCount() - 1;
                        endRowTemp = Math.max(endRowTemp,index);
                        isSet = true
                    }
                endRowTemp = isSet ? Math.min(endRowTemp,this.rowEnd) : this.rowEnd;
                this.actualEndRow = endRowTemp;
                this.isActualEndRowSet = true;
                return this.actualEndRow
            }
        },
        actualEndColumn: function()
        {
            return this.columnEnd
        },
        next: function(refRow, refColumn)
        {
            if(this.searchOrder === GrapeCity.UI.SearchOrder.ZOrder)
            {
                var endColumnCurrenRow = this.getActualEndColumnZOrder(refRow.value);
                if(refColumn.value + 1 <= endColumnCurrenRow)
                {
                    refColumn.value += 1;
                    return this.isZOrderOver(refRow.value,refColumn.value)
                }
                else if(refRow.value + 1 <= this.getActualEndRow())
                {
                    refRow.value += 1;
                    if(this.isBlockRange)
                        refColumn.value = this.columnStart;
                    else
                        refColumn.value = 0;
                    return this.isZOrderOver(refRow.value,refColumn.value)
                }
                else
                    return false
            }
            else if(this.searchOrder === GrapeCity.UI.SearchOrder.NOrder)
            {
                var endRowCurrenColumn = this.getActualEndRowNOrder(refColumn.value);
                if(refRow.value + 1 <= endRowCurrenColumn)
                {
                    refRow.value += 1;
                    return this.isNOrderOver(refRow.value,refColumn.value)
                }
                else if(refColumn.value + 1 <= this.actualEndColumn())
                {
                    refColumn.value += 1;
                    if(this.isBlockRange)
                        refRow.value = this.rowStart;
                    else
                        refRow.value = 0;
                    return this.isNOrderOver(refRow.value,refColumn.value)
                }
                else
                    return false
            }
            return false
        },
        getActualEndColumnZOrder: function(row)
        {
            if(row >= this.rowStart && row <= this.rowEnd)
            {
                var endColumnTemp = -1;
                var isSet = false;
                if((this.options & EnumeratorOption.HasValue) > 0)
                    if(this.block)
                    {
                        endColumnTemp = Math.max(endColumnTemp,this.block.getColumnCount() - 1);
                        isSet = true
                    }
                if(row === this.rowEnd || this.isBlockRange)
                    endColumnTemp = isSet ? Math.min(endColumnTemp,this.columnEnd) : this.columnEnd;
                else
                    endColumnTemp = isSet ? Math.max(endColumnTemp,this.worksheet.getColumnCount(this.sheetArea) - 1) : this.worksheet.getColumnCount(this.sheetArea) - 1;
                return endColumnTemp
            }
            else
                return-1
        },
        getActualEndRowNOrder: function(column)
        {
            if(column >= this.columnStart && column <= this.columnEnd)
            {
                var endRowTemp = -1;
                var isSet = false;
                if((this.options & EnumeratorOption.HasValue) > 0)
                    if(this.block)
                    {
                        endRowTemp = Math.max(endRowTemp,this.rowEnd);
                        isSet = true
                    }
                if(column === this.columnEnd || this.isBlockRange)
                    endRowTemp = isSet ? Math.min(endRowTemp,this.rowEnd) : this.rowEnd;
                else
                    endRowTemp = isSet ? Math.max(endRowTemp,this.worksheet.getRowCount(this.sheetArea) - 1) : this.worksheet.getRowCount(this.sheetArea) - 1;
                return endRowTemp
            }
            else
                return-1
        },
        isNOrderOver: function(row, column)
        {
            if(this.isBlockRange)
                return row >= this.rowStart && row <= this.getActualEndRow() && column >= this.columnStart && column <= this.actualEndColumn();
            else
            {
                if(column > this.actualEndColumn())
                    return false;
                if(column === this.actualEndColumn())
                    if(row < 0 || row > this.getActualEndRow())
                        return false;
                if(column < this.columnStart)
                    return false;
                if(column === this.columnStart)
                    if(row < this.rowStart)
                        return false
            }
            return true
        },
        current: function()
        {
            if(0 <= this.currentRow && this.currentRow < this.worksheet.getRowCount(this.sheetArea) && 0 <= this.currentColumn && this.currentColumn < this.worksheet.getColumnCount(this.sheetArea))
                return new GrapeCity.UI.Cell(this.worksheet,this.currentRow,this.currentColumn,this.sheetArea);
            return null
        }
    };
    GrapeCity.UI.SearchCondition = function()
    {
        this.startSheetIndex = -1;
        this.endSheetIndex = -1;
        this.searchString = null;
        this.searchFlags = GrapeCity.UI.SearchFlags.None;
        this.searchOrder = GrapeCity.UI.SearchOrder.ZOrder;
        this.searchTarget = GrapeCity.UI.SearchFoundFlags.CellText;
        this.sheetArea = GrapeCity.UI.SheetArea.viewport;
        this.rowStart = -1;
        this.columnStart = -1;
        this.rowEnd = -1;
        this.columnEnd = -1;
        this.findBeginRow = -1;
        this.findBeginColumn = -1
    };
    GrapeCity.UI.SearchResult = function()
    {
        this.searchFoundFlag = GrapeCity.UI.SearchFoundFlags.None;
        this.foundSheetIndex = -1;
        this.foundRowIndex = -1;
        this.foundColumnIndex = -1;
        this.foundString = null
    };
    UI.SearchFlags = {
        None: 0,
        IgnoreCase: 1,
        ExactMatch: 2,
        UseWildCards: 4,
        BlockRange: 8
    };
    UI.SearchOrder = {
        ZOrder: 0,
        NOrder: 1
    };
    UI.SearchFoundFlags = {
        None: 0,
        CellText: 1,
        CellFormula: 8
    }
})(window);
(function(window)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    function RowFilterBase(range)
    {
        this.range = range;
        this.reset()
    }
    RowFilterBase.prototype = {
        sheet: null,
        range: null,
        filterInRows: null,
        filterItemMap: null,
        filteredColMap: null,
        filteredItems: null,
        filteredInRowsWithColIndexs: null,
        sortInfo: null,
        setShowFilterButton: function(value)
        {
            this.showFilterButton = value
        },
        getShowFilterButton: function()
        {
            return this.showFilterButton
        },
        addFilterItem: function(col, filterItem)
        {
            if(filterItem === undefined || filterItem === null)
                throw new Error("FilterItem is null.");
            if(col < -1 || col >= this.sheet.getColumnCount())
                throw new Error("Invalid column index.");
            if(!this.range)
                return;
            var range = this.sheet._getActualRange(this.range);
            if(col < range.col || col >= range.col + range.colCount)
                return;
            var filterItems = this.filterItemMap[col];
            if(!filterItems)
                filterItems = [];
            filterItems.push(filterItem);
            this.filterItemMap[col] = filterItems
        },
        addAverageFilter: function(col, compareType)
        {
            var con = new GrapeCity.UI.AverageCondition(compareType);
            this.addFilterItem(col,con)
        },
        addBackgroundFilter: function(col, color)
        {
            var con = new GrapeCity.UI.ColorCondition(GrapeCity.UI.ColorCompareType.BackgroundColor,color);
            this.addFilterItem(col,con)
        },
        addDateFilter: function(col, compareType, date)
        {
            var con = new GrapeCity.UI.DateCondition(compareType,date);
            this.addFilterItem(col,con)
        },
        addForegroundFilter: function(col, color)
        {
            var con = new GrapeCity.UI.ColorCondition(GrapeCity.UI.ColorCompareType.ForegroundColor,color);
            this.addFilterItem(col,con)
        },
        addNumberFilter: function(col, compareType, number)
        {
            var con = new GrapeCity.UI.NumberCondition(compareType,number);
            this.addFilterItem(col,con)
        },
        addTextFilter: function(col, compareType, text)
        {
            var con = new GrapeCity.UI.TextCondition(compareType,text);
            this.addFilterItem(col,con)
        },
        addTop10Filter: function(col, compareType, rank)
        {
            var con = new GrapeCity.UI.Top10Condition(compareType,rank);
            this.addFilterItem(col,con)
        },
        removeFilterItems: function(col)
        {
            if(this.filterItemMap[col])
                this.filterItemMap.splice(col,1,null);
            this.unfilter(col)
        },
        unfilter: function(col)
        {
            if(this.sheet)
            {
                var self = this;
                this.sheet._bindToAutoRefresh(function(col)
                {
                    if(col !== null && col !== undefined)
                        self._unfilterColumn(col);
                    else if(self.filteredColMap)
                        for(var i = 0; i < self.filteredColMap.length; i++)
                            self._unfilterColumn(self.filteredColMap[i])
                })(col)
            }
        },
        _filterColumn: function(col)
        {
            this._unfilterColumn(col);
            var rr,
                rrc;
            if(this.filterItemMap[col])
            {
                if(this.range && this.range.col !== -1 && (col < this.range.col || col > this.range.col + this.range.colCount - 1))
                    return;
                var rc = this.sheet.getRowCount();
                if(this.range)
                {
                    rr = this.range.row;
                    rrc = this.range.rowCount;
                    if(rr === -1)
                    {
                        rr = 0;
                        rrc = rc
                    }
                }
                for(var row = 0; row < rc; row++)
                {
                    if(this.range && (row < rr || row >= rr + rrc))
                    {
                        this._addRowFilteredIn(row);
                        continue
                    }
                    this._filterRowByCell(row,col)
                }
                this._setColumnFilteredState(col,true)
            }
        },
        filter: function(col)
        {
            if(this.sheet)
            {
                var self = this;
                this.sheet._bindToAutoRefresh(function(col)
                {
                    if(col !== null && col !== undefined)
                        self._filterColumn(col);
                    else if(self.filterItemMap)
                        for(var i = 0; i < self.filterItemMap.length; i++)
                            if(self.filterItemMap[i] && self.filterItemMap[i].length > 0)
                                self._filterColumn(i)
                })(col)
            }
        },
        isHideRowFilter: function()
        {
            return false
        },
        isFiltered: function()
        {
            return this.filteredColMap.length > 0
        },
        isColumnFiltered: function(col)
        {
            return this.filteredColMap.contains(col)
        },
        isRowFilteredOut: function(row)
        {
            if(this.isFiltered())
                return!this.filterInRows.contains(row);
            return false
        },
        reset: function()
        {
            this.filterInRows = [];
            this.filterItemMap = [];
            this.filteredColMap = [];
            this.filteredItems = [];
            this.filteredInRowsWithColIndexs = {};
            this.showFilterButton = true;
            this.sortInfo = null
        },
        isFilterHeader: function(row, col, sheetArea)
        {
            var result = false;
            if(this.range)
            {
                var range = this.sheet._getActualRange(this.range,sheetArea);
                if(sheetArea === GrapeCity.UI.SheetArea.colHeader && row === this.sheet.getRowCount(sheetArea) - 1 && range.row - 1 < 0)
                {
                    if(col >= range.col && col < range.col + range.colCount)
                        result = true
                }
                else if(sheetArea === GrapeCity.UI.SheetArea.viewport && row === range.row - 1)
                    if(col >= range.col && col < range.col + range.colCount)
                        result = true
            }
            return result
        },
        isLastFilteredColumn: function(col)
        {
            var count = this.filteredColMap.length;
            if(count > 0)
                return this.filteredColMap[count - 1] === col;
            return false
        },
        getFilterItems: function(col)
        {
            var itemList = this.filterItemMap[col];
            if(!itemList)
                itemList = [];
            return itemList
        },
        getFilteredItems: function()
        {
            return this.filteredItems
        },
        sortColumn: function(col, ascending)
        {
            if(this.sheet)
            {
                var self = this;
                this.sheet._bindToAutoRefresh(function(col, ascending)
                {
                    var range = self.sheet._getActualRange(self.range);
                    var oldState = self.sheet.isPaintSuspended();
                    self.sheet.isPaintSuspended(true);
                    var ret = self.sheet.sortRange(range.row,range.col,range.rowCount,range.colCount,true,[{
                                index: col,
                                ascending: ascending
                            }]);
                    self.sheet.isPaintSuspended(oldState);
                    if(ret)
                    {
                        self.sortInfo = {
                            index: col,
                            ascending: ascending
                        };
                        if(self.isColumnFiltered(col))
                            self.reFilter()
                    }
                })(col,ascending)
            }
        },
        getSortState: function(col)
        {
            var state = GrapeCity.UI.SortState.None;
            if(this.sortInfo)
                if(this.sortInfo.index === col)
                    if(this.sortInfo.ascending)
                        state = GrapeCity.UI.SortState.Ascending;
                    else
                        state = GrapeCity.UI.SortState.Descending;
            return state
        },
        reFilter: function()
        {
            this.filteredInRowsWithColIndexs = {};
            if(this.filteredItems || this.filteredItems.length > 0)
                this.filteredItems.length = 0;
            if(this.filteredColMap || this.filteredColMap.length > 0)
            {
                var tempIndex = [];
                tempIndex = tempIndex.concat(this.filteredColMap);
                this.filteredColMap.length = 0;
                for(var i = 0; i < tempIndex.length; i++)
                    this.filter(tempIndex[i])
            }
        },
        _addRowFilteredIn: function(row)
        {
            if(!this.filterInRows.contains(row))
                this.filterInRows.push(row)
        },
        _addFilteredInRowsWithColumnIndex: function(row, col)
        {
            var colIndexs = this.filteredInRowsWithColIndexs[row];
            if(colIndexs === undefined || colIndexs === null)
                colIndexs = [col];
            else
            {
                if(colIndexs.contains(col))
                    colIndexs.remove(col);
                colIndexs.push(col)
            }
            this.filteredInRowsWithColIndexs[row] = colIndexs
        },
        _addFilteredItem: function(filterItem)
        {
            if(!this.filteredItems.contains(filterItem))
                this.filteredItems.push(filterItem)
        },
        _unfilterColumn: function(col)
        {
            if(!this.range)
                return;
            var range = this.sheet._getActualRange(this.range),
                row;
            if(this.isColumnFiltered(col))
            {
                if(!(this.filteredColMap.length > 1 && col === this.filteredColMap[0]))
                    for(row = range.row; row < range.row + range.rowCount; row++)
                    {
                        var isOutbyColumn = this._isRowfilteredOutByColumn(row,col);
                        if(isOutbyColumn)
                            this._addRowFilteredIn(row)
                    }
                this._setColumnFilteredState(col,false);
                this._removeFilteredInRowsWithColumnIndex(col);
                this._removeFilteredItems(col)
            }
            else
                for(row = range.row; row < range.row + range.rowCount; row++)
                {
                    var isOut = this.isRowFilteredOut(row);
                    if(!isOut)
                        this._addRowFilteredIn(row)
                }
        },
        _setColumnFilteredState: function(col, isFiltered)
        {
            if(isFiltered)
            {
                if(this.filteredColMap.length > 0)
                    if(this.filteredColMap[this.filteredColMap.length - 1] === col)
                        return;
                    else
                        this.filteredColMap.remove(col);
                this.filteredColMap.push(col)
            }
            else
                this.filteredColMap.remove(col)
        },
        _removeFilteredItems: function(col)
        {
            if(!this.filterItemMap[col])
                return;
            var removeItems = [];
            var otherItems = [];
            for(var i = 0; i < this.filterItemMap.length; i++)
                if(i === col)
                    removeItems = removeItems.concat(this.filterItemMap[i]);
                else
                    otherItems = otherItems.concat(this.filterItemMap[i]);
            for(var i = 0; i < removeItems.length; i++)
            {
                var item = removeItems[i];
                if(!otherItems.contains(item))
                    this.filteredItems.remove(item)
            }
        },
        _removeFilteredInRowsWithColumnIndex: function(col)
        {
            for(var key in this.filteredInRowsWithColIndexs)
                if(key)
                {
                    var colIndexs = this.filteredInRowsWithColIndexs[key];
                    if(colIndexs.contains(col))
                        colIndexs.remove(col)
                }
        },
        _filterRowByCell: function(row, col)
        {
            var filterItems = this.filterItemMap[col];
            for(var i = 0; i < filterItems.length; i++)
            {
                var filterItem = filterItems[i];
                var value1,
                    value2;
                if(filterItem.conditionType === "RelationCondition" || filterItem instanceof GrapeCity.UI.RelationCondition)
                {
                    if(filterItem.item1)
                    {
                        value1 = this._getActualValue(filterItem.item1,row,col);
                        if(filterItem.item1.conditionType === "Top10Condition" || filterItem.item1 instanceof GrapeCity.UI.Top10Condition)
                            filterItem.item1.ranges = this._getReviseRanges(col,this.range);
                        else if(filterItem.item1.conditionType === "AverageCondition" || filterItem.item1 instanceof GrapeCity.UI.AverageCondition)
                            filterItem.item1.ranges = this._getReviseRanges(col,this.range)
                    }
                    if(filterItem.item2)
                    {
                        value2 = this._getActualValue(filterItem.item2,row,col);
                        if(filterItem.item2.conditionType === "Top10Condition" || filterItem.item2 instanceof GrapeCity.UI.Top10Condition)
                            filterItem.item2.ranges = this._getReviseRanges(col,this.range);
                        else if(filterItem.item2.conditionType === "AverageCondition" || filterItem.item2 instanceof GrapeCity.UI.AverageCondition)
                            filterItem.item2.ranges = this._getReviseRanges(col,this.range)
                    }
                }
                else
                {
                    value1 = this._getActualValue(filterItem,row,col);
                    if(filterItem.conditionType === "Top10Condition" || filterItem instanceof GrapeCity.UI.Top10Condition)
                        filterItem.ranges = this._getReviseRanges(col,this.range);
                    else if(filterItem.conditionType === "AverageCondition" || filterItem instanceof GrapeCity.UI.AverageCondition)
                        filterItem.ranges = this._getReviseRanges(col,this.range)
                }
                if(filterItem.evaluate(this.sheet,row,col,value1,value2))
                {
                    this._addRowFilteredIn(row);
                    this._addFilteredInRowsWithColumnIndex(row,col);
                    this._addFilteredItem(filterItem);
                    break
                }
                else
                    this.filterInRows.remove(row)
            }
        },
        _getReviseRanges: function(col, range)
        {
            var reviseRange = [];
            if(range)
            {
                var actualRange = this.sheet._getActualRange(range);
                if(actualRange.col <= col && col < actualRange.col + actualRange.colCount)
                    reviseRange.push(new GrapeCity.UI.Range(actualRange.row,col,actualRange.rowCount,1))
            }
            return reviseRange
        },
        _getActualValue: function(filterItem, row, col)
        {
            var value = null;
            if(filterItem.conditionType === "TextCondition" || filterItem.conditionType === "TextLengthCondition")
                value = this.sheet.getText(row,col);
            else if(filterItem.conditionType === "ColorCondition")
            {
                var style = this.sheet.getActualStyle(row,col);
                if(style)
                    if(filterItem.compareType === GrapeCity.UI.ColorCompareType.BackgroundColor)
                        value = style.backColor;
                    else if(filterItem.compareType === GrapeCity.UI.ColorCompareType.ForegroundColor)
                        value = style.foreColor
            }
            else
                value = this.sheet.getValue(row,col);
            return value
        },
        _getCandindateDataItems: function(col, outHasBlank)
        {
            var items = [];
            var range = this.sheet._getActualRange(this.range);
            if(col < range.col || col > range.col + range.colCount - 1)
                return items;
            var hasBlank = false;
            for(var row = range.row; row < range.row + range.rowCount; row++)
            {
                var text = this.sheet.getText(row,col);
                if(text === "")
                {
                    hasBlank = true;
                    continue
                }
                if(!items.contains(text))
                    if(!this.isRowFilteredOut(row))
                    {
                        if(this.sheet.getRowHeight(row,GrapeCity.UI.SheetArea.viewport) > 0)
                            items.push(text)
                    }
                    else if(!this.isFiltered() || this.isLastFilteredColumn(col) && this._isRowfilteredOutByColumn(row,col))
                        items.push(text);
                    else if(this.sheet.getRowHeight(row,GrapeCity.UI.SheetArea.viewport) > 0)
                        items.push(text)
            }
            if(hasBlank && outHasBlank)
                outHasBlank.hasBlank = true;
            return items
        },
        _isRowfilteredOutByColumn: function(row, col)
        {
            if(this.filteredColMap.length === 0)
                return false;
            if(this.filteredInRowsWithColIndexs)
            {
                var previousColumn = -1;
                var currentIndex = this.filteredColMap.indexOf(col);
                if(currentIndex > 0)
                    previousColumn = this.filteredColMap[currentIndex - 1];
                var colIndexs = this.filteredInRowsWithColIndexs[row];
                if(previousColumn > -1)
                    if(!colIndexs || colIndexs.length === 0)
                        return false;
                    else
                        return previousColumn === colIndexs[colIndexs.length - 1];
                else if(this.filteredColMap.length === 1)
                    return!colIndexs || colIndexs.length === 0;
                else
                    return false
            }
            return false
        },
        _addRows: function(row, count)
        {
            if(!this.range)
                return;
            var endRow = this.sheet.getRowCount() - count - 1;
            if(this.range.row > -1)
            {
                var cs = this.range;
                endRow = cs.row + cs.rowCount - 1;
                if(cs.row >= row)
                    this._setRangeInternal(new GrapeCity.UI.Range(cs.row + count,cs.col,cs.rowCount,cs.colCount));
                else if(cs.row < row && row < cs.row + cs.rowCount)
                    this._setRangeInternal(new GrapeCity.UI.Range(cs.row,cs.col,cs.rowCount + count,cs.colCount))
            }
            if(this.isFiltered() && this.filterInRows)
                for(var i = this.filterInRows.length - 1; i >= 0; i--)
                {
                    var oldKey = this.filterInRows[i];
                    if(oldKey >= row && oldKey <= endRow)
                    {
                        var oldValue = this.filterInRows[oldKey];
                        var newKey = oldKey + count;
                        this.filterInRows[newKey] = oldValue;
                        this.filterInRows.remove(oldKey)
                    }
                }
            this.reFilter()
        },
        _addColumns: function(column, count)
        {
            if(!this.range)
                return;
            var c;
            if(column >= 0 && this._isSortted())
            {
                var addCount = 0;
                for(var i = 0; i < count; i++)
                {
                    c = i + column;
                    if(c <= this._sorttedColumn())
                        addCount++
                }
                this._sorttedColumn(this._sorttedColumn() + addCount)
            }
            var startColumn = -1;
            var columnCount = 0;
            var cs = this.range;
            if(cs.col > -1)
                if(cs.col >= column)
                {
                    startColumn = this.range.col;
                    this._setRangeInternal(new GrapeCity.UI.Range(cs.row,cs.col + count,cs.rowCount,cs.colCount));
                    columnCount = this.range.colCount
                }
                else if(cs.col < column && column < cs.col + cs.colCount)
                {
                    startColumn = column;
                    columnCount = cs.columnCount - (column - cs.col);
                    this._setRangeInternal(new GrapeCity.UI.Range(cs.row,cs.col,cs.rowCount,cs.colCount + count))
                }
            if(startColumn < 0)
            {
                startColumn = 0;
                columnCount = this.sheet.getColumnCount() - count
            }
            for(c = startColumn + columnCount - 1; c >= startColumn; c--)
                if(c >= column)
                {
                    var newColumn = c + count;
                    var index = this.filteredColMap.indexOf(c);
                    if(index >= 0)
                        this.filteredColMap[index] = newColumn;
                    var conditions = this.filterItemMap[c];
                    if(conditions && conditions.length > 0)
                    {
                        this.filterItemMap.remove(c);
                        this.filterItemMap[newColumn] = conditions
                    }
                }
            this.reFilter()
        },
        _removeRows: function(row, count)
        {
            if(!this.range)
                return;
            var startRow = 0;
            var endRow = this.sheet.getRowCount() + count - 1;
            if(this.range.row > -1)
            {
                var cs = this.range;
                startRow = cs.row;
                endRow = cs.row + cs.rowCount - 1;
                if(cs.row >= row)
                    if(cs.row === row + 1)
                        this._setRangeInternal(null);
                    else if(cs.row + cs.rowCount <= row + count)
                        this._setRangeInternal(null);
                    else if(cs.row < row + count)
                        this._setRangeInternal(new GrapeCity.UI.Range(row,cs.col,cs.row + cs.rowCount - (row + count),cs.colCount));
                    else
                        this._setRangeInternal(new GrapeCity.UI.Range(cs.row - count,cs.col,cs.rowCount,cs.colCount));
                else if(cs.row < row && row < cs.row + cs.rowCount)
                    this._setRangeInternal(new GrapeCity.UI.Range(cs.row,cs.col,cs.rowCount - Math.min(cs.row + cs.rowCount - row,count),cs.colCount))
            }
            if(this.isFiltered() && this.filterInRows)
                for(var i = startRow; i <= endRow; i++)
                    if(i >= row)
                        if(i < row + count)
                            this.filterInRows.remove(i);
                        else
                        {
                            var oldKey = i;
                            var oldValue = this.filterInRows[oldKey];
                            if(oldValue !== undefined && oldValue !== null)
                            {
                                var newKey = oldKey - count;
                                this.filterInRows[newKey] = oldValue;
                                this.filterInRows.remove(oldKey)
                            }
                        }
            this.reFilter()
        },
        _setRangeInternal: function(newRange)
        {
            this.range = newRange
        },
        _removeColumns: function(column, count)
        {
            if(!this.range)
                return;
            if(column >= 0 && this._isSortted())
                if(!(column <= this._sorttedColumn() && this._sorttedColumn() < column + count))
                {
                    if(this._sorttedColumn() >= column)
                        this._sorttedColumn(this._sorttedColumn() - count)
                }
                else
                    this._sorttedColumn(-1);
            var cs = this.range;
            var startColumn = cs.col;
            var columnCount = cs.colCount;
            if(startColumn < 0)
            {
                startColumn = 0;
                columnCount = this.sheet.getColumnCount() + count
            }
            else
                columnCount = this.range.colCount;
            var removeEnd = column + count;
            for(var c = startColumn; c < startColumn + columnCount; c++)
                if(c >= column)
                    if(c < removeEnd)
                        this.removeFilterItems(c);
                    else
                    {
                        var newColumn = c - count;
                        var index = this.filteredColMap.indexOf(c);
                        if(index >= 0)
                            this.filteredColMap[index] = newColumn;
                        var conditions = this.filterItemMap[c];
                        if(conditions && conditions.length > 0)
                        {
                            this.filterItemMap.remove(c);
                            this.filterItemMap[newColumn] = conditions
                        }
                    }
            if(cs.col >= 0)
                if(column < cs.col)
                    if(removeEnd <= cs.col)
                        this._setRangeInternal(new GrapeCity.UI.Range(cs.row,cs.col - count,cs.rowCount,cs.colCount));
                    else if(removeEnd <= cs.col + cs.colCount)
                        this._setRangeInternal(new GrapeCity.UI.Range(cs.row,column,cs.rowCount,cs.col + cs.colCount - removeEnd));
                    else
                        this._setRangeInternal(null);
                else if(column < cs.col + cs.colCount)
                    if(removeEnd <= cs.col + cs.colCount)
                        this._setRangeInternal(new GrapeCity.UI.Range(cs.row,cs.col,cs.rowCount,cs.colCount - count));
                    else
                        this._setRangeInternal(new GrapeCity.UI.Range(cs.row,cs.col,cs.rowCount,column - cs.col));
            this.reFilter()
        },
        _isSortted: function()
        {
            if(this.sortInfo)
                return this.sortInfo.index > -1 && this.getSortState(this.sortInfo.index) !== GrapeCity.UI.SortState.None;
            return false
        },
        _sorttedColumn: function(col)
        {
            if(arguments.length === 0)
                if(this.sortInfo)
                    return this.sortInfo.index;
                else
                    return-1;
            else
            {
                if(!this.sortInfo)
                    this.sortInfo = {
                        index: col,
                        ascending: false
                    };
                else
                    this.sortInfo.index = col;
                return this
            }
        },
        _clear: function(row, column, rowCount, columnCount)
        {
            if(!this.range)
                return;
            var clearRange = new GrapeCity.UI.Range(row,column,rowCount,columnCount);
            if(this.showFilterButton)
            {
                var newRow = this.range.row - 1;
                var newRowCount = this.range.rowCount + 1;
                if(newRow < 0)
                {
                    newRow = -1;
                    newRowCount = -1
                }
                if(clearRange.containsRange(new GrapeCity.UI.Range(newRow,this.range.col,newRowCount,this.range.colCount)))
                    this.unfilter()
            }
            else if(clearRange.containsRange(this.range))
                this.unfilter()
        },
        _getConditionTypeDictionary: function()
        {
            if(!this._ruleTypeDictionary)
            {
                var dict = {};
                dict[GrapeCity.UI.ConditionType.RelationCondition] = GrapeCity.UI.RelationCondition;
                dict[GrapeCity.UI.ConditionType.NumberCondition] = GrapeCity.UI.NumberCondition;
                dict[GrapeCity.UI.ConditionType.TextCondition] = GrapeCity.UI.TextCondition;
                dict[GrapeCity.UI.ConditionType.ColorCondition] = GrapeCity.UI.ColorCondition;
                dict[GrapeCity.UI.ConditionType.FormulaCondition] = GrapeCity.UI.FormulaCondition;
                dict[GrapeCity.UI.ConditionType.DateCondition] = GrapeCity.UI.DateCondition;
                dict[GrapeCity.UI.ConditionType.DateExCondition] = GrapeCity.UI.DateExCondition;
                dict[GrapeCity.UI.ConditionType.TextLengthCondition] = GrapeCity.UI.TextLengthCondition;
                dict[GrapeCity.UI.ConditionType.Top10Condition] = GrapeCity.UI.Top10Condition;
                dict[GrapeCity.UI.ConditionType.UniqueCondition] = GrapeCity.UI.UniqueCondition;
                dict[GrapeCity.UI.ConditionType.AverageCondition] = GrapeCity.UI.AverageCondition;
                dict[GrapeCity.UI.ConditionType.CellValueCondition] = GrapeCity.UI.CellValueCondition;
                dict[GrapeCity.UI.ConditionType.AreaCondition] = GrapeCity.UI.AreaCondition;
                this._ruleTypeDictionary = dict
            }
            return this._ruleTypeDictionary
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.filterItemMap)
                for(var i = 0; i < settings.filterItemMap.length; i++)
                {
                    var conditionArray = settings.filterItemMap[i].conditions;
                    if(conditionArray)
                        for(var k = 0; k < conditionArray.length; k++)
                        {
                            var conditionSettings = conditionArray[k];
                            var condition = null;
                            if(conditionSettings)
                            {
                                var dict = this._getConditionTypeDictionary();
                                var conditionClass = dict[conditionSettings.conType];
                                if(conditionClass)
                                {
                                    condition = new conditionClass;
                                    condition.fromJSON(conditionSettings);
                                    this.addFilterItem(settings.filterItemMap[i].index,condition)
                                }
                            }
                        }
                }
            if(settings.filteredColMap)
                for(var i = 0; i < settings.filteredColMap.length; i++)
                {
                    var filteredCol = settings.filteredColMap[i];
                    if(filteredCol !== undefined && filteredCol !== null)
                        this.filter(filteredCol)
                }
            if(settings.sortInfo)
                this.sortColumn(settings.sortInfo.index,settings.sortInfo.ascending);
            if(settings.showFilterButton !== null && settings.showFilterButton !== undefined)
                this.showFilterButton = settings.showFilterButton
        },
        toJSON: function()
        {
            var filterItemMapData = [];
            for(var i = 0, k = 0; i < this.filterItemMap.length; i++)
            {
                var filterItem = this.filterItemMap[i];
                if(filterItem)
                {
                    var conditions = [];
                    for(var c = 0; c < filterItem.length; c++)
                    {
                        var condition = filterItem[c];
                        conditions.push(condition ? condition.toJSON() : null)
                    }
                    filterItemMapData[k++] = {
                        index: i,
                        conditions: conditions
                    }
                }
            }
            return{
                    range: this.range,
                    filterItemMap: filterItemMapData,
                    filteredColMap: this.filteredColMap,
                    sortInfo: this.sortInfo,
                    showFilterButton: this.showFilterButton
                }
        }
    };
    function HideRowFilter(range)
    {
        this.range = range;
        this.filterInRows = [];
        this.filterItemMap = [];
        this.filteredColMap = [];
        this.filteredItems = [];
        this.filteredInRowsWithColIndexs = {};
        this.sortInfo = null
    }
    HideRowFilter.prototype = new RowFilterBase;
    HideRowFilter.prototype.isHideRowFilter = function()
    {
        return true
    };
    UI.RowFilterBase = RowFilterBase;
    UI.HideRowFilter = HideRowFilter
})(window);
(function(window, $)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    function getDomElementString(name, id, content, attrPairs, noCloseTag)
    {
        var tag = "<" + name + " ";
        if(id)
            tag += "id='" + id + "' ";
        if(attrPairs)
            $.each(attrPairs,function(i, v)
            {
                tag += i + "='" + v + "' "
            });
        if(content)
            tag += ">" + content + "</" + name + ">";
        else
            tag += noCloseTag === true ? ">" : "/>";
        return tag
    }
    var domDiv = "div",
        domTable = "table",
        domTr = "tr",
        domTd = "td",
        domImg = "img",
        domUl = "ul",
        domLi = "li",
        domSelect = "select",
        domInput = "input";
    var FilterButtonState = {
            noSortFilter: 0,
            ascend: 1,
            descend: 2,
            filter: 3,
            filterAscend: 4,
            filterDescend: 5,
            sortAscending: 6,
            sortDescending: 7
        };
    var menuWidgetName = "ui-menu";
    function GcFilterDialog(sheet, filterButtonInfo)
    {
        this.sheet = sheet;
        this.filterButtonInfo = filterButtonInfo;
        this._init()
    }
    GcFilterDialog.prototype = {
        _init: function()
        {
            this.dialogID = "gc_filterDialog";
            this.topMenuID = "gc_filterTopMenu";
            this.sortAscID = "gc_sortAsc";
            this.sortDesID = "gc_sortDes";
            this.itemListID = "gc_filterItemList";
            this.okID = "gc_filterOK";
            this.cancelID = "gc_filterCancel";
            this.superPanelID = "gc_filterSuperPanel";
            this.activeCheckItemClass = "gc_filterActiveCheckItem";
            this.filterCheckItemName = "multiselect_gc_filterItemList";
            this.allItemsLength = -1;
            var self = this;
            this._creatDom();
            var $dlg = $("#" + this.dialogID);
            this.dialog = $dlg.wijsheetfilterdialog({
                show: "blind",
                _filterDialog: null
            });
            $dlg.wijsheetfilterdialog('option','_filterDialog',this);
            this.dialog.parent().css("boxShadow","2px 4px 5px rgba(0,0,0,0.4)");
            var $menu = $("#" + this.topMenuID);
            $menu.menu({select: function(event, arg)
                {
                    var id = $(arg.item).attr("id");
                    if(id === self.sortAscID)
                        self._sortByUser(true);
                    else if(id === self.sortDesID)
                        self._sortByUser(false);
                    self.close()
                }})[0].tabIndex = -1;
            var oldActivate = $menu.data(menuWidgetName)._activate;
            $menu.data(menuWidgetName)._activate = function(event)
            {
                if(this.active)
                    oldActivate.apply(this,arguments)
            };
            var $itemListID = $("#" + this.itemListID),
                $okID = $("#" + this.okID),
                $cancelID = $("#" + this.cancelID);
            this.itemList = $itemListID.wijsheetmultiselect().wijsheetmultiselectfilter();
            $okID.button();
            $cancelID.button();
            $okID.unbind("click");
            $okID.click(function()
            {
                self.dialog.wijsheetfilterdialog("close");
                var checkedValues = self._getCheckedValuesByUser();
                self._updateData(checkedValues);
                self._filter(checkedValues)
            });
            $cancelID.unbind("click");
            $cancelID.click(function()
            {
                self.dialog.wijsheetfilterdialog("close")
            });
            $itemListID.unbind("change");
            $itemListID.bind("change",function()
            {
                self._setButtonState()
            });
            $("#" + this.superPanelID).wijspreadpanelex();
            var $selectFilter_input = $(".ui-multiselect-filter > input");
            $selectFilter_input.unbind(".gcSheetInternal");
            $selectFilter_input.bind("keyup.gcSheetInternal",function()
            {
                $("#" + self.superPanelID).wijspreadpanelex("refresh")
            });
            $selectFilter_input.bind("search.gcSheetInternal",function()
            {
                self.itemList.wijsheetmultiselect("refresh");
                self.itemList.wijsheetmultiselectfilter("updateCache");
                $("#" + self.superPanelID).wijspreadpanelex("refresh")
            })
        },
        _setButtonState: function()
        {
            var checkedLength = this.itemList.wijsheetmultiselect("getChecked").length;
            if(checkedLength > 0)
                $("#" + this.okID).button("option","disabled",false);
            else
                $("#" + this.okID).button("option","disabled",true)
        },
        _sortByUser: function(isAsc)
        {
            var drf = this.sheet.rowFilter();
            if(!drf)
                return;
            var colIndex = this.filterButtonInfo.col;
            this.sheet._trigger(GrapeCity.UI.Events.RangeSorting,{
                sheet: this.sheet,
                sheetName: this.sheet._name,
                col: colIndex,
                ascending: isAsc
            });
            drf.sortColumn(colIndex,isAsc);
            this.sheet._trigger(GrapeCity.UI.Events.RangeSorted,{
                sheet: this.sheet,
                sheetName: this.sheet._name,
                col: colIndex,
                ascending: isAsc
            });
            this.sheet.invalidateLayout();
            this.sheet.repaint()
        },
        _getCheckedValuesByUser: function()
        {
            var checkValues = [];
            this.itemList.wijsheetmultiselect("getChecked").each(function()
            {
                checkValues.push(this.value)
            });
            if(this.allItemsLength === checkValues.length)
                checkValues = [];
            return checkValues
        },
        _getDomString: function()
        {
            var html = getDomElementString(domDiv,this.dialogID,getDomElementString(domTable,null,getDomElementString(domTr,null,getDomElementString(domTd,null,getDomElementString(domDiv,null,getDomElementString(domDiv,null,"",{style: "height: 10px"}) + getDomElementString(domDiv,null,getDomElementString(domImg,null,null,{src: GcFilterDialog.getImageSrc(FilterButtonState.sortAscending)},true)) + getDomElementString(domDiv,null,"",{style: "height: 5px"}) + getDomElementString(domDiv,null,getDomElementString(domImg,null,null,{src: GcFilterDialog.getImageSrc(FilterButtonState.sortDescending)})),{style: "text-align: left"}),{style: "border-right: 1px solid #CCC;vertical-align: top"}) + getDomElementString(domTd,null,getDomElementString(domTable,null,getDomElementString(domTr,null,getDomElementString(domTd,null,getDomElementString(domDiv,null,getDomElementString(domUl,this.topMenuID,getDomElementString(domLi,this.sortAscID,"<a style='cursor:default'>Αύξουσα ταξινόμηση</a>",{
                    style: "width: 195px",
                    tabindex: 0
                }) + getDomElementString(domLi,this.sortDesID,"<a style='cursor:default'>Φθίνουσα ταξινόμηση</a>",{tabindex: 0}),{style: "border-width: 0;"}),{style: "text-align: left"}),{style: "border-bottom: 1px solid #CCC"})) + getDomElementString(domTr,null,getDomElementString(domTd,null,getDomElementString(domSelect,this.itemListID,"")))) + getDomElementString(domDiv,null,getDomElementString(domInput,this.okID,null,{
                    type: "button",
                    value: "OK",
                    style: "width: 70px;"
                }) + getDomElementString(domInput,this.cancelID,null,{
                    type: "button",
                    value: "Άκυρο",
                    style: "width: 70px;"
                }),{style: "text-align: right;"})))),{style: "font-size: 10pt;padding: 0;display: none; overflow: hidden"});
            return html
        },
        _hasDom: function()
        {
            return!!window.document.getElementById(this.dialogID)
        },
        _creatDom: function()
        {
            var canvas = this.sheet._getCanvas();
            if(canvas && !this._hasDom())
            {
                var domStr = this._getDomString();
                var child = $(domStr)[0];
                canvas.appendChild(child)
            }
        },
        _initData: function(colIndex)
        {
            var drf = this.sheet.rowFilter();
            if(!drf)
                return;
            var allValues = this._getAllData(colIndex);
            this._bindData(allValues);
            var checkedValues = [];
            if(drf.isColumnFiltered(colIndex))
                checkedValues = this._getCheckedData(colIndex);
            else
                checkedValues = allValues;
            var checkedActualValues = this._getActualValues(allValues,checkedValues);
            this._setCheckedValues(checkedActualValues);
            this.itemList.wijsheetmultiselect("refresh",true);
            this.itemList.wijsheetmultiselectfilter("updateCache")
        },
        _updateData: function(checkedValues)
        {
            var colIndex = this.filterButtonInfo.col;
            var drf = this.sheet.rowFilter();
            if(!drf)
                return;
            drf.unfilter(colIndex);
            drf.removeFilterItems(colIndex);
            for(var i = 0; i < checkedValues.length; i++)
            {
                var value = checkedValues[i];
                if(value === "(Κενά)")
                    value = "";
                var tc = new GrapeCity.UI.TextCondition(GrapeCity.UI.TextCompareType.EqualsTo,value,null);
                drf.addFilterItem(colIndex,tc)
            }
        },
        _filter: function(filterValues)
        {
            var drf = this.sheet.rowFilter();
            if(!drf)
                return;
            if(filterValues && filterValues.length > 0)
                this.sheet._trigger(GrapeCity.UI.Events.RangeFiltering,{
                    sheet: this.sheet,
                    sheetName: this.sheet._name,
                    col: this.filterButtonInfo.col,
                    filterValues: filterValues
                });
            drf.filter(this.filterButtonInfo.col);
            if(filterValues && filterValues.length > 0)
                this.sheet._trigger(GrapeCity.UI.Events.RangeFiltered,{
                    sheet: this.sheet,
                    sheetName: this.sheet._name,
                    col: this.filterButtonInfo.col,
                    filterValues: filterValues
                });
            this.sheet.invalidateLayout();
            this.sheet.repaint()
        },
        _setCheckedValues: function(checkedActualValues)
        {
            $("#" + this.itemListID + " option").each(function()
            {
                var value = $(this).val();
                if(checkedActualValues.contains(value))
                    $(this).attr("aria-selected","true");
                else
                    $(this).attr("aria-selected","false")
            })
        },
        _getActualValues: function(allValues, checkedValues)
        {
            var checkedActualValues = [];
            for(var i = 0; i < checkedValues.length; i++)
            {
                var item = checkedValues[i];
                if(item === undefined || item === null || item === "")
                    item = "(Κενά)";
                if(allValues.contains(item) && !checkedActualValues.contains(item))
                    checkedActualValues.push(item)
            }
            return checkedActualValues
        },
        _bindData: function(textArray)
        {
            var select = $("#" + this.itemListID)[0];
            select.options.length = 0;
            for(var i = 0; i < textArray.length; i++)
            {
                var text = textArray[i];
                select.options.add(new window.Option(text,text))
            }
            this.allItemsLength = select.options.length
        },
        _getAllData: function(colIndex)
        {
            var textArray = [];
            var drf = this.sheet.rowFilter();
            if(!drf)
                return textArray;
            var range = this.sheet._getActualRange(drf.range);
            if(colIndex < range.col || colIndex > range.col + range.colCount - 1)
                return textArray;
            var outHasBlank = {hasBlank: false};
            textArray = drf._getCandindateDataItems(colIndex,outHasBlank);
            if(outHasBlank.hasBlank)
                textArray.push("(Κενά)");
            return textArray
        },
        _getCheckedData: function(colIndex)
        {
            var checkedValues = [];
            var filter = this.sheet.rowFilter();
            if(!filter)
                return checkedValues;
            if(filter.isColumnFiltered(colIndex))
            {
                var startRow = filter.range.row === -1 ? 0 : filter.range.row;
                var rowCount = filter.range.rowCount === -1 ? this.sheet.getRowCount() : filter.range.rowCount;
                for(var row = startRow; row < startRow + rowCount; row++)
                    if(!filter.isRowFilteredOut(row))
                    {
                        var cellValue = this.sheet.getValue(row,colIndex);
                        var dataItem = null;
                        if(cellValue instanceof Date)
                            dataItem = cellValue;
                        else
                            dataItem = this.sheet.getText(row,colIndex);
                        if(!checkedValues.contains(dataItem))
                            checkedValues.push(dataItem)
                    }
            }
            return checkedValues
        },
        open: function()
        {
            if(this.dialog)
            {
                var self = this;
                this._initData(this.filterButtonInfo.col);
                var t = $(this.sheet._getCanvas()).offset();
                var dialogWidth = this.dialog.wijsheetfilterdialog("option","width");
                var dialogHeight = this.dialog.wijsheetfilterdialog("option","height");
                var x = this.filterButtonInfo.x + this.filterButtonInfo.width - dialogWidth;
                var y = this.filterButtonInfo.y + this.filterButtonInfo.height;
                x += t.left - 8;
                y += t.top;
                var scrollLeft = $(window.document).scrollLeft();
                var scrollTop = $(window.document).scrollTop();
                x -= scrollLeft;
                y -= scrollTop;
                this.dialog.wijsheetfilterdialog({
                    show: "blind",
                    position: [x,y],
                    open: function()
                    {
                        window.gcGlobal.suspendEvent();
                        $("#" + self.superPanelID).wijspreadpanelex("refresh")
                    },
                    close: function()
                    {
                        window.gcGlobal.resumeEvent();
                        if(self.sheet)
                            self.sheet._eventHandler._setFocus()
                    }
                });
                this.dialog.wijsheetfilterdialog("open");
                $(".ui-multiselect-filter > input").val("");
                this.itemList.wijsheetmultiselectfilter("initFirstOpenCache");
                $("#" + this.dialogID + " *").removeClass("ui-state-hover");
                var items = this.dialog.find("ul").last();
                var maxLabelWidth = 0;
                items.find("span").each(function(i, element)
                {
                    maxLabelWidth = Math.max(maxLabelWidth,element.offsetWidth)
                });
                var labelWidth = items.find("label").width();
                var checkBoxWidth = items.find("input").outerWidth(true);
                maxLabelWidth += checkBoxWidth;
                if(maxLabelWidth > labelWidth)
                    this.dialog.find("ul").last().find("label").width(maxLabelWidth)
            }
        },
        close: function()
        {
            if(this.dialog)
                this.dialog.wijsheetfilterdialog("close");
            if(this.sheet)
                this.sheet._eventHandler._setFocus()
        },
        isOpen: function()
        {
            if(this.dialog)
                return this.dialog.wijsheetfilterdialog("isOpen");
            return false
        }
    };
    GcFilterDialog.getImageSrc = function(state)
    {
        if(state === FilterButtonState.noSortFilter)
            return"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVX" + "DjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4" + "EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/" + "EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAES" + "ggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2At" + "qKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDr" + "FiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1" + "akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rf" + "q79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiF" + "I8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgK" + "fep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybu" + "IC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/P" + "bFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwD" + "a0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22" + "gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlw" + "G3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAA" + "Xb5JfxUYAAAMOSURBVHjaXJNdaJRHFIbfM7PfGremUTfkpxgpiheiBOmFtYUgRXNRqNnd75sEhBoIKhrwQpFUUSsihhIQ41+FNrUGqYhRY43STZqgARNRTIzRaFqz6xJTtals3K8xRQ3x7UV215+LB2bOxTNn3jMDs+mEIgmS+LwQiqSQ9JG03FHX54666pn7TLmJEZVIjCCRGMkYGxvNiI9FvaENZzLw/Pmqt" + "CDFV/4WkERw8TI03K6XqqrNWLBgPoafDmNwMCbd3TestpMDHpJAuIcWSZjSUrnS0SFOWamXJLpuDYAkAiYAkioYDCIej0+Lx12QrervFxOemjWfCvIyIcmTVeLVC1986OkUklhRtlgutLYrkpnOKlt1dl0BSfTHmnCnm5oTm0AS6I0xLaDLrK1PusTp/cb7/rUAyH8kSL+Vqt0ebpuGP699LCRROAdqf/suGfo" + "rCpLi4DMpAOQQLAEgfE2sRDZIv5cknGxi74HDFnZ/32olk9f//vOTPIj05ZnKZpp14QlnfZgpTOXvNGsvjrvuH9MnOwAOYlhQ/UPYmxRkJVvzPHw8JF8EdrKoZEeapSt2cHDo4Sxn7R0hiV8/hEQe3Ad21jZNJQnbDklSBBas0ddvRj2Fi4qZIhaL+Q3ni8N0ZohGe4Dv6sKZfFP0kvkSWLZdGvLpiUTuTS9cV" + "MxI5H5+fz+UfXyOto/Ns5bUL7dyjsBjTsKDbbVnM0iqYNHXYi9ZLiSlaRbFn089zlzvwN1IzkDlmDhfdiin5KgOBY5qO9iojX1K9+VRcOCXnlySsFkOkjj/WGschtR0Kt/Ga9D2aYj5DWKaIeYSxTlFMS2T66udBDbXHS94Z97ZUB8Ayt7+iVVddlkzl5LCvrklvQ9dr1fnF0IQbng5O/VQsmAJSfnxSLuqCG3" + "Rxxpfz3xbXv0IEkzm5YR7lXOCGs6+PVNJYjaQnoKwSgBIDSqkjjPUuv3QLP9ImW+hG1/lKJtzp5jaCmW2nrNAUpOUkqrWrJUb23T5rhZf0eoWhbeEKX7O4eTPHe8DSTjF59T/AwCfnhbaDaIICgAAAABJRU5ErkJggg==";
        else if(state === FilterButtonState.ascend)
            return"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVX" + "DjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4" + "EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/" + "EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAES" + "ggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2At" + "qKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDr" + "FiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1" + "akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt" + "1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8y" + "wWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3" + "U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8" + "hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk" + "1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM" + "99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3" + "lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw" + "83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m" + "978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H" + "5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAALLSURBVHjadJNtaNVlGMav+/kfjy3GTDfcsQW1CMrhWSZ6AsmXmflFDts5/0eYA52i02M6tdBIRQQJBLX8Yg4xYlMEJaHphzx" + "KzoiaZptN0MBtacyXoYIobUgT/fXhtOMS+vBw89wXXPfLdd0CLNxwyACF648YYH7tnuEYAeQbv3CA/EeHBcivazbAUo1fv6hwfYtJEjDy2XP//8vpGbA9ZiE7xQ6USs3S7sUx9YHNbMqYn1IerGmQ3pr8uYXsNUAqnZ+LvqHJAA2BeIoGT6DJbMqBbAwArR77vs4/u" + "hOje9BxlXEcww13pXDvtwaoes1MB6iz8xfxGkIIiLCUIEXaDTxiFJOmKp5YxDcciS6jVpRi8g1H87PVgLYslwO0GYzf+x377ubwXoLKafXEE/WUTfC9tBIhwNTJdAPkw4R1fdkkOmJW1/LAYNBV54iDJK8HH7NPd+/9MTqeWER//62YHxrrJvCDydd99Z/tpjLboz7" + "znQF2/vSfufwY9Ne1e9r9JtHKaUvxqWY3UgXX23dNbccOlhQUFxUN9GweVsaWIQNZRfkeByidrnDxRD19N29U5AnChkMO0BMYAwRA4GkPAKuZc/2F8JWTDlB716sCIm9PXcz1K9+/lCc4erstABQmP3U8LBvlV2VJr8yyIHMSv+LUk1QmCzADBjQnrB0dT9TT0/243" + "NfqXx8szwaAzq6Q1bDJnWnrmlKV3EZVchvvJbfQ2nqmDLDqD0/YXFqs8t0l/Haxu3j+vOPDO9ifu4P9jwPAeSk6RE/RpHc+4KefL8waYWEDrOPX9ol/cycA1KH7pvCTsxFAPxZGXQ1YmDhlgNIHsoWQd5wONw4JxgfhwmeKXSUjXbq0Oi/j80el28UjqpdqK1ivcCB" + "7KJwXTuHKcwWAPtPCoJkShy7kjKU3dIXLAvQyVdq1C3WVXNbE2RgUOphuqVXH3T8DAFTcPgRQEEuLAAAAAElFTkSuQmCC";
        else if(state === FilterButtonState.descend)
            return"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWF" + "BURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEI" + "iAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50" + "cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBW" + "K8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhE" + "LTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBo" + "mxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMC" + "aIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgt" + "Uq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73Vwn" + "UCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7" + "ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun" + "3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQW" + "NBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5" + "JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFK" + "uUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk6" + "26s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXB" + "aKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509" + "E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8c" + "fvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAJKSURBVHjanJJfSJNhFMaf9/02R9MFCVZLUOgfpLMCmwrd7KIIm6X4fXoTY1oXltaN86oEi" + "4xodtNNeNMHJZQ0gpDQmUKEyUAiyKCFW4PNtSFMoyKJSp4uputbJIUX5+K855zfe85zDkhiza4u2+HXn4M64dcBvw5BTotLtyDJ49KvI2fapK/Q/8qkwAjYiEHj4rrBG+ft4p+AB51f8pKazxxDY/djhLYj95bJLKG5fR+w5whUVxNUtR6qOiHUrmABNH3AkgcoqsB" + "M11O56gvVUy2GV6CQNA/WjFdkMpnyhYV0NjZOAf6ksQMZCGQL3e526fUWm0kKrlAJRT7CUXeac+Gp3bRTaJpHXsReoLl3yGQAKPUtlaJh8yHxKI0s+C3xLHHXwjIqlbXtTKc/lKTScaRSFItMAWReB4WtdfdMJCVJTI18xihLNzFCyWXC4WxjMpksjiOK5BytjXIGU" + "H0XCowjkFsESeF27BQLSy9F/2FK0ip6wa2VtV7OxxNF8XgciUQCKpMCZFoaRdQ6PXIHshvQDtACUJDARA/tVTVe6rHrSs97mMPh16j3vQECvpKc4iSLWzrGqXWMrahnx6h1jP5oPRf8Ho1FLA5nG/c7vayq8TIajdoikRi0nqCEdn9gDaCQLNVOXZOuk310nbhCV0M" + "vhx+OWlbhykGnh+Hou20sy2rUdLtbAJxUfs9PwW9PQM4qjuqjfDEd6ssX+VP56kdZ/+s80H9nZJcBYDxT6x8b+vspDw4Fbf+TuC7gpn7ZtoFCSdJE0vxrAN8Jcr/DWZzEAAAAAElFTkSuQmCC";
        else if(state === FilterButtonState.filter)
            return"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWF" + "BURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEI" + "iAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50" + "cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBW" + "K8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhE" + "LTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBo" + "mxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMC" + "aIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgt" + "Uq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73Vwn" + "UCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7" + "ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun" + "3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQW" + "NBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5" + "JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFK" + "uUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk6" + "26s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXB" + "aKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509" + "E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8c" + "fvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAMuSURBVHjaZJN/aNR1GMefz+f7/d6P3aZGDGVKERLDbXdMJAQhlr+ujWo62g5DpUBiEyJjp" + "xi0ZRtyDSQvV0cDd2uw213b1EHiFjbGZq5bIbc5dTuyybRyK+ZCDrG0evWHdwfWHx8+PDwPz/vzeb2fR451DGvpQB2ODJvnP24zDkewD7Zct6h9qD1j4TzPWNgsip80isbCxuS7vzinGm5aZwcwbwVPGoAhgAIEsAAN2NKxSscmYKRvE1iRPrY3Vt7Ski4WQKaJOmb" + "jyF3WS//3jfr4hVXGEA55J5aXc+ZMdF0yOZWbFtCAKtx5zCUQLQBk32svPFJ8eHN16u+fCn6fv+MErKWlRWdvPzr9QpUVnIroxravlYz0tTvWdi9XRFGA6ugKOMPh7qHu7u5Ue3s7ra2tBIMnaG4+Sl1dHTU1NZSXl7P42x39zRxKYl82mYD8Q0wA8bjFmv855Xqzd" + "u/swsICDx78xfz8r0xMXKa+/iBer5fx8W+PDi22PjH43pKSoi2bzIS+reVlUYCVn18q11hl9vb2GIFAgFQqxczMDKFQCK/Xy8WLF4ozkN2e00o8O07YA1KpxCUCyKYSUX1fxDQgc3NzRjAY/DMej+Pz+ejpiTkzAAEpqfpEJHeP2PZJXO32VBsbKryydY3IPZB1a3I" + "VIH6/n1AoREVFBWmQjzknK7aLHm4bWXs5cel48uq0ziQGolE9e/uG8vsPceDttygrK8s2cBfkZ9zQ8syGbeaVyQmp3v8VNXWDvFo3SPX+8/hqzxEPjqiD9X5eqqoiN8fFjzeu5wHidh9xZTjIgZr1BqAaWi6Zm3e8z/OVDZS90kBv74AdUH7/IbZu3oJps0h8N7N62" + "IXpLt2VnVbZWfKc+jTx+aM/3bti85RuZ3RsvBjQxFgWO9VnTSebzB+SMyvhtMMTFsv9+l4r26Czq0sxcV9F+3eprnOJwumr14ozpMMfnLV/duQjPdeDmiTiaGrutLs3Nhjujc86sxD/uHs/DxCPjFrbXly0N7ZEbR9GcPb0jz4NS/q/1P/nwlOFlYpOrZ4UkeUiOt8" + "nRk6xyGNzT3YXTGBZeqEcgP53ACxeTBjELwuFAAAAAElFTkSuQmCC";
        else if(state === FilterButtonState.filterAscend)
            return"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWF" + "BURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEI" + "iAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50" + "cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBW" + "K8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhE" + "LTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBo" + "mxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMC" + "aIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgt" + "Uq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73Vwn" + "UCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7" + "ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun" + "3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQW" + "NBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5" + "JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFK" + "uUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk6" + "26s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXB" + "aKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509" + "E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8c" + "fvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAMNSURBVHjalJNfaFt1FMfP796bm9hublCwa93AVd3W5d67ddisFFw1lovUl7r0jx2rWWW6p" + "rpCkzfrUwjVFx0LwSrYPtiO0YJPDsuwIOJKq1PL0q43iZ3OuUp04p8owmbl40PXMgaCPnzhcPjCOef7PV8BxD6WETrKjXcviUbjTlW870/ta9mqfiqv1kuRQePjk68pkj4FSfX66HRA8ujfDPapvveWN4n1xKAC5H9CB3SRo767m9uhvRxQUGXAEXOt3uCodViWpT4" + "Yi+tC1Rrhj99/luV8QQ43unrkxWaNVSqvXSNgHSeQSqU2A4Zt2xUdHR3bp6amwp8uTNU91jRgij2PAWg1da+sTwmeGTv768TExN8jIyOk0xlOnTpNMpkiFovR3t6O67rcuPHDJkCtr6U7zVH/gffPKrwravLzC75EIkGxWOTWrVWKxR+Zn79EPB7HdV3m5uYaAK12/" + "xfmxm0P1uwW62D36p77nzdXVr4Txwn5h4aGVkqlEp6XJ5PJ4LouMzMzDYAGiPXkGwGJLGIAyqmPYod6sEM9vziRF0xALMvS0+n0zdnZWTo7O5mcnNwHaNevi27bqN0v+w1pWyj5AOUEjygn1I3VeLzaund/xfj4uAASi8WWM5kMLS0tAAYgV69+K5cvvyq/zZ7TpG1" + "hzZaqbZbsDR3DPnSi2n6ka0uhUJB8Pt+QSCTo7++nqamJ204YXV1PG5aF2nFxUgn7PlGAWIefVXYoihVufcBWorLZrOTz+YMDAwN/tba2UlZWRi6Xq7itmQbb9JnU25qMvvmRAKrmoafEqY/edOqf2bljV/iebDYrgPT29q6Ew2FM06RQKFQMDw+L4ziabaP3nXwpI" + "By9oACVnBblHOh52N4Vqaxr7jYAFQwGty4uLm72PE/3PK8yt+QZuawnjgSN0TOiDj36nCmP9364pjiY1hJ67RX8F5dMBUjtW7Xav+Xh3PRnIiK6CF/qd/66886YHvW3Sfh8lQHfy11Z2MCegBgioknbCZQTPr1l71di/IcUqjsgLfHzvn8GACNDKumTxWELAAAAAEl" + "FTkSuQmCC";
        else if(state === FilterButtonState.filterDescend)
            return"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWF" + "BURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEI" + "iAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50" + "cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBW" + "K8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhE" + "LTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBo" + "mxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMC" + "aIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgt" + "Uq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73Vwn" + "UCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7" + "ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun" + "3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQW" + "NBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5" + "JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFK" + "uUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk6" + "26s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXB" + "aKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509" + "E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8c" + "fvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAANBSURBVHjaXJNfSGR1HMXP73fvdcYdo7Vy1KRlZ9DdnOvv5uTODK0s1mxDyKIYSRYoUwQik" + "kgzPUS0RuW6QRCEuIiM/XuxNdiH6m17WnZFCMSHtjuOszONf0ZnGgW1iLbw9DBl0sPhy3k5HDjfD0h6w/VvSJJC8a5sOPeUYQ1TIjAuPOc+kN6Kae2JthodGBCVrSEdpmXAd8pJUrb2julgiWgJ9MMXABIXYADApQA0ACLc8Jjg9bAkKbhbD5LimDTgNQn15sc6SZD" + "1onyPZIAUSyQOSS124oLzDxL7hwRJjaTIFbYcUJ5xB0lYp29oZ8+OypZnPhfvkXiHdGz8nHHs7++6e+vede6UiiiVio69vR2v1+2S5slqQRJ4XvdXkBS7O+uuUv43bO79UvPZp4nfr8/N/Tk7O8urVz9cu3x5zH6x76Xvuru7f+zq6joIhUKFYrHYSBJA4yVBUm5sb" + "WPm15nqH1bvOpRScnBwcL1QKPD+/b+4vV3k0tIy4/E4I5EIFxcXu0nKckDwWUlSV6peqGCUW/mvJUmoNr82MTHBg4MD2rbNqakpRiIR3r59a5ykzOc3QBJob5txkZSbuQ00h/q5ub7htZSJ9NYmlDLF5OQkFxYW2NfXx/n5+YskhWk2G0p9IV5BXgDBAZ1k5Z07tx6" + "wAlHmcjlHjrUim13D8vIaYrEYr12bZmdnJ0k6ygvVHi0GOD2SpHGPhApGmclkKjKZ15HJpHHzJrV4PM6RkRF2dHQwm83WeTweMTw8cCwA1YKkUwWjVMEofaFXeS99w1CksKwWIxaLsaenh1VVVUyn0yf8fmBoKCpIoqeBAr3n50DywdXVlWorEGUqufp0imnpM8eEa" + "ZpaPB5nOBymrutMpVKuM2dGdQvjGh+icfFxCCA0YJB0tKC9Mpn8qTKZhLSS0BOJUWmaprBtu8K2bW1lZeVR27alUq3SwpDBh2t1fAUN/eepkYfCOvW26/grWxNB6XukvYJJwgKE+c1zUs270TQdkmq4WeKtTwS+hETiCgUJafrb/89CWTU9/8JT9k2UnP7HN1Ag+OQ" + "V8Ps6Yb78vigj/a08ahGCaGzql/zIrbmhy/9IzAmSztMvzJz8ewAvdf7cUVI5hQAAAABJRU5ErkJggg==";
        else if(state === FilterButtonState.sortAscending)
            return"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAABy0lEQVR42pyST2gTQ" + "RTGv9nEpCnqTasHES8VLCioiFDIYYum+I/ebFXQm1BbsdSDhKgoXhS8tLQSqAhexIInETwJIqJiexJEZXupwRJNbRsy2Z3dzHxesjGEChs/eMx7A+837817giQAYCD3cZuScqG8Wtj19tG5n/iHhBAkKcLYCh3fdSd04HXCq5xHG2oAdOAdNIG3qAJ5tW1A/9hrm8Z" + "sV+7vDLTe2nsmP9AWwATuXe27330lLwvLkloHY5EBR0dexkB0xxKp9Ieno8MbOjYP1ZRMHz77YE8kAMk35V/fkpWSc6L3dL7Tl6uPAaAm155EAYhwjFHVOsY4AEwPXXkG7W+0EqmCodlRW17oCkrO5Pjc55lon7i2mBqenc7ETTmLFWdvaenLpijJDUBcywsAYFRwu" + "/jja1dMWNfWKf2WEOJ43T8ihLgHACAJkpgZvNQ3kTnJG/u6Z8O7ZgNwAACbzCb5dxNVLPnQW3aKyY7EKABM2XZPcwUk5wG8qIfvSL5qtJAfHLnvF+Z2GsPr2fefipPH0luq5aXsOi3frJ+5xhSm+k/1VErOxRWlqgkrfujO/t191ZqxLeB5azbJeSFELnz9v/agVX8" + "GAIjG5r9I9yjkAAAAAElFTkSuQmCC";
        else if(state === FilterButtonState.sortDescending)
            return"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsSAAALEgHS3X78AAAAGXRFWHRTb2Z0d2FyZ" + "QBQYWludC5ORVQgVjMuNS41Tib51gAAAXlJREFUOE9jYACCqRH5a6eGZu6cHl00d2pUwa6J7r4Xe4w1U0ByWMB/DLHJnoHbQIKzI1PEp3r6vKjVU72HQzNIGNOA6W5uYiCZmUHxM+v0Vf836KuHkWQASPGciGznSe6+/+v01Vbh0YzdBeBwiC560GWi+aLVXEccxJ/" + "i6KhNdBjMCM/u7be3+99hpJEK0jTJ01a0y1hjKVEGTHH31e4w1vxSrqP0tVZPbXazodqKSl2VV9W6KnOJMoCAf4mLRpAq/+pTEh4F+79aJSwGxwjJseBZdHCVa/b2/1YR00vJMsAtd+c957T1D42De17iMKARKO4NTUiuQLoLrs694IAT0PbvNrFzNYwDOv9bRc4Iw" + "GKIMVQzKCWCsBNcDVDzaafUdbfMwiZNMwnq/mIeNvkgDldsgWo+hqyZ2TVr+0egIRIgQcuomb7G/h3/zaOmaeFxBcJ2l6xtx8xDJ/6wDJ+cYhU2g8vYv/M9yABj//aLOFxRTUbU49YCALt6h216b0oVAAAAAElFTkSuQmCC";
        return""
    };
    UI._GcFilterDialog = GcFilterDialog;
    UI._FilterButtonState = FilterButtonState
})(window,jQuery);
(function(window, $)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    var uiDialogClasses = 'ui-dialog ' + 'ui-widget ' + 'ui-widget-content ',
        sizeRelatedOptions = {
            buttons: true,
            height: true,
            maxHeight: true,
            maxWidth: true,
            minHeight: true,
            minWidth: true,
            width: true
        },
        resizableRelatedOptions = {
            maxHeight: true,
            maxWidth: true,
            minHeight: true,
            minWidth: true
        },
        attrFn = $.attrFn || {
            val: true,
            css: true,
            html: true,
            text: true,
            data: true,
            width: true,
            height: true,
            offset: true,
            click: true
        };
    $.widget("wijmo.wijsheetfilterdialog",{
        options: {
            autoOpen: false,
            buttons: {},
            closeOnEscape: true,
            closeText: 'close',
            dialogClass: '',
            draggable: false,
            hide: null,
            height: 'auto',
            maxHeight: false,
            maxWidth: false,
            minHeight: 150,
            minWidth: 235,
            modal: true,
            position: {
                my: 'center',
                at: 'center',
                collision: 'fit',
                using: function(pos)
                {
                    var topOffset = $(this).css(pos).offset().top;
                    if(topOffset < 0)
                        $(this).css('top',pos.top - topOffset)
                }
            },
            resizable: false,
            show: null,
            stack: true,
            title: '',
            width: 235,
            zIndex: 1000
        },
        _create: function()
        {
            this.originalTitle = this.element.attr('title');
            if(typeof this.originalTitle !== "string")
                this.originalTitle = "";
            this.options.title = this.options.title || this.originalTitle;
            var self = this,
                options = self.options,
                title = options.title || '&#160;',
                titleId = $.ui.dialog.getTitleId(self.element),
                uiDialog = (self.uiDialog = $('<div></div>')).appendTo(window.document.body).hide().addClass(uiDialogClasses + options.dialogClass).css({zIndex: options.zIndex}).attr('tabIndex',-1).css('outline',0).keydown(function(event)
                {
                    if(options.closeOnEscape && event.keyCode && event.keyCode === $.ui.keyCode.ESCAPE)
                    {
                        self.close(event);
                        event.preventDefault()
                    }
                }).attr({
                    role: 'dialog',
                    'aria-labelledby': titleId
                }).mousedown(function(event)
                {
                    self.moveToTop(false,event)
                }),
                uiDialogContent = self.element.show().removeAttr('title').addClass('ui-dialog-content ui-widget-content').appendTo(uiDialog);
            if($.isFunction(options.beforeclose) && !$.isFunction(options.beforeClose))
                options.beforeClose = options.beforeclose;
            if(options.draggable && $.fn.draggable)
                self._makeDraggable();
            if(options.resizable && $.fn.resizable)
                self._makeResizable();
            self._createButtons(options.buttons);
            self._isOpen = false;
            if($.fn.bgiframe)
                uiDialog.bgiframe()
        },
        _init: function()
        {
            if(this.options.autoOpen)
                this.open()
        },
        destroy: function()
        {
            var self = this;
            if(self.overlay)
                self.overlay.destroy();
            self.uiDialog.hide();
            self.element.unbind('.dialog').removeData('dialog').removeClass('ui-dialog-content ui-widget-content').hide().appendTo('body');
            self.uiDialog.remove();
            if(self.originalTitle)
                self.element.attr('title',self.originalTitle);
            return self
        },
        widget: function()
        {
            return this.uiDialog
        },
        close: function(event)
        {
            var self = this,
                maxZ,
                thisZ;
            if(false === self._trigger('beforeClose',event))
                return;
            var filterDialog = self.options._filterDialog;
            if(filterDialog)
            {
                $("#" + filterDialog.dialogID + " *").removeClass("ui-state-hover");
                $("#" + filterDialog.dialogID + " *").removeClass("ui-state-focus");
                $("#" + filterDialog.superPanelID).unbind("wijspreadpanelexscrolled")
            }
            if(self.overlay)
            {
                self.overlay.$el.unbind("mousedown.wijsheetfilterdialog-overlay");
                self.overlay.destroy()
            }
            $(document).unbind('keydown.ui-dialog');
            self.uiDialog.unbind('mousewheel.ui-dialog');
            self.uiDialog.unbind("mouseover.wijsheetfilterdialog-overlay");
            self._isOpen = false;
            if(self.options.hide)
                self.uiDialog.hide(self.options.hide,function()
                {
                    self._trigger('close',event)
                });
            else
            {
                self.uiDialog.hide();
                self._trigger('close',event)
            }
            $.ui.dialog.overlay.resize();
            if(self.options.modal)
            {
                maxZ = 0;
                $('.ui-dialog').each(function()
                {
                    if(this !== self.uiDialog[0])
                    {
                        thisZ = $(this).css('z-index');
                        if(!isNaN(thisZ))
                            maxZ = Math.max(maxZ,thisZ)
                    }
                });
                $.ui.dialog.maxZ = maxZ
            }
            return self
        },
        isOpen: function()
        {
            return this._isOpen
        },
        moveToTop: function(force, event)
        {
            var self = this,
                options = self.options,
                saveScroll;
            if(options.modal && !force || !options.stack && !options.modal)
                return self._trigger('focus',event);
            if(options.zIndex > $.ui.dialog.maxZ)
                $.ui.dialog.maxZ = options.zIndex;
            if(self.overlay)
            {
                $.ui.dialog.maxZ += 1;
                self.overlay.$el.css('z-index',$.ui.dialog.overlay.maxZ = $.ui.dialog.maxZ)
            }
            saveScroll = {
                scrollTop: self.element.attr('scrollTop'),
                scrollLeft: self.element.attr('scrollLeft')
            };
            $.ui.dialog.maxZ += 1;
            self.uiDialog.css('z-index',$.ui.dialog.maxZ);
            self.element.attr(saveScroll);
            self._trigger('focus',event);
            return self
        },
        open: function()
        {
            if(this._isOpen)
                return;
            var self = this,
                options = self.options,
                uiDialog = self.uiDialog;
            self.overlay = options.modal ? new $.ui.dialog.overlay(self) : null;
            self._size();
            self._position(options.position);
            uiDialog.show(options.show);
            self.moveToTop(true);
            if(options.modal)
            {
                $(document).bind('keydown.ui-dialog',function(event)
                {
                    var filterDialog = self.options._filterDialog;
                    if(!(filterDialog && filterDialog.dialogID))
                        return;
                    var hoverItem = null;
                    if(hoverItem === null)
                    {
                        itemList = $("#" + filterDialog.dialogID + " .ui-state-hover");
                        if(itemList.length > 0)
                        {
                            var inputList = $(itemList[0]).children("input");
                            if(inputList.length > 0 && inputList[0].name && inputList[0].name.toLowerCase() === "multiselect_gc_filteritemlist")
                                hoverItem = inputList[0];
                            if(hoverItem === null)
                                if(itemList[0].parentNode.id === "gc_sortAsc" || itemList[0].parentNode.id === "gc_sortDes")
                                    hoverItem = itemList[0].parentNode;
                            if(hoverItem === null)
                                hoverItem = itemList[0]
                        }
                    }
                    if(hoverItem === null)
                    {
                        var itemList = $("#" + filterDialog.dialogID + " .ui-state-focus");
                        if(itemList.length > 0)
                            hoverItem = itemList[0]
                    }
                    if(hoverItem === null)
                        hoverItem = event.target;
                    if(event.keyCode === $.ui.keyCode.ENTER)
                    {
                        var targetID = hoverItem.id;
                        if(targetID === filterDialog.sortAscID)
                        {
                            filterDialog._sortByUser(true);
                            filterDialog.close()
                        }
                        else if(targetID === filterDialog.sortDesID)
                        {
                            filterDialog._sortByUser(false);
                            filterDialog.close()
                        }
                        return
                    }
                    else if(event.keyCode !== $.ui.keyCode.TAB && event.keyCode !== $.ui.keyCode.DOWN && event.keyCode !== $.ui.keyCode.UP)
                        return;
                    var isForward = true;
                    switch(event.keyCode)
                    {
                        case $.ui.keyCode.TAB:
                            if(event.shiftKey)
                                isForward = false;
                            break;
                        case $.ui.keyCode.UP:
                            isForward = false;
                            break
                    }
                    var tabbables = $(':tabbable',self.uiDialog[0]);
                    var index = $.inArray(hoverItem,tabbables);
                    var targetElement = null;
                    var currentItem = index >= 0 ? tabbables[index] : null;
                    if(currentItem && currentItem.name && currentItem.name === filterDialog.filterCheckItemName)
                        if(event.keyCode === $.ui.keyCode.TAB)
                        {
                            var topElement = null;
                            var bottomElement = null;
                            for(var i = 0; i < tabbables.length; i++)
                            {
                                var item = tabbables[i];
                                if(item.name && item.name === filterDialog.filterCheckItemName)
                                {
                                    if(topElement === null)
                                        topElement = tabbables[i - 1];
                                    bottomElement = tabbables[i + 1]
                                }
                            }
                            targetElement = isForward ? bottomElement : topElement
                        }
                    if(targetElement === null)
                    {
                        if(index !== -1)
                            if(!isForward)
                                if(index === 0)
                                    index = tabbables.length - 1;
                                else
                                    index = index - 1;
                            else if(index === tabbables.length - 1)
                                index = 0;
                            else
                                index = index + 1;
                        else
                            index = 0;
                        targetElement = tabbables[index];
                        if(targetElement.name && targetElement.name === filterDialog.filterCheckItemName)
                            if(!(currentItem && currentItem.name && currentItem.name === filterDialog.filterCheckItemName))
                            {
                                var activeCheckItem = $("#" + filterDialog.superPanelID + " ." + filterDialog.activeCheckItemClass);
                                if(activeCheckItem.length > 0)
                                    targetElement = $(activeCheckItem[0]).children("input")[0]
                            }
                    }
                    $("#" + filterDialog.dialogID + " *").removeClass("ui-state-hover");
                    $("#" + filterDialog.dialogID + " *").removeClass("ui-state-focus");
                    if(targetElement.name && targetElement.name === filterDialog.filterCheckItemName)
                    {
                        var checkLabel = targetElement.parentNode;
                        $("#" + filterDialog.superPanelID + " *").removeClass(filterDialog.activeCheckItemClass);
                        $(checkLabel).addClass("ui-state-hover " + filterDialog.activeCheckItemClass);
                        if($("#" + filterDialog.superPanelID).wijspreadpanelex("needToScroll",checkLabel))
                        {
                            $("#" + filterDialog.superPanelID).wijspreadpanelex("scrollChildIntoView",checkLabel);
                            uiDialog.focus()
                        }
                        else
                            $(targetElement).focus().addClass("ui-state-focus ui-state-hover");
                        return false
                    }
                    if(targetElement.id === "gc_sortAsc" || targetElement.id === "gc_sortDes")
                    {
                        self.uiDialog.focus(1);
                        $(targetElement).children("a").addClass("ui-state-focus ui-state-hover");
                        return false
                    }
                    $(targetElement).focus(1).addClass("ui-state-focus");
                    return false
                });
                if(self.options._filterDialog)
                    $("#" + self.options._filterDialog.superPanelID).bind("wijspreadpanelexscrolled",function()
                    {
                        var filterDialog = self.options._filterDialog;
                        var itemList = $("#" + filterDialog.dialogID + " .ui-state-hover");
                        if(itemList.length > 0)
                        {
                            var inputList = $(itemList[0]).children("input");
                            if(inputList.length > 0 && inputList[0].name && inputList[0].name === filterDialog.filterCheckItemName)
                                if(!$("#" + filterDialog.superPanelID).wijspreadpanelex("needToScroll",inputList[0]))
                                    $(inputList[0]).focus().addClass("ui-state-focus ui-state-hover")
                        }
                    });
                uiDialog.bind('mousewheel.ui-dialog',function(event)
                {
                    return false
                });
                uiDialog.bind("mouseover.wijsheetfilterdialog-overlay",function(event)
                {
                    var filterDialog = self.options._filterDialog;
                    if(!(filterDialog && filterDialog.dialogID))
                        return;
                    $("#" + filterDialog.dialogID + " .ui-state-hover").removeClass("ui-state-hover");
                    $("#" + filterDialog.dialogID + " .ui-state-focus").removeClass("ui-state-focus");
                    if(!(document.activeElement && document.activeElement.nodeName.toLowerCase() === "input" && document.activeElement.type.toLowerCase() === "search"))
                        uiDialog.focus();
                    var findScrollElement = function()
                        {
                            var $eventTarget = $(event.target);
                            $(this).find(".ui-state-default").each(function()
                            {
                                if($eventTarget.closest(this).length > 0)
                                {
                                    $(this).addClass("ui-state-hover");
                                    return
                                }
                            })
                        };
                    $("#" + filterDialog.dialogID + " .wijmo-wijsuperpanel-vbarcontainer").each(findScrollElement);
                    $("#" + filterDialog.dialogID + " .wijmo-wijsuperpanel-hbarcontainer").each(findScrollElement);
                    var tabbables = $(':tabbable',this);
                    var index = $.inArray(event.target,tabbables);
                    var focusElement = null;
                    if(index !== -1)
                        focusElement = tabbables[index];
                    else
                    {
                        var inputList = $(event.target).children("input");
                        if(inputList.length > 0 && inputList[0].name && inputList[0].name === filterDialog.filterCheckItemName)
                            focusElement = inputList[0];
                        if(focusElement === null)
                        {
                            inputList = $(event.target.parentNode).children("input");
                            if(inputList.length > 0 && inputList[0].name && inputList[0].name === filterDialog.filterCheckItemName)
                                focusElement = inputList[0]
                        }
                        if(focusElement === null)
                            for(var i = 0; i < tabbables.length; i++)
                                if($(event.target).closest(tabbables[i]).length > 0)
                                {
                                    focusElement = tabbables[i];
                                    break
                                }
                    }
                    if(focusElement !== null)
                    {
                        if(focusElement.nodeName.toLowerCase() === "input" && focusElement.type.toLowerCase() === "search")
                            return;
                        if(focusElement.name && focusElement.name === filterDialog.filterCheckItemName)
                        {
                            $(focusElement.parentNode).addClass("ui-state-hover");
                            return
                        }
                        if(focusElement.id === "gc_sortAsc" || focusElement.id === "gc_sortDes")
                        {
                            $(focusElement).children("a").addClass("ui-state-hover");
                            return
                        }
                        $(focusElement).addClass("ui-state-hover")
                    }
                });
                self.overlay.$el.css({opacity: 0});
                self.overlay.$el.bind("mousedown.wijsheetfilterdialog-overlay",function(e)
                {
                    if(self.isOpen())
                    {
                        self.close();
                        UI.cancelDefault(e)
                    }
                });
                $(window).trigger("resize.dialog-overlay")
            }
            $(self.element.find(':tabbable').get().concat(uiDialog.find('.ui-dialog-buttonpane :tabbable').get().concat(uiDialog.get()))).eq(0).focus();
            self._isOpen = true;
            self._trigger('open');
            uiDialog.focus(1);
            if(self.options._filterDialog)
                $("#" + self.options._filterDialog.superPanelID).wijspreadpanelex("scrollTo",0,0);
            return self
        },
        _createButtons: function(buttons)
        {
            var self = this,
                hasButtons = false,
                uiDialogButtonPane = $('<div></div>').addClass('ui-dialog-buttonpane ' + 'ui-widget-content ' + 'ui-helper-clearfix'),
                uiButtonSet = $("<div></div>").addClass("ui-dialog-buttonset").appendTo(uiDialogButtonPane);
            self.uiDialog.find('.ui-dialog-buttonpane').remove();
            if(typeof buttons === 'object' && buttons !== null)
                $.each(buttons,function()
                {
                    return!(hasButtons = true)
                });
            if(hasButtons)
            {
                $.each(buttons,function(name, props)
                {
                    props = $.isFunction(props) ? {
                        click: props,
                        text: name
                    } : props;
                    var button = $('<button type="button"></button>').click(function()
                        {
                            props.click.apply(self.element[0],arguments)
                        }).appendTo(uiButtonSet);
                    $.each(props,function(key, value)
                    {
                        if(key === "click")
                            return;
                        if(key in attrFn)
                            button[key](value);
                        else
                            button.attr(key,value)
                    });
                    if($.fn.button)
                        button.button()
                });
                uiDialogButtonPane.appendTo(self.uiDialog)
            }
        },
        _makeDraggable: function()
        {
            var self = this,
                options = self.options,
                doc = $(window.document),
                heightBeforeDrag;
            function filteredUi(ui)
            {
                return{
                        position: ui.position,
                        offset: ui.offset
                    }
            }
            self.uiDialog.draggable({
                cancel: '.ui-dialog-content, .ui-dialog-titlebar-close',
                handle: '.ui-dialog-titlebar',
                containment: 'document',
                start: function(event, ui)
                {
                    heightBeforeDrag = options.height === "auto" ? "auto" : $(this).height();
                    $(this).height($(this).height()).addClass("ui-dialog-dragging");
                    self._trigger('dragStart',event,filteredUi(ui))
                },
                drag: function(event, ui)
                {
                    self._trigger('drag',event,filteredUi(ui))
                },
                stop: function(event, ui)
                {
                    options.position = [ui.position.left - doc.scrollLeft(),ui.position.top - doc.scrollTop()];
                    $(this).removeClass("ui-dialog-dragging").height(heightBeforeDrag);
                    self._trigger('dragStop',event,filteredUi(ui));
                    $.ui.dialog.overlay.resize()
                }
            })
        },
        _makeResizable: function(handles)
        {
            handles = handles === undefined ? this.options.resizable : handles;
            var self = this,
                options = self.options,
                position = self.uiDialog.css('position'),
                resizeHandles = typeof handles === 'string' ? handles : 'n,e,s,w,se,sw,ne,nw';
            function filteredUi(ui)
            {
                return{
                        originalPosition: ui.originalPosition,
                        originalSize: ui.originalSize,
                        position: ui.position,
                        size: ui.size
                    }
            }
            self.uiDialog.resizable({
                cancel: '.ui-dialog-content',
                containment: 'document',
                alsoResize: self.element,
                maxWidth: options.maxWidth,
                maxHeight: options.maxHeight,
                minWidth: options.minWidth,
                minHeight: self._minHeight(),
                handles: resizeHandles,
                start: function(event, ui)
                {
                    $(this).addClass("ui-dialog-resizing");
                    self._trigger('resizeStart',event,filteredUi(ui))
                },
                resize: function(event, ui)
                {
                    self._trigger('resize',event,filteredUi(ui))
                },
                stop: function(event, ui)
                {
                    $(this).removeClass("ui-dialog-resizing");
                    options.height = $(this).height();
                    options.width = $(this).width();
                    self._trigger('resizeStop',event,filteredUi(ui));
                    $.ui.dialog.overlay.resize()
                }
            }).css('position',position).find('.ui-resizable-se').addClass('ui-icon ui-icon-grip-diagonal-se')
        },
        _minHeight: function()
        {
            var options = this.options;
            if(options.height === 'auto')
                return options.minHeight;
            else
                return Math.min(options.minHeight,options.height)
        },
        _position: function(position)
        {
            var myAt = [],
                offset = [0,0],
                isVisible;
            if(position)
            {
                if(typeof position === 'string' || typeof position === 'object' && '0' in position)
                {
                    myAt = position.split ? position.split(' ') : [position[0],position[1]];
                    if(myAt.length === 1)
                        myAt[1] = myAt[0];
                    $.each(['left','top'],function(i, offsetPosition)
                    {
                        if(+myAt[i] === myAt[i])
                        {
                            offset[i] = myAt[i];
                            myAt[i] = offsetPosition
                        }
                    });
                    position = {
                        my: myAt.join(" "),
                        at: myAt.join(" "),
                        offset: offset.join(" ")
                    }
                }
                position = $.extend({},$.ui.dialog.prototype.options.position,position)
            }
            else
                position = $.ui.dialog.prototype.options.position;
            isVisible = this.uiDialog.is(':visible');
            if(!isVisible)
                this.uiDialog.show();
            this.uiDialog.css({
                top: 0,
                left: 0
            }).position($.extend({of: window},position));
            if(!isVisible)
                this.uiDialog.hide()
        },
        _setOptions: function(options)
        {
            var self = this,
                resizableOptions = {},
                resize = false;
            $.each(options,function(key, value)
            {
                self._setOption(key,value);
                if(key in sizeRelatedOptions)
                    resize = true;
                if(key in resizableRelatedOptions)
                    resizableOptions[key] = value
            });
            if(resize)
                this._size();
            if(this.uiDialog.is(":data(resizable)"))
                this.uiDialog.resizable("option",resizableOptions)
        },
        _setOption: function(key, value)
        {
            var self = this,
                uiDialog = self.uiDialog;
            switch(key)
            {
                case"beforeclose":
                    key = "beforeClose";
                    break;
                case"buttons":
                    self._createButtons(value);
                    break;
                case"dialogClass":
                    uiDialog.removeClass(self.options.dialogClass).addClass(uiDialogClasses + value);
                    break;
                case"disabled":
                    if(value)
                        uiDialog.addClass('ui-dialog-disabled');
                    else
                        uiDialog.removeClass('ui-dialog-disabled');
                    break;
                case"draggable":
                    var isDraggable = uiDialog.is(":data(draggable)");
                    if(isDraggable && !value)
                        uiDialog.draggable("destroy");
                    if(!isDraggable && value)
                        self._makeDraggable();
                    break;
                case"position":
                    self._position(value);
                    break;
                case"resizable":
                    var isResizable = uiDialog.is(":data(resizable)");
                    if(isResizable && !value)
                        uiDialog.resizable('destroy');
                    if(isResizable && typeof value === 'string')
                        uiDialog.resizable('option','handles',value);
                    if(!isResizable && value !== false)
                        self._makeResizable(value);
                    break
            }
            $.Widget.prototype._setOption.apply(self,arguments)
        },
        _size: function()
        {
            var options = this.options,
                nonContentHeight,
                minContentHeight,
                isVisible = this.uiDialog.is(":visible");
            this.element.show().css({
                width: 'auto',
                minHeight: 0,
                height: 0
            });
            if(options.minWidth > options.width)
                options.width = options.minWidth;
            nonContentHeight = this.uiDialog.css({
                height: 'auto',
                width: options.width
            }).height();
            minContentHeight = Math.max(0,options.minHeight - nonContentHeight);
            if(options.height === "auto")
                if($.support.minHeight)
                    this.element.css({
                        minHeight: minContentHeight,
                        height: "auto"
                    });
                else
                {
                    this.uiDialog.show();
                    var autoHeight = this.element.css("height","auto").height();
                    if(!isVisible)
                        this.uiDialog.hide();
                    this.element.height(Math.max(autoHeight,minContentHeight))
                }
            else
                this.element.height(Math.max(options.height - nonContentHeight,0));
            if(this.uiDialog.is(':data(resizable)'))
                this.uiDialog.resizable('option','minHeight',this._minHeight())
        }
    });
    var multiselectID = 0;
    $.widget("wijmo.wijsheetmultiselect",{
        options: {
            header: true,
            height: 175,
            minWidth: 200,
            classes: '',
            checkAllText: 'Επιλογή όλων',
            uncheckAllText: 'Καθαρισμός όλων',
            noneSelectedText: 'Select options',
            selectedText: '# selected',
            selectedList: 0,
            show: '',
            hide: '',
            autoOpen: false,
            multiple: true,
            position: {}
        },
        _create: function()
        {
            var el = this.element.hide(),
                o = this.options;
            this.speed = $.fx.speeds._default;
            this._isOpen = false;
            var menu = (this.menu = $('<div />')).addClass('ui-multiselect-menu ui-widget ui-corner-all').addClass(o.classes).insertAfter(el),
                header = (this.header = $('<div />')).addClass('ui-widget-header ui-corner-all ui-multiselect-header ui-helper-clearfix').appendTo(menu),
                headerLinkContainer = (this.headerLinkContainer = $('<ul style="cursor: pointer" />')).addClass('ui-helper-reset').html(function()
                {
                    if(o.header === true)
                        return'<li><a class="ui-multiselect-all"><span class="ui-icon ui-icon-check"></span><span>' + o.checkAllText + '</span></a></li><li><a class="ui-multiselect-none"><span class="ui-icon ui-icon-closethick"></span><span>' + o.uncheckAllText + '</span></a></li>';
                    else if(typeof o.header === "string")
                        return'<li>' + o.header + '</li>';
                    else
                        return''
                }).appendTo(header),
                superPanel = $("<div id='gc_filterSuperPanel' />").addClass('ui-multiselect-superpanel').appendTo(menu),
                checkboxContainer = (this.checkboxContainer = $('<ul />')).addClass('ui-multiselect-checkboxes ui-helper-reset').appendTo(superPanel);
            this._bindEvents();
            this.refresh(true);
            if(!o.multiple)
                menu.addClass('ui-multiselect-single')
        },
        _init: function()
        {
            if(this.options.header === false)
                this.header.hide();
            if(!this.options.multiple)
                this.headerLinkContainer.find('.ui-multiselect-all, .ui-multiselect-none').hide();
            if(this.options.autoOpen)
                this.open();
            if(this.element.is(':disabled'))
                this.disable()
        },
        refresh: function(init)
        {
            var el = this.element,
                o = this.options,
                menu = this.menu,
                checkboxContainer = this.checkboxContainer,
                optgroups = [],
                html = [],
                id = el.attr('id') || multiselectID++;
            el.find('option').each(function(i)
            {
                var $this = $(this),
                    parent = this.parentNode,
                    title = this.innerHTML,
                    description = this.title,
                    value = this.value,
                    inputID = this.id || 'ui-multiselect-' + id + '-option-' + i,
                    isDisabled = this.disabled,
                    isSelected = $this.attr("aria-selected"),
                    labelClasses = ['ui-corner-all'],
                    optLabel;
                if(parent.tagName.toLowerCase() === 'optgroup')
                {
                    optLabel = parent.getAttribute('label');
                    if($.inArray(optLabel,optgroups) === -1)
                    {
                        html.push('<li class="ui-multiselect-optgroup-label"><a href="#">' + optLabel + '</a></li>');
                        optgroups.push(optLabel)
                    }
                }
                if(isDisabled)
                    labelClasses.push('ui-state-disabled');
                if(isSelected && !o.multiple)
                    labelClasses.push('ui-state-active');
                html.push('<li class="' + (isDisabled ? 'ui-multiselect-disabled' : '') + '">');
                html.push('<label for="' + inputID + '" title="' + description + '" class="' + labelClasses.join(' ') + '">');
                html.push('<input id="' + inputID + '" name="multiselect_' + id + '" type="' + (o.multiple ? "checkbox" : "radio") + '" value="' + value + '" title="' + title + '"');
                if(isSelected === "true")
                {
                    html.push(' checked="checked"');
                    html.push(' aria-selected="true"')
                }
                if(isDisabled)
                {
                    html.push(' disabled="disabled"');
                    html.push(' aria-disabled="true"')
                }
                html.push(' /><span style="font-weight:normal">' + title + '</span></label></li>')
            });
            checkboxContainer.html(html.join(''));
            this.labels = menu.find('label');
            if(!init)
                this._trigger('refresh')
        },
        update: function()
        {
            var o = this.options,
                $inputs = this.labels.find('input'),
                $checked = $inputs.filter('[checked]'),
                numChecked = $checked.length,
                value;
            if(numChecked === 0)
                value = o.noneSelectedText;
            else if($.isFunction(o.selectedText))
                value = o.selectedText.call(this,numChecked,$inputs.length,$checked.get());
            else if(/\d/.test(o.selectedList) && o.selectedList > 0 && numChecked <= o.selectedList)
                value = $checked.map(function()
                {
                    return $(this).next().text()
                }).get().join(', ');
            else
                value = o.selectedText.replace('#',numChecked).replace('#',$inputs.length);
            return value
        },
        _bindEvents: function()
        {
            var self = this;
            function clickHandler()
            {
                self[self._isOpen ? 'close' : 'open']();
                return false
            }
            this.header.delegate('a','click.multiselect',function(e)
            {
                if($(this).hasClass('ui-multiselect-close'))
                    self.close();
                else
                {
                    self[$(this).hasClass('ui-multiselect-all') ? 'checkAll' : 'uncheckAll']();
                    self.element.trigger("change")
                }
                e.preventDefault()
            });
            this.menu.delegate('li.ui-multiselect-optgroup-label a','click.multiselect',function(e)
            {
                e.preventDefault();
                var $this = $(this),
                    $inputs = $this.parent().nextUntil('li.ui-multiselect-optgroup-label').find('input:visible:not(:disabled)'),
                    nodes = $inputs.get(),
                    label = $this.parent().text();
                if(self._trigger('beforeoptgrouptoggle',e,{
                    inputs: nodes,
                    label: label
                }) === false)
                    return;
                self._toggleChecked($inputs.filter('[checked]').length !== $inputs.length,$inputs);
                self._trigger('optgrouptoggle',e,{
                    inputs: nodes,
                    label: label,
                    checked: nodes[0].checked
                })
            }).delegate('label','mouseenter.multiselect',function()
            {
                if(!$(this).hasClass('ui-state-disabled'))
                    self.labels.removeClass('ui-state-hover')
            }).delegate('label','keydown.multiselect',function(e)
            {
                e.preventDefault();
                switch(e.which)
                {
                    case 9:
                    case 27:
                        break;
                    case 38:
                    case 40:
                    case 37:
                    case 39:
                        break;
                    case 13:
                        $(this).find('input')[0].click();
                        break
                }
            }).delegate('input[type="checkbox"], input[type="radio"]','click.multiselect',function(e)
            {
                var $this = $(this),
                    val = this.value,
                    checked = this.checked,
                    tags = self.element.find('option');
                if(this.disabled || self._trigger('click',e,{
                    value: val,
                    text: this.title,
                    checked: checked
                }) === false)
                {
                    e.preventDefault();
                    return
                }
                $this.focus();
                $this.attr('aria-selected',checked);
                $("#gc_filterSuperPanel *").removeClass("gc_filterActiveCheckItem");
                $(this.parentNode).addClass("gc_filterActiveCheckItem");
                tags.each(function()
                {
                    if(this.value === val)
                        this.selected = checked;
                    else if(!self.options.multiple)
                        this.selected = false
                });
                if(!self.options.multiple)
                {
                    self.labels.removeClass('ui-state-active');
                    $this.closest('label').toggleClass('ui-state-active',checked);
                    self.close()
                }
                self.element.trigger("change");
                window.setTimeout($.proxy(self.update,self),10)
            });
            $(window.document).bind('mousedown.multiselect',function(e)
            {
                if(self._isOpen && !$.contains(self.menu[0],e.target) && !$.contains(self.button[0],e.target) && e.target !== self.button[0])
                    self.close()
            });
            $(this.element[0].form).bind('reset.multiselect',function()
            {
                window.setTimeout($.proxy(self.refresh,self),10)
            })
        },
        _traverse: function(which, start)
        {
            var $start = $(start),
                moveToLast = which === 38 || which === 37,
                $next = $start.parent()[moveToLast ? 'prevAll' : 'nextAll']('li:not(.ui-multiselect-disabled, .ui-multiselect-optgroup-label)')[moveToLast ? 'last' : 'first']();
            if(!$next.length)
            {
                var $container = this.menu.find('ul').last();
                this.menu.find('label')[moveToLast ? 'last' : 'first']().trigger('mouseover');
                $container.scrollTop(moveToLast ? $container.height() : 0)
            }
            else
                $next.find('label').trigger('mouseover')
        },
        _toggleState: function(prop, flag)
        {
            return function()
                {
                    if(!this.disabled)
                        this[prop] = flag;
                    if(flag)
                        this.setAttribute('aria-selected',true);
                    else
                        this.removeAttribute('aria-selected')
                }
        },
        _toggleChecked: function(flag, group)
        {
            var $inputs = group && group.length ? group : this.labels.find('input'),
                self = this;
            $inputs.each(this._toggleState('checked',flag));
            $inputs.eq(0).focus();
            this.update();
            var values = $inputs.map(function()
                {
                    return this.value
                }).get();
            this.element.find('option').each(function()
            {
                if(!this.disabled && $.inArray(this.value,values) > -1)
                    self._toggleState('selected',flag).call(this)
            });
            if($inputs.length)
                this.element.trigger("change")
        },
        _toggleDisabled: function(flag)
        {
            this.button.attr({
                disabled: flag,
                'aria-disabled': flag
            })[flag ? 'addClass' : 'removeClass']('ui-state-disabled');
            this.menu.find('input').attr({
                disabled: flag,
                'aria-disabled': flag
            }).parent()[flag ? 'addClass' : 'removeClass']('ui-state-disabled');
            this.element.attr({
                disabled: flag,
                'aria-disabled': flag
            })
        },
        open: function(e)
        {
            var self = this,
                button = this.button,
                menu = this.menu,
                speed = this.speed,
                o = this.options;
            if(this._trigger('beforeopen') === false || button.hasClass('ui-state-disabled') || this._isOpen)
                return;
            var $container = menu.find('ul').last(),
                effect = o.show,
                pos = button.offset();
            if($.isArray(o.show))
            {
                effect = o.show[0];
                speed = o.show[1] || self.speed
            }
            $container.scrollTop(0).height(o.height);
            if($.ui.position && !$.isEmptyObject(o.position))
            {
                o.position.of = o.position.of || button;
                menu.show().position(o.position).hide().show(effect,speed)
            }
            else
                menu.css({
                    top: pos.top + button.outerHeight(),
                    left: pos.left
                }).show(effect,speed);
            this.labels.eq(0).trigger('mouseover').trigger('mouseenter').find('input').trigger('focus');
            button.addClass('ui-state-active');
            this._isOpen = true;
            this._trigger('open')
        },
        close: function()
        {
            if(this._trigger('beforeclose') === false)
                return;
            var o = this.options,
                effect = o.hide,
                speed = this.speed;
            if($.isArray(o.hide))
            {
                effect = o.hide[0];
                speed = o.hide[1] || this.speed
            }
            this.menu.hide(effect,speed);
            this.button.removeClass('ui-state-active').trigger('blur').trigger('mouseleave');
            this._isOpen = false;
            this._trigger('close')
        },
        enable: function()
        {
            this._toggleDisabled(false)
        },
        disable: function()
        {
            this._toggleDisabled(true)
        },
        checkAll: function(e)
        {
            this._toggleChecked(true);
            this._trigger('checkAll')
        },
        uncheckAll: function()
        {
            this._toggleChecked(false);
            this._trigger('uncheckAll')
        },
        getChecked: function()
        {
            return this.menu.find('input:checked')
        },
        destroy: function()
        {
            $.Widget.prototype.destroy.call(this);
            this.menu.remove();
            this.element.show();
            return this
        },
        isOpen: function()
        {
            return this._isOpen
        },
        widget: function()
        {
            return this.menu
        },
        _setOption: function(key, value)
        {
            var menu = this.menu;
            switch(key)
            {
                case'header':
                    menu.find('div.ui-multiselect-header')[value ? 'show' : 'hide']();
                    break;
                case'checkAllText':
                    menu.find('a.ui-multiselect-all span').eq(-1).text(value);
                    break;
                case'uncheckAllText':
                    menu.find('a.ui-multiselect-none span').eq(-1).text(value);
                    break;
                case'height':
                    menu.find('ul').last().height(parseInt(value,10));
                    break;
                case'minWidth':
                    this.options[key] = parseInt(value,10);
                    this._setButtonWidth();
                    this._setMenuWidth();
                    break;
                case'selectedText':
                case'selectedList':
                case'noneSelectedText':
                    this.options[key] = value;
                    this.update();
                    break;
                case'classes':
                    menu.add(this.button).removeClass(this.options.classes).addClass(value);
                    break
            }
            $.Widget.prototype._setOption.apply(this,arguments)
        }
    });
    var rEscape = /[\-\[\]{}()*+?.,\\\^$|#\s]/g;
    $.widget("wijmo.wijsheetmultiselectfilter",{
        options: {
            label: "",
            width: 165,
            placeholder: "Search",
            autoReset: false
        },
        _create: function()
        {
            var self = this,
                opts = this.options,
                instance = this.instance = $(this.element).data("wijmo-wijsheetmultiselect"),
                header = this.header = instance.menu.find(".ui-multiselect-header").addClass("ui-multiselect-hasfilter"),
                wrapper = this.wrapper = $('<div class="ui-multiselect-filter">' + (opts.label.length ? opts.label : '') + '<input placeholder="' + opts.placeholder + '" type="search"' + (/\d/.test(opts.width) ? 'style="width:' + opts.width + 'px"' : '') + ' /></div>').prependTo(this.header);
            this.inputs = instance.menu.find('input[type="checkbox"], input[type="radio"]');
            this.input = wrapper.find("input").bind({
                keydown: function(e)
                {
                    if(e.which === 13)
                        e.preventDefault()
                },
                keyup: function(e)
                {
                    switch(e.keyCode)
                    {
                        case $.ui.keyCode.TAB:
                        case $.ui.keyCode.UP:
                        case $.ui.keyCode.RIGHT:
                        case $.ui.keyCode.DOWN:
                        case $.ui.keyCode.LEFT:
                            return;
                        default:
                            $.proxy(self._handler(e),self)
                    }
                }
            });
            this.updateCache();
            instance._toggleChecked = function(flag, group)
            {
                var $inputs = group && group.length ? group : this.labels.find('input'),
                    _self = this,
                    selector = self.instance._isOpen ? ":disabled, :hidden" : ":disabled";
                $inputs = $inputs.not(selector).each(this._toggleState('checked',flag));
                this.update();
                var values = $inputs.map(function()
                    {
                        return this.value
                    }).get();
                this.element.find('option').filter(function()
                {
                    if(!this.disabled && $.inArray(this.value,values) > -1)
                        _self._toggleState('selected',flag).call(this)
                })
            };
            var doc = $(window.document).bind("wijsheetmultiselectrefresh",function()
                {
                    self.updateCache();
                    self._handler()
                });
            if(this.options.autoReset)
                doc.bind("wijsheetmultiselectclose",$.proxy(this._reset,this))
        },
        _handler: function(e)
        {
            var term = $.trim(this.input[0].value.toLowerCase()),
                rows = this.rows,
                inputs = rows.find("input"),
                cache = this.cache;
            if(!term)
            {
                rows.show();
                if(!this._firstOpenCache)
                    this.initFirstOpenCache();
                else
                {
                    inputs.prop("checked",false);
                    var self = this;
                    $.each(inputs,function(i, v)
                    {
                        if($.inArray(v.value,self._firstOpenCache) !== -1)
                            $(v).prop("checked",true)
                    })
                }
            }
            else
            {
                rows.hide();
                inputs.prop("checked",false);
                var regex = new RegExp(term.replace(rEscape,"\\$&"),'gi');
                this._trigger("filter",e,$.map(cache,function(v, i)
                {
                    if(v.search(regex) !== -1)
                    {
                        rows.eq(i).show();
                        var currentInput = inputs.get(i);
                        $(currentInput).prop("checked",true);
                        return currentInput
                    }
                    return null
                }))
            }
            this.instance.menu.find(".ui-multiselect-optgroup-label").each(function()
            {
                var $this = $(this);
                var isVisible = $this.nextUntil('.ui-multiselect-optgroup-label').filter(function()
                    {
                        return $.css(this,"display") !== 'none'
                    }).length;
                $this[isVisible ? 'show' : 'hide']()
            });
            this.instance.element.trigger("change")
        },
        initFirstOpenCache: function()
        {
            var rows = this.rows,
                inputs = rows.find("input");
            this._firstOpenCache = [];
            var self = this;
            $.each(inputs,function(i, v)
            {
                if($(v).prop("checked") === true)
                    self._firstOpenCache.push(v.value)
            })
        },
        _reset: function()
        {
            this.input.val('').trigger('keyup')
        },
        updateCache: function()
        {
            this.rows = this.instance.menu.find(".ui-multiselect-checkboxes li:not(.ui-multiselect-optgroup-label)");
            this.cache = this.element.children().map(function()
            {
                var self = $(this);
                if(this.tagName.toLowerCase() === "optgroup")
                    self = self.children();
                return self.map(function()
                    {
                        return this.innerHTML.toLowerCase()
                    }).get()
            }).get()
        },
        widget: function()
        {
            return this.wrapper
        },
        destroy: function()
        {
            $.Widget.prototype.destroy.call(this);
            this.input.val('').trigger("keyup");
            this.wrapper.remove()
        }
    })
})(window,jQuery);
(function(window)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    UI.FillSeries = {
        Column: 0,
        Row: 1
    };
    UI.FillType = {
        Direction: 0,
        Linear: 1,
        Growth: 2,
        Date: 3,
        Auto: 4
    };
    UI.FillDateUnit = {
        Day: 0,
        Weekday: 1,
        Month: 2,
        Year: 3
    };
    var floor = Math.floor;
    function __createRange(row, column, rowCount, columnCount)
    {
        return new UI.Range(row,column,rowCount,columnCount)
    }
    function __createDateTimeHelper(date)
    {
        return new UI._DateTimeHelper(date)
    }
    function __isNumber(value)
    {
        return UI.FormatConverter.IsNumber(value)
    }
    function __fromOADate(value)
    {
        return UI._DateTimeHelper.fromOADate(value)
    }
    function FillCachePool(sheet)
    {
        this._sheet = sheet
    }
    function NumberSource(startIndex)
    {
        if(startIndex === undefined || startIndex === null)
            this._startIndex = -1;
        else
            this._startIndex = startIndex;
        this._indexes = [];
        this._innerValues = [];
        this._type = null
    }
    NumberSource.prototype = {
        dataCount: function()
        {
            return this._innerValues.length
        },
        values: function()
        {
            if(this._innerValues.length > 0)
            {
                var values = [];
                for(var i = 0; i < this._innerValues.length; i++)
                {
                    var value = this._innerValues[i];
                    var ret;
                    if(this._type === "object")
                        ret = this.toDateTime(value);
                    else
                        ret = value;
                    values.push(ret)
                }
                return values
            }
            return[]
        },
        indexes2: function()
        {
            var count = this._indexes.length;
            if(count > 0)
            {
                var startIndex = this._startIndex;
                if(startIndex === -1)
                    startIndex = this._indexes[0];
                var value = new Array(1);
                value[0] = new Array(count);
                for(var i = 0; i < count; i++)
                    value[0][i] = this._indexes[i] - startIndex + 1;
                return new GrapeCity.Calc._ConcreteArray(value,count)
            }
            return null
        },
        values2: function()
        {
            var count = this._innerValues.length;
            if(count > 0)
            {
                var value = [this._innerValues];
                return new GrapeCity.Calc._ConcreteArray(value,count)
            }
            return null
        },
        insert: function(insertIndex, index, value)
        {
            if(!__isNumber(value))
                throw new Error("Only works for Numbers");
            if(this._type === undefined || this._type === null)
                if(value instanceof Date)
                    this._type = "date";
                else
                    this._type = "number";
            var ArrayHelper = GrapeCity.UI._ArrayHelper;
            new ArrayHelper(this._indexes).insert(insertIndex,index);
            new ArrayHelper(this._innerValues).insert(insertIndex,this.toDouble(value))
        },
        add: function(index, value)
        {
            if(this._type === undefined || this._type === null)
                if(value instanceof Date)
                    this._type = "date";
                else
                    this._type = "number";
            this._indexes.push(index);
            this._innerValues.push(this.toDouble(value))
        },
        toActualValue: function(value)
        {
            var ret;
            if(this._type === "date")
                ret = this.toDateTime(value);
            else
                ret = value;
            return ret
        },
        toDouble: function(value)
        {
            return UI.FormatConverter.ToDouble(value)
        },
        toDateTime: function(value)
        {
            var ret = null;
            if(value instanceof Date)
                ret = value;
            else
                ret = GrapeCity.Calc.Convert.toDateTime(value);
            return ret
        }
    };
    function bindStandardFunction(e, name)
    {
        function evalateStandardFunction(evaluator, values, indexes, dest)
        {
            var fn = GrapeCity.Calc.Functions.findGlobalFunction(name);
            var result = fn.evaluate([values,indexes,dest]);
            return result[0][0]
        }
        return function()
            {
                return evalateStandardFunction.apply(e,arguments)
            }
    }
    var FormulaEvaluator = {
            TREND: bindStandardFunction(this,"Trend"),
            GROWTH: bindStandardFunction(this,"Growth"),
            FORECAST: bindStandardFunction(this,"Forecast")
        };
    function FillImp(sheet)
    {
        this._worksheet = sheet;
        this._fillCache = new FillCachePool(sheet)
    }
    FillImp.prototype = {
        fillLinear: function(range, series, step, stop)
        {
            if((step === undefined || step === null) && (stop === undefined || stop === null))
                this.seriesTrendFillRange(range,series,UI.FillType.Linear);
            else
                this.seriesFillRange(range,series,UI.FillType.Linear,step,stop,null)
        },
        fillGrowth: function(range, series, step, stop)
        {
            if((step === undefined || step === null) && (stop === undefined || stop === null))
                this.seriesTrendFillRange(range,series,UI.FillType.Growth);
            else
                this.seriesFillRange(range,series,UI.FillType.Growth,step,stop,null)
        },
        fillDate: function(range, series, unit, step, stop)
        {
            this.seriesFillRange(range,series,UI.FillType.Date,step,stop,unit)
        },
        seriesTrendFillRange: function(range, fillSeries, fillType)
        {
            var newRange = this.fixRange(range);
            var row = newRange.row;
            var column = newRange.col;
            var rowCount = newRange.rowCount;
            var columnCount = newRange.colCount;
            if(this.hasSpans(row,column,rowCount,columnCount))
                throw new Error("Range should not have merged cell.");
            var sourceData,
                targetData,
                i;
            if(fillSeries === UI.FillSeries.Row)
                for(var r = row; r < row + rowCount; r++)
                {
                    sourceData = this.getSeriesSource(r,column,1,columnCount,fillSeries);
                    if(sourceData && sourceData.dataCount() > 0)
                    {
                        targetData = this.calcSeriesTrendData(sourceData,columnCount,fillType);
                        if(targetData && targetData.length > 0)
                            for(i = 0; i < targetData.length; i++)
                                this.copyCell(this._worksheet,r,sourceData._indexes[0],r,column + i,sourceData.toActualValue(targetData[i]),fillType)
                    }
                }
            else if(fillSeries === UI.FillSeries.Column)
                for(var c = column; c < column + columnCount; c++)
                {
                    sourceData = this.getSeriesSource(row,c,rowCount,1,fillSeries);
                    if(sourceData && sourceData.dataCount() > 0)
                    {
                        targetData = this.calcSeriesTrendData(sourceData,rowCount,fillType);
                        if(targetData && targetData.length > 0)
                            for(i = 0; i < targetData.length; i++)
                                this.copyCell(this._worksheet,sourceData._indexes[0],c,row + i,c,sourceData.toActualValue(targetData[i]),fillType)
                    }
                }
        },
        getSeriesSource: function(row, column, rowCount, columnCount, fillSeries)
        {
            var numberSource = null,
                value;
            if(fillSeries === UI.FillSeries.Row)
            {
                var c = column + columnCount - 1;
                while(c >= column)
                {
                    value = this._worksheet.getValue(row,c);
                    if(__isNumber(value))
                    {
                        if(!numberSource)
                            numberSource = new NumberSource(column);
                        numberSource.insert(0,c,value)
                    }
                    c--
                }
            }
            else if(fillSeries === UI.FillSeries.Column)
            {
                var r = row + rowCount - 1;
                while(r >= row)
                {
                    value = this._worksheet.getValue(r,column);
                    if(__isNumber(value))
                    {
                        if(!numberSource)
                            numberSource = new NumberSource(row);
                        numberSource.insert(0,r,value)
                    }
                    r--
                }
            }
            return numberSource
        },
        calcSeriesTrendData: function(sourceData, count, type)
        {
            if(sourceData && sourceData.dataCount() > 0)
                if(type === UI.FillType.Linear || type === UI.FillType.Growth)
                {
                    var newValues = [];
                    if(sourceData.dataCount() === 1)
                        if(type === UI.FillType.Linear)
                            sourceData.add(sourceData._indexes[0] + 1,sourceData.toActualValue(sourceData._innerValues[0] + 1));
                        else if(type === UI.FillType.Growth)
                            sourceData.add(sourceData._indexes[0] + 1,sourceData.values()[0]);
                    for(var i = 0; i < count; i++)
                        if(type === UI.FillType.Linear)
                            newValues.push(FormulaEvaluator.TREND(this._worksheet,sourceData.values2(),sourceData.indexes2(),i + 1));
                        else if(type === UI.FillType.Growth)
                            newValues.push(FormulaEvaluator.GROWTH(this._worksheet,sourceData.values2(),sourceData.indexes2(),i + 1));
                    return newValues
                }
            return null
        },
        fillAuto: function(range, series)
        {
            var sheet = this._worksheet;
            var newRange = this.fixRange(range);
            var row = newRange.row;
            var column = newRange.col;
            var rowCount = newRange.rowCount;
            var columnCount = newRange.colCount;
            var sourceRange = this.fixRange(sheet._eventHandler._dragFillStartRange);
            if(sourceRange)
            {
                if(this.hasPartSpans(sourceRange.row,sourceRange.col,sourceRange.rowCount,sourceRange.colCount) || this.hasPartSpans(row,column,rowCount,columnCount))
                    throw new Error("Can not change part of merged cell.");
                this.autoFillRange(sourceRange,rowCount,columnCount,series)
            }
        },
        fixRange: function(range)
        {
            var row = range.row;
            var column = range.col;
            var rowCount = range.rowCount;
            var columnCount = range.colCount;
            if(row === -1)
            {
                row = 0;
                rowCount = this._worksheet.getRowCount()
            }
            if(column === -1)
            {
                column = 0;
                columnCount = this._worksheet.getColumnCount()
            }
            return __createRange(row,column,rowCount,columnCount)
        },
        autoFillRange: function(sourceRange, rowCount, columnCount, fillSeries)
        {
            var nWhole,
                nPartial,
                targetRange,
                i;
            if(fillSeries === UI.FillSeries.Row)
            {
                nWhole = floor(columnCount / sourceRange.colCount);
                nPartial = columnCount % sourceRange.colCount;
                if(nWhole > 1)
                    for(i = 1; i < nWhole; i++)
                    {
                        targetRange = __createRange(sourceRange.row,sourceRange.col + i * sourceRange.colCount,sourceRange.rowCount,sourceRange.colCount);
                        this.copyRange(sourceRange,targetRange,fillSeries,UI.FillType.Auto)
                    }
                if(nWhole > 0 && nPartial > 0)
                {
                    targetRange = __createRange(sourceRange.row,sourceRange.col + nWhole * sourceRange.colCount,sourceRange.rowCount,nPartial);
                    this.copyRange(sourceRange,targetRange,fillSeries,UI.FillType.Auto)
                }
            }
            else if(fillSeries === UI.FillSeries.Column)
            {
                nWhole = floor(rowCount / sourceRange.rowCount);
                nPartial = rowCount % sourceRange.rowCount;
                if(nWhole > 1)
                    for(i = 1; i < nWhole; i++)
                    {
                        targetRange = __createRange(sourceRange.row + i * sourceRange.rowCount,sourceRange.col,sourceRange.rowCount,sourceRange.colCount);
                        this.copyRange(sourceRange,targetRange,fillSeries,UI.FillType.Auto)
                    }
                if(nWhole > 0 && nPartial > 0)
                {
                    targetRange = __createRange(sourceRange.row + nWhole * sourceRange.rowCount,sourceRange.col,nPartial,sourceRange.colCount);
                    this.copyRange(sourceRange,targetRange,fillSeries,UI.FillType.Auto)
                }
            }
        },
        copyRange: function(sourceRange, targetRange, fillSeries, fillType)
        {
            var sheet = this._worksheet;
            var r,
                c,
                sourceColumn,
                sourceRow,
                targetRow,
                targetColumn,
                sourceValue,
                formula,
                valueType,
                sourceSpan,
                trendData = null,
                trendDataType = null;
            if(fillSeries === UI.FillSeries.Row)
                for(r = 0; r < sourceRange.rowCount; r++)
                {
                    trendData = null;
                    trendDataType = null;
                    sourceRow = sourceRange.row + r;
                    targetRow = targetRange.row + r;
                    c = 0;
                    while(c < sourceRange.colCount)
                    {
                        sourceColumn = sourceRange.col + c;
                        targetColumn = targetRange.col + c;
                        sourceValue = null;
                        formula = sheet.getFormula(sourceRow,sourceColumn);
                        if(!formula || formula === "")
                            sourceValue = sheet.getValue(sourceRow,sourceColumn);
                        if(fillType === UI.FillType.Auto)
                            if(__isNumber(sourceValue))
                            {
                                if(!trendData)
                                    trendData = new NumberSource;
                                valueType = null;
                                if(sourceValue instanceof Date)
                                    valueType = "date";
                                else
                                    valueType = "number";
                                if(!trendDataType)
                                    trendDataType = valueType;
                                if(trendDataType === valueType)
                                {
                                    trendData.add(sourceColumn,sourceValue);
                                    c++;
                                    continue
                                }
                            }
                        if(sourceValue && trendData && trendData.dataCount() > 0)
                        {
                            this.autoFillRowTrendValues(sourceRange,targetRange,sourceRow,targetRow,trendData);
                            trendData = null;
                            trendDataType = null;
                            continue
                        }
                        sourceSpan = sheet._spanModel.find(sourceRow,sourceColumn);
                        if(sourceSpan)
                        {
                            if(sourceSpan.row === sourceRow)
                                if(targetColumn < targetRange.col + targetRange.colCount)
                                    this.copyCell(sheet,sourceRow,sourceColumn,targetRow,targetColumn,sourceValue,fillType);
                            c += sourceSpan.colCount
                        }
                        else
                        {
                            if(targetColumn < targetRange.col + targetRange.colCount)
                                this.copyCell(sheet,sourceRow,sourceColumn,targetRow,targetColumn,sourceValue,fillType);
                            c++
                        }
                    }
                    if(trendData && trendData.dataCount() > 0)
                        this.autoFillRowTrendValues(sourceRange,targetRange,sourceRow,targetRow,trendData)
                }
            else if(fillSeries === UI.FillSeries.Column)
                for(c = 0; c < sourceRange.colCount; c++)
                {
                    trendData = null;
                    trendDataType = null;
                    sourceColumn = sourceRange.col + c;
                    targetColumn = targetRange.col + c;
                    r = 0;
                    while(r < sourceRange.rowCount)
                    {
                        sourceRow = sourceRange.row + r;
                        targetRow = targetRange.row + r;
                        sourceValue = null;
                        formula = sheet.getFormula(sourceRow,sourceColumn);
                        if(!formula || formula === "")
                            sourceValue = sheet.getValue(sourceRow,sourceColumn);
                        if(fillType === UI.FillType.Auto)
                            if(__isNumber(sourceValue))
                            {
                                if(!trendData)
                                    trendData = new NumberSource;
                                valueType = null;
                                if(sourceValue instanceof Date)
                                    valueType = "date";
                                else
                                    valueType = "number";
                                if(!trendDataType)
                                    trendDataType = valueType;
                                if(trendDataType === valueType)
                                {
                                    trendData.add(sourceRow,sourceValue);
                                    r++;
                                    continue
                                }
                            }
                        if(sourceValue && trendData && trendData.dataCount() > 0)
                        {
                            this.autoFillColumnTrendValues(sourceRange,targetRange,sourceColumn,targetColumn,trendData);
                            trendData = null;
                            trendDataType = null;
                            continue
                        }
                        sourceSpan = sheet._spanModel.find(sourceRow,sourceColumn);
                        if(sourceSpan)
                        {
                            if(sourceSpan.col === sourceColumn)
                                if(targetRow < targetRange.row + targetRange.rowCount)
                                    this.copyCell(sheet,sourceRow,sourceColumn,targetRow,targetColumn,sourceValue,fillType);
                            r += sourceSpan.rowCount
                        }
                        else
                        {
                            if(targetRow < targetRange.row + targetRange.rowCount)
                                this.copyCell(sheet,sourceRow,sourceColumn,targetRow,targetColumn,sourceValue,fillType);
                            r++
                        }
                    }
                    if(trendData && trendData.dataCount() > 0)
                        this.autoFillColumnTrendValues(sourceRange,targetRange,sourceColumn,targetColumn,trendData)
                }
        },
        autoFillRowTrendValues: function(sourceRange, targetRange, sourceRow, targetRow, trendData)
        {
            var sheet = this._worksheet;
            var count,
                i,
                newV,
                sourceColumn,
                targetColumn;
            if(trendData && trendData.dataCount() > 0)
            {
                var multiplier = (targetRange.col - sourceRange.col) / sourceRange.colCount;
                if(this.isArithmeticProgression(trendData._indexes,trendData._innerValues))
                {
                    var trendValues = trendData.values2();
                    var value = new Array(1);
                    count = trendData.dataCount();
                    value[0] = new Array(count);
                    for(i = 0; i < count; i++)
                        value[0][i] = i + 1;
                    var trendIndexes = new GrapeCity.Calc._ConcreteArray(value,count);
                    for(i = 0; i < count; i++)
                    {
                        newV = FormulaEvaluator.TREND(sheet,trendValues,trendIndexes,count * multiplier + i + 1);
                        sourceColumn = trendData._indexes[i];
                        targetColumn = sourceColumn + multiplier * sourceRange.colCount;
                        if(targetColumn < targetRange.col + targetRange.colCount)
                            this.copyCell(sheet,sourceRow,sourceColumn,targetRow,targetColumn,trendData.toActualValue(newV),UI.FillType.Auto)
                    }
                }
                else
                {
                    var startColumn = trendData._indexes[0];
                    count = trendData._indexes[trendData.dataCount() - 1] - startColumn + 1;
                    if(trendData.dataCount() === 1)
                        trendData.add(trendData._indexes[0] + 1,trendData.toActualValue(trendData._innerValues[0] + 1));
                    for(i = 0; i < count; i++)
                    {
                        newV = FormulaEvaluator.TREND(sheet,trendData.values2(),trendData.indexes2(),count * multiplier + i + 1);
                        sourceColumn = startColumn + i;
                        targetColumn = sourceColumn + multiplier * sourceRange.colCount;
                        if(targetColumn < targetRange.col + targetRange.colCount)
                            this.copyCell(sheet,sourceRow,sourceColumn,targetRow,targetColumn,trendData.toActualValue(newV),UI.FillType.Auto)
                    }
                }
            }
        },
        copyCell: function(sheet, fromRow, fromColumn, toRow, toColumn, newValue, type)
        {
            if(sheet.hasFormula(fromRow,fromColumn) || sheet.hasFormula(toRow,toColumn))
            {
                sheet.setFormula(toRow,toColumn,null);
                if(type === UI.FillType.Direction || type === UI.FillType.Auto)
                {
                    var formula = sheet.getFormula(fromRow,fromColumn);
                    if(formula)
                        sheet._copyFormula(fromRow,fromColumn,toRow,toColumn,1,1)
                }
            }
            sheet.setValue(toRow,toColumn,newValue);
            var style = sheet.getCompositeStyle(fromRow,fromColumn);
            if(!style)
                sheet.setStyle(toRow,toColumn,null,GrapeCity.UI.SheetArea.viewport);
            else
            {
                var clonedStyle = new GrapeCity.UI.Style;
                clonedStyle.copyFrom(style);
                sheet.setStyle(toRow,toColumn,clonedStyle,GrapeCity.UI.SheetArea.viewport)
            }
            if(!sheet._spanModel.isEmpty())
            {
                sheet.removeSpan(toRow,toColumn,GrapeCity.UI.SheetArea.viewport);
                var span = sheet._spanModel.find(fromRow,fromColumn);
                if(span)
                    sheet._addSpanImp(toRow,toColumn,span.rowCount,span.colCount,GrapeCity.UI.SheetArea.viewport)
            }
        },
        autoFillColumnTrendValues: function(sourceRange, targetRange, sourceColumn, targetColumn, trendData)
        {
            var sheet = this._worksheet;
            var count,
                i,
                newV,
                sourceRow,
                targetRow;
            if(trendData && trendData.dataCount() > 0)
            {
                var multiplier = (targetRange.row - sourceRange.row) / sourceRange.rowCount;
                if(this.isArithmeticProgression(trendData._indexes,trendData._innerValues))
                {
                    var trendValues = trendData.values2();
                    var value = new Array(1);
                    count = trendData.dataCount();
                    value[0] = new Array(count);
                    for(i = 0; i < count; i++)
                        value[0][i] = i + 1;
                    var trendIndexes = new GrapeCity.Calc._ConcreteArray(value,count);
                    for(i = 0; i < count; i++)
                    {
                        newV = FormulaEvaluator.TREND(sheet,trendValues,trendIndexes,count * multiplier + i + 1);
                        sourceRow = trendData._indexes[i];
                        targetRow = sourceRow + multiplier * sourceRange.rowCount;
                        if(targetRow < targetRange.row + targetRange.rowCount)
                            this.copyCell(sheet,sourceRow,sourceColumn,targetRow,targetColumn,trendData.toActualValue(newV),UI.FillType.Auto)
                    }
                }
                else
                {
                    var startRow = trendData._indexes[0];
                    count = trendData._indexes[trendData.dataCount() - 1] - startRow + 1;
                    if(trendData.dataCount() === 1)
                        trendData.add(trendData._indexes[0] + 1,trendData.toActualValue(trendData._innerValues[0] + 1));
                    for(i = 0; i < count; i++)
                    {
                        newV = FormulaEvaluator.TREND(sheet,trendData.values2(),trendData.indexes2(),count * multiplier + i + 1);
                        sourceRow = startRow + i;
                        targetRow = sourceRow + multiplier * sourceRange.rowCount;
                        if(targetRow < targetRange.row + targetRange.rowCount)
                            this.copyCell(sheet,startRow,sourceColumn,targetRow,targetColumn,trendData.toActualValue(newV),UI.FillType.Auto)
                    }
                }
            }
        },
        isArithmeticProgression: function(indexes, values)
        {
            if(indexes.length !== values.length)
                return false;
            var count = values.length;
            if(count <= 1)
                return false;
            else if(count === 2)
                return true;
            else
            {
                var indexDiff = indexes[1] - indexes[0];
                var valueDiff = values[1] - values[0];
                for(var i = 2; i < count; i++)
                {
                    if(indexes[i] - indexes[i - 1] !== indexDiff)
                        return false;
                    if(values[i] - values[i - 1] !== valueDiff)
                        return false
                }
                return true
            }
        },
        fillAutobyDirection: function(range, direction)
        {
            var newRange = this.fixRange(range);
            var row = newRange.row;
            var column = newRange.col;
            var rowCount = newRange.rowCount;
            var columnCount = newRange.colCount;
            var sourceRange = this.getDirectionFillSourceRange(row,column,rowCount,columnCount,direction);
            if(sourceRange)
                this.directionFillRange(sourceRange,row,column,rowCount,columnCount,direction)
        },
        directionFillRange: function(sourceRange, row, column, rowCount, columnCount, direction)
        {
            var _fillRangeHasMergedCellErrorMessage = "Target range should not have merged cells.";
            var _fillRangeHaveDifferentSizeErrorMessage = "This operation requires the merged cells to be identically sized.";
            var FillDirection = UI.FillDirection,
                nWhole,
                nPartial,
                targetRange,
                i;
            if(direction === FillDirection.Left)
            {
                if(this.hasSpans(row,column,rowCount,columnCount - sourceRange.colCount))
                    throw new Error(_fillRangeHasMergedCellErrorMessage);
                nWhole = floor(columnCount / sourceRange.colCount);
                nPartial = columnCount % sourceRange.colCount;
                if(nPartial !== 0)
                    throw new Error(_fillRangeHaveDifferentSizeErrorMessage);
                if(nWhole > 1)
                    for(i = 1; i < nWhole; i++)
                    {
                        targetRange = __createRange(sourceRange.row,sourceRange.col - i * sourceRange.colCount,sourceRange.rowCount,sourceRange.colCount);
                        this.copyRange(sourceRange,targetRange,UI.FillSeries.Row,UI.FillType.Direction)
                    }
            }
            else if(direction === FillDirection.Right)
            {
                if(this.hasSpans(row,column + sourceRange.colCount,rowCount,columnCount - sourceRange.colCount))
                    throw new Error(_fillRangeHasMergedCellErrorMessage);
                nWhole = floor(columnCount / sourceRange.colCount);
                nPartial = columnCount % sourceRange.colCount;
                if(nPartial !== 0)
                    throw new Error(_fillRangeHaveDifferentSizeErrorMessage);
                if(nWhole > 1)
                    for(i = 1; i < nWhole; i++)
                    {
                        targetRange = __createRange(sourceRange.row,sourceRange.col + i * sourceRange.colCount,sourceRange.rowCount,sourceRange.colCount);
                        this.copyRange(sourceRange,targetRange,UI.FillSeries.Row,UI.FillType.Direction)
                    }
            }
            else if(direction === FillDirection.Up)
            {
                if(this.hasSpans(row,column,rowCount - sourceRange.rowCount,columnCount))
                    throw new Error(_fillRangeHasMergedCellErrorMessage);
                nWhole = floor(rowCount / sourceRange.rowCount);
                nPartial = rowCount % sourceRange.rowCount;
                if(nPartial !== 0)
                    throw new Error(_fillRangeHaveDifferentSizeErrorMessage);
                if(nWhole > 1)
                    for(i = 1; i < nWhole; i++)
                    {
                        targetRange = __createRange(sourceRange.row - i * sourceRange.rowCount,sourceRange.col,sourceRange.rowCount,sourceRange.colCount);
                        this.copyRange(sourceRange,targetRange,UI.FillSeries.Column,UI.FillType.Direction)
                    }
            }
            else if(direction === FillDirection.Down)
            {
                if(this.hasSpans(row + sourceRange.rowCount,column,rowCount - sourceRange.rowCount,columnCount))
                    throw new Error(_fillRangeHasMergedCellErrorMessage);
                nWhole = floor(rowCount / sourceRange.rowCount);
                nPartial = rowCount % sourceRange.rowCount;
                if(nPartial !== 0)
                    throw new Error(_fillRangeHaveDifferentSizeErrorMessage);
                if(nWhole > 1)
                    for(i = 1; i < nWhole; i++)
                    {
                        targetRange = __createRange(sourceRange.row + i * sourceRange.rowCount,sourceRange.col,sourceRange.rowCount,sourceRange.colCount);
                        this.copyRange(sourceRange,targetRange,UI.FillSeries.Column,UI.FillType.Direction)
                    }
            }
        },
        getDirectionFillSourceRange: function(row, column, rowCount, columnCount, direction)
        {
            var sourceRange = null,
                FillDirection = UI.FillDirection;
            if(direction === FillDirection.Left)
                sourceRange = __createRange(row,column + columnCount - 1,rowCount,1);
            else if(direction === FillDirection.Right)
                sourceRange = __createRange(row,column,rowCount,1);
            else if(direction === FillDirection.Up)
                sourceRange = __createRange(row + rowCount - 1,column,1,columnCount);
            else if(direction === FillDirection.Down)
                sourceRange = __createRange(row,column,1,columnCount);
            return this.inflateCellRange(sourceRange)
        },
        inflateCellRange: function(range)
        {
            var spans = this._worksheet.getSpans();
            if(spans && range)
                range = this._worksheet._cellRangeInflate(spans,range);
            return range
        },
        hasPartSpans: function(row, column, rowCount, columnCount)
        {
            var spans = this._worksheet._spanModel.getEnumerator(row,column,rowCount,columnCount);
            while(spans.moveNext())
            {
                var cs = spans.current();
                if(cs.row < row || cs.row + cs.rowCount > row + rowCount)
                    return true;
                if(cs.col < column || cs.col + cs.colCount > column + columnCount)
                    return true
            }
            return false
        },
        hasSpans: function(row, column, rowCount, columnCount)
        {
            var spans = this._worksheet._spanModel.getEnumerator(row,column,rowCount,columnCount);
            return spans.moveNext()
        },
        seriesFillRange: function(range, fillSeries, type, stepValue, stopValue, dateUnit)
        {
            var newRange = this.fixRange(range);
            var row = newRange.row,
                column = newRange.col,
                rowCount = newRange.rowCount,
                columnCount = newRange.colCount,
                sourceData,
                targetData,
                i;
            if(this.hasSpans(row,column,rowCount,columnCount))
                throw new Error("The range should not have a merged cell.");
            if(fillSeries === UI.FillSeries.Row)
                for(var r = row; r < row + rowCount; r++)
                {
                    sourceData = this.getSeriesSource(r,column,1,1,fillSeries);
                    if(sourceData && sourceData.dataCount() > 0)
                    {
                        targetData = this.calcSeriesData(sourceData,columnCount,type,stepValue,stopValue,dateUnit);
                        if(targetData && targetData.length > 0)
                            for(i = 0; i < targetData.length; i++)
                                this.copyCell(this._worksheet,r,sourceData._indexes[0],r,column + i,sourceData.toActualValue(targetData[i]),type)
                    }
                }
            else if(fillSeries === UI.FillSeries.Column)
                for(var c = column; c < column + columnCount; c++)
                {
                    sourceData = this.getSeriesSource(row,c,1,1,fillSeries);
                    if(sourceData && sourceData.dataCount() > 0)
                    {
                        targetData = this.calcSeriesData(sourceData,rowCount,type,stepValue,stopValue,dateUnit);
                        if(targetData && targetData.length > 0)
                            for(i = 0; i < targetData.length; i++)
                                this.copyCell(this._worksheet,sourceData._indexes[0],c,row + i,c,sourceData.toActualValue(targetData[i]),type)
                    }
                }
        },
        calcSeriesData: function(sourceData, count, type, stepValue, stopValue, dateUnit)
        {
            if(sourceData && sourceData.dataCount() > 0)
            {
                var newValues = [];
                var initValue = sourceData._innerValues[0];
                var currentValue = initValue;
                for(var i = 0; i < count; i++)
                    if(stopValue === undefined || stopValue === null || currentValue <= stopValue)
                    {
                        newValues.push(currentValue);
                        if(type === UI.FillType.Linear)
                            currentValue += stepValue;
                        else if(type === UI.FillType.Growth)
                            currentValue *= stepValue;
                        else if(type === UI.FillType.Date && dateUnit !== undefined && dateUnit !== null)
                            currentValue = this.getNextDateValue(dateUnit,initValue,currentValue,stepValue,i + 1)
                    }
                return newValues
            }
            return null
        },
        getNextDateValue: function(dateUnit, initValue, currentValue, stepValue, nextIndex)
        {
            var nextValue = currentValue,
                date;
            if(dateUnit === UI.FillDateUnit.Day)
            {
                date = __fromOADate(currentValue);
                nextValue = __createDateTimeHelper(date.setDate(date.getDate() + stepValue)).toOADate()
            }
            else if(dateUnit === UI.FillDateUnit.Weekday)
            {
                date = __fromOADate(currentValue);
                var addValue = Math.abs(stepValue);
                while(addValue > 0)
                {
                    if(stepValue > 0)
                        date.setDate(date.getDate() + Math.min(1,addValue));
                    else
                        date.setDate(date.getDate() - Math.min(1,addValue));
                    if(date.getDay() !== 6 && date.getDay() !== 0)
                        addValue -= 1
                }
                nextValue = __createDateTimeHelper(date).toOADate()
            }
            else if(dateUnit === UI.FillDateUnit.Month)
            {
                date = __fromOADate(initValue);
                nextValue = __createDateTimeHelper(date.setMonth(date.getMonth() + floor(nextIndex * stepValue))).toOADate()
            }
            else if(dateUnit === UI.FillDateUnit.Year)
            {
                date = __fromOADate(initValue);
                nextValue = __createDateTimeHelper(date.setFullYear(date.getFullYear() + floor(nextIndex * stepValue))).toOADate()
            }
            return nextValue
        }
    };
    UI.FillImp = FillImp
})(window);
(function(window, $)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    var FillSmartTag = {
            Tag: 0,
            Down: 1
        };
    function GcFillDialog(sheet, fillInfo)
    {
        this.sheet = sheet;
        this.fillInfo = fillInfo;
        this._init()
    }
    function getDomElementString(name, id, content, attrPairs, noCloseTag)
    {
        var tag = "<" + name + " ";
        if(id)
            tag += "id='" + id + "' ";
        if(attrPairs)
            $.each(attrPairs,function(i, v)
            {
                tag += i + "='" + v + "' "
            });
        if(content)
            tag += ">" + content + "</" + name + ">";
        else
            tag += noCloseTag === true ? ">" : "/>";
        return tag
    }
    var domDiv = "div",
        domImg = "img",
        domLabel = "label",
        domInput = "input",
        cssAuto = "auto",
        smartTagID = "gc_smartTag",
        smartTagDownID = "gc_smartTagDown",
        smartMenuID = "gc_smartMenu",
        smartMenuItemClass = "gc_smartMenuItem",
        fillTypeRadioLableClass = "gc_fillTypeRadioLable",
        fillTypeTextClass = "gc_fillTypeText",
        fillTypeRadioClass = "gc_fillTypeRadio";
    GcFillDialog.prototype = {
        _init: function()
        {
            this._creatDom();
            var $smartTag = $("#" + smartTagID),
                $smartMenu = $("#" + smartMenuID);
            this.dialogTag = $smartTag.wijsheetfilterdialog({
                height: cssAuto,
                width: cssAuto,
                minHeight: cssAuto,
                minWidth: cssAuto
            });
            this.dialogTag.removeClass("ui-dialog-content");
            this.dialogTag.parent().removeClass("ui-widget-content");
            this.dialog = $smartMenu.wijsheetfilterdialog({
                height: cssAuto,
                width: cssAuto,
                minHeight: cssAuto,
                minWidth: cssAuto,
                show: cssAuto
            });
            this.dialog.removeClass("ui-dialog-content");
            this.dialog.parent().removeClass("ui-widget-content");
            $smartTag.addClass("ui-widget-header");
            $smartMenu.addClass("ui-widget-content")
        },
        _getMenuItemDomString: function(inputId, inputValue, labelContent, checked)
        {
            var attrs = {
                    name: "gc_fillType",
                    "class": fillTypeRadioClass,
                    type: "radio",
                    value: inputValue
                };
            if(checked)
                attrs.checked = "";
            var divInputCopyCells = getDomElementString(domDiv,null,getDomElementString(domInput,inputId,null,attrs,true),{"class": fillTypeRadioLableClass});
            var divLabelCopyCells = getDomElementString(domDiv,null,getDomElementString(domLabel,null,labelContent,{"class": fillTypeTextClass}));
            return getDomElementString(domDiv,null,divInputCopyCells + divLabelCopyCells,{"class": smartMenuItemClass})
        },
        _getDomString: function()
        {
            var divTag = getDomElementString(domDiv,smartTagID,getDomElementString(domImg,null,null,{
                    style: "vertical-align: bottom",
                    src: GcFillDialog.getImageSrc(FillSmartTag.Tag)
                }) + getDomElementString(domImg,smartTagDownID,null,{
                    style: "vertical-align: bottom",
                    src: GcFillDialog.getImageSrc(FillSmartTag.Down)
                }));
            var divMenu = getDomElementString(domDiv,smartMenuID,this._getMenuItemDomString("gc_smartMenuCopyCells","0","Αντιγραφή κελιών",true) + this._getMenuItemDomString("gc_smartMenuFillSeries","1","Συμπλήρωση σειράς") + this._getMenuItemDomString("gc_smartMenuFillFormattingOnly","2","Αντιγραφή μόνο της μορφοποίησης") + this._getMenuItemDomString("gc_smartMenuFillWithoutFormatting","3","Αντιγραφή χωρίς μορφοποίηση"));
            return divTag + divMenu
        },
        _hasDom: function()
        {
            var dom = window.document.getElementById(smartTagID);
            return!!dom
        },
        _creatDom: function()
        {
            var canvas = this.sheet._getCanvas();
            if(canvas && !this._hasDom())
            {
                var domStr = this._getDomString();
                $(domStr).appendTo(canvas);
                this._prepareLayout()
            }
        },
        _prepareLayout: function()
        {
            $("#" + smartTagDownID).hide();
            var $smartMenu = $("#" + smartMenuID);
            $smartMenu.hide();
            $smartMenu.css({
                width: "270px",
                boxShadow: "1px 2px 5px rgba(0,0,0,0.4)"
            });
            var $smartMenuItemClass = $("." + smartMenuItemClass);
            $smartMenuItemClass.css({
                width: "250px",
                height: "20px"
            });
            $smartMenuItemClass.addClass("ui-state-default");
            var $fillTypeRadioClass = $("." + fillTypeRadioClass);
            $fillTypeRadioClass.css({display: "none"});
            $fillTypeRadioClass.parent().css({
                width: "16px",
                height: "16px",
                margin: "1px",
                float: "left"
            });
            $("." + fillTypeRadioClass + "[checked]").parent().addClass("ui-icon ui-icon-check");
            var $fillTypeTextClass = $("." + fillTypeTextClass);
            $fillTypeTextClass.parent().css({
                paddingTop: "3px",
                paddingLeft: "20px"
            });
            $fillTypeTextClass.css({
                fontSize: "9pt",
                fontFamily: "Arial",
                fontWeight: "normal"
            });
            $("#" + smartTagID).css({
                width: "18px",
                height: "18px",
                padding: "0px",
                overflow: "hidden"
            })
        },
        _registerEvent: function()
        {
            var self = this;
            var $smartTag = $("#" + smartTagID);
            $smartTag.unbind();
            $smartTag.hover(function()
            {
                var isOpen = self.dialog.wijsheetfilterdialog("isOpen");
                if(isOpen)
                    return;
                $(this).css({
                    width: "32px",
                    height: "18px"
                });
                $(this).addClass("ui-state-hover");
                $("#" + smartTagDownID).show()
            },function()
            {
                var isOpen = self.dialog.wijsheetfilterdialog("isOpen");
                if(isOpen)
                    return;
                $(this).css({
                    width: "18px",
                    height: "18px"
                });
                $(this).removeClass("ui-state-hover");
                $("#" + smartTagDownID).hide()
            });
            $smartTag.toggle(function()
            {
                $(this).addClass("ui-state-active");
                self._openMenu()
            },function()
            {
                $(this).removeClass("ui-state-active");
                self._closeMenu()
            });
            var $smartMenuItem = $("." + smartMenuItemClass);
            $smartMenuItem.unbind();
            $smartMenuItem.hover(function()
            {
                $(this).addClass("ui-state-hover")
            },function()
            {
                $(this).removeClass("ui-state-hover")
            });
            $smartMenuItem.click(function()
            {
                $(this).find(":radio")[0].checked = true;
                $(this).find(":radio").change();
                $smartTag.click();
                $smartTag.mouseleave();
                var fillType = parseInt($(this).find(":radio")[0].value,10);
                window.setTimeout(function()
                {
                    self._changeFill(fillType)
                },10)
            });
            var $smartMenuRadio = $("#" + smartMenuID + " :radio");
            $smartMenuRadio.unbind();
            $smartMenuRadio.change(function()
            {
                $("." + fillTypeRadioLableClass).removeClass("ui-icon ui-icon-check");
                $(this).parent().addClass("ui-icon ui-icon-check")
            })
        },
        _changeFill: function(fillType)
        {
            var sheet = this.sheet;
            sheet._skipCloseDragFillSmartTag = true;
            GrapeCity.UI.SpreadActions.undo.call(sheet);
            sheet._skipCloseDragFillSmartTag = false;
            sheet._eventHandler.executeDragFillAction(sheet._eventHandler._currentFillRange,fillType)
        },
        _initData: function()
        {
            var fillType = this.fillInfo.fillType;
            var radio = this.dialog.find(":radio")[fillType];
            if(radio && radio.checked === false)
            {
                radio.checked = true;
                $(radio).change()
            }
        },
        open: function()
        {
            if(this.dialogTag)
            {
                var self = this;
                this._registerEvent();
                this._initData();
                var t = $(this.sheet._getCanvas()).offset();
                var x = this.fillInfo.x;
                var y = this.fillInfo.y;
                x += t.left - 3;
                y += t.top - 3;
                var scrollLeft = $(window.document).scrollLeft();
                var scrollTop = $(window.document).scrollTop();
                x -= scrollLeft;
                y -= scrollTop;
                this.dialogTag.wijsheetfilterdialog({
                    position: [x, y],
                    open: function () { },
                    close: function()
                    {
                        self.close()
                    }
                });
                this.reset();
                this.dialogTag.wijsheetfilterdialog("open")
            }
        },
        _openMenu: function()
        {
            if(this.dialog)
            {
                var self = this;
                var t = $(this.dialogTag).offset();
                var x = t.left - 2.5;
                var y = t.top + 17.5;
                var scrollLeft = $(window.document).scrollLeft();
                var scrollTop = $(window.document).scrollTop();
                x -= scrollLeft;
                y -= scrollTop;
                this.dialog.wijsheetfilterdialog({
                    position: [x,y],
                    modal: false
                });
                this.dialog.wijsheetfilterdialog("open")
            }
        },
        _closeMenu: function()
        {
            if(this.dialog)
                this.dialog.wijsheetfilterdialog("close")
        },
        reset: function()
        {
            if(this.dialog)
            {
                var isOpen = this.dialog.wijsheetfilterdialog("isOpen");
                var $smartTagID = $("#" + smartTagID);
                if(isOpen)
                    $smartTagID.click();
                $smartTagID.mouseleave()
            }
        },
        close: function()
        {
            this.reset();
            if(this.isOpen())
            {
                this.dialog.wijsheetfilterdialog("close");
                this.dialogTag.wijsheetfilterdialog("close")
            }
            if(this.sheet)
                this.sheet._eventHandler._setFocus()
        },
        isOpen: function()
        {
            if(this.dialogTag)
                return this.dialogTag.wijsheetfilterdialog("isOpen");
            return false
        }
    };
    GcFillDialog.getImageSrc = function(state)
    {
        if(state === FillSmartTag.Tag)
            return"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg" + "6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABR" + "mS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOna" + "V/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCA" + "AARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQa" + "PYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8l" + "sKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnU" + "lqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6" + "UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rut" + "u6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH" + "5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+" + "qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7" + "OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aX" + "Dm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRp" + "tTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+" + "3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOp" + "gAABdvkl/FRgAAAFJJREFUeNrclEEKACAIBOfp/Xy7i4WSRnRYBIVRVhFJVIhyEAxllQa5E/wBSnsU6Rza2nugqNmASi57C/KKNg/Iqn+iVWzx6M4bOdUEAAD//wMAAYRMfiNaiqEAAAAASUVORK5CYII=";
        else if(state === FillSmartTag.Down)
            return"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAASCAYAAACXScT7AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVX" + "DjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4" + "EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/" + "EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAES" + "ggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2At" + "qKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDr" + "FiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1" + "akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rf" + "q79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiF" + "I8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgK" + "fep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybu" + "IC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/P" + "bFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwD" + "a0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22" + "gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlw" + "G3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAA" + "Xb5JfxUYAAAGCSURBVHjafNFPKKRxHAbw5/v7GYdBYUQjLm5SkoNykCQHtcx431/KgdKumJvSrAO7Nwc3RQ5kd3OkHJC/hUJREluonVcToqRh3sRFPA6M1Mbhc3z6Pj1fkMRHQNJL0uPeul731lU37o1y49cqHr8GvvgWQRLBsmpM/P0j4XAXiooKcXl1CZDEzl4EJBEwAZBUwWAQsVgsFSRR11gmM8trimSa3" + "WypzZ31l5v2/vfk/4oAcv9aSGyUSz4gg/AIAOET0YQswIQWaNrnH+2OeSaY0BJN2+wDTi/OpCrwkxX1vW8q63p5cnaaB+Z/09u7x0nFJTVMiEajPsNCQaC6Ryb8THKcw/Tikho6zj//0RGUNV6gMZ1H8fmpH5iTHDlwsiOhO7FrN5RdP6aBIUj/pvJ2bkFbkxAzBzELELNCQQqgrJ5ST1/jqmYOJcHa7dYYGV5" + "TrQ3d+vfUU+b7IfrOIRCGBYD0o1VGmaHaB6DZkqvMD2hUfF1UAISkvE/+yqbCZ89+HgBtwgFOrBUzJgAAAABJRU5ErkJggg==";
        return""
    };
    UI._FillSmartTag = FillSmartTag;
    GrapeCity.UI._GcFillDialog = GcFillDialog
})(window,jQuery);
(function(window, $)
{
    "use strict";;
    var const_undefined = "undefined";
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === const_undefined)
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === const_undefined)
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    function GcImageLoader(loadedCallBack)
    {
        this._imageCount = 0;
        this._loadedCount = 0;
        this.onLoadedComplete = loadedCallBack
    }
    GcImageLoader.prototype = {
        addImage: function(imgUrl)
        {
            if(!this._imgs)
                this._imgs = {};
            var imgObj = this._imgs[imgUrl];
            if(!imgObj)
            {
                imgObj = {};
                var imgElement = $("<img/>");
                var self = this;
                imgElement.bind("load",function()
                {
                    self.imageLoaded(imgUrl)
                });
                imgElement.bind("error",function()
                {
                    self.imageLoaded(imgUrl)
                });
                imgElement.bind("abort",function()
                {
                    self.imageLoaded(imgUrl)
                });
                this._imageCount++;
                imgObj.state = false;
                imgObj.img = imgElement;
                this._imgs[imgUrl] = imgObj;
                imgElement[0].src = imgUrl
            }
        },
        getImage: function(imgUrl)
        {
            return this._imgs[imgUrl].img[0]
        },
        getState: function(imgUrl)
        {
            if(!this._imgs)
                return false;
            if(!this._imgs[imgUrl])
                return false;
            return this._imgs[imgUrl].state
        },
        imageLoaded: function(imgUrl)
        {
            this._loadedCount++;
            this._imgs[imgUrl].state = true;
            if(this._loadedCount >= this._imageCount)
                if(this.onLoadedComplete)
                    this.onLoadedComplete()
        }
    };
    UI._GcImageLoader = GcImageLoader
})(window,jQuery);
(function(window, $)
{
    "use strict";;
    var const_undefined = "undefined",
        const_function = "function";
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === const_undefined)
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI == const_undefined)
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    UI.CellBindingSource = function(source)
    {
        this._source = source;
        this.__cellBindingSource__ = true
    };
    UI.CellBindingSource.prototype = {
        setValue: function(path, value)
        {
            if(this._source && path)
            {
                var ko = window.ko;
                var obj = this._source;
                var paths = path.split(".");
                for(var i = 0; i < paths.length; i++)
                {
                    var p = paths[i];
                    if(!obj)
                        break;
                    if(i === paths.length - 1)
                        if(typeof obj[p] === const_function)
                        {
                            if(typeof ko === const_undefined || ko.isWriteableObservable(obj[p]))
                                obj[p](value)
                        }
                        else
                            obj[p] = value;
                    else if(typeof obj[p] === const_function)
                        obj = obj[p]();
                    else
                        obj = obj[p]
                }
            }
        },
        getValue: function(path)
        {
            if(this._source && path)
            {
                var obj = this._source;
                var paths = path.split(".");
                for(var i = 0; i < paths.length; i++)
                {
                    var p = paths[i];
                    if(!obj)
                        return null;
                    if(typeof obj[p] === const_function)
                        obj = obj[p]();
                    else
                        obj = obj[p]
                }
                return obj
            }
            return null
        },
        getSource: function()
        {
            return this._source
        }
    }
})(window,jQuery);
(function(window, $)
{
    "use strict";;
    var const_undefined = "undefined",
        const_string = "string",
        const_boolean = "boolean";
    var $_invalidArg = "Invalid arguments";
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === const_undefined)
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === const_undefined)
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    if(typeof GrapeCity.Calc === const_undefined)
        GrapeCity.Calc = {};
    var Calc = GrapeCity.Calc;
    if(typeof Calc.Functions === const_undefined)
        Calc.Functions = {};
    var Functions = Calc.Functions;
    Functions._builtInFunctions = Functions._builtInFunctions || {};
    if(typeof Functions._defineBuildInFunction === const_undefined)
        Functions._defineBuildInFunction = function(name, fnEvaluate, options)
        {
            if(name === undefined || name === null)
                throw"Invalid function name";
            var fn;
            name = name.toUpperCase();
            if(!Functions._builtInFunctions.hasOwnProperty(name))
            {
                fn = new Functions.Function(name,0,255);
                Functions._builtInFunctions[name] = fn
            }
            else
            {
                fn = Functions._builtInFunctions[name];
                if(!fn)
                {
                    Functions._builtInFunctions[name] = new Functions.Function(name,0,255);
                    fn = Functions[name.toUpperCase()]
                }
                else if(!options || !options.override)
                    throw"Attempt to override function while override is not allowed";
            }
            if(fnEvaluate && typeof fnEvaluate === "function")
                fn.evaluate = fnEvaluate;
            if(options)
                for(var prop in options)
                    if(options.hasOwnProperty(prop) && prop !== 'override')
                        fn[prop] = options[prop];
            return fn
        };
    var def = Functions._defineBuildInFunction;
    function acceptsAny(i)
    {
        return true
    }
    function acceptsNotZero(i)
    {
        return i !== 0
    }
    function acceptsOne(i)
    {
        return i === 1
    }
    function acceptsTwo(i)
    {
        return i === 2
    }
    function acceptsZeroTwo(i)
    {
        return i === 0 || i === 2
    }
    function acceptsZeroOdd(i)
    {
        return i === 0 || i % 2 === 1
    }
    function acceptsThree(i)
    {
        return i === 3
    }
    function isVolatile()
    {
        return true
    }
    function acceptsOneTwo(i)
    {
        return i === 1 || i === 2
    }
    function isBranch()
    {
        return true
    }
    function acceptsZero(i)
    {
        return i === 0
    }
    function __iterate(obj, fn, ctx)
    {
        var Convert = Calc.Convert;
        if(Convert.isError(obj))
        {
            ctx.value = obj;
            return false
        }
        else if(Convert._isCalcReference(obj))
        {
            for(var a = 0; a < obj.getRangeCount(); a++)
                for(var r = 0; r < obj.getRowCount(a); r++)
                    for(var c = 0; c < obj.getColumnCount(a); c++)
                        if(obj instanceof Calc._SheetRangeReference)
                        {
                            for(var i = 0; i < obj.getSheetCount(); i++)
                                if(ctx.includeSubtotal || !obj.isSubtotal(i,a,r,c))
                                    if(fn(obj.getValue(i,a,r,c),ctx) === false)
                                        return false
                        }
                        else if(ctx.includeSubtotal || !obj.isSubtotal(a,r,c))
                            if(fn(obj.getValue(a,r,c),ctx) === false)
                                return false
        }
        else if(Convert._isCalcArray(obj))
        {
            for(var i1 = 0; i1 < obj.length(); i1++)
                if(fn(obj.getValueByIndex(i1),ctx) === false)
                    return false
        }
        else if($.isArray(obj))
            $.each(obj,function(i, v)
            {
                return fn(v,ctx)
            });
        else if(fn(obj,ctx) === false)
            return false;
        return true
    }
    function findTestArgument()
    {
        return 0
    }
    function findBranchArgument(test)
    {
        var Convert = Calc.Convert;
        if(Convert.isError(test))
            return-1;
        var result = false;
        try
        {
            result = Convert.toBool(test)
        }
        catch(err){}
        return result ? 1 : 2
    }
    function KeyValuePair(charKey, intValue)
    {
        this.Key = charKey;
        this.Value = intValue
    }
    var regExpKeyPattern = [/\\/g,/\(/g,/\[/g,/\{/g,/\^/g,/\$/g,/\|/g,/\)/g,/\+/g,/\./g];
    function __createStringcomparisonRegexPattern(s1)
    {
        if(s1 === "" || s1 === undefined || s1 === null)
            return s1;
        for(var i in regExpKeyPattern)
            if(regExpKeyPattern.hasOwnProperty(i))
                s1 = s1.replace(regExpKeyPattern[i],regExpKeyPattern[i].source);
        s1 = s1.replace("~?","{113E2532-EAF5-444c-A5CB-3D7446971C4D}");
        s1 = s1.replace("~*","{E21523B3-0F1F-458f-B547-23D25713D0EC}");
        s1 = s1.replace("?",".");
        s1 = s1.replace("*","((.|\\n)*)");
        s1 = s1.replace("{113E2532-EAF5-444c-A5CB-3D7446971C4D}","\\?");
        s1 = s1.replace("{E21523B3-0F1F-458f-B547-23D25713D0EC}","\\*");
        return s1.toString()
    }
    function mt_abs(args)
    {
        var num;
        var Convert = Calc.Convert;
        if(isNaN(num = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        return Convert.toResult(Math.abs(num))
    }
    function mt_int(args)
    {
        var num = parseFloat(args[0]);
        if(num === undefined || num === null)
            return Calc.Errors.Value;
        return Functions._MathHelper.approxFloor(num)
    }
    function mt_ceiling(args)
    {
        var Convert = Calc.Convert;
        var num = Convert.toDouble(args[0]);
        var sign = Convert.toDouble(args[1]);
        if(isNaN(num) || isNaN(sign))
            return Calc.Errors.Value;
        if(num === 0.0 || sign === 0.0)
            return 0.0;
        if(num > 0.0 && sign < 0.0)
            return Calc.Errors.Number;
        if(num < 0.0 && 0.0 < sign)
        {
            sign = -sign;
            return Functions._MathHelper.approxFloor(num / sign) * sign
        }
        return Functions._MathHelper.approxCeiling(num / sign) * sign
    }
    function mt_combin(args)
    {
        var n = parseFloat(args[0]);
        var k = parseFloat(args[1]);
        if(isNaN(n) || isNaN(k))
            return Calc.Errors.Value;
        if(n < 0.0 || k < 0.0 || n < k)
            return Calc.Errors.Number;
        var result = 1.0;
        k = Math.min(n - k,k);
        for(var i = 1.0; i <= k; i++)
        {
            result *= n - i + 1.0;
            result /= i
        }
        return Calc.Convert.toResult(result)
    }
    function mt_degrees(args)
    {
        var num;
        if(isNaN(num = parseFloat(args[0])))
            return Calc.Errors.Value;
        return 180.0 * num / Math.PI
    }
    function mt_even(args)
    {
        var num = parseFloat(args[0]);
        if(num < 0.0)
        {
            num = Math.floor(num);
            if(num % 2.0 !== 0.0)
                num -= 1.0
        }
        else
        {
            num = Math.ceil(num);
            if(num % 2.0 !== 0.0)
                num += 1.0
        }
        return num
    }
    function mt_fact(args)
    {
        var num = parseInt(args[0],10);
        var result = 1.0;
        if(isNaN(num))
            return Calc.Errors.Value;
        if(num < 0 || 170 < num)
            return Calc.Errors.Number;
        for(var i = 1; i <= num; i++)
            result *= i;
        return result
    }
    function mt_factdouble(args)
    {
        var num = parseInt(args[0],10);
        var result = 1.0;
        if(isNaN(num))
            return Calc.Errors.Value;
        if(num < 0 || 300 < num)
            return Calc.Errors.Number;
        for(var i = num; i > 1; i -= 2)
            result *= i;
        return result
    }
    function mt_ln(args)
    {
        var num = parseFloat(args[0]);
        if(num === undefined || num === null)
            return Calc.Errors.Value;
        if(num <= 0.0)
            return Calc.Errors.Number;
        return Calc.Convert.toResult(Math.log(num))
    }
    function mt_mod(args)
    {
        var num = parseFloat(args[0]);
        var divisor = parseFloat(args[1]);
        if(divisor === 0.0)
            return"DivideByZero";
        return num - divisor * Math.floor(num / divisor)
    }
    function mt_odd(args)
    {
        var num = parseFloat(args[0]);
        if(num < 0.0)
        {
            num = Math.floor(num);
            if(num % 2.0 === 0.0)
                num -= 1.0
        }
        else
        {
            num = Math.ceil(num);
            if(num % 2.0 === 0.0)
                num += 1.0
        }
        return num
    }
    function mt_pi(args)
    {
        return Math.PI
    }
    function mt_power(args)
    {
        var num = parseFloat(args[0]);
        var power = parseFloat(args[1]);
        return Math.pow(num,power)
    }
    function __sumItems(item, ctx)
    {
        var Convert = Calc.Convert;
        if(Convert.isError(item))
        {
            ctx.value = item;
            return false
        }
        else if(Convert._isCalcReference(item) || Convert._isCalcArray(item) || $.isArray(item))
            return __iterate(item,__sumItems,ctx);
        else
        {
            var v = parseFloat(item);
            if(!isNaN(v) && isFinite(item))
                ctx.value += v
        }
        return true
    }
    function mt_sum(args)
    {
        if(!args || args.length < 1)
            throw $_invalidArg;
        var ctx = {
                value: 0,
                includeSubtotal: true
            };
        for(var i = 0; i < args.length; i++)
            if(__iterate(args[i],__sumItems,ctx) === false)
                break;
        return ctx.value
    }
    function mt_sign(args)
    {
        var num = parseFloat(args[0]);
        if(num > 0)
            return 1;
        else if(num === 0)
            return 0;
        else
            return-1
    }
    function __gcdImp(a, b)
    {
        while(b !== 0)
        {
            var r = a % b;
            a = b;
            b = r
        }
        return a
    }
    function mt_gcd(args)
    {
        var result = 0;
        var length = args.length;
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < length; i++)
        {
            if(Convert.isError(args[i]))
                return args[i];
            if(_ArrayHelper.isArrayOrReference(args[i]))
            {
                var len = _ArrayHelper.getLength(args[i]);
                for(var j = 0; j < len; j++)
                {
                    var obj = _ArrayHelper.getValueByIndex(args[i],j);
                    if(Convert.isError(obj))
                        return obj;
                    else
                    {
                        var x = Convert.toInt(obj);
                        if(x < 0)
                            return Calc.Errors.Number;
                        result = __gcdImp(result,x)
                    }
                }
            }
            else
            {
                var x1 = Convert.toInt(args[i]);
                if(x1 < 0)
                    return Calc.Errors.Number;
                result = __gcdImp(result,x1)
            }
        }
        return result
    }
    function mt_lcm(args)
    {
        var result = 1;
        var nums = [];
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            if(Convert.isError(args[i]))
                return args[i];
            if(_ArrayHelper.isArrayOrReference(args[i]))
                for(var j = 0; j < _ArrayHelper.getLength(args[i]); j++)
                {
                    var obj = _ArrayHelper.getValueByIndex(args[i],j);
                    if(Convert.isError(obj))
                        return obj;
                    else
                        nums.push(Convert.toInt(obj))
                }
            else
                nums.push(Convert.toInt(args[i]));
            for(var k = 0; k < nums.length; k++)
            {
                var item = nums[k];
                if(item < 0)
                    return Calc.Errors.Number;
                else if(item === 0)
                    return 0;
                result /= __gcdImp(result,item);
                result *= item
            }
        }
        return result
    }
    function __productImp(args, includeSubtotals)
    {
        var result = 1.0;
        var count = 0;
        var Convert = Calc.Convert;
        var rowCount,
            columnCount,
            row,
            column,
            o;
        for(var i = 0; i < args.length; i++)
        {
            if(Convert.isError(args[i]))
                return args[i];
            if(Convert._isCalcArray(args[i]))
            {
                columnCount = args[i].getColumnCount();
                rowCount = args[i].getRowCount();
                for(column = 0; column < columnCount; column++)
                    for(row = 0; row < rowCount; row++)
                    {
                        o = args[i].getValue(row,column);
                        if(Convert.isNumber(o))
                        {
                            result *= Convert.toDouble(o);
                            count++
                        }
                        else if(Convert.isError(o))
                            return o
                    }
            }
            else if(Convert._isCalcReference(args[i]))
            {
                var rangeCount = args[i].getRangeCount();
                columnCount = 0;
                rowCount = 0;
                for(var area = 0; area < rangeCount; area++)
                {
                    columnCount = args[i].getColumnCount(area);
                    for(column = 0; column < columnCount; column++)
                    {
                        rowCount = args[i].getRowCount(area);
                        for(row = 0; row < rowCount; row++)
                        {
                            if(args[i] instanceof Calc._SheetRangeReference)
                            {
                                for(var sheetIndex = 0; sheetIndex < args[i].getSheetCount(); sheetIndex++)
                                    if(includeSubtotals || !args[i].isSubtotal(sheetIndex,area,row,column))
                                        o = args[i].getValue(sheetIndex,area,row,column)
                            }
                            else if(includeSubtotals || !args[i].isSubtotal(area,row,column))
                                o = args[i].getValue(area,row,column);
                            if(Convert.isNumber(o))
                            {
                                result *= Convert.toDouble(o);
                                count++
                            }
                            else if(Convert.isError(o))
                                return o
                        }
                    }
                }
            }
            else
            {
                if(isNaN(Convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                result *= args[i];
                count++
            }
        }
        return Convert.toResult(count > 0 ? result : 0.0)
    }
    function mt_product(args)
    {
        return __productImp(args,true)
    }
    function mt_sqrt(args)
    {
        var num = parseFloat(args[0]);
        if(num >= 0)
            return Math.sqrt(num);
        else
            return Calc.Errors.Number
    }
    function mt_quotient(args)
    {
        var Convert = Calc.Convert;
        if(Convert.isError(args[0]))
            return args[0];
        if(Convert.isError(args[1]))
            return args[1];
        var num = Convert.toDouble(args[0]);
        var denom = Convert.toDouble(args[1]);
        if(isNaN(num) || isNaN(denom))
            return Calc.Errors.Value;
        if(denom === 0.0)
            return Calc.Errors.DivideByZero;
        return parseInt(num / denom,10)
    }
    function mt_subtotal(args)
    {
        var number = Calc.Convert.toInt(args[0]);
        var list = [];
        for(var i = 1; i < args.length; i++)
            list[i - 1] = args[i];
        var StatHelper = Functions.StatHelper;
        if(number === 1 || number === 101)
            return StatHelper.__averageIncludeSubtotals(list,false);
        else if(number === 2 || number === 102)
            return StatHelper.__countIncludeSubtotals(list,false);
        else if(number === 3 || number === 103)
            return StatHelper.__countaIncludeSubtotals(list,false);
        else if(number === 4 || number === 104)
            return StatHelper.__maxIncludeSubtotals(list,false);
        else if(number === 5 || number === 105)
            return StatHelper.__minIncludeSubtotals(list,false);
        else if(number === 6 || number === 106)
            return __productImp(list,false);
        else if(number === 7 || number === 107)
            return StatHelper.__stdevIncludeSubtotals(list,false);
        else if(number === 8 || number === 108)
            return StatHelper.__stdevpIncludeSubtotals(list,false);
        else if(number === 9 || number === 109)
        {
            if(!list || list.length < 1)
                throw $_invalidArg;
            var ctx = {
                    value: 0,
                    includeSubtotal: false
                };
            for(var i1 = 0; i1 < list.length; i1++)
                if(__iterate(list[i1],__sumItems,ctx) === false)
                    break;
            return ctx.value
        }
        else if(number === 10 || number === 110)
            return StatHelper.__varrIncludeSubtotals(list,false);
        else if(number === 11 || number === 111)
            return StatHelper.__varpIncludeSubtotals(list,false);
        return Calc.Errors.Value
    }
    function mt_floor(args)
    {
        var Convert = Calc.Convert;
        var num = Convert.toDouble(args[0]);
        var sign = Convert.toDouble(args[1]);
        if(isNaN(num) || isNaN(sign))
            return Calc.Errors.Value;
        if(num === 0.0 || sign === 0.0)
            return 0.0;
        if(num > 0.0 && sign < 0.0)
            return Calc.Errors.Number;
        var _MathHelper = Functions._MathHelper;
        if(num < 0.0 && 0.0 < sign)
        {
            sign = -sign;
            return _MathHelper.approxCeiling(num / sign) * sign
        }
        return _MathHelper.approxFloor(num / sign) * sign
    }
    function mt_mround(args)
    {
        var Convert = Calc.Convert;
        var number = Convert.toDouble(args[0]);
        var multiple = Convert.toDouble(args[1]);
        if(isNaN(number) || isNaN(multiple))
            return Calc.Errors.Value;
        if(number === 0.0 || multiple === 0.0)
            return 0.0;
        if(number < 0.0 && 0.0 < multiple || multiple < 0.0 && 0.0 < number)
            return Calc.Errors.Number;
        return Functions._MathHelper.approxFloor(number / multiple + 0.5) * multiple
    }
    function mt_round(args)
    {
        var Convert = Calc.Convert;
        var number = Convert.toDouble(args[0]);
        if(isNaN(number))
            return Calc.Errors.Value;
        var digits = Convert.toInt(args[1]);
        var _MathHelper = Functions._MathHelper;
        var power = _MathHelper.pow10(Math.abs(digits));
        if(digits < 0)
            number /= power;
        else
            number *= power;
        if(number < 0.0)
            number = _MathHelper.approxCeiling(number - 0.5);
        else
            number = _MathHelper.approxFloor(number + 0.5);
        if(digits < 0)
            number *= power;
        else
            number /= power;
        return Convert.toResult(number)
    }
    function mt_rounddown(args)
    {
        var Convert = Calc.Convert;
        var number = Convert.toDouble(args[0]);
        if(isNaN(number))
            return Calc.Errors.Value;
        var digits = Convert.toInt(args[1]);
        var _MathHelper = Functions._MathHelper;
        var power = _MathHelper.pow10(Math.abs(digits));
        if(digits < 0)
            number /= power;
        else
            number *= power;
        if(number < 0.0)
            number = _MathHelper.approxCeiling(number);
        else
            number = _MathHelper.approxFloor(number);
        if(digits < 0)
            number *= power;
        else
            number /= power;
        return Convert.toResult(number)
    }
    function mt_roundup(args)
    {
        var Convert = Calc.Convert;
        var number = Convert.toDouble(args[0]);
        if(isNaN(number))
            return Calc.Errors.Value;
        var digits = Convert.toInt(args[1]);
        var _MathHelper = Functions._MathHelper;
        var power = _MathHelper.pow10(Math.abs(digits));
        if(digits < 0)
            number /= power;
        else
            number *= power;
        if(number < 0.0)
            number = _MathHelper.approxFloor(number);
        else
            number = _MathHelper.approxCeiling(number);
        if(digits < 0)
            number *= power;
        else
            number /= power;
        return Convert.toResult(number)
    }
    function mt_trunc(args)
    {
        var Convert = Calc.Convert;
        var number = Convert.toDouble(args[0]);
        if(isNaN(number))
            return Calc.Errors.Value;
        var digits = Calc._Helper._argumentExists(args,1) ? Convert.toInt(args[1]) : 0;
        var _MathHelper = Functions._MathHelper;
        var power = _MathHelper.pow10(Math.abs(digits));
        if(digits < 0)
            number /= power;
        else
            number *= power;
        if(number < 0.0)
            number = _MathHelper.approxCeiling(number);
        else
            number = _MathHelper.approxFloor(number);
        if(digits < 0)
            number *= power;
        else
            number /= power;
        return Convert.toResult(number)
    }
    function mt_exp(args)
    {
        var d;
        var Convert = Calc.Convert;
        if(isNaN(d = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        return Convert.toResult(Math.exp(d))
    }
    var MathEx = {
            log: function(a, base)
            {
                if(isNaN(a))
                    return a;
                if(isNaN(base))
                    return base;
                if(base !== 1.0 && a === 1.0 || base !== 0.0 && base !== Number.POSITIVE_INFINITY)
                    return Math.log(a) / Math.log(base);
                return NaN
            },
            log10: function(num)
            {
                return MathEx.log(num,10)
            }
        };
    function mt_log(args)
    {
        var Convert = Calc.Convert;
        var num = Convert.toDouble(args[0]);
        if(isNaN(num))
            return Calc.Errors.Value;
        var newBase;
        if(Calc._Helper._argumentExists(args,1))
        {
            newBase = Convert.toDouble(args[1]);
            if(isNaN(newBase))
                return Calc.Errors.Value
        }
        else
            newBase = 10.0;
        if(num <= 0.0 || newBase <= 0.0)
            return Calc.Errors.Number;
        if(newBase === 1.0)
            return Calc.Errors.DivideByZero;
        return Convert.toResult(MathEx.log(num,newBase))
    }
    function mt_log10(args)
    {
        var Convert = Calc.Convert;
        var num = Convert.toDouble(args[0]);
        if(isNaN(num))
            return Calc.Errors.Value;
        if(num <= 0.0)
            return Calc.Errors.Number;
        return Convert.toResult(MathEx.log10(num))
    }
    function _evaluateSingleCriteria(criteria, range, sumRange)
    {
        var sum = 0.0;
        if(criteria === undefined || criteria === null)
            return sum;
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        var criteriaFun = Functions._MathHelper.parseCriteria(criteria);
        if(_ArrayHelper.getRowCount(range) !== _ArrayHelper.getRowCount(sumRange) || _ArrayHelper.getColumnCount(range) !== _ArrayHelper.getColumnCount(sumRange))
            return Calc.Errors.Value;
        for(var j = 0; j < _ArrayHelper.getLength(range); j++)
        {
            var val = _ArrayHelper.getValueByIndex(range,j);
            if(Convert.isError(val))
                return val;
            if(criteriaFun && criteriaFun(val))
            {
                var obj = _ArrayHelper.getValueByIndex(sumRange,j);
                if(Convert.isNumber(obj))
                    sum += Convert.toDouble(obj);
                else if(Convert.isError(obj))
                    return obj
            }
        }
        return Convert.toResult(sum)
    }
    function mt_sumif(args)
    {
        var range = args[0];
        var criteria = args[1];
        if(criteria === undefined || criteria === null)
            throw"criteria";
        var sumRange = Calc._Helper._argumentExists(args,2) ? args[2] : args[0];
        var Convert = Calc.Convert;
        var critieriaRef = args[1];
        var rowCount,
            colCount;
        if(Convert._isCalcReference(critieriaRef))
        {
            rowCount = critieriaRef.getRowCount(0);
            colCount = critieriaRef.getColumnCount(0);
            if(rowCount === 1 && colCount === 1)
                return _evaluateSingleCriteria(critieriaRef.getValue(0,0,0),range,sumRange);
            return new Calc._TernaryCompositeConcreteReference(critieriaRef.getSource(),critieriaRef.getRow(0),critieriaRef.getColumn(0),rowCount,colCount,_evaluateSingleCriteria,range,sumRange)
        }
        var criteriaArray = args[1];
        if(Convert._isCalcArray(criteriaArray))
        {
            rowCount = criteriaArray.getRowCount();
            colCount = criteriaArray.getColumnCount();
            var result = new Calc._ConcreteArray(criteriaArray,colCount);
            for(var r = 0; r < rowCount; r++)
                for(var c = 0; c < colCount; c++)
                    result._value[r][c] = _evaluateSingleCriteria(criteriaArray.getValue(r,c),range,sumRange);
            return result
        }
        return _evaluateSingleCriteria(criteria,range,sumRange)
    }
    function ___assertParams(args)
    {
        if(args.length < 3 || args.length % 2 !== 1)
            return false;
        var rc = GrapeCity.Calc._ArrayHelper.getRowCount(args[0]);
        var cc = GrapeCity.Calc._ArrayHelper.getColumnCount(args[0]);
        for(var i = 1; i < args.length; i = i + 2)
        {
            if(rc !== GrapeCity.Calc._ArrayHelper.getRowCount(args[i]))
                return false;
            if(cc !== GrapeCity.Calc._ArrayHelper.getColumnCount(args[i]))
                return false
        }
        return true
    }
    function mt_sumifs(args)
    {
        if(!___assertParams(args))
            return Calc.Errors.Value;
        var sum = 0.0;
        var sumRange = args[0];
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        var length = _ArrayHelper.getLength(args[0]);
        for(var i = 0; i < length; i++)
        {
            var condition = true;
            for(var j = 1; j < args.length; j = j + 2)
            {
                var range = args[j];
                var criteria = args[j + 1];
                var criteriaFun = Functions._MathHelper.parseCriteria(criteria);
                var obj1 = _ArrayHelper.getValueByIndex(range,i);
                condition = criteriaFun && criteriaFun(obj1);
                if(!condition)
                    break
            }
            if(condition)
            {
                var obj = _ArrayHelper.getValueByIndex(sumRange,i);
                if(Convert.isNumber(obj))
                    sum += Convert.toDouble(obj);
                else if(Convert.isError(obj))
                    return obj
            }
        }
        return Convert.toResult(sum)
    }
    function mt_sumproduct(args)
    {
        var sum = 0.0;
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        var rowCount = _ArrayHelper.getRowCount(args[0]);
        var columnCount = _ArrayHelper.getColumnCount(args[0]);
        for(var i = 1; i < args.length; i++)
        {
            if(rowCount !== _ArrayHelper.getRowCount(args[i]))
                return Calc.Errors.Value;
            if(columnCount !== _ArrayHelper.getColumnCount(args[i]))
                return Calc.Errors.Value
        }
        for(var r = 0; r < rowCount; r++)
            for(var c = 0; c < columnCount; c++)
            {
                var product = 1.0;
                for(var i1 = 0; i1 < args.length; i1++)
                {
                    var o = _ArrayHelper.getValue(args[i1],r,c);
                    if(Convert.isError(o))
                        return o;
                    else if(Convert.isNumber(o))
                        product *= Convert.toDouble(o);
                    else
                        product = 0.0
                }
                sum += product
            }
        return Convert.toResult(sum)
    }
    function mt_sumsq(args)
    {
        var x;
        var sumx2 = 0.0;
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            if(Convert.isError(args[i]))
                return args[i];
            if(_ArrayHelper.isArrayOrReference(args[i]))
                for(var j = 0; j < _ArrayHelper.getLength(args[i]); j++)
                {
                    var o = _ArrayHelper.getValueByIndex(args[i],j);
                    if(Convert.isNumber(o))
                    {
                        x = Convert.toDouble(o);
                        sumx2 += x * x
                    }
                    else if(Convert.isError(o))
                        return o
                }
            else
            {
                if(isNaN(x = Convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                sumx2 += x * x
            }
        }
        return Convert.toResult(sumx2)
    }
    function mt_sumx2my2(args)
    {
        var sum = 0.0;
        var x;
        var y;
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        if(_ArrayHelper.getLength(args[0]) !== _ArrayHelper.getLength(args[1]))
            return Calc.Errors.NotAvailable;
        if(Convert.isError(args[0]))
            return args[0];
        if(Convert.isError(args[1]))
            return args[1];
        if(_ArrayHelper.isArrayOrReference(args[0]))
            for(var i = 0; i < _ArrayHelper.getLength(args[0]); i++)
            {
                var o1 = _ArrayHelper.getValueByIndex(args[0],i);
                var o2 = _ArrayHelper.getValueByIndex(args[1],i);
                if(Convert.isNumber(o1) && Convert.isNumber(o2))
                {
                    x = Convert.toDouble(o1);
                    y = Convert.toDouble(o2);
                    sum += x * x - y * y
                }
                else if(Convert.isError(o1))
                    return o1;
                else if(Convert.isError(o2))
                    return o2
            }
        else
        {
            if(isNaN(x = Convert.toDouble(args[0])) || isNaN(y = Convert.toDouble(args[1])))
                return Calc.Errors.Value;
            sum += x * x - y * y
        }
        return sum
    }
    function mt_sumx2py2(args)
    {
        var sum = 0.0;
        var x;
        var y;
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        if(_ArrayHelper.getLength(args[0]) !== _ArrayHelper.getLength(args[1]))
            return Calc.Errors.NotAvailable;
        if(Convert.isError(args[0]))
            return args[0];
        if(Convert.isError(args[1]))
            return args[1];
        if(_ArrayHelper.isArrayOrReference(args[0]))
            for(var i = 0; i < _ArrayHelper.getLength(args[0]); i++)
            {
                var o1 = _ArrayHelper.getValueByIndex(args[0],i);
                var o2 = _ArrayHelper.getValueByIndex(args[1],i);
                if(Convert.isNumber(o1) && Convert.isNumber(o2))
                {
                    x = Convert.toDouble(o1);
                    y = Convert.toDouble(o2);
                    sum += x * x + y * y
                }
                else if(Convert.isError(o1))
                    return o1;
                else if(Convert.isError(o2))
                    return o2
            }
        else
        {
            if(isNaN(x = Convert.toDouble(args[0])) || isNaN(y = Convert.toDouble(args[1])))
                return Calc.Errors.Value;
            sum += x * x + y * y
        }
        return Convert.toResult(sum)
    }
    function mt_sumxmy2(args)
    {
        var sum = 0.0;
        var x;
        var y;
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        if(_ArrayHelper.getLength(args[0]) !== _ArrayHelper.getLength(args[1]))
            return Calc.Errors.NotAvailable;
        if(Convert.isError(args[0]))
            return args[0];
        if(Convert.isError(args[1]))
            return args[1];
        if(_ArrayHelper.isArrayOrReference(args[0]))
            for(var i = 0; i < _ArrayHelper.getLength(args[0]); i++)
            {
                var o1 = _ArrayHelper.getValueByIndex(args[0],i);
                var o2 = _ArrayHelper.getValueByIndex(args[1],i);
                if(Convert.isNumber(o1) && Convert.isNumber(o2))
                {
                    x = Convert.toDouble(o1);
                    y = Convert.toDouble(o2);
                    sum += (x - y) * (x - y)
                }
                else if(Convert.isError(o1))
                    return o1;
                else if(Convert.isError(o2))
                    return o2
            }
        else
        {
            if(isNaN(x = Convert.toDouble(args[0])) || isNaN(y = Convert.toDouble(args[1])))
                return Calc.Errors.Value;
            sum += (x - y) * (x - y)
        }
        return Convert.toResult(sum)
    }
    function mt_seriessum(args)
    {
        var x;
        var m,
            n;
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        if(isNaN(x = Convert.toDouble(args[0])) || isNaN(n = Convert.toInt(args[1])) || isNaN(m = Convert.toInt(args[2])))
            return Calc.Errors.Value;
        var coeff = args[3];
        var sum = 0.0;
        for(var i = 0; i < _ArrayHelper.getLength(coeff); i++)
        {
            var a;
            if(isNaN(a = Convert.toDouble(_ArrayHelper.getValueByIndex(coeff,i))))
                return Calc.Errors.Value;
            sum += a * Math.pow(x,n + i * m)
        }
        return Convert.toResult(sum)
    }
    function mt_sqrtpi(args)
    {
        var number;
        var Convert = Calc.Convert;
        if(isNaN(number = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        if(number < 0.0)
            return Calc.Errors.Number;
        return Convert.toResult(Math.sqrt(number * Math.PI))
    }
    function mt_radians(args)
    {
        var d;
        if(isNaN(d = Calc.Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        return Math.PI * d / 180.0
    }
    function mt_cos(args)
    {
        var d;
        var Convert = Calc.Convert;
        if(isNaN(d = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        return Convert.toResult(Math.cos(d))
    }
    function mt_acos(args)
    {
        var num;
        var Convert = Calc.Convert;
        if(isNaN(num = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        if(num < -1.0 || 1.0 < num)
            return Calc.Errors.Number;
        return Convert.toResult(Math.acos(num))
    }
    function mt_cosh(args)
    {
        var d;
        var Convert = Calc.Convert;
        if(isNaN(d = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        return Convert.toResult((Math.exp(d) + Math.exp(-d)) / 2)
    }
    function mt_acosh(args)
    {
        var num;
        var Convert = Calc.Convert;
        if(isNaN(num = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        if(num < 1.0)
            return Calc.Errors.Number;
        return Convert.toResult(Math.log(num + Math.sqrt(num * num - 1.0)))
    }
    function mt_sin(args)
    {
        var d;
        var Convert = Calc.Convert;
        if(isNaN(d = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        return Convert.toResult(Math.sin(d))
    }
    function mt_asin(args)
    {
        var num;
        var Convert = Calc.Convert;
        if(isNaN(num = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        if(num < -1.0 || 1.0 < num)
            return Calc.Errors.Number;
        return Convert.toResult(Math.asin(num))
    }
    function mt_sinh(args)
    {
        var d;
        var Convert = Calc.Convert;
        if(isNaN(d = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        return Convert.toResult((Math.exp(d) - Math.exp(-d)) / 2)
    }
    function mt_asinh(args)
    {
        var number;
        var Convert = Calc.Convert;
        if(isNaN(number = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        return Convert.toResult(Math.log(number + Math.sqrt(number * number + 1.0)))
    }
    function mt_tan(args)
    {
        var d;
        var Convert = Calc.Convert;
        if(isNaN(d = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        return Convert.toResult(Math.tan(d))
    }
    function mt_atan(args)
    {
        var d;
        var Convert = Calc.Convert;
        if(isNaN(d = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        return Convert.toResult(Math.atan(d))
    }
    function mt_atan2(args)
    {
        var x,
            y;
        var Convert = Calc.Convert;
        if(isNaN(x = Convert.toDouble(args[0])) || isNaN(y = Convert.toDouble(args[1])))
            return Calc.Errors.Value;
        if(x === 0.0 && y === 0.0)
            return Calc.Errors.DivideByZero;
        return Convert.toResult(Math.atan2(y,x))
    }
    function mt_tanh(args)
    {
        var d;
        var Convert = Calc.Convert;
        if(isNaN(d = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        var x = Math.exp(d);
        var y = Math.exp(-d);
        return Convert.toResult(x - y) / (x + y)
    }
    function mt_atanh(args)
    {
        var number;
        var Convert = Calc.Convert;
        if(isNaN(number = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        if(number <= -1.0 || 1.0 <= number)
            return Calc.Errors.Number;
        return Convert.toResult(Math.log((1.0 + number) / (1.0 - number)) / 2.0)
    }
    function mt_mdeterm(args)
    {
        var Convert = Calc.Convert;
        var array = Convert._toArray(args[0]);
        if(array.getRowCount() !== array.getColumnCount())
            return Calc.Errors.Value;
        var dim = array.getRowCount();
        var a = [];
        for(var i1 = 0; i1 < dim; i1++)
        {
            a[i1] = [dim];
            for(var j1 = 0; j1 < dim; j1++)
            {
                var value = array.getValue(i1,j1);
                if(Convert.isError(value))
                    return value;
                if(!Convert.isNumber(value))
                    return Calc.Errors.Value;
                a[i1][j1] = Convert.toDouble(value)
            }
        }
        var result = 1.0;
        for(var i2 = 0; i2 < dim - 1; i2++)
        {
            if(a[i2][i2] === 0.0)
            {
                var found = false;
                for(var j2 = i2 + 1; !found && j2 < dim; j2++)
                    if(a[j2][i2] !== 0.0)
                    {
                        for(var k1 = i2; k1 < dim; k1++)
                        {
                            var temp = a[i2][k1];
                            a[i2][k1] = a[j2][k1];
                            a[j2][k1] = temp
                        }
                        result *= -1.0;
                        found = true
                    }
                if(!found)
                    return 0.0
            }
            for(var j3 = i2 + 1; j3 < dim; j3++)
                if(a[j3][i2] !== 0.0)
                {
                    var factor = a[j3][i2] / a[i2][i2];
                    for(var k2 = i2; k2 < dim; k2++)
                        a[j3][k2] -= factor * a[i2][k2]
                }
        }
        for(var i = 0; i < dim; i++)
            result *= a[i][i];
        return result
    }
    function mt_minverse(args)
    {
        var Convert = Calc.Convert;
        var array = Convert._toArray(args[0]);
        if(array.getRowCount() !== array.getColumnCount())
            return Calc.Errors.Value;
        var dim = array.getRowCount();
        var a = [];
        var b = [];
        for(var i = 0; i < dim; i++)
        {
            a[i] = [dim];
            for(var j = 0; j < dim; j++)
            {
                var value = array.getValue(i,j);
                if(Convert.isError(value))
                    return value;
                if(!Convert.isNumber(value))
                    return Calc.Errors.Value;
                a[i][j] = Convert.toDouble(value)
            }
        }
        for(var i1 = 0; i1 < dim; i1++)
        {
            b[i1] = [dim];
            for(var j1 = 0; j1 < dim; j1++)
                b[i1][j1] = i1 === j1 ? 1.0 : 0.0
        }
        for(var i2 = 0; i2 < dim; i2++)
        {
            if(a[i2][i2] === 0.0)
            {
                var found = false;
                for(var j2 = i2 + 1; !found && j2 < dim; j2++)
                    if(a[j2][i2] !== 0.0)
                    {
                        for(var k = i2; k < dim; k++)
                        {
                            var temp = a[i2][k];
                            a[i2][k] = a[j2][k];
                            a[j2][k] = temp
                        }
                        for(var k1 = 1; k1 < dim; k1++)
                        {
                            var temp1 = b[i2][k1];
                            b[i2][k1] = b[j2][k1];
                            b[j2][k1] = temp1
                        }
                        found = true
                    }
                if(!found)
                    return Calc.Errors.Number
            }
            for(var j3 = 0; j3 < dim; j3++)
                if(j3 !== i2 && a[j3][i2] !== 0.0)
                {
                    var factor = a[j3][i2] / a[i2][i2];
                    for(var k2 = i2; k2 < dim; k2++)
                        a[j3][k2] -= factor * a[i2][k2];
                    for(var k3 = 0; k3 < dim; k3++)
                        b[j3][k3] -= factor * b[i2][k3]
                }
        }
        for(var i3 = 0; i3 < dim; i3++)
        {
            var factor1 = a[i3][i3];
            for(var k4 = 0; k4 < dim; k4++)
                b[i3][k4] /= factor1
        }
        return new Calc._ConcreteArray(b)
    }
    function mt_mmult(args)
    {
        var Convert = Calc.Convert;
        var arrayA = Convert._toArray(args[0]);
        var arrayB = Convert._toArray(args[1]);
        var rowCountA = arrayA.getRowCount();
        var columnCountA = arrayA.getColumnCount();
        var rowCountB = arrayB.getRowCount();
        var columnCountB = arrayB.getColumnCount();
        if(columnCountA !== rowCountB)
            return Calc.Errors.Value;
        var a = [];
        var b = [];
        var result = [];
        for(var i = 0; i < rowCountA; i++)
        {
            a[i] = [columnCountA];
            for(var j = 0; j < columnCountA; j++)
            {
                var value = arrayA.getValue(i,j);
                if(Convert.isError(value))
                    return value;
                if(!Convert.isNumber(value))
                    return Calc.Errors.Value;
                a[i][j] = Convert.toDouble(value)
            }
        }
        for(var i1 = 0; i1 < rowCountB; i1++)
        {
            b[i1] = [columnCountB];
            for(var j1 = 0; j1 < columnCountB; j1++)
            {
                var value1 = arrayB.getValue(i1,j1);
                if(Convert.isError(value1))
                    return value1;
                if(!Convert.isNumber(value1))
                    return Calc.Errors.Value;
                b[i1][j1] = Convert.toDouble(value1)
            }
        }
        for(var i2 = 0; i2 < rowCountA; i2++)
        {
            result[i2] = [columnCountB];
            for(var j2 = 0; j2 < columnCountB; j2++)
            {
                var sum = 0.0;
                for(var k = 0; k < rowCountB; k++)
                    sum += a[i2][k] * b[k][j2];
                result[i2][j2] = sum
            }
        }
        return new Calc._ConcreteArray(result)
    }
    function __fact(x)
    {
        var result = 1.0;
        for(var i = x; i > 1; i--)
            result *= i;
        return result
    }
    function mt_multinomial(args)
    {
        var sumx = 0;
        var prod = 1.0;
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            if(Convert.isError(args[i]))
                return args[i];
            if(_ArrayHelper.isArrayOrReference(args[i]))
                for(var j = 0; j < _ArrayHelper.getLength(args[i]); j++)
                {
                    var obj = _ArrayHelper.getValueByIndex(args[i],j);
                    if(Convert.isError(obj))
                        return obj;
                    else
                    {
                        var x = Convert.toInt(obj);
                        if(x < 0 || 170 < x)
                            return Calc.Errors.Number;
                        sumx += x;
                        prod *= __fact(x)
                    }
                }
            else
            {
                var x1 = Convert.toInt(args[i]);
                if(x1 < 0 || 170 < x1)
                    return Calc.Errors.Number;
                sumx += x1;
                prod *= __fact(x1)
            }
        }
        if(sumx < 0 || 170 < sumx)
            return Calc.Errors.Number;
        return __fact(sumx) / prod
    }
    function mt_rand(args)
    {
        var random = Math.random();
        var result = -1 + random * 2;
        return Math.abs(result)
    }
    function mt_randbetween(args)
    {
        var Convert = Calc.Convert;
        if(Convert.isError(args[1]))
            return args[1];
        if(Convert.isError(args[0]))
            return args[0];
        var min,
            max;
        if(isNaN(min = Convert.toInt(args[0])) || isNaN(max = Convert.toInt(args[1])))
            return Calc.Errors.Value;
        if(max < min)
            return Calc.Errors.Number;
        var random = Math.random();
        return Convert.toInt(min + random * (max - min + 1))
    }
    function _getInfoList()
    {
        var info = [];
        info.push(new KeyValuePair('M',1000));
        info.push(new KeyValuePair('D',500));
        info.push(new KeyValuePair('C',100));
        info.push(new KeyValuePair('L',50));
        info.push(new KeyValuePair('X',10));
        info.push(new KeyValuePair('V',5));
        info.push(new KeyValuePair('I',1));
        return info
    }
    function mt_roman(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        if(Convert.isError(args[0]))
            return args[0];
        var number = Convert.toInt(args[0]);
        var form = 0;
        if(_Helper._argumentExists(args,1))
        {
            if(Convert.isError(args[1]))
                return args[1];
            form = typeof args[1] === 'boolean' ? args[1] ? 0 : 4 : Convert.toInt(args[1])
        }
        var sb = [];
        if(number < 0 || 3999 < number || form < 0 || 4 < form)
            return Calc.Errors.Value;
        var info = _getInfoList();
        for(var i = 0; i < info.length; i += 2)
        {
            if(2 <= i && info[i - 2].Value - info[i].Value <= number)
            {
                var i1 = i;
                var i2 = i - 2;
                for(var step = 0; step < form && i1 + 1 < info.length && info[i2].Value - info[i1 + 1].Value <= number; step++)
                    i1++;
                sb.push(info[i1].Key);
                sb.push(info[i2].Key);
                number += info[i1].Value;
                number -= info[i2].Value
            }
            if(1 <= i && info[i - 1].Value <= number)
            {
                sb.push(info[i - 1].Key);
                number -= info[i - 1].Value
            }
            if(1 <= i && info[i - 1].Value - info[i].Value <= number)
            {
                var i21 = i;
                var i22 = i - 1;
                for(var step1 = 0; step1 < form && i21 + 1 < info.length && info[i22].Value - info[i21 + 1].Value <= number; step1++)
                    i21++;
                sb.push(info[i21].Key);
                sb.push(info[i22].Key);
                number += info[i21].Value;
                number -= info[i22].Value
            }
            while(info[i].Value <= number)
            {
                sb.push(info[i].Key);
                number -= info[i].Value
            }
        }
        return sb.join('')
    }
    function lg_and(args)
    {
        var result = true;
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            var t = args[i];
            for(var j = 0; j < _ArrayHelper.getLength(t); j++)
            {
                var tmpValue = _ArrayHelper.getValueByIndex(t,j);
                if(tmpValue !== undefined && tmpValue !== null)
                {
                    var val;
                    try
                    {
                        val = Convert.toBool(tmpValue)
                    }
                    catch(err)
                    {
                        return Calc.Errors.Value
                    }
                    result &= val
                }
                else
                    return false
            }
        }
        return!!result
    }
    function lg_or(args)
    {
        var result = false;
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            var t = args[i];
            for(var j = 0; j < _ArrayHelper.getLength(t); j++)
            {
                var tmpValue = _ArrayHelper.getValueByIndex(t,j);
                if(tmpValue !== undefined && tmpValue !== null)
                {
                    var val;
                    try
                    {
                        val = Convert.toBool(tmpValue)
                    }
                    catch(err)
                    {
                        return Calc.Errors.Value
                    }
                    result |= val
                }
            }
        }
        return!!result
    }
    function _not$evaluateSingleValue(value)
    {
        try
        {
            var result = Calc.Convert.toBool(value);
            return!result
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function lg_not(args)
    {
        var Convert = Calc.Convert;
        if(Convert._isCalcReference(args[0]))
        {
            if(args[0].getRangeCount() > 1)
                return Calc.Errors.Value;
            return new Calc._UnaryCompositeConcreteReference(args[0].getSource(),args[0].getRow(0),args[0].getColumn(0),args[0].getRowCount(0),args[0].getColumnCount(0),_not$evaluateSingleValue)
        }
        if(Convert._isCalcArray(args[0]))
        {
            var result = [];
            var rowCount = args[0].getRowCount();
            var columnCount = args[0].getColumnCount();
            for(var i = 0; i < rowCount; i++)
            {
                result[i] = [columnCount];
                for(var j = 0; j < columnCount; j++)
                    result[i][j] = _not$evaluateSingleValue(args[0].getValue(i,j))
            }
            return new Calc._ConcreteArray(result)
        }
        return _not$evaluateSingleValue(args[0])
    }
    function _if$evaluateSingleValue(arg0, arg1, arg2)
    {
        try
        {
            var condition = Calc.Convert.toBool(arg0);
            var result = condition ? arg1 : arg2;
            return result !== undefined && result !== null ? result : 0
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function lg_if(args)
    {
        var arg0 = args[0];
        var arg1 = args[1];
        var _Helper = Calc._Helper;
        var arg2 = _Helper._argumentExists(args,2) ? args[2] : false;
        var val = _Helper.tryExtractToSingleValue(arg0);
        if(val.success)
        {
            arg0 = val.value;
            return _if$evaluateSingleValue(arg0,arg1,arg2)
        }
        val = _Helper.tryExtractToSingleValue(arg1);
        arg1 = val.value;
        var isArg1Simple = val.success;
        val = _Helper.tryExtractToSingleValue(arg2);
        arg2 = val.value;
        var isArg2Simple = val.success;
        var rowCount = arg0.getRowCount();
        var columnCount = arg0.getColumnCount();
        var result = [];
        for(var i = 0; i < rowCount; i++)
        {
            result[i] = [columnCount];
            for(var j = 0; j < columnCount; j++)
                result[i][j] = _if$evaluateSingleValue(arg0.getValue(i,j),isArg1Simple ? arg1 : _Helper.getArrayValue(arg1,i,j),isArg2Simple ? arg2 : _Helper.getArrayValue(arg2,i,j))
        }
        return new Calc._ConcreteArray(result)
    }
    function _iferror$evaluateSingle(value, valueIfError)
    {
        return Calc.Convert.isError(value) ? valueIfError !== undefined && valueIfError !== null ? valueIfError : 0 : value !== undefined && value !== null ? value : 0
    }
    function lg_iferror(args)
    {
        var Convert = Calc.Convert;
        var isError = Convert.isError(args[0]);
        if(!isError && Convert._isCalcReference(args[0]))
        {
            var arg0Ref = args[0];
            if(arg0Ref.getRangeCount() > 1)
                return Calc.Errors.Value;
            return new Calc._BinaryCompositeConcreteReference(arg0Ref.getSource(),arg0Ref.getRow(0),arg0Ref.getColumn(0),arg0Ref.getRowCount(0),arg0Ref.getColumnCount(0),_iferror$evaluateSingle,args[1])
        }
        return isError ? args[1] !== undefined && args[1] !== null ? args[1] : 0 : args[0] !== undefined && args[0] !== null ? args[0] : 0
    }
    function lg_true(args)
    {
        return true
    }
    function lg_false(args)
    {
        return false
    }
    function dt_date(args)
    {
        try
        {
            var Convert = Calc.Convert;
            var year,
                month,
                day;
            if(isNaN(year = Convert.toInt(args[0])) || isNaN(month = Convert.toInt(args[1])) || isNaN(day = Convert.toInt(args[2])))
                return Calc.Errors.Value;
            if(year < 0 || 9999 < year)
                return Calc.Errors.Number;
            if(year <= 1899)
                year += 1900;
            var date = new Date(year,month - 1,day);
            if(date < new Date(1899,11,30))
                return Calc.Errors.Number;
            return date
        }
        catch(err)
        {
            return Calc.Errors.Number
        }
    }
    function dt_time(args)
    {
        var hour,
            minute,
            second;
        try
        {
            var Convert = Calc.Convert;
            if(isNaN(hour = Convert.toInt(args[0])) || isNaN(minute = Convert.toInt(args[1])) || isNaN(second = Convert.toInt(args[2])))
                return Calc.Errors.Value;
            var time = GrapeCity.UI._DateTimeHelper.fromOADate(0);
            time.setHours(hour);
            time.setMinutes(minute);
            time.setSeconds(second);
            time.setMilliseconds(0);
            if(time.getHours() < 0 && time.getMinutes() < 0 && time.getSeconds() < 0)
                return Calc.Errors.Number;
            return time
        }
        catch(err)
        {
            return Calc.Errors.Number
        }
    }
    function _isLeapYear(year)
    {
        return year % 400 === 0 || year % 4 === 0 && year % 100 !== 0 || year === 1900
    }
    function dt_datevalue(args)
    {
        var text = Calc.Convert.toString(args[0]);
        if(text === undefined || text === null || text === '')
            return Calc.Errors.Value;
        try
        {
            var date = UI._DateTimeHelper.parseLocale(text);
            var year = date.getFullYear();
            if(year < 1900)
                return Calc.Errors.Value;
            var month = date.getMonth();
            var day = date.getDate();
            var days = 0;
            for(var i = 1900; i < year; i++)
                if(_isLeapYear(i))
                    days += 366;
                else
                    days += 365;
            for(var i1 = 0; i1 < month; i1++)
                switch(i1)
                {
                    case 0:
                    case 2:
                    case 4:
                    case 6:
                    case 7:
                    case 9:
                    case 11:
                        days += 31;
                        break;
                    case 1:
                        if(_isLeapYear(year))
                            days += 29;
                        else
                            days += 28;
                        break;
                    case 3:
                    case 5:
                    case 8:
                    case 10:
                        days += 30;
                        break
                }
            days += day;
            return days
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function __parseTime(text)
    {
        try
        {
            return UI._DateTimeHelper.parseLocale(text)
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function dt_timevalue(args)
    {
        var text = Calc.Convert.toString(args[0]);
        if(text === undefined || text === null || text === '')
            return Calc.Errors.Value;
        try
        {
            var time = __parseTime(text);
            var totalSeconds = (time.getHours() * 60 + time.getMinutes()) * 60 + time.getSeconds();
            var allSecondsADay = 24 * 3600;
            return totalSeconds / allSecondsADay
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function dt_now(args)
    {
        return new Date
    }
    function dt_today(args)
    {
        var date = new Date;
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        date.setMilliseconds(0);
        return date
    }
    function dt_hour(args)
    {
        try
        {
            var dt = Calc.Convert.toDateTime(args[0]);
            return dt.getHours()
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function dt_minute(args)
    {
        try
        {
            var dt = Calc.Convert.toDateTime(args[0]);
            return dt.getMinutes()
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function dt_second(args)
    {
        try
        {
            var dt = Calc.Convert.toDateTime(args[0]);
            return dt.getSeconds()
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function dt_day(args)
    {
        try
        {
            var dt = Calc.Convert.toDateTime(args[0]);
            return dt.getDate()
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function dt_month(args)
    {
        try
        {
            var dt = Calc.Convert.toDateTime(args[0]);
            return dt.getMonth() + 1
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function dt_year(args)
    {
        try
        {
            var dt = Calc.Convert.toDateTime(args[0]);
            return dt.getFullYear()
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function _getDayOfYear(date)
    {
        var year = date.getFullYear();
        var month = date.getMonth();
        var day = date.getDate();
        var days = 0;
        for(var i = 0; i < month; i++)
            switch(i)
            {
                case 0:
                case 2:
                case 4:
                case 6:
                case 7:
                case 9:
                case 11:
                    days += 31;
                    break;
                case 1:
                    if(_isLeapYear(year))
                        days += 29;
                    else
                        days += 28;
                    break;
                case 3:
                case 5:
                case 8:
                case 10:
                    days += 30;
                    break
            }
        days += day;
        return days
    }
    function _getWeekOfYear(date, type)
    {
        var days = _getDayOfYear(date);
        var dayOfFirstDay = new Date(date.getFullYear(),0,1).getDay();
        if(type === 2)
        {
            dayOfFirstDay -= 1;
            if(dayOfFirstDay < 0)
                dayOfFirstDay = 6
        }
        var dayDiff = days - 1 - (6 - dayOfFirstDay);
        if(dayDiff < 0)
            dayDiff = 0;
        return 1 + Calc.Convert.toInt(dayDiff / 7) + (dayDiff % 7 !== 0 ? 1 : 0)
    }
    function dt_weeknum(args)
    {
        var date;
        var Convert = Calc.Convert;
        try
        {
            date = Convert.toDateTime(args[0])
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
        var type;
        if(isNaN(type = Convert.toInt(Calc._Helper._argumentExists(args,1) ? args[1] : 1)))
            return Calc.Errors.Value;
        var cal;
        switch(type)
        {
            case 1:
            case 2:
                return _getWeekOfYear(date,type);
            default:
                return Calc.Errors.Number
        }
    }
    function dt_weekday(args)
    {
        var dt;
        try
        {
            dt = Calc.Convert.toDateTime(args[0])
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
        var type;
        if(isNaN(type = Calc.Convert.toInt(Calc._Helper._argumentExists(args,1) ? args[1] : 1)))
            return Calc.Errors.Value;
        var result;
        switch(type)
        {
            case 1:
                result = dt.getDay() + 1;
                break;
            case 2:
                if(dt.getDay() === 0)
                    result = 7;
                else
                    result = dt.getDay();
                break;
            case 3:
                if(dt.getDay() === 0)
                    result = 6;
                else
                    result = dt.getDay() - 1;
                break;
            default:
                return Calc.Errors.Number
        }
        return result
    }
    function dt_edate(args)
    {
        var startDate;
        var months;
        var Convert = Calc.Convert;
        try
        {
            startDate = Convert.toDateTime(args[0])
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
        if(isNaN(months = Convert.toInt(args[1])))
            return Calc.Errors.Value;
        startDate.setMonth(startDate.getMonth() + months);
        return startDate
    }
    function _getDaysInMonth(year, month)
    {
        switch(month)
        {
            case 0:
            case 2:
            case 4:
            case 6:
            case 7:
            case 9:
            case 11:
                return 31;
            case 1:
                return _isLeapYear(year) ? 29 : 28;
            case 3:
            case 5:
            case 8:
            case 10:
                return 30
        }
    }
    function dt_eomonth(args)
    {
        var startDate;
        var months;
        var Convert = Calc.Convert;
        try
        {
            startDate = Convert.toDateTime(args[0])
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
        if(isNaN(months = Convert.toInt(args[1])))
            return Calc.Errors.Value;
        startDate.setMonth(startDate.getMonth() + months);
        var days = _getDaysInMonth(startDate.getFullYear(),startDate.getMonth());
        startDate.setDate(days);
        return startDate
    }
    function _compareDateTime(date1, date2)
    {
        return date1 - date2
    }
    function _containsDate(array, date)
    {
        var length = array.length;
        for(var i = 0; i < length; i++)
        {
            var current = array[i];
            if(_compareDateTime(current,date) === 0)
                return true
        }
        return false
    }
    function dt_workday(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper,
            _ArrayHelper = Calc._ArrayHelper;
        var date = Convert.toDateTime(args[0]);
        var days = Convert.toInt(args[1]);
        var cnt = _Helper._argumentExists(args,2) ? _ArrayHelper.getLength(args[2]) : 0;
        var values = [];
        for(var k = 0; k < cnt; k++)
            values.push(Convert.toDateTime(_ArrayHelper.getValueByIndex(args[2],k)));
        var array = [];
        array[0] = date;
        array[1] = 3;
        var o = dt_weekday(array);
        if(Convert.isError(o))
            return o;
        var weekdayValue = Convert.toInt(o);
        for(; days < 0; ++days)
        {
            try
            {
                date.setDate(date.getDate() - 1)
            }
            catch(err)
            {
                return Calc.Errors.Number
            }
            if(weekdayValue === 0)
                weekdayValue = 6;
            else
                weekdayValue--;
            if(weekdayValue === 5 || weekdayValue === 6)
                days--;
            else if(_containsDate(values,date))
                days--
        }
        for(; days > 0; --days)
        {
            try
            {
                date.setDate(date.getDate() + 1)
            }
            catch(err1)
            {
                return Calc.Errors.Number
            }
            if(weekdayValue === 6)
                weekdayValue = 0;
            else
                weekdayValue++;
            if(weekdayValue === 5 || weekdayValue === 6)
                days++;
            else if(_containsDate(values,date))
                days++
        }
        return new GrapeCity.UI._DateTimeHelper(date).toOADate()
    }
    function dt_days360(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var startDate = Convert.toDateTime(args[0]);
        var endDate = Convert.toDateTime(args[1]);
        var method = _Helper._argumentExists(args,2) ? Convert.toBool(args[2]) : false;
        var startDay = startDate.getDate();
        var endDay = endDate.getDate();
        var startMonth = startDate.getMonth();
        var endMonth = endDate.getMonth();
        var StartYear = startDate.getFullYear();
        var endYear = endDate.getFullYear();
        if(method)
        {
            endDay = endDay === 31 ? 30 : endDay;
            startDay = startDay === 31 ? 30 : startDay
        }
        else
        {
            startDay = startDay === 31 ? 30 : startDay;
            if(endDay === 31)
                if(startDay < 30)
                {
                    endDay = 1;
                    endMonth++;
                    if(endMonth > 12)
                    {
                        endMonth = 1;
                        endYear++
                    }
                }
                else
                    endDay = 30
        }
        return((endYear - StartYear) * 12 + (endMonth - startMonth)) * 30 + (endDay - startDay)
    }
    function _isWeekEnd(date)
    {
        return date.getDay() === 6 || date.getDay() === 0
    }
    function _removeHolidaies(args, start_serial, end_serial, res)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper,
            _ArrayHelper = Calc._ArrayHelper;
        var cnt = _Helper._argumentExists(args,2) ? _ArrayHelper.getLength(args[2]) : 0;
        var list = [];
        for(var k = 0; k < cnt; k++)
        {
            var tmpDateTime = Convert.toDateTime(_ArrayHelper.getValueByIndex(args[2],k));
            var tmpDateNum = new GrapeCity.UI._DateTimeHelper(tmpDateTime).toOADate();
            if(!_containsDate(list,tmpDateTime) && !_isWeekEnd(tmpDateTime) && tmpDateNum >= start_serial && tmpDateNum <= end_serial)
                list.push(tmpDateTime)
        }
        res -= list.length;
        return res
    }
    function _removeWeekends(start_date, end_date, res)
    {
        var Convert = Calc.Convert,
            DateTimeHelper = UI._DateTimeHelper;
        var offsetInOneWeek = Convert.toInt(new DateTimeHelper(end_date).toOADate() - new DateTimeHelper(start_date).toOADate()) % 7;
        var weekDays = Convert.toInt(dt_weekday([start_date,2]) + offsetInOneWeek);
        offsetInOneWeek = weekDays > 5 ? weekDays - 5 : 0;
        var maxOffset = start_date.getDay() === 0 ? 1 : 2;
        offsetInOneWeek = offsetInOneWeek > maxOffset ? maxOffset : offsetInOneWeek;
        res -= offsetInOneWeek;
        var weekends = Convert.toInt(res / 7 * 2);
        res -= weekends;
        return res
    }
    function dt_networkdays(args)
    {
        var Convert = Calc.Convert,
            DateTimeHelper = UI._DateTimeHelper;
        var start_date = Convert.toDateTime(args[0]);
        var end_date = Convert.toDateTime(args[1]);
        var start_serial = Convert.toInt(new DateTimeHelper(start_date).toOADate());
        var end_serial = Convert.toInt(new DateTimeHelper(end_date).toOADate());
        var isNegative = false;
        if(start_serial > end_serial)
        {
            var tmpDate = start_date;
            var tmp = start_serial;
            start_serial = end_serial;
            end_serial = tmp;
            start_date = end_date;
            end_date = tmpDate;
            isNegative = true
        }
        var res = end_serial - start_serial + 1;
        res = _removeWeekends(start_date,end_date,res);
        if(res <= 0)
            return 0;
        res = _removeHolidaies(args,start_serial,end_serial,res);
        res = isNegative ? -res : res;
        return res
    }
    function _yearfracImp(from, to, basis)
    {
        var Convert = Calc.Convert,
            _FinancialHelper = Functions._FinancialHelper;
        var days = _FinancialHelper.days_between_basis(from,to,basis);
        var peryear;
        if(days < 0)
        {
            var tmp;
            days = -days;
            tmp = from;
            from = to;
            to = tmp
        }
        if(basis === 1)
        {
            var y1 = from.getFullYear();
            var y2 = to.getFullYear();
            var d1,
                d2;
            var feb29s,
                years;
            d1 = from;
            d1.setFullYear(d1.getFullYear() + 1);
            if(_compareDateTime(to,d1) > 0)
            {
                years = y2 + 1 - y1;
                d1 = new Date(y1,0,1);
                d2 = new Date(y2 + 1,0,1);
                var DateTimeHelper = UI._DateTimeHelper;
                feb29s = Convert.toInt(new DateTimeHelper(d2).toOADate() - new DateTimeHelper(d1).toOADate()) - 365 * (y2 + 1 - y1)
            }
            else
            {
                years = 1;
                if(_isLeapYear(y1) && from.getMonth() < 3 || _isLeapYear(y2) && to.getMonth() * 0x100 + to.getDate() >= 2 * 0x100 + 29)
                    feb29s = 1;
                else
                    feb29s = 0
            }
            var d = Convert.toDouble(feb29s) / Convert.toDouble(years);
            peryear = 365.0 + d
        }
        else
            peryear = _FinancialHelper.annual_year_basis(new Date,basis);
        return days / peryear
    }
    function dt_yearfrac(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var startDate = Convert.toDateTime(args[0]);
        var endDate = Convert.toDateTime(args[1]);
        var basis = _Helper._argumentExists(args,2) ? Convert.toInt(args[2]) : 0;
        if(basis < 0 || basis > 4)
            return Calc.Errors.Number;
        return _yearfracImp(startDate,endDate,basis)
    }
    Functions._DateTimeHelper = {
        days360: dt_days360,
        yearfrac: dt_yearfrac
    };
    function _calcDateDifFunction()
    {
        var builtinProcedure = [];
        builtinProcedure[0] = function(startDate, endDate)
        {
            return endDate.getFullYear() - startDate.getFullYear() + (endDate.getMonth() < startDate.getMonth() || endDate.getMonth() === startDate.getMonth() && endDate.getDate() < startDate.getDate() ? -1 : 0)
        };
        builtinProcedure[1] = function(startDate, endDate)
        {
            return 12 * (endDate.getFullYear() - startDate.getFullYear()) + (endDate.getMonth() - startDate.getMonth()) + (endDate.getDate() < startDate.getDate() ? -1 : 0)
        };
        builtinProcedure[2] = function(startDate, endDate)
        {
            return(endDate.getTime() - startDate.getTime()) / (1000 * 3600 * 24)
        };
        builtinProcedure[3] = function(startDate, endDate)
        {
            var date = new Date(endDate.getFullYear(),endDate.getMonth() + endDate.getDate() < startDate.getDate() ? -1 : 0,startDate.getDate());
            return(endDate.getTime() - date.getTime()) / (1000 * 3600 * 24)
        };
        builtinProcedure[4] = function(startDate, endDate)
        {
            return endDate.getMonth() - startDate.getMonth() + (endDate.getMonth() < startDate.getMonth() || endDate.getMonth() === startDate.getMonth() && endDate.getDate() < startDate.getDate() ? 12 : 0) + (endDate.getDate() < startDate.getDate() ? -1 : 0)
        };
        builtinProcedure[5] = function(startDate, endDate)
        {
            var date = new Date(endDate.getFullYear() + (endDate.getMonth() < startDate.getMonth() || endDate.getMonth() === startDate.getMonth() && endDate.getDate() < startDate.getDate() ? -1 : 0),startDate.getMonth(),startDate.getDate());
            return(endDate - date) / (1000 * 3600 * 24)
        };
        return builtinProcedure
    }
    function _getValueFromBuiltinProcedure(builtinProcedure, unit)
    {
        var index;
        switch(unit)
        {
            case"Y":
                index = 0;
                break;
            case"M":
                index = 1;
                break;
            case"D":
                index = 2;
                break;
            case"MD":
                index = 3;
                break;
            case"YM":
                index = 4;
                break;
            case"YD":
                index = 5;
                break;
            default:
                throw"NotSupportException";
        }
        return builtinProcedure[index]
    }
    function dt_datedif(args)
    {
        var Convert = Calc.Convert;
        var startDate = Convert.toDateTime(args[0]);
        var endDate = Convert.toDateTime(args[1]);
        var unit = Convert.toString(args[2]).toLocaleUpperCase();
        if(endDate < startDate)
            return Calc.Errors.Number;
        var builtinProcedure = _calcDateDifFunction();
        var procedure = _getValueFromBuiltinProcedure(builtinProcedure,unit);
        if(procedure)
            try
            {
                return procedure(startDate,endDate)
            }
            catch(err)
            {
                return Calc.Errors.Value
            }
        return Calc.Errors.Number
    }
    function _isControlChar(ch)
    {
        return 0x0 <= ch && ch <= 0x1F || 0x7F === ch || 0x80 <= ch && ch <= 0x9F
    }
    function tx_clean(args)
    {
        var text = Calc.Convert.toString(args[0]);
        var sb = [];
        for(var i = 0; i < text.length; i++)
            if(!_isControlChar(text.charCodeAt(i)))
                sb.push(text[i]);
        return sb.join("")
    }
    function _isWhiteSpace(ch)
    {
        return ch === ' ' || ch === '\t' || ch === '\n'
    }
    function tx_trim(args)
    {
        var text = Calc.Convert.toString(args[0]).trim();
        var str = [];
        var bAddWhiteSp = true;
        for(var i = 0; i < text.length; i++)
        {
            var ch = text.charAt(i);
            if(!_isWhiteSpace(ch) || bAddWhiteSp)
                str.push(ch);
            bAddWhiteSp = !_isWhiteSpace(ch)
        }
        return str.join("")
    }
    function _addCommas(value)
    {
        var str = value.toString();
        var prefix = "";
        if(value < 0)
        {
            str = str.substr(1);
            prefix = "-"
        }
        var splits = str.split(".");
        if(splits.length < 1 || splits.length > 2)
            return Calc.Errors.Value;
        var result = [];
        if(splits.length === 2)
        {
            result.push(splits[1]);
            result.push(".")
        }
        str = splits[0];
        for(var i = str.length - 1 - 2; i >= 0; i = i - 3)
        {
            result.push(str.substr(i,3));
            if(i > 0)
                result.push(",")
        }
        result.push(str.substring(0,i + 3));
        result.reverse();
        return prefix + result.join("")
    }
    function _toCurrency(value)
    {
        var commasValue = _addCommas(value);
        if(value < 0)
            commasValue = commasValue.substr(1);
        var result = [];
        result.push("$");
        result.push(commasValue);
        if(value < 0)
        {
            result.push(")");
            result.unshift("(")
        }
        return result.join("")
    }
    function tx_dollar(args)
    {
        var number;
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        if(isNaN(number = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        var decimals = _Helper._argumentExists(args,1) ? Convert.toInt(args[1]) : 2;
        if(decimals > 99)
            return Calc.Errors.Value;
        number = Convert.toDouble(mt_round([number,decimals]));
        return _toCurrency(number)
    }
    function tx_fixed(args)
    {
        var number;
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        if(isNaN(number = Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        var decimals = _Helper._argumentExists(args,1) ? Convert.toInt(args[1]) : 2;
        var noCommas = _Helper._argumentExists(args,2) ? Convert.toBool(args[2]) : false;
        var list = new Array(2);
        var divisor = 0;
        if(decimals < 0)
        {
            divisor = Convert.toInt(Math.pow(10,Math.abs(decimals)));
            number /= divisor;
            list[0] = number;
            list[1] = 0
        }
        else
        {
            list[0] = number;
            list[1] = decimals
        }
        number = Convert.toDouble(mt_round(list));
        if(decimals < 0)
            number *= divisor;
        if(noCommas)
            return number.toString();
        else
            return _addCommas(number)
    }
    function _removeColorFormat(format)
    {
        if(format.indexOf("[") !== -1 && format.indexOf("]") !== -1)
        {
            var lpIndex = format.indexOf("[");
            var rpIndex = format.indexOf("]",lpIndex);
            while(lpIndex !== -1 && rpIndex !== -1)
            {
                var before = format.substring(0,lpIndex - 1);
                var after = format.substring(rpIndex + 1);
                format = before.concat(after);
                lpIndex = format.indexOf("[");
                rpIndex = lpIndex === -1 ? -1 : format.indexOf("]",lpIndex)
            }
        }
        return format
    }
    function tx_text(args)
    {
        var value = args[0];
        var Convert = Calc.Convert,
            StandardDateTimeFormatter = UI._StandardDateTimeFormatter,
            StandardNumberFormatter = UI._StandardNumberFormatter;
        var format = Calc.Convert.toString(args[1]);
        if(format === "" || format === null || format === undefined)
            return Convert.toString(value);
        if(value === null || value === undefined)
            value = 0;
        format = _removeColorFormat(format);
        try
        {
            var formatter = new StandardDateTimeFormatter(format);
            if(!formatter.EvaluateFormat(format))
            {
                formatter = new StandardNumberFormatter(format);
                if(!formatter.EvaluateFormat(format))
                    formatter = new UI.GeneralFormatter(format)
            }
            return formatter.Format(value)
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function tx_value(args)
    {
        var Convert = Calc.Convert,
            DateTimeHelper = UI._DateTimeHelper;
        var text = Convert.toString(args[0]);
        var dt = DateTimeHelper.parseLocale(text);
        if(dt !== undefined && dt !== null)
        {
            var result = new DateTimeHelper(dt).toOADate();
            if(!Convert.isError(result))
                return result;
            else
                return dt_timevalue([dt])
        }
        var d;
        if(!isNaN(d = parseFloat(text)))
            return d;
        return Calc.Errors.Value
    }
    function tx_lower(args)
    {
        var text = Calc.Convert.toString(args[0]);
        return text.toLowerCase()
    }
    function tx_upper(args)
    {
        var text = Calc.Convert.toString(args[0]);
        return text.toUpperCase()
    }
    function tx_char(args)
    {
        var number = Calc.Convert.toInt(args[0]);
        if(number < 1 || 255 < number)
            return Calc.Errors.Value;
        return String.fromCharCode(number)
    }
    function tx_code(args)
    {
        var text = Calc.Convert.toString(args[0]);
        if(text === "" || text === undefined || text === null)
            return Calc.Errors.Value;
        try
        {
            return text[0].charCodeAt(0)
        }
        catch(err)
        {
            return Calc.Errors.Value
        }
    }
    function tx_replace(args)
    {
        var Convert = Calc.Convert;
        var oldText = Convert.toString(args[0]);
        var start = Convert.toInt(args[1]);
        var length = Convert.toInt(args[2]);
        var newText = Convert.toString(args[3]);
        if(start < 1 || length < 0)
            return Calc.Errors.Value;
        start = Math.min(start,oldText.length + 1);
        length = Math.min(length,oldText.length - start + 1);
        var before = oldText.substring(0,start - 1);
        var after = oldText.substr(start - 1 + length);
        return before.concat(newText).concat(after)
    }
    function tx_substitute(args)
    {
        var Convert = Calc.Convert;
        var searchtext = Convert.toString(args[0]);
        var oldtext = Convert.toString(args[1]);
        var newtext = Convert.toString(args[2]);
        var result;
        if(oldtext === "" || oldtext === undefined || oldtext === null)
            return searchtext;
        if(args.length > 3)
        {
            var inst = Convert.toInt(args[3]);
            var index = 0;
            if(inst < 1)
                return Calc.Errors.Value;
            for(var i = 0; i < inst; i++)
            {
                index = searchtext.indexOf(oldtext,index);
                if(index === -1)
                    return searchtext;
                index += oldtext.length
            }
            index -= oldtext.length;
            var before = searchtext.substring(0,index);
            var after = searchtext.substr(index + oldtext.length);
            result = before.concat(newtext).concat(after)
        }
        else
            result = searchtext.replace(oldtext,newtext);
        return result
    }
    function tx_concatenate(args)
    {
        var sb = [];
        var length = args.length;
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < length; i++)
        {
            var len = _ArrayHelper.getLength(args[i]);
            for(var j = 0; j < len; j++)
            {
                var tmpValue = _ArrayHelper.getValueByIndex(args[i],j);
                if(tmpValue !== undefined && tmpValue !== null)
                    sb.push(Convert.toString(tmpValue))
            }
        }
        return sb.join("")
    }
    function tx_left(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var text = Convert.toString(args[0]);
        var numChars = _Helper._argumentExists(args,1) ? Convert.toInt(args[1]) : 1;
        if(numChars < 0)
            return Calc.Errors.Value;
        else if(numChars >= text.length)
            return text;
        else
            return text.substr(0,numChars)
    }
    function tx_mid(args)
    {
        var Convert = Calc.Convert;
        var text = Convert.toString(args[0]);
        var start = Convert.toInt(args[1]) - 1;
        var length = Convert.toInt(args[2]);
        if(start < 0 || length < 0)
            return Calc.Errors.Value;
        else if(start >= text.length)
            return"";
        else if(text.length < start + length)
            return text.substr(start);
        else
            return text.substr(start,length)
    }
    function tx_right(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var text = Convert.toString(args[0]);
        var numChars = _Helper._argumentExists(args,1) ? Convert.toInt(args[1]) : 1;
        if(numChars < 0)
            return Calc.Errors.Value;
        else if(text.length < numChars)
            return text;
        else
            return text.substr(text.length - numChars,numChars)
    }
    function tx_rept(args)
    {
        var Convert = Calc.Convert;
        var text = Convert.toString(args[0]);
        var count = Convert.toInt(args[1]);
        if(count < 0 || 32767 < count * text.length)
            return Calc.Errors.Value;
        var result = [];
        for(var i = 0; i < count; i++)
            result.push(text);
        return result.join("")
    }
    function tx_len(args)
    {
        return Calc.Convert.toString(args[0]).length
    }
    function tx_find(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var searchtext = Convert.toString(args[0]);
        var text = Convert.toString(args[1]);
        var start = _Helper._argumentExists(args,2) ? Convert.toInt(args[2]) : 1;
        var found;
        if(start < 1 || text.length < start)
            return Calc.Errors.Value;
        found = text.indexOf(searchtext,start - 1);
        if(found === -1)
            return Calc.Errors.Value;
        return found + 1
    }
    function tx_search(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var searchtext = Convert.toString(args[0]);
        var text = Convert.toString(args[1]);
        var startIndex = _Helper._argumentExists(args,2) ? Convert.toInt(args[2]) : 1;
        startIndex--;
        if(startIndex < 0)
            return Calc.Errors.Value;
        var index = -1;
        try
        {
            if(searchtext.indexOf('*') === -1 && searchtext.indexOf('?') === -1)
                index = text.toLowerCase().indexOf(searchtext.toLowerCase(),startIndex);
            else
            {
                var pattern = __createStringcomparisonRegexPattern(searchtext).toLowerCase();
                var regex = new RegExp(pattern,"i");
                var matchStr = regex.exec(text);
                if(matchStr !== undefined && matchStr !== null)
                    index = matchStr.index;
                else
                    index = -1
            }
        }
        catch(err){}
        if(index === -1)
            return Calc.Errors.Value;
        return index + 1
    }
    function tx_exact(args)
    {
        var Convert = Calc.Convert;
        var text1 = Convert.toString(args[0]);
        var text2 = Convert.toString(args[1]);
        return text1 === text2
    }
    function tx_t(args)
    {
        var val = args[0];
        return typeof val === 'string' ? val : ""
    }
    function _isError(obj, ctx)
    {
        if(Calc.Convert.isError(obj))
            ctx.value = true;
        else if(typeof obj === const_string)
        {
            var err = Calc.Error.parse(obj);
            ctx.value = err !== undefined && err !== null
        }
        ctx.value = false
    }
    function _isErr(obj, ctx)
    {
        if(Calc.Convert.isError(obj) && obj._code !== Calc.Errors.NotAvailable._code)
            ctx.value = true;
        else if(typeof obj === const_string)
        {
            var err = Calc.Error.parse(obj);
            ctx.value = err !== undefined && err !== null && err !== Calc.Errors.NotAvailable
        }
        ctx.value = false
    }
    function _checkArgumentsLength(args)
    {
        if(args === undefined || args === null)
            throw $_invalidArg;
        else if(args.length < 1 || args.length > 1)
            throw $_invalidArg;
    }
    function in_isError(args)
    {
        _checkArgumentsLength(args);
        var result = {value: false};
        if(Calc.Convert.isError(args[0]))
            result.value = true;
        else
            __iterate(args[0],_isError,result);
        return result.value
    }
    function in_isErr(args)
    {
        _checkArgumentsLength(args);
        var Convert = Calc.Convert;
        var result = {value: false};
        if(Convert.isError(args[0]) && args[0]._code !== Calc.Errors.NotAvailable._code)
            result.value = true;
        else if(Convert.isError(args[0]) && args[0]._code === Calc.Errors.NotAvailable._code)
            result.value = false;
        else
            __iterate(args[0],_isErr,result);
        return result.value
    }
    function in_isNA(args)
    {
        _checkArgumentsLength(args);
        if(Calc.Convert.isError(args[0]))
            return args[0]._code === Calc.Errors.NotAvailable._code;
        return false
    }
    function in_error_Type(args)
    {
        _checkArgumentsLength(args);
        var err = args[0];
        if(err !== undefined && err !== null)
            if(Calc.Convert.isError(err))
                switch(err._code)
                {
                    case Calc.Errors.Null._code:
                        return 1;
                    case Calc.Errors.DivideByZero._code:
                        return 2;
                    case Calc.Errors.Value._code:
                        return 3;
                    case Calc.Errors.Reference._code:
                        return 4;
                    case Calc.Errors.Name._code:
                        return 5;
                    case Calc.Errors.Number._code:
                        return 6;
                    case Calc.Errors.NotAvailable._code:
                        return 7;
                    default:
                        break
                }
        return Calc.Errors.NotAvailable
    }
    function in_isNumber(args)
    {
        _checkArgumentsLength(args);
        return Calc.Convert.isNumber(args[0])
    }
    function in_isEven(args)
    {
        _checkArgumentsLength(args);
        var num;
        if(isNaN(num = Calc.Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        return Functions._MathHelper.approxFloor(Math.abs(num)) % 2.0 === 0.0
    }
    function in_isOdd(args)
    {
        _checkArgumentsLength(args);
        var num;
        if(isNaN(num = Calc.Convert.toDouble(args[0])))
            return Calc.Errors.Value;
        return Functions._MathHelper.approxFloor(Math.abs(num)) % 2.0 !== 0.0
    }
    function in_number(args)
    {
        _checkArgumentsLength(args);
        var value = args[0];
        var Convert = Calc.Convert;
        if(Convert.isNumber(value))
            return Convert.toDouble(value);
        else if(typeof value === const_boolean)
            return value ? 1.0 : 0.0;
        else if(Convert.isError(value))
            return value;
        return 0.0
    }
    function in_isBlank(args)
    {
        _checkArgumentsLength(args);
        return args[0] === undefined || args[0] === null
    }
    function in_isLogical(args)
    {
        _checkArgumentsLength(args);
        return typeof args[0] === const_boolean
    }
    function in_isText(args)
    {
        _checkArgumentsLength(args);
        return typeof args[0] === const_string
    }
    function in_isNonText(args)
    {
        _checkArgumentsLength(args);
        return typeof args[0] !== const_string
    }
    function in_isRef(args)
    {
        _checkArgumentsLength(args);
        return Calc.Convert._isCalcReference(args[0])
    }
    function in_type(args)
    {
        _checkArgumentsLength(args);
        var value = args[0];
        var Convert = Calc.Convert;
        if(Convert.isNumber(value))
            return 1;
        else if(typeof value === const_string)
            return 2;
        else if(typeof value === const_boolean)
            return 4;
        else if(Convert.isError(value))
            return 16;
        else if(Convert._isCalcArray(value))
            return 64;
        else
            return Calc.Errors.Value
    }
    function in_na(args)
    {
        return Calc.Errors.NotAvailable
    }
    Functions._MathHelper = function()
    {
        function _approxFloor(x)
        {
            var r = Math.floor(x);
            if(Functions._MathHelper.approxEqual(x,r + 1.0))
                return r + 1.0;
            return r
        }
        function _approxCeiling(x)
        {
            var r = Math.ceil(x);
            if(Functions._MathHelper.approxEqual(x,r - 1.0))
                return r - 1.0;
            return r
        }
        function _approxEqual(x, y)
        {
            if(x === y)
                return true;
            return Math.abs(x - y) < Math.abs(x) / (16777216.0 * 16777216.0)
        }
        function _initPow10()
        {
            var pow10Array = new Array(17);
            pow10Array[0x00] = 1e0;
            pow10Array[0x01] = 1e1;
            pow10Array[0x02] = 1e2;
            pow10Array[0x03] = 1e3;
            pow10Array[0x04] = 1e4;
            pow10Array[0x05] = 1e5;
            pow10Array[0x06] = 1e6;
            pow10Array[0x07] = 1e7;
            pow10Array[0x08] = 1e8;
            pow10Array[0x09] = 1e9;
            pow10Array[0x0a] = 1e10;
            pow10Array[0x0b] = 1e11;
            pow10Array[0x0c] = 1e12;
            pow10Array[0x0d] = 1e13;
            pow10Array[0x0e] = 1e14;
            pow10Array[0x0f] = 1e15;
            pow10Array[0x10] = 1e16;
            return pow10Array
        }
        function _pow10(n)
        {
            var pow10Array = _initPow10();
            var value = pow10Array[n];
            if(value !== undefined && value !== null)
                return value;
            return Math.pow(10.0,parseFloat(n))
        }
        function _buildCriteria(crit, criteria)
        {
            var compareString;
            var compareDouble;
            switch(crit)
            {
                case 0:
                    compareString = function(v1, v2)
                    {
                        return v1 <= v2
                    };
                    compareDouble = function(v1, v2)
                    {
                        return v1 <= v2
                    };
                    break;
                case 1:
                    compareString = function(v1, v2)
                    {
                        return v1 >= v2
                    };
                    compareDouble = function(v1, v2)
                    {
                        return v1 >= v2
                    };
                    break;
                case 2:
                    compareString = function(v1, v2)
                    {
                        return v1 !== v2
                    };
                    compareDouble = function(v1, v2)
                    {
                        return v1 !== v2
                    };
                    break;
                case 3:
                    compareString = function(v1, v2)
                    {
                        return v1 < v2
                    };
                    compareDouble = function(v1, v2)
                    {
                        return v1 < v2
                    };
                    break;
                case 4:
                    compareString = function(v1, v2)
                    {
                        return v1 === v2
                    };
                    compareDouble = function(v1, v2)
                    {
                        return v1 === v2
                    };
                    break;
                case 5:
                    compareString = function(v1, v2)
                    {
                        return v1 > v2
                    };
                    compareDouble = function(v1, v2)
                    {
                        return v1 > v2
                    };
                    break;
                default:
                    return function(v)
                        {
                            return false
                        }
            }
            var critVal = -1;
            var isCritNumber = true;
            try
            {
                if(criteria === undefined || criteria === null)
                    critVal = 0;
                else if(isNaN(critVal = Calc.Convert.toDouble(criteria.toString())))
                    isCritNumber = false
            }
            catch(e)
            {
                isCritNumber = false
            }
            return function(value)
                {
                    if(value === undefined || value === null)
                        return false;
                    if(isCritNumber)
                    {
                        var chkVal = -1;
                        try
                        {
                            if(!isNaN(chkVal = Calc.Convert.toDouble(value)))
                                return compareDouble(chkVal,critVal)
                        }
                        catch(e){}
                    }
                    return compareString(value.toString().toUpperCase(),criteria !== undefined && criteria !== null ? criteria.toString().toUpperCase() : "")
                }
        }
        function _parseCriteria(criteria)
        {
            if(Calc.Convert.isNumber(criteria))
                return _buildCriteria(4,criteria);
            var OPERATORS_INFIX = "=><";
            var critString = criteria !== undefined && criteria !== null ? criteria.toString().toUpperCase() : "";
            var prevChar = '\0';
            for(var i = 0; i < 2 && i < critString.length; i++)
            {
                var tc = critString[i];
                if(OPERATORS_INFIX.indexOf(tc) !== -1)
                    if(tc === '=')
                        switch(prevChar)
                        {
                            case'<':
                                return _buildCriteria(0,critString.substring(2));
                            case'>':
                                return _buildCriteria(1,critString.substring(2));
                            default:
                                return _buildCriteria(4,prevChar === '\0' ? critString.substring(1) : criteria)
                        }
                    else
                    {
                        if(prevChar === '\0')
                        {
                            prevChar = tc;
                            continue
                        }
                        if(prevChar === '<')
                            if(tc === '>')
                                return _buildCriteria(2,critString.substring(2));
                            else
                                return _buildCriteria(3,critString.substring(1));
                        else if(prevChar === '>')
                            return _buildCriteria(5,critString.substring(1))
                    }
                else
                    switch(prevChar)
                    {
                        case'<':
                            return _buildCriteria(3,critString.substring(1));
                        case'>':
                            return _buildCriteria(5,critString.substring(1));
                        default:
                            break
                    }
            }
            return _buildCriteria(4,criteria)
        }
        function _round(number, digits)
        {
            if(isNaN(number))
                return Calc.Errors.Value;
            var power = _pow10(Math.abs(digits));
            if(digits < 0)
                number /= power;
            else
                number *= power;
            if(number < 0.0)
                number = _approxCeiling(number - 0.5);
            else
                number = _approxFloor(number + 0.5);
            if(digits < 0)
                number *= power;
            else
                number /= power;
            return Calc.Convert.toResult(number)
        }
        function _combin(n, k)
        {
            if(isNaN(n) || isNaN(k))
                return Calc.Errors.Value;
            if(n < 0.0 || k < 0.0 || n < k)
                return Calc.Errors.Number;
            var result = 1.0;
            k = Math.min(n - k,k);
            for(var i = 1.0; i <= k; i++)
            {
                result *= n - i + 1.0;
                result /= i
            }
            return Calc.Convert.toResult(result)
        }
        function _log1p(x)
        {
            return Math.log(1.0 + x) - (1.0 + x - 1.0 - x) / (1.0 + x)
        }
        function _pow1p(x, y)
        {
            var ret;
            if(Math.abs(x) > 0.5)
                ret = Math.pow(1.0 + x,y);
            else
                ret = Math.exp(y * _log1p(x));
            if(!isFinite(ret))
            {
                if(ret === Number.POSITIVE_INFINITY)
                    ret = 1.79769e+308;
                else if(ret === Number.NEGATIVE_INFINITY)
                    ret = -1.79769e+308
            }
            else if(isNaN(ret))
                ret = 4.94066e-324;
            return ret
        }
        function _cosh(d)
        {
            return(Math.exp(d) + Math.exp(-d)) / 2
        }
        function _sinh(d)
        {
            return(Math.exp(d) - Math.exp(-d)) / 2
        }
        return{
                approxFloor: _approxFloor,
                approxCeiling: _approxCeiling,
                approxEqual: _approxEqual,
                pow10: _pow10,
                parseCriteria: _parseCriteria,
                round: _round,
                combin: _combin,
                log10: MathEx.log10,
                log: MathEx.log,
                pow1p: _pow1p,
                log1p: _log1p,
                cosh: _cosh,
                sinh: _sinh
            }
    }();
    def("ABS",mt_abs,{
        minArgs: 1,
        maxArgs: 1
    });
    def("ACOS",mt_acos,{
        minArgs: 1,
        maxArgs: 1
    });
    def("ASIN",mt_asin,{
        minArgs: 1,
        maxArgs: 1
    });
    def("ATAN",mt_atan,{
        minArgs: 1,
        maxArgs: 1
    });
    def("ATAN2",mt_atan2,{
        minArgs: 2,
        maxArgs: 2
    });
    def("COS",mt_cos,{
        minArgs: 1,
        maxArgs: 1
    });
    def("CEILING",mt_ceiling,{
        minArgs: 2,
        maxArgs: 2
    });
    def("ODD",mt_odd,{
        minArgs: 1,
        maxArgs: 1
    });
    def("EVEN",mt_even,{
        minArgs: 1,
        maxArgs: 1
    });
    def("FLOOR",mt_floor,{
        minArgs: 2,
        maxArgs: 2
    });
    def("LN",mt_ln,{
        minArgs: 1,
        maxArgs: 1
    });
    def("SQRT",mt_sqrt,{
        minArgs: 1,
        maxArgs: 1
    });
    def("SIN",mt_sin,{
        minArgs: 1,
        maxArgs: 1
    });
    def("TAN",mt_tan,{
        minArgs: 1,
        maxArgs: 1
    });
    def("SIGN",mt_sign,{
        minArgs: 1,
        maxArgs: 1
    });
    def("GCD",mt_gcd,{
        minArgs: 1,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("LCM",mt_lcm,{
        minArgs: 1,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("PRODUCT",mt_product,{
        minArgs: 1,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("POWER",mt_power,{
        minArgs: 2,
        maxArgs: 2
    });
    def("MOD",mt_mod,{
        minArgs: 2,
        maxArgs: 2
    });
    def("QUOTIENT",mt_quotient,{
        minArgs: 2,
        maxArgs: 2
    });
    def("SUBTOTAL",mt_subtotal,{
        minArgs: 2,
        acceptsReference: acceptsNotZero,
        acceptsArray: acceptsNotZero
    });
    def("INT",mt_int,{
        minArgs: 1,
        maxArgs: 1
    });
    def("MROUND",mt_mround,{
        minArgs: 2,
        maxArgs: 2
    });
    def("ROUND",mt_round,{
        minArgs: 2,
        maxArgs: 2
    });
    def("ROUNDDOWN",mt_rounddown,{
        minArgs: 2,
        maxArgs: 2
    });
    def("ROUNDUP",mt_roundup,{
        minArgs: 2,
        maxArgs: 2
    });
    def("TRUNC",mt_trunc,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("EXP",mt_exp,{
        minArgs: 1,
        maxArgs: 1
    });
    def("LOG",mt_log,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("LOG10",mt_log10,{
        minArgs: 1,
        maxArgs: 1
    });
    def("SUM",mt_sum,{
        minArgs: 1,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("SUMIF",mt_sumif,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsTwo,
        acceptsReference: acceptsZeroTwo,
        acceptsArray: acceptsZeroTwo
    });
    def("SUMIFS",mt_sumifs,{
        minArgs: 3,
        acceptsReference: acceptsZeroOdd,
        acceptsArray: acceptsZeroOdd
    });
    def("SUMPRODUCT",mt_sumproduct,{
        minArgs: 1,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("SUMSQ",mt_sumsq,{
        minArgs: 1,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("SUMX2MY2",mt_sumx2my2,{
        minArgs: 2,
        maxArgs: 2,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("SUMX2PY2",mt_sumx2py2,{
        minArgs: 2,
        maxArgs: 2,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("SUMXMY2",mt_sumxmy2,{
        minArgs: 2,
        maxArgs: 2,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("SERIESSUM",mt_seriessum,{
        minArgs: 4,
        maxArgs: 4,
        acceptsReference: acceptsThree,
        acceptsArray: acceptsThree
    });
    def("PI",mt_pi,{
        minArgs: 0,
        maxArgs: 0
    });
    def("SQRTPI",mt_sqrtpi,{
        minArgs: 1,
        maxArgs: 1
    });
    def("DEGREES",mt_degrees,{
        minArgs: 1,
        maxArgs: 1
    });
    def("RADIANS",mt_radians,{
        minArgs: 1,
        maxArgs: 1
    });
    def("COSH",mt_cosh,{
        minArgs: 1,
        maxArgs: 1
    });
    def("ACOSH",mt_acosh,{
        minArgs: 1,
        maxArgs: 1
    });
    def("SINH",mt_sinh,{
        minArgs: 1,
        maxArgs: 1
    });
    def("ASINH",mt_asinh,{
        minArgs: 1,
        maxArgs: 1
    });
    def("TANH",mt_tanh,{
        minArgs: 1,
        maxArgs: 1
    });
    def("ATANH",mt_atanh,{
        minArgs: 1,
        maxArgs: 1
    });
    def("MDETERM",mt_mdeterm,{
        minArgs: 1,
        maxArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("MINVERSE",mt_minverse,{
        minArgs: 1,
        maxArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("MMULT",mt_mmult,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("FACT",mt_fact,{
        minArgs: 1,
        maxArgs: 1
    });
    def("FACTDOUBLE",mt_factdouble,{
        minArgs: 1,
        maxArgs: 1
    });
    def("MULTINOMIAL",mt_multinomial,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("RAND",mt_rand,{
        minArgs: 0,
        maxArgs: 0,
        isVolatile: isVolatile
    });
    def("RANDBETWEEN",mt_randbetween,{
        minArgs: 2,
        maxArgs: 2,
        isVolatile: isVolatile
    });
    def("COMBIN",mt_combin,{
        minArgs: 2,
        maxArgs: 2
    });
    def("ROMAN",mt_roman,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("AND",lg_and,{
        minArgs: 1,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("OR",lg_or,{
        minArgs: 1,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("NOT",lg_not,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IF",lg_if,{
        minArgs: 2,
        maxArgs: 3,
        acceptsReference: acceptsOneTwo,
        acceptsArray: acceptsOneTwo,
        acceptsError: acceptsOneTwo,
        acceptsMissingArgument: acceptsTwo,
        isBranch: isBranch,
        findTestArgument: findTestArgument,
        findBranchArgument: findBranchArgument
    });
    def("IFERROR",lg_iferror,{
        minArgs: 2,
        maxArgs: 2,
        acceptsReference: acceptsZero,
        acceptsError: acceptsZero
    });
    def("TRUE",lg_true,{
        minArgs: 0,
        maxArgs: 0
    });
    def("FALSE",lg_false,{
        minArgs: 0,
        maxArgs: 0
    });
    def("DATE",dt_date,{
        minArgs: 3,
        maxArgs: 3
    });
    def("TIME",dt_time,{
        minArgs: 3,
        maxArgs: 3
    });
    def("DATEVALUE",dt_datevalue,{
        minArgs: 1,
        maxArgs: 1
    });
    def("TIMEVALUE",dt_timevalue,{
        minArgs: 1,
        maxArgs: 1
    });
    def("NOW",dt_now,{
        minArgs: 0,
        maxArgs: 0,
        isVolatile: isVolatile
    });
    def("TODAY",dt_today,{
        minArgs: 0,
        maxArgs: 0,
        isVolatile: isVolatile
    });
    def("HOUR",dt_hour,{
        minArgs: 1,
        maxArgs: 1
    });
    def("MINUTE",dt_minute,{
        minArgs: 1,
        maxArgs: 1
    });
    def("SECOND",dt_second,{
        minArgs: 1,
        maxArgs: 1
    });
    def("DAY",dt_day,{
        minArgs: 1,
        maxArgs: 1
    });
    def("MONTH",dt_month,{
        minArgs: 1,
        maxArgs: 1
    });
    def("YEAR",dt_year,{
        minArgs: 1,
        maxArgs: 1
    });
    def("WEEKNUM",dt_weeknum,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("WEEKDAY",dt_weekday,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("EDATE",dt_edate,{
        minArgs: 2,
        maxArgs: 2
    });
    def("EOMONTH",dt_eomonth,{
        minArgs: 2,
        maxArgs: 2
    });
    def("WORKDAY",dt_workday,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsTwo,
        acceptsArray: acceptsTwo,
        acceptsReference: acceptsTwo
    });
    def("DAYS360",dt_days360,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsTwo
    });
    def("NETWORKDAYS",dt_networkdays,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsTwo,
        acceptsArray: acceptsTwo,
        acceptsReference: acceptsTwo
    });
    def("YEARFRAC",dt_yearfrac,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsTwo
    });
    def("DATEDIF",dt_datedif,{
        minArgs: 3,
        maxArgs: 3
    });
    def("CLEAN",tx_clean,{
        minArgs: 1,
        maxArgs: 1
    });
    def("TRIM",tx_trim,{
        minArgs: 1,
        maxArgs: 1
    });
    def("DOLLAR",tx_dollar,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("FIXED",tx_fixed,{
        minArgs: 1,
        maxArgs: 3,
        acceptsMissingArgument: acceptsOneTwo
    });
    def("TEXT",tx_text,{
        minArgs: 2,
        maxArgs: 2
    });
    def("VALUE",tx_value,{
        minArgs: 1,
        maxArgs: 1
    });
    def("LOWER",tx_lower,{
        minArgs: 1,
        maxArgs: 1
    });
    def("UPPER",tx_upper,{
        minArgs: 1,
        maxArgs: 1
    });
    def("CHAR",tx_char,{
        minArgs: 1,
        maxArgs: 1
    });
    def("CODE",tx_code,{
        minArgs: 1,
        maxArgs: 1
    });
    def("REPLACE",tx_replace,{
        minArgs: 4,
        maxArgs: 4,
        acceptsMissingArgument: acceptsTwo
    });
    def("SUBSTITUTE",tx_substitute,{
        minArgs: 3,
        maxArgs: 4,
        acceptsMissingArgument: acceptsThree
    });
    def("CONCATENATE",tx_concatenate,{
        minArgs: 2,
        acceptsArray: acceptsAny
    });
    def("LEFT",tx_left,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("MID",tx_mid,{
        minArgs: 3,
        maxArgs: 3
    });
    def("RIGHT",tx_right,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("REPT",tx_rept,{
        minArgs: 2,
        maxArgs: 2
    });
    def("LEN",tx_len,{
        minArgs: 1,
        maxArgs: 1
    });
    def("FIND",tx_find,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsTwo
    });
    def("SEARCH",tx_search,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsTwo
    });
    def("EXACT",tx_exact,{
        minArgs: 2,
        maxArgs: 2
    });
    def("T",tx_t,{
        minArgs: 1,
        maxArgs: 1
    });
    def("ISERROR",in_isError,{
        minArgs: 1,
        maxArgs: 1,
        acceptsError: acceptsAny
    });
    def("ISERR",in_isErr,{
        minArgs: 1,
        maxArgs: 1,
        acceptsError: acceptsAny
    });
    def("ISNA",in_isNA,{
        minArgs: 1,
        maxArgs: 1,
        acceptsError: acceptsAny
    });
    def("ERROR.TYPE",in_error_Type,{
        minArgs: 1,
        maxArgs: 1,
        acceptsError: acceptsAny
    });
    def("ISNUMBER",in_isNumber,{
        minArgs: 1,
        maxArgs: 1,
        acceptsError: acceptsAny
    });
    def("ISEVEN",in_isEven,{
        minArgs: 1,
        maxArgs: 1
    });
    def("ISODD",in_isOdd,{
        minArgs: 1,
        maxArgs: 1
    });
    def("N",in_number,{
        minArgs: 1,
        maxArgs: 1
    });
    def("ISBLANK",in_isBlank,{
        minArgs: 1,
        maxArgs: 1,
        acceptsError: acceptsAny
    });
    def("ISLOGICAL",in_isLogical,{
        minArgs: 1,
        maxArgs: 1,
        acceptsError: acceptsAny
    });
    def("ISTEXT",in_isText,{
        minArgs: 1,
        maxArgs: 1,
        acceptsError: acceptsAny
    });
    def("ISNONTEXT",in_isNonText,{
        minArgs: 1,
        maxArgs: 1,
        acceptsError: acceptsAny
    });
    def("ISREF",in_isRef,{
        minArgs: 1,
        maxArgs: 1,
        acceptsError: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("TYPE",in_type,{
        minArgs: 1,
        maxArgs: 1,
        acceptsArray: acceptsAny,
        acceptsError: acceptsAny
    });
    def("NA",in_na,{
        minArgs: 0,
        maxArgs: 0
    })
})(window,jQuery);
(function(window, $)
{
    "use strict";;
    var const_undefined = "undefined";
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === const_undefined)
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === const_undefined)
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    if(typeof GrapeCity.Calc === const_undefined)
        GrapeCity.Calc = {};
    var Calc = GrapeCity.Calc;
    UI.RelationCompareType = {
        Or: 0,
        And: 1
    };
    UI.ComparisonOperator = {
        EqualsTo: 0,
        NotEqualsTo: 1,
        GreaterThan: 2,
        GreaterThanOrEqualsTo: 3,
        LessThan: 4,
        LessThanOrEqualsTo: 5,
        Between: 6,
        NotBetween: 7
    };
    UI.GeneralCompareType = {
        EqualsTo: 0,
        NotEqualsTo: 1,
        GreaterThan: 2,
        GreaterThanOrEqualsTo: 3,
        LessThan: 4,
        LessThanOrEqualsTo: 5
    };
    UI.TextComparisonOperator = {
        Contains: 0,
        DoesNotContain: 1,
        BeginsWith: 2,
        EndsWith: 3
    };
    UI.TextCompareType = {
        EqualsTo: 0,
        NotEqualsTo: 1,
        BeginsWith: 2,
        DoesNotBeginWith: 3,
        EndsWith: 4,
        DoesNotEndWith: 5,
        Contains: 6,
        DoesNotContain: 7
    };
    UI.ColorCompareType = {
        BackgroundColor: 0,
        ForegroundColor: 1
    };
    UI.CustomValueType = {
        Empty: 0,
        NonEmpty: 1,
        Error: 2,
        NonError: 3,
        Formula: 4
    };
    UI.DateCompareType = {
        EqualsTo: 0,
        NotEqualsTo: 1,
        Before: 2,
        BeforeEqualsTo: 3,
        After: 4,
        AfterEqualsTo: 5
    };
    UI.Top10ConditionType = {
        Top: 0,
        Bottom: 1
    };
    UI.DateOccurringType = {
        Today: 0,
        Yesterday: 1,
        Tomorrow: 2,
        Last7Days: 3,
        ThisMonth: 4,
        LastMonth: 5,
        NextMonth: 6,
        ThisWeek: 7,
        LastWeek: 8,
        NextWeek: 9
    };
    UI.QuarterType = {
        Quarter1: 0,
        Quarter2: 1,
        Quarter3: 2,
        Quarter4: 3
    };
    UI.SortState = {
        None: 0,
        Ascending: 1,
        Descending: 2
    };
    UI.AverageConditionType = {
        Above: 0,
        Below: 1,
        EqualOrAbove: 2,
        EqualOrBelow: 3,
        Above1StdDev: 4,
        Below1StdDev: 5,
        Above2StdDev: 6,
        Below2StdDev: 7,
        Above3StdDev: 8,
        Below3StdDev: 9
    };
    UI.ScaleValueType = {
        Number: 0,
        LowestValue: 1,
        HighestValue: 2,
        Percent: 3,
        Percentile: 4,
        Automin: 5,
        Formula: 6,
        Automax: 7
    };
    UI.BarDirection = {
        LeftToRight: 0,
        RightToLeft: 1
    };
    UI.DataBarAxisPosition = {
        Automatic: 0,
        CellMidPoint: 1,
        None: 2
    };
    UI.IconSetType = {
        ThreeArrowsColored: 0x00,
        ThreeArrowsGray: 0x01,
        ThreeTriangles: 0x02,
        ThreeStars: 0x03,
        ThreeFlags: 0x04,
        ThreeTrafficLightsUnrimmed: 0x05,
        ThreeTrafficLightsRimmed: 0x06,
        ThreeSigns: 0x07,
        ThreeSymbolsCircled: 0x08,
        ThreeSymbolsUncircled: 0x09,
        FourArrowsColored: 0x0A,
        FourArrowsGray: 0x0B,
        FourRedToBlack: 0x0C,
        FourRatings: 0x0D,
        FourTrafficLights: 0x0E,
        FiveArrowsColored: 0x0F,
        FiveArrowsGray: 0x10,
        FiveRatings: 0x11,
        FiveQuarters: 0x12,
        FiveBoxes: 0x13
    };
    UI.IconValueType = {
        Number: 1,
        Percent: 4,
        Formula: 7,
        Percentile: 5
    };
    UI.ConditionType = {
        RelationCondition: 0,
        NumberCondition: 1,
        TextCondition: 2,
        ColorCondition: 3,
        FormulaCondition: 4,
        DateCondition: 5,
        DateExCondition: 6,
        TextLengthCondition: 7,
        Top10Condition: 8,
        UniqueCondition: 9,
        AverageCondition: 10,
        CellValueCondition: 11,
        AreaCondition: 12
    };
    UI.RuleType = {
        ConditionRuleBase: 0,
        CellValueRule: 1,
        SpecificTextRule: 2,
        FormulaRule: 3,
        DateOccurringRule: 4,
        Top10Rule: 5,
        UniqueRule: 6,
        DuplicateRule: 7,
        AverageRule: 8,
        ScaleRule: 9,
        TwoScaleRule: 10,
        ThreeScaleRule: 11,
        DataBarRule: 12,
        IconSetRule: 13
    };
    var RelationCompareType = UI.RelationCompareType;
    var ComparisonOperator = UI.ComparisonOperator;
    var GeneralCompareType = UI.GeneralCompareType;
    var TextComparisonOperator = UI.TextComparisonOperator;
    var TextCompareType = UI.TextCompareType;
    var ColorCompareType = UI.ColorCompareType;
    var CustomValueType = UI.CustomValueType;
    var DateCompareType = UI.DateCompareType;
    var Top10ConditionType = UI.Top10ConditionType;
    var DateOccurringType = UI.DateOccurringType;
    var QuarterType = UI.QuarterType;
    var SortState = UI.SortState;
    var AverageConditionType = UI.AverageConditionType;
    var ScaleValueType = UI.ScaleValueType;
    var BarDirection = UI.BarDirection;
    var DataBarAxisPosition = UI.DataBarAxisPosition;
    var IconSetType = UI.IconSetType;
    var IconValueType = UI.IconValueType;
    var ConditionType = UI.ConditionType;
    var RuleType = UI.RuleType;
    UI.ConditionRuleBase = function(style)
    {
        this.style = style
    };
    UI.ConditionRuleBase.prototype = {
        condition: null,
        ranges: null,
        style: null,
        hasNoReference: function()
        {
            return!(this.ranges && this.ranges.length > 0)
        },
        evaluate: function(evaluator, baseRow, baseColumn, actual)
        {
            if(this.contains(baseRow,baseColumn))
            {
                this.initCondition();
                var baseCoord = {
                        baseRow: 0,
                        baseCol: 0
                    };
                this.getBaseCoordinate(baseCoord);
                this.condition.adjustOffset(baseRow - baseCoord.baseRow,baseColumn - baseCoord.baseCol);
                var result = null;
                if(this.condition.evaluate(evaluator,baseRow,baseColumn,actual))
                    result = this.getExpected();
                this.condition.adjustOffset(0,0);
                return result
            }
            return null
        },
        contains: function(row, column)
        {
            if(this.ranges)
                for(var i = 0; i < this.ranges.length; i++)
                {
                    var range = this.ranges[i];
                    if(range.contains(row,column))
                        return true
                }
            return false
        },
        createCondition: function(){},
        initCondition: function()
        {
            if(!this.condition)
                this.condition = this.createCondition()
        },
        getExpected: function()
        {
            return this.style
        },
        reset: function()
        {
            this.ranges = null;
            this.condition = null;
            this.style = null
        },
        intersects: function(row, column, rowCount, columnCount)
        {
            if(this.ranges)
                for(var n = 0; n < this.ranges.length; n++)
                {
                    var cs = this.ranges[n];
                    if(cs.intersect(row,column,rowCount,columnCount))
                        return true
                }
            return false
        },
        isScaleRule: function()
        {
            return false
        },
        getBaseCoordinate: function(baseCoord)
        {
            baseCoord.baseRow = Number.MAX_VALUE;
            baseCoord.baseCol = Number.MAX_VALUE;
            if(this.ranges && this.ranges.length > 0)
                for(var i = 0; i < this.ranges.length; i++)
                {
                    var range = this.ranges[i];
                    baseCoord.baseRow = Math.min(range.row,baseCoord.baseRow);
                    baseCoord.baseCol = Math.min(range.col,baseCoord.baseCol)
                }
            else
            {
                baseCoord.baseRow = 0;
                baseCoord.baseColumn = 0
            }
        },
        _addRows: function(row, rowCount)
        {
            if(this.ranges)
            {
                var length = this.ranges.length;
                for(var i = 0; i < length; i++)
                {
                    var range = this.ranges[i];
                    if(range.row >= row)
                        this.ranges[i] = new UI.Range(range.row + rowCount,range.col,range.rowCount,range.colCount);
                    else if(range.row < row && row < range.row + range.rowCount)
                        this.ranges[i] = new UI.Range(range.row,range.col,range.rowCount + rowCount,range.colCount)
                }
            }
        },
        _addColumns: function(col, colCount)
        {
            if(this.ranges)
            {
                var length = this.ranges.length;
                for(var i = 0; i < length; i++)
                {
                    var range = this.ranges[i];
                    if(range.col >= col)
                        this.ranges[i] = new UI.Range(range.row,range.col + colCount,range.rowCount,range.colCount);
                    else if(range.col < col && col < range.col + range.colCount)
                        this.ranges[i] = new UI.Range(range.row,range.col,range.rowCount,range.colCount + colCount)
                }
            }
        },
        _removeRows: function(row, rowCount)
        {
            if(this.ranges)
            {
                var delList = [];
                var length = this.ranges.length;
                for(var i = 0; i < length; i++)
                {
                    var range = this.ranges[i];
                    if(range.row > row)
                        if(range.row + range.rowCount <= row + rowCount)
                            delList.push(range);
                        else
                            this.ranges[i] = new UI.Range(range.row - rowCount,range.col,range.rowCount,range.colCount);
                    else if(range.row <= row && row < range.row + range.rowCount)
                    {
                        var newRange = new UI.Range(range.row,range.col,range.rowCount - Math.min(range.row + range.rowCount - row,rowCount),range.colCount);
                        if(newRange.colCount === 0 || newRange.rowCount === 0)
                            delList.push(range);
                        else
                            this.ranges[i] = newRange
                    }
                }
                var len = delList.length;
                for(var j = 0; j < len; j++)
                {
                    var delRange = delList[j];
                    this.ranges.remove(delRange)
                }
            }
        },
        _removeColumns: function(col, colCount)
        {
            if(this.ranges)
            {
                var delList = [];
                var length = this.ranges.length;
                for(var i = 0; i < length; i++)
                {
                    var range = this.ranges[i];
                    if(range.col > col)
                        if(range.col + range.colCount <= col + colCount)
                            delList.push(range);
                        else
                            this.ranges[i] = new UI.Range(range.row,range.col - colCount,range.rowCount,range.colCount);
                    else if(range.col <= col && col < range.col + range.colCount)
                    {
                        var newRange = new UI.Range(range.row,range.col,range.rowCount,range.colCount - Math.min(range.col + range.colCount - col,colCount));
                        if(newRange.colCount === 0 || newRange.rowCount === 0)
                            delList.push(range);
                        else
                            this.ranges[i] = newRange
                    }
                }
                var len = delList.length;
                for(var j = 0; j < len; j++)
                {
                    var delRange = delList[j];
                    this.ranges.remove(delRange)
                }
            }
        },
        toJSON: function(){},
        fromJSON: function(settings){}
    };
    var ConditionRuleBase = UI.ConditionRuleBase;
    UI.RelationCondition = function(compareType, item1, item2)
    {
        this.compareType = compareType;
        this.item1 = item1;
        this.item2 = item2
    };
    UI.RelationCondition.prototype = {
        conditionType: "RelationCondition",
        compareType: RelationCompareType.And,
        item1: null,
        item2: null,
        ignoreBlank: false,
        adjustOffset: function(row, col){},
        create: function(compareType, item1, item2)
        {
            return new RelationCondition(compareType,item1,item2)
        },
        _getActualValue: function(evaluator, item, baseRow, baseColumn, actualValue)
        {
            if(evaluator && item)
                if(item.conditionType === "ColorCondition")
                {
                    var style = evaluator.getActualStyle(baseRow,baseColumn);
                    if(style)
                        if(item.compareType === ColorCompareType.BackgroundColor)
                            actualValue = style.backColor;
                        else if(item.compareType === ColorCompareType.ForegroundColor)
                            actualValue = style.foreColor
                }
            return actualValue
        },
        evaluate: function(evaluator, baseRow, baseColumn, actualValue1, actualValue2)
        {
            var value1,
                value2;
            if(this.item1)
            {
                this.item1.ignoreBlank = this.ignoreBlank;
                if(arguments.length < 5)
                    value1 = this._getActualValue(evaluator,this.item1,baseRow,baseColumn,actualValue1);
                else
                    value1 = actualValue1
            }
            if(this.item2)
            {
                this.item2.ignoreBlank = this.ignoreBlank;
                if(arguments.length < 5)
                    value2 = this._getActualValue(evaluator,this.item2,baseRow,baseColumn,actualValue1);
                else
                    value2 = actualValue2
            }
            var c1,
                c2;
            if(this.compareType === RelationCompareType.And)
            {
                c1 = this.item1 === undefined || this.item1 === null ? false : this.item1.evaluate(evaluator,baseRow,baseColumn,value1);
                if(!c1)
                    return false;
                c2 = this.item2 === undefined || this.item2 === null ? false : this.item2.evaluate(evaluator,baseRow,baseColumn,value2);
                return c2
            }
            else if(this.compareType === RelationCompareType.Or)
            {
                c1 = this.item1 === undefined || this.item1 === null ? false : this.item1.evaluate(evaluator,baseRow,baseColumn,value1);
                if(c1)
                    return true;
                c2 = this.item2 === undefined || this.item2 === null ? false : this.item2.evaluate(evaluator,baseRow,baseColumn,value2);
                return c2
            }
            return false
        },
        reset: function()
        {
            this.ignoreBlank = false;
            this.compareType = RelationCompareType.And;
            this.item1 = null;
            this.item2 = null
        },
        _getConditionTypeDictionary: function()
        {
            if(!this._dict)
            {
                var dict = {};
                dict[UI.ConditionType.RelationCondition] = UI.RelationCondition;
                dict[UI.ConditionType.NumberCondition] = UI.NumberCondition;
                dict[UI.ConditionType.TextCondition] = UI.TextCondition;
                dict[UI.ConditionType.ColorCondition] = UI.ColorCondition;
                dict[UI.ConditionType.FormulaCondition] = UI.FormulaCondition;
                dict[UI.ConditionType.DateCondition] = UI.DateCondition;
                dict[UI.ConditionType.DateExCondition] = UI.DateExCondition;
                dict[UI.ConditionType.TextLengthCondition] = UI.TextLengthCondition;
                dict[UI.ConditionType.Top10Condition] = UI.Top10Condition;
                dict[UI.ConditionType.UniqueCondition] = UI.UniqueCondition;
                dict[UI.ConditionType.AverageCondition] = UI.AverageCondition;
                dict[UI.ConditionType.CellValueCondition] = UI.CellValueCondition;
                dict[UI.ConditionType.AreaCondition] = UI.AreaCondition;
                this._dict = dict
            }
            return this._dict
        },
        toJSON: function()
        {
            return{
                    compareType: this.compareType,
                    item1: this.item1 ? this.item1.toJSON() : null,
                    item2: this.item2 ? this.item2.toJSON() : null,
                    conType: ConditionType.RelationCondition,
                    ignoreBlank: this.ignoreBlank
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.compareType !== null && settings.compareType !== undefined)
                this.compareType = settings.compareType;
            if(settings.item1 !== null && settings.item1 !== undefined)
            {
                var condition = null;
                var dict = this._getConditionTypeDictionary();
                var conditionClass = dict[settings.item1.conType];
                if(conditionClass)
                {
                    condition = new conditionClass;
                    condition.fromJSON(settings.item1)
                }
                this.item1 = condition
            }
            if(settings.item2 !== null && settings.item2 !== undefined)
            {
                var condition = null;
                var dict = this._getConditionTypeDictionary();
                var conditionClass = dict[settings.item2.conType];
                if(conditionClass)
                {
                    condition = new conditionClass;
                    condition.fromJSON(settings.item2)
                }
                this.item2 = condition
            }
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank
        }
    };
    var RelationCondition = UI.RelationCondition;
    function CellValueCondition(compareType, expected, formula)
    {
        this.compareType = compareType;
        this.expected = expected;
        this.formula = formula
    }
    function CellValueRule(operator, value1, value2, style)
    {
        this.operator = operator;
        this.value1 = typeof value1 === "string" ? $.trim(value1) : value1;
        this.value2 = typeof value2 === "string" ? $.trim(value2) : value2;
        this.style = style
    }
    CellValueRule.prototype = new ConditionRuleBase;
    CellValueRule.prototype.createCondition = function()
    {
        var formula1 = this.isFormula(this.value1) ? this.value1.replace("=","") : null;
        var expect1 = this.isFormula(this.value1) ? null : this.value1;
        var formula2 = this.isFormula(this.value2) ? this.value2.replace("=","") : null;
        var expect2 = this.isFormula(this.value2) ? null : this.value2;
        var condition1,
            condition2;
        var op = this.operator,
            compareType = null;
        if(op === ComparisonOperator.Between)
        {
            condition1 = new CellValueCondition(GeneralCompareType.GreaterThanOrEqualsTo,this.value1,formula1);
            condition1.treatNullValueAsZero = true;
            condition2 = new CellValueCondition(GeneralCompareType.LessThanOrEqualsTo,this.value2,formula2);
            condition2.treatNullValueAsZero = true;
            return new RelationCondition(RelationCompareType.And,condition1,condition2)
        }
        else if(op === ComparisonOperator.NotBetween)
        {
            condition1 = new CellValueCondition(GeneralCompareType.LessThan,this.value1,formula1);
            condition1.treatNullValueAsZero = true;
            condition2 = new CellValueCondition(GeneralCompareType.GreaterThan,this.value2,formula2);
            condition2.treatNullValueAsZero = true;
            return new RelationCondition(RelationCompareType.Or,condition1,condition2)
        }
        else if(op === ComparisonOperator.EqualsTo)
            compareType = GeneralCompareType.EqualsTo;
        else if(op === ComparisonOperator.GreaterThan)
            compareType = GeneralCompareType.GreaterThan;
        else if(op === ComparisonOperator.GreaterThanOrEqualsTo)
            compareType = GeneralCompareType.GreaterThanOrEqualsTo;
        else if(op === ComparisonOperator.LessThan)
            compareType = GeneralCompareType.LessThan;
        else if(op === ComparisonOperator.LessThanOrEqualsTo)
            compareType = GeneralCompareType.LessThanOrEqualsTo;
        else if(op === ComparisonOperator.NotEqualsTo)
            compareType = GeneralCompareType.NotEqualsTo;
        if(compareType !== null)
        {
            var t = new CellValueCondition(compareType,this.value1,formula1);
            t.treatNullValueAsZero = true;
            return t
        }
        return null
    };
    CellValueRule.prototype.reset = function()
    {
        this.operator = ComparisonOperator.Between;
        this.value1 = null;
        this.value2 = null;
        this.style = null
    };
    CellValueRule.prototype.isFormula = function(val)
    {
        return val !== undefined && val !== null && val[0] === "="
    };
    CellValueRule.prototype.toJSON = function()
    {
        return{
                ruleType: RuleType.CellValueRule,
                ranges: this.ranges,
                style: this.style,
                operator: this.operator,
                value1: this.value1,
                value2: this.value2
            }
    };
    CellValueRule.prototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.ranges)
        {
            this.ranges = [];
            for(var i = 0; i < settings.ranges.length; i++)
            {
                var r = settings.ranges[i];
                this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
            }
        }
        if(settings.style)
        {
            this.style = new UI.Style;
            this.style.copyFrom(settings.style)
        }
        if(settings.operator !== null && settings.operator !== undefined)
            this.operator = settings.operator;
        if(settings.value1 !== null && settings.value1 !== undefined)
            this.value1 = settings.value1;
        if(settings.value2 !== null && settings.value2 !== undefined)
            this.value2 = settings.value2
    };
    UI.CellValueRule = CellValueRule;
    CellValueCondition.prototype = {
        compareType: null,
        treatNullValueAsZero: false,
        adjustOffset: function(row, col){},
        getExpected: function(evaluator, baseRow, baseColumn, isArrayFormula)
        {
            if(this.formula && this.formula.length > 0)
            {
                var calcService = evaluator.getCalcService();
                var expr = calcService.parse(this.formula,baseRow,baseColumn);
                return calcService.evaluateParsedFormula(evaluator._getSheetSource(),expr,baseRow,baseColumn)
            }
            else
                return this.expected
        },
        evaluate: function(evaluator, baseRow, baseColumn, actualValue)
        {
            var expected = this.getExpected(evaluator,baseRow,baseColumn);
            return this.checkCondition(expected,actualValue)
        },
        isSatisfyingCondition: function(value)
        {
            var expected = this.getExpected(null,0,0);
            return this.checkCondition(expected,value)
        },
        checkCondition: function(expectedValue, actualValue)
        {
            var dActualValue = 0;
            var isNumber = false;
            var convertToDouble = Calc.Convert.toDouble;
            if(typeof expectedValue === "boolean")
            {
                var expectedBoolValue = expectedValue;
                switch(this.compareType)
                {
                    case GeneralCompareType.EqualsTo:
                        return actualValue === expectedValue;
                    case GeneralCompareType.NotEqualsTo:
                        return actualValue !== expectedValue;
                    case GeneralCompareType.GreaterThan:
                        return this._compareBool(expectedBoolValue,actualValue) < 0;
                    case GeneralCompareType.GreaterThanOrEqualsTo:
                        return this._compareBool(expectedBoolValue,actualValue) <= 0;
                    case GeneralCompareType.LessThan:
                        return this._compareBool(expectedBoolValue,actualValue) > 0;
                    case GeneralCompareType.LessThanOrEqualsTo:
                        return this._compareBool(expectedBoolValue,actualValue);
                    default:
                        break
                }
            }
            try
            {
                dActualValue = convertToDouble(actualValue);
                isNumber = !isNaN(dActualValue)
            }
            catch(err)
            {
                isNumber = false
            }
            if((actualValue === undefined || actualValue === null) && (expectedValue === undefined || expectedValue === null))
                switch(this.compareType)
                {
                    case GeneralCompareType.EqualsTo:
                    case GeneralCompareType.GreaterThanOrEqualsTo:
                    case GeneralCompareType.LessThanOrEqualsTo:
                        return true;
                    case GeneralCompareType.NotEqualsTo:
                    case GeneralCompareType.GreaterThan:
                    case GeneralCompareType.LessThan:
                        return false;
                    default:
                        return false
                }
            if(this.treatNullValueAsZero && (actualValue === undefined || actualValue === null))
            {
                isNumber = true;
                dActualValue = 0
            }
            if(isNumber)
            {
                var doubleExpectedValue = 0.0;
                try
                {
                    doubleExpectedValue = convertToDouble(expectedValue)
                }
                catch(ex)
                {
                    switch(this.compareType)
                    {
                        case GeneralCompareType.EqualsTo:
                            return false;
                        case GeneralCompareType.NotEqualsTo:
                            return true
                    }
                    return false
                }
                switch(this.compareType)
                {
                    case GeneralCompareType.EqualsTo:
                        return dActualValue === doubleExpectedValue;
                    case GeneralCompareType.NotEqualsTo:
                        return dActualValue !== doubleExpectedValue;
                    case GeneralCompareType.GreaterThan:
                        return dActualValue > doubleExpectedValue;
                    case GeneralCompareType.GreaterThanOrEqualsTo:
                        return dActualValue >= doubleExpectedValue;
                    case GeneralCompareType.LessThan:
                        return dActualValue < doubleExpectedValue;
                    case GeneralCompareType.LessThanOrEqualsTo:
                        return dActualValue <= doubleExpectedValue
                }
            }
            else if(typeof actualValue === "string")
            {
                var stringExpectedValue = null;
                if(typeof expectedValue === "string")
                    stringExpectedValue = expectedValue;
                else
                {
                    switch(this.compareType)
                    {
                        case GeneralCompareType.EqualsTo:
                            return false;
                        case GeneralCompareType.NotEqualsTo:
                            return true
                    }
                    return false
                }
                var stringActualValue = actualValue;
                switch(this.compareType)
                {
                    case GeneralCompareType.EqualsTo:
                        return stringActualValue === stringExpectedValue;
                    case GeneralCompareType.NotEqualsTo:
                        return stringActualValue !== stringExpectedValue;
                    case GeneralCompareType.GreaterThan:
                        return stringExpectedValue < stringActualValue;
                    case GeneralCompareType.GreaterThanOrEqualsTo:
                        return stringExpectedValue <= stringActualValue;
                    case GeneralCompareType.LessThan:
                        return stringExpectedValue > stringActualValue;
                    case GeneralCompareType.LessThanOrEqualsTo:
                        return stringExpectedValue >= stringActualValue
                }
            }
            return false
        },
        _compareBool: function(value1, value2)
        {
            if(value1 === value2)
                return 0;
            else if(value1 === false && value2 === true)
                return-1;
            else
                return 1
        },
        toJSON: function()
        {
            return{
                    compareType: this.compareType,
                    expected: this.expected,
                    formula: this.formula,
                    conType: ConditionType.CellValueCondition,
                    treatNullValueAsZero: this.treatNullValueAsZero
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.compareType !== null && settings.compareType !== undefined)
                this.compareType = settings.compareType;
            if(settings.expected !== null && settings.expected !== undefined)
                this.expected = settings.expected;
            if(settings.formula !== null && settings.formula !== undefined)
                this.formula = settings.formula;
            if(settings.treatNullValueAsZero !== null && settings.treatNullValueAsZero !== undefined)
                this.treatNullValueAsZero = settings.treatNullValueAsZero
        }
    };
    UI.CellValueCondition = CellValueCondition;
    function NumberCondition(compareType, expected, formula)
    {
        this.compareType = compareType;
        this.expected = expected;
        this.formula = typeof formula === "string" ? $.trim(formula).replace("=","") : formula
    }
    NumberCondition.prototype = {
        conditionType: "NumberCondition",
        ignoreBlank: false,
        integerValue: false,
        compareType: GeneralCompareType.EqualsTo,
        evaluate: function(evaluator, baseRow, basecol, actualValue)
        {
            var expected = this.getExpected(evaluator,baseRow,basecol);
            if(this.integerValue)
                if(isNaN(expected))
                    expected = null;
                else
                    expected = parseInt(expected,10);
            return this.checkCondition(expected,actualValue)
        },
        checkCondition: function(expectedValue, actualValue)
        {
            if((actualValue === undefined || actualValue === null || actualValue === "") && this.ignoreBlank)
                return true;
            if(isNaN(actualValue))
                return false;
            if(expectedValue === undefined || expectedValue === null)
                if(this.ignoreBlank)
                    return true;
                else
                    expectedValue = 0;
            var doubleCellValue = 0.0;
            try
            {
                doubleCellValue = parseFloat(actualValue)
            }
            catch(e)
            {
                return false
            }
            if(this.integerValue === true)
            {
                var isValueInteger = doubleCellValue - Math.floor(doubleCellValue) === 0;
                if(isValueInteger === false)
                    return false
            }
            switch(this.compareType)
            {
                case GeneralCompareType.EqualsTo:
                    return doubleCellValue === expectedValue;
                case GeneralCompareType.NotEqualsTo:
                    return doubleCellValue !== expectedValue;
                case GeneralCompareType.GreaterThan:
                    return doubleCellValue > expectedValue;
                case GeneralCompareType.GreaterThanOrEqualsTo:
                    return doubleCellValue >= expectedValue;
                case GeneralCompareType.LessThan:
                    return doubleCellValue < expectedValue;
                case GeneralCompareType.LessThanOrEqualsTo:
                    return doubleCellValue <= expectedValue
            }
            return false
        },
        getExpected: function(evaluator, baseRow, basecol)
        {
            if(this.formula && this.formula.length > 0)
            {
                var calcService = evaluator.getCalcService();
                return calcService.evaluate(evaluator._getSheetSource(),this.formula,baseRow,basecol)
            }
            else
                return this.expected
        },
        reset: function()
        {
            this.expected = null;
            this.ignoreBlank = false;
            this.compareType = GeneralCompareType.EqualsTo;
            this.integerValue = false
        },
        toJSON: function()
        {
            return{
                    compareType: this.compareType,
                    expected: this.expected,
                    formula: this.formula,
                    conType: ConditionType.NumberCondition,
                    ignoreBlank: this.ignoreBlank,
                    integerValue: this.integerValue
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.compareType !== null && settings.compareType !== undefined)
                this.compareType = settings.compareType;
            if(settings.expected !== null && settings.expected !== undefined)
                this.expected = settings.expected;
            if(settings.formula !== null && settings.formula !== undefined)
                this.formula = settings.formula;
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank;
            if(settings.integerValue !== null && settings.integerValue !== undefined)
                this.integerValue = settings.integerValue
        }
    };
    UI.NumberCondition = NumberCondition;
    function TextCondition(compareType, expected, formula)
    {
        this.compareType = compareType;
        this.expected = expected;
        this.formula = typeof formula === "string" ? $.trim(formula).replace("=","") : formula;
        this.forceValue2Text = false;
        this.useWildCards = true;
        this.regex = null;
        this.ignoreCase = false
    }
    function SpecificTextRule(operator, text, style)
    {
        this.operator = operator;
        this.text = text;
        this.style = style
    }
    SpecificTextRule.prototype = new ConditionRuleBase;
    SpecificTextRule.prototype.createCondition = function()
    {
        var type;
        switch(this.operator)
        {
            case TextComparisonOperator.BeginsWith:
                type = TextCompareType.BeginsWith;
                break;
            case TextComparisonOperator.EndsWith:
                type = TextCompareType.EndsWith;
                break;
            case TextComparisonOperator.Contains:
                type = TextCompareType.Contains;
                break;
            case TextComparisonOperator.DoesNotContain:
                type = TextCompareType.DoesNotContain;
                break;
            default:
                type = TextCompareType.EqualsTo;
                break
        }
        var condition = new TextCondition(type,this.text,null);
        condition.ignoreCase = true;
        return condition
    };
    SpecificTextRule.prototype.reset = function()
    {
        this.operator = TextCompareType.Contains;
        this.text = "";
        this.style = null
    };
    SpecificTextRule.prototype.toJSON = function()
    {
        return{
                ruleType: RuleType.SpecificTextRule,
                ranges: this.ranges,
                style: this.style,
                operator: this.operator,
                text: this.text
            }
    };
    SpecificTextRule.prototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.ranges)
        {
            this.ranges = [];
            for(var i = 0; i < settings.ranges.length; i++)
            {
                var r = settings.ranges[i];
                this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
            }
        }
        if(settings.style)
        {
            this.style = new UI.Style;
            this.style.copyFrom(settings.style)
        }
        if(settings.operator !== null && settings.operator !== undefined)
            this.operator = settings.operator;
        if(settings.text !== null && settings.text !== undefined)
            this.text = settings.text
    };
    UI.SpecificTextRule = SpecificTextRule;
    TextCondition.prototype = {
        conditionType: "TextCondition",
        questionMarkWildcard: "?",
        questionMarkWildcardRegularExpression: ".",
        asteriskWildcard: "*",
        asteriskWildcardRegularExpression: "[.\n]*",
        ignoreBlank: false,
        adjustOffset: function(row, col){},
        evaluate: function(evaluator, baseRow, baseColumn, actualObj)
        {
            var actual = null;
            if(this.forceValue2Text === true || typeof actualObj === "string" || typeof actualObj === "number")
                actual = actualObj !== undefined && actualObj !== null ? actualObj.toString() : "";
            else
            {
                if(actualObj instanceof Date)
                {
                    if(this.compareType === TextCompareType.BeginsWith || this.compareType === TextCompareType.EndsWith || this.compareType === TextCompareType.Contains)
                        return false;
                    return this.compareType === TextCompareType.DoesNotBeginWith || this.compareType === TextCompareType.DoesNotEndWith || this.compareType === TextCompareType.DoesNotContain
                }
                actual = actualObj !== undefined && actualObj !== null ? actualObj.toString() : ""
            }
            if(this.ignoreBlank && (actual === undefined || actual === null || actual === ""))
                return true;
            var expected = this.getExpectedString(evaluator,baseRow,baseColumn);
            if(this.hasWildcard(expected))
                if(typeof actualObj === "number")
                    return this.compareType === TextCompareType.DoesNotBeginWith || this.compareType === TextCompareType.DoesNotContain || this.compareType === TextCompareType.DoesNotEndWith || this.compareType === TextCompareType.NotEqualsTo;
            switch(this.compareType)
            {
                case TextCompareType.EqualsTo:
                    return this.isEquals(expected,actual);
                case TextCompareType.NotEqualsTo:
                    return!this.isEquals(expected,actual);
                case TextCompareType.BeginsWith:
                    return this.isStartWith(expected,actual);
                case TextCompareType.DoesNotBeginWith:
                    return!this.isStartWith(expected,actual);
                case TextCompareType.EndsWith:
                    return this.isEndWith(expected,actual);
                case TextCompareType.DoesNotEndWith:
                    return!this.isEndWith(expected,actual);
                case TextCompareType.Contains:
                    return this.isContains(expected,actual);
                case TextCompareType.DoesNotContain:
                    return!this.isContains(expected,actual)
            }
            return false
        },
        getExpectedString: function(evaluator, baseRow, baseColumn)
        {
            var obj = this.getExpected(evaluator,baseRow,baseColumn);
            return obj === undefined || obj === null ? null : obj.toString()
        },
        getExpected: function(evaluator, baseRow, basecol)
        {
            if(this.formula && this.formula.length > 0)
            {
                var calcService = evaluator.getCalcService();
                var expr = calcService.parse(this.formula,baseRow,basecol);
                return calcService.evaluateParsedFormula(evaluator._getSheetSource(),expr,baseRow,basecol)
            }
            else
                return this.expected
        },
        hasWildcard: function(text)
        {
            if(text === undefined || text === null || text === "")
                return false;
            return text.indexOf(this.asteriskWildcard) > -1 || text.indexOf(this.questionMarkWildcard) > -1
        },
        isEquals: function(expected, value)
        {
            if(this.useWildCards && this.hasWildcard(expected))
            {
                var regex = this.createEqualsRegex(expected);
                if(regex)
                {
                    var testValue = value === undefined && value === null ? "" : value;
                    regex.lastIndex = -1;
                    if(testValue !== undefined && testValue !== null)
                        return testValue.search(regex) > -1
                }
                return false
            }
            else if(value !== undefined && value !== null && value !== "")
            {
                var testExpected = expected !== undefined && expected !== null ? expected : "";
                if(this.ignoreCase)
                    return testExpected.toLowerCase() === value.toLowerCase();
                else
                    return testExpected === value
            }
            else
                return expected === undefined || expected === null || expected === ""
        },
        createEqualsRegex: function(expression)
        {
            if(this.regex)
                return this.regex;
            expression = this.encodeExpression(expression);
            var sb = expression;
            var re = new RegExp("\\" + this.questionMarkWildcard,"g");
            sb = sb.replace(re,this.questionMarkWildcardRegularExpression);
            re = new RegExp("\\" + this.asteriskWildcard,"g");
            sb = sb.replace(re,this.asteriskWildcardRegularExpression);
            var regex = null;
            try
            {
                var options = this.ignoreCase ? "ig" : "g";
                regex = new RegExp("^" + sb + "$",options)
            }
            catch(ex)
            {
                return null
            }
            return regex
        },
        encodeExpression: function(expression)
        {
            if(!expression)
                return null;
            var encodedExpresstion = expression;
            encodedExpresstion = encodedExpresstion.replace(/\^/g,"\\^");
            encodedExpresstion = encodedExpresstion.replace(/\$/g,"\\$");
            encodedExpresstion = encodedExpresstion.replace(/\(/g,"\\(");
            encodedExpresstion = encodedExpresstion.replace(/\)/g,"\\)");
            encodedExpresstion = encodedExpresstion.replace(/\[/g,"\\[");
            encodedExpresstion = encodedExpresstion.replace(/\]/g,"\\]");
            encodedExpresstion = encodedExpresstion.replace(/\{/g,"\\{");
            encodedExpresstion = encodedExpresstion.replace(/\}/g,"\\}");
            encodedExpresstion = encodedExpresstion.replace(/\./g,"\\.");
            encodedExpresstion = encodedExpresstion.replace(/\+/g,"\\+");
            encodedExpresstion = encodedExpresstion.replace(/\|/g,"\\|");
            return encodedExpresstion
        },
        isStartWith: function(expected, value)
        {
            var testValue;
            if(this.useWildCards && this.hasWildcard(expected))
            {
                var regex = this.createStartWithRegex(expected);
                if(regex)
                {
                    testValue = value === undefined || value === null ? "" : value;
                    regex.lastIndex = -1;
                    if(testValue !== undefined && testValue !== null)
                        return testValue.search(regex) > -1
                }
                return false
            }
            else if(value !== undefined && value !== null && value !== "")
            {
                if(expected === undefined || expected === null)
                    return false;
                var testExpected = expected;
                testValue = value;
                if(this.ignoreCase)
                {
                    testExpected = testExpected.toLowerCase();
                    testValue = testValue.toLowerCase()
                }
                var result = testValue.match("^" + testExpected);
                if(result !== undefined && result !== null && result.length > 0)
                    return result[0] === testExpected;
                else
                    return false
            }
            else
                return expected === undefined || expected === null || expected === ""
        },
        createStartWithRegex: function(expression)
        {
            if(this.regex)
                return this.regex;
            expression = this.encodeExpression(expression);
            var sb = expression;
            var re = new RegExp("\\" + this.questionMarkWildcard,"g");
            sb = sb.replace(re,this.questionMarkWildcardRegularExpression);
            re = new RegExp("\\" + this.asteriskWildcard,"g");
            sb = sb.replace(re,this.asteriskWildcardRegularExpression);
            var regex = null;
            try
            {
                var options = this.ignoreCase ? "ig" : "g";
                if(expression[0] === this.asteriskWildcard)
                    regex = new RegExp(sb,options);
                else
                    regex = new RegExp("^" + sb,options)
            }
            catch(ex)
            {
                return null
            }
            return regex
        },
        isEndWith: function(expected, value)
        {
            var testValue;
            if(this.useWildCards && this.hasWildcard(expected))
            {
                var regex = this.createEndWithRegex(expected);
                if(regex)
                {
                    testValue = value === undefined || value === null ? "" : value;
                    regex.lastIndex = -1;
                    if(testValue !== undefined && testValue !== null)
                        return testValue.search(regex) > -1
                }
                return false
            }
            else if(value !== undefined && value !== null && value !== "")
            {
                if(expected === undefined || expected === null)
                    return false;
                var testExpected = expected;
                testValue = value;
                if(this.ignoreCase)
                {
                    testExpected = testExpected.toLowerCase();
                    testValue = testValue.toLowerCase()
                }
                var result = testValue.match(testExpected + "$");
                if(result !== undefined && result !== null && result.length > 0)
                    return result[0] === testExpected;
                else
                    return false
            }
            else
                return expected === undefined || expected === null || expected === ""
        },
        createEndWithRegex: function(expression)
        {
            if(this.regex)
                return this.regex;
            expression = this.encodeExpression(expression);
            var sb = expression;
            var re = new RegExp("\\" + this.questionMarkWildcard,"g");
            sb = sb.replace(re,this.questionMarkWildcardRegularExpression);
            re = new RegExp("\\" + this.asteriskWildcard,"g");
            sb = sb.replace(re,this.asteriskWildcardRegularExpression);
            var regex = null;
            try
            {
                var options = this.ignoreCase ? "ig" : "g";
                if(expression[expression.length - 1] === this.asteriskWildcard)
                    regex = new RegExp(sb,options);
                else
                    regex = new RegExp(sb + "$",options)
            }
            catch(ex)
            {
                return null
            }
            return regex
        },
        isContains: function(expected, value)
        {
            var testValue;
            if(this.useWildCards && this.hasWildcard(expected))
            {
                var regex = this.createContainsRegex(expected);
                if(regex)
                {
                    testValue = value === undefined || value === null ? "" : value;
                    regex.lastIndex = -1;
                    if(testValue !== undefined && testValue !== null)
                        return testValue.search(regex) > -1
                }
                return false
            }
            else if(value !== undefined && value !== null && value !== "")
            {
                if(expected === undefined || expected === null)
                    return false;
                var testExpected = expected;
                testValue = value;
                if(this.ignoreCase)
                {
                    testExpected = testExpected.toLowerCase();
                    testValue = testValue.toLowerCase()
                }
                return testValue.indexOf(testExpected) > -1
            }
            else
                return expected === undefined || expected === null || expected === ""
        },
        createContainsRegex: function(expression)
        {
            if(this.regex)
                return this.regex;
            expression = this.encodeExpression(expression);
            var sb = expression;
            var re = new RegExp("\\" + this.questionMarkWildcard,"g");
            sb = sb.replace(re,this.questionMarkWildcardRegularExpression);
            re = new RegExp("\\" + this.asteriskWildcard,"g");
            sb = sb.replace(re,this.asteriskWildcardRegularExpression);
            var regex = null;
            try
            {
                var options = this.ignoreCase ? "ig" : "g";
                regex = new RegExp(sb,options)
            }
            catch(ex)
            {
                return null
            }
            return regex
        },
        reset: function()
        {
            this.expected = null;
            this.ignoreBlank = false;
            this.compareType = TextCompareType.EqualsTo;
            this.useWildCards = true;
            this.ignoreCase = false;
            this.forceValue2Text = false
        },
        toJSON: function()
        {
            return{
                    compareType: this.compareType,
                    expected: this.expected,
                    formula: this.formula,
                    forceValue2Text: this.forceValue2Text,
                    useWildCards: this.useWildCards,
                    ignoreCase: this.ignoreCase,
                    regex: this.regex,
                    conType: ConditionType.TextCondition,
                    ignoreBlank: this.ignoreBlank
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.compareType !== null && settings.compareType !== undefined)
                this.compareType = settings.compareType;
            if(settings.expected !== null && settings.expected !== undefined)
                this.expected = settings.expected;
            if(settings.formula !== null && settings.formula !== undefined)
                this.formula = settings.formula;
            if(settings.forceValue2Text !== null && settings.forceValue2Text !== undefined)
                this.forceValue2Text = settings.forceValue2Text;
            if(settings.useWildCards !== null && settings.useWildCards !== undefined)
                this.useWildCards = settings.useWildCards;
            if(settings.ignoreCase !== null && settings.ignoreCase !== undefined)
                this.ignoreCase = settings.ignoreCase;
            if(settings.regex !== null && settings.regex !== undefined)
                this.regex = settings.regex;
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank
        }
    };
    UI.TextCondition = TextCondition;
    function ColorCondition(compareType, expected)
    {
        this.compareType = compareType;
        this.expected = expected
    }
    ColorCondition.prototype = {
        conditionType: "ColorCondition",
        ignoreBlank: false,
        evaluate: function(evaluator, baseRow, baseColumn, actualObj)
        {
            return this.isEqualsColor(evaluator,actualObj)
        },
        isEqualsColor: function(evaluator, actualObj)
        {
            var expectedColor = this.getColorFromString(this.expected);
            if(expectedColor !== undefined && expectedColor !== null && expectedColor !== "")
            {
                var actualColor = this.getColorFromString(actualObj);
                if(actualColor !== undefined && actualColor !== null && actualColor !== "")
                    return actualColor.a === expectedColor.a && actualColor.r === expectedColor.r && actualColor.g === expectedColor.g && actualColor.b === expectedColor.b;
                else
                {
                    var defaultColor;
                    if(this.compareType === ColorCompareType.BackgroundColor)
                    {
                        if(evaluator && evaluator.getDefaultStyle)
                            defaultColor = evaluator.getDefaultStyle().backColor
                    }
                    else if(this.compareType === ColorCompareType.ForegroundColor)
                        if(evaluator && evaluator.getDefaultStyle)
                            defaultColor = evaluator.getDefaultStyle().foreColor;
                    if(defaultColor)
                        return expectedColor.a === defaultColor.a && expectedColor.r === defaultColor.r && expectedColor.g === defaultColor.g && expectedColor.b === defaultColor.b
                }
            }
            else if(this.ignoreBlank)
                return true;
            return false
        },
        getColorFromString: function(colorStr)
        {
            return colorStr === undefined || colorStr === null || colorStr === "" ? null : UI._Color.parse(colorStr)
        },
        reset: function()
        {
            this.expected = null;
            this.ignoreBlank = false;
            this.compareType = ColorCompareType.BackgroundColor
        },
        toJSON: function()
        {
            return{
                    compareType: this.compareType,
                    expected: this.expected,
                    conType: ConditionType.ColorCondition,
                    ignoreBlank: this.ignoreBlank
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.compareType !== null && settings.compareType !== undefined)
                this.compareType = settings.compareType;
            if(settings.expected !== null && settings.expected !== undefined)
                this.expected = settings.expected;
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank
        }
    };
    UI.ColorCondition = ColorCondition;
    function FormulaCondition(customValueType, formula)
    {
        this.customValueType = customValueType;
        this.expected = null;
        var revisedFormula = formula;
        if(typeof revisedFormula === "string")
        {
            var temp = $.trim(revisedFormula);
            if(temp[0] === "=")
                revisedFormula = temp.substr(1);
            else
                revisedFormula = temp
        }
        this.formula = revisedFormula;
        this.ignoreBlank = false
    }
    function FormulaRule(formula, style)
    {
        if(formula && typeof formula !== "string")
            throw new Error("ArgumentException");
        this.formula = formula;
        this.style = style
    }
    FormulaRule.prototype = new ConditionRuleBase;
    FormulaRule.prototype.createCondition = function()
    {
        var condition = new FormulaCondition(CustomValueType.Formula,!this.formula || this.formula === "" ? null : this.formula);
        if(this.ranges && this.ranges.length > 0)
        {
            condition._baseRow = this.ranges[0].row;
            condition._baseCol = this.ranges[0].col
        }
        return condition
    };
    FormulaRule.prototype.reset = function()
    {
        this.formula = null;
        this.style = null
    };
    FormulaRule.prototype.isFormula = function(val)
    {
        return val && val[0] === "="
    };
    FormulaRule.prototype.toJSON = function()
    {
        return{
                ruleType: RuleType.FormulaRule,
                ranges: this.ranges,
                style: this.style,
                formula: this.formula
            }
    };
    FormulaRule.prototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.ranges)
        {
            this.ranges = [];
            for(var i = 0; i < settings.ranges.length; i++)
            {
                var r = settings.ranges[i];
                this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
            }
        }
        if(settings.style)
        {
            this.style = new UI.Style;
            this.style.copyFrom(settings.style)
        }
        if(settings.formula !== null && settings.formula !== undefined)
            this.formula = settings.formula
    };
    UI.FormulaRule = FormulaRule;
    FormulaCondition.prototype = {
        conditionType: "FormulaCondition",
        adjustOffset: function(row, col){},
        evaluate: function(evaluator, baseRow, baseColumn, actualObj)
        {
            if(this.customValueType === CustomValueType.Formula)
            {
                var expected = this.getExpected(evaluator,baseRow,baseColumn);
                if(this.ignoreBlank && (expected === undefined || expected === null || expected === ""))
                    return true;
                try
                {
                    return Calc.Convert.toBool(expected)
                }
                catch(err)
                {
                    return false
                }
            }
            else
            {
                var isError = Calc.Convert.isError;
                switch(this.customValueType)
                {
                    case CustomValueType.Empty:
                        return actualObj === undefined || actualObj === null || actualObj === "";
                    case CustomValueType.NonEmpty:
                        return actualObj !== undefined && actualObj !== null && actualObj !== "";
                    case CustomValueType.Error:
                        return isError(actualObj);
                    case CustomValueType.NonError:
                        return!isError(actualObj)
                }
            }
            return false
        },
        getExpected: function(evaluator, baseRow, baseCol)
        {
            if(this.formula && this.formula.length > 0)
            {
                var calcService = evaluator.getCalcService();
                if(!this._expr)
                {
                    if((this._baseRow === undefined || this._baseRow === null) && (this._baseCol === undefined || this._baseCol === null))
                    {
                        this._baseRow = baseRow;
                        this._baseCol = baseCol
                    }
                    this._expr = calcService.parse(this.formula,this._baseRow,this._baseCol)
                }
                var v = calcService.evaluateParsedFormula(evaluator._getSheetSource(),this._expr,baseRow,baseCol,true);
                var rowCount,
                    colCount,
                    resultArray,
                    r,
                    c;
                if(v instanceof Calc.Reference)
                {
                    rowCount = v.getRowCount(0);
                    colCount = v.getColumnCount(0);
                    resultArray = [];
                    for(r = 0; r < rowCount; r++)
                    {
                        resultArray[r] = [];
                        for(c = 0; c < colCount; c++)
                            resultArray[r][c] = v.getValue(0,r,c)
                    }
                    v = resultArray
                }
                else if(v instanceof Calc.Array)
                {
                    rowCount = v.getRowCount();
                    colCount = v.getColumnCount();
                    resultArray = [];
                    for(r = 0; r < rowCount; r++)
                    {
                        resultArray[r] = [];
                        for(c = 0; c < colCount; c++)
                            resultArray[r][c] = v.getValue(r,c)
                    }
                    v = resultArray
                }
                if(v instanceof Array)
                {
                    rowCount = v.length;
                    colCount = v[0].length;
                    if(rowCount === 1 && colCount === 1)
                        return v[0][0];
                    if(baseRow - this._baseRow < rowCount && baseCol - this._baseCol < colCount)
                        return v[0][0];
                    else
                        return Calc.Errors.NotAvailable
                }
                return v
            }
            else
                return this.expected
        },
        reset: function()
        {
            this.expected = null;
            this.ignoreBlank = false;
            this.customValueType = CustomValueType.Empty;
            this._expr = null;
            this._baseRow = null;
            this._baseCol = null
        },
        toJSON: function()
        {
            return{
                    customValueType: this.customValueType,
                    expected: this.expected,
                    formula: this.formula,
                    conType: ConditionType.FormulaCondition,
                    ignoreBlank: this.ignoreBlank
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.customValueType !== null && settings.customValueType !== undefined)
                this.customValueType = settings.customValueType;
            if(settings.expected !== null && settings.expected !== undefined)
                this.expected = settings.expected;
            if(settings.formula !== null && settings.formula !== undefined)
                this.formula = settings.formula;
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank
        }
    };
    UI.FormulaCondition = FormulaCondition;
    function DateCondition(compareType, expected, formula)
    {
        this.compareType = compareType;
        this.expected = expected;
        this.formula = typeof formula === "string" ? $.trim(formula).replace("=","") : formula
    }
    DateCondition.prototype = {
        conditionType: "DateCondition",
        ignoreBlank: false,
        evaluate: function(evaluator, baseRow, baseColumn, actualObj)
        {
            if(actualObj === undefined || actualObj === null || actualObj === "")
                if(this.ignoreBlank)
                    return true;
            if(!(actualObj instanceof Date))
                return false;
            var expected = this.getExpectedDateTime(evaluator,baseRow,baseColumn);
            if(expected === undefined || expected === null)
                return!!this.ignoreBlank;
            switch(this.compareType)
            {
                case DateCompareType.EqualsTo:
                    return this.isEquals(expected,actualObj);
                case DateCompareType.NotEqualsTo:
                    return!this.isEquals(expected,actualObj);
                case DateCompareType.After:
                    return this.isAfter(expected,actualObj);
                case DateCompareType.AfterEqualsTo:
                    return this.isAfter(expected,actualObj) || this.isEquals(expected,actualObj);
                case DateCompareType.Before:
                    return this.isBefore(expected,actualObj);
                case DateCompareType.BeforeEqualsTo:
                    return this.isBefore(expected,actualObj) || this.isEquals(expected,actualObj)
            }
            return false
        },
        getExpectedDateTime: function(evaluator, baseRow, basecol)
        {
            var obj = this.getExpected(evaluator,baseRow,basecol);
            if(obj instanceof Date)
                return obj;
            else if(typeof obj === "string")
                return UI._DateTimeHelper.parseLocale(obj);
            return null
        },
        getExpected: function(evaluator, baseRow, basecol)
        {
            if(this.formula && this.formula.length > 0)
            {
                var calcService = evaluator.getCalcService();
                var expr = calcService.parse(this.formula,baseRow,basecol);
                return calcService.evaluateParsedFormula(evaluator._getSheetSource(),expr,baseRow,basecol)
            }
            else
                return this.expected
        },
        isEquals: function(expectedValue, value)
        {
            return expectedValue.getYear() === value.getYear() && expectedValue.getMonth() === value.getMonth() && expectedValue.getDate() === value.getDate()
        },
        isAfter: function(expectedValue, value)
        {
            var date = this.createDayEnding(expectedValue);
            return value > date
        },
        createDayEnding: function(datetime)
        {
            return new Date(datetime.getFullYear(),datetime.getMonth(),datetime.getDate(),23,59,59,999)
        },
        isBefore: function(expectedValue, value)
        {
            var date = this.createDayBeginning(expectedValue);
            return value < date
        },
        createDayBeginning: function(datetime)
        {
            return new Date(datetime.getFullYear(),datetime.getMonth(),datetime.getDate(),0,0,0,0)
        },
        reset: function()
        {
            this.expected = null;
            this.ignoreBlank = false;
            this.compareType = DateCompareType.EqualsTo
        },
        toJSON: function()
        {
            return{
                    compareType: this.compareType,
                    expected: this.expected,
                    formula: this.formula,
                    conType: ConditionType.DateCondition,
                    ignoreBlank: this.ignoreBlank
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.compareType !== null && settings.compareType !== undefined)
                this.compareType = settings.compareType;
            if(settings.expected !== null && settings.expected !== undefined)
                if($.type(settings.expected) === "string")
                    this.expected = new Date(settings.expected);
                else
                    this.expected = settings.expected;
            if(settings.formula !== null && settings.formula !== undefined)
                this.formula = settings.formula;
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank
        }
    };
    UI.DateCondition = DateCondition;
    function DateExCondition(expected)
    {
        this.ignoreBlank = false;
        this.formula = null;
        this.expected = expected;
        this.expectTypeId = 0
    }
    function DateOccurringRule(type, style)
    {
        this.type = type;
        this.style = style
    }
    DateOccurringRule.prototype = new ConditionRuleBase;
    DateOccurringRule.prototype.createCondition = function()
    {
        return new DateExCondition(this.type)
    };
    DateOccurringRule.prototype.reset = function()
    {
        this.type = DateOccurringType.Today;
        this.style = null
    };
    DateOccurringRule.prototype.toJSON = function()
    {
        return{
                ruleType: RuleType.DateOccurringRule,
                ranges: this.ranges,
                style: this.style,
                type: this.type
            }
    };
    DateOccurringRule.prototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.ranges)
        {
            this.ranges = [];
            for(var i = 0; i < settings.ranges.length; i++)
            {
                var r = settings.ranges[i];
                this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
            }
        }
        if(settings.style)
        {
            this.style = new UI.Style;
            this.style.copyFrom(settings.style)
        }
        if(settings.type !== null && settings.type !== undefined)
            this.type = settings.type
    };
    UI.DateOccurringRule = DateOccurringRule;
    DateExCondition.prototype = {
        conditionType: "DateExCondition",
        adjustOffset: function(row, col){},
        evaluate: function(evaluator, baseRow, baseColumn, actualObj)
        {
            var expected = this.getExpectedInt(evaluator,baseRow,baseColumn);
            if(expected !== undefined && expected !== null)
                return this.checkCondition(expected,actualObj);
            return false
        },
        getExpectedInt: function(evaluator, baseRow, baseColumn)
        {
            var obj = this.getExpected(evaluator,baseRow,baseColumn);
            obj = parseInt(obj,10);
            return isNaN(obj) ? null : obj
        },
        getExpected: function(evaluator, baseRow, basecol)
        {
            if(this.formula && this.formula.length > 0)
            {
                var calcService = evaluator.getCalcService();
                var expr = calcService.parse(this.formula,baseRow,basecol);
                return calcService.evaluateParsedFormula(evaluator._getSheetSource(),expr,baseRow,basecol)
            }
            else
                return this.expected
        },
        checkCondition: function(expected, actualValue)
        {
            var actual = actualValue;
            if(this.ignoreBlank && (actual === undefined || actual === null || actual === ""))
                return true;
            try
            {
                actual = Calc.Convert.toDateTime(actual)
            }
            catch(err)
            {
                return false
            }
            var expectedNumber;
            if(this.expectTypeId === 0)
            {
                var from = null;
                var to = null;
                var now = new Date;
                var now2 = new Date(now.getFullYear(),now.getMonth(),now.getDate(),now.getHours(),now.getMinutes(),now.getSeconds(),now.getMilliseconds());
                switch(expected)
                {
                    case DateOccurringType.Last7Days:
                        now2.setDate(now2.getDate() - 6);
                        from = new Date(now2.getFullYear(),now2.getMonth(),now2.getDate(),0,0,0,0);
                        to = new Date(now.getFullYear(),now.getMonth(),now.getDate(),23,59,59,999);
                        break;
                    case DateOccurringType.Yesterday:
                        now2.setDate(now2.getDate() - 1);
                        from = new Date(now2.getFullYear(),now2.getMonth(),now2.getDate(),0,0,0,0);
                        to = new Date(now2.getFullYear(),now2.getMonth(),now2.getDate(),23,59,59,999);
                        break;
                    case DateOccurringType.Today:
                        from = new Date(now.getFullYear(),now.getMonth(),now.getDate(),0,0,0,0);
                        to = new Date(now.getFullYear(),now.getMonth(),now.getDate(),23,59,59,999);
                        break;
                    case DateOccurringType.Tomorrow:
                        now2.setDate(now2.getDate() + 1);
                        from = new Date(now2.getFullYear(),now2.getMonth(),now2.getDate(),0,0,0,0);
                        to = new Date(now2.getFullYear(),now2.getMonth(),now2.getDate(),23,59,59,999);
                        break;
                    case DateOccurringType.LastWeek:
                        var firstDayOfLastWeek = now;
                        firstDayOfLastWeek.setDate(now.getDate() - now.getDay() - 7);
                        var lastDayOfLastWeek = now2;
                        lastDayOfLastWeek.setDate(now2.getDate() - now2.getDay() - 1);
                        from = new Date(firstDayOfLastWeek.getFullYear(),firstDayOfLastWeek.getMonth(),firstDayOfLastWeek.getDate(),0,0,0,0);
                        to = new Date(lastDayOfLastWeek.getFullYear(),lastDayOfLastWeek.getMonth(),lastDayOfLastWeek.getDate(),23,59,59,999);
                        break;
                    case DateOccurringType.ThisWeek:
                        var firstDayOfThisWeek = now;
                        firstDayOfThisWeek.setDate(now.getDate() - now.getDay());
                        var lastDayOfThisWeek = now2;
                        lastDayOfThisWeek.setDate(now2.getDate() - now2.getDay() + 6);
                        from = new Date(firstDayOfThisWeek.getFullYear(),firstDayOfThisWeek.getMonth(),firstDayOfThisWeek.getDate(),0,0,0,0);
                        to = new Date(lastDayOfThisWeek.getFullYear(),lastDayOfThisWeek.getMonth(),lastDayOfThisWeek.getDate(),23,59,59,999);
                        break;
                    case DateOccurringType.NextWeek:
                        var firstDayOfNextWeek = now;
                        firstDayOfNextWeek.setDate(now.getDate() - now.getDay() + 7);
                        var lastDayOfNextWeek = now2;
                        lastDayOfNextWeek.setDate(now2.getDate() - now2.getDay() + 13);
                        from = new Date(firstDayOfNextWeek.getFullYear(),firstDayOfNextWeek.getMonth(),firstDayOfNextWeek.getDate(),0,0,0,0);
                        to = new Date(lastDayOfNextWeek.getFullYear(),lastDayOfNextWeek.getMonth(),lastDayOfNextWeek.getDate(),23,59,59,999);
                        break;
                    case DateOccurringType.LastMonth:
                        var firstDayOfLastMonth = now;
                        firstDayOfLastMonth.setDate(1);
                        firstDayOfLastMonth.setMonth(now.getMonth() - 1);
                        var lastDayOfLastMonth = now2;
                        lastDayOfLastMonth.setDate(0);
                        from = new Date(firstDayOfLastMonth.getFullYear(),firstDayOfLastMonth.getMonth(),firstDayOfLastMonth.getDate(),0,0,0,0);
                        to = new Date(lastDayOfLastMonth.getFullYear(),lastDayOfLastMonth.getMonth(),lastDayOfLastMonth.getDate(),23,59,59,999);
                        break;
                    case DateOccurringType.ThisMonth:
                        var firstDayOfThisMonth = now;
                        firstDayOfThisMonth.setDate(1);
                        var lastDayOfThisMonth = now2;
                        lastDayOfThisMonth.setDate(1);
                        lastDayOfThisMonth.setMonth(now2.getMonth() + 1);
                        lastDayOfThisMonth.setDate(0);
                        from = new Date(firstDayOfThisMonth.getFullYear(),firstDayOfThisMonth.getMonth(),firstDayOfThisMonth.getDate(),0,0,0,0);
                        to = new Date(lastDayOfThisMonth.getFullYear(),lastDayOfThisMonth.getMonth(),lastDayOfThisMonth.getDate(),23,59,59,999);
                        break;
                    case DateOccurringType.NextMonth:
                        var firstDayOfNextMonth = now;
                        firstDayOfNextMonth.setDate(1);
                        firstDayOfNextMonth.setMonth(now.getMonth() + 1);
                        var lastDayOfNextMonth = now2;
                        lastDayOfNextMonth.setDate(1);
                        lastDayOfNextMonth.setMonth(now2.getMonth() + 2);
                        lastDayOfNextMonth.setDate(0);
                        from = new Date(firstDayOfNextMonth.getFullYear(),firstDayOfNextMonth.getMonth(),firstDayOfNextMonth.getDate(),0,0,0,0);
                        to = new Date(lastDayOfNextMonth.getFullYear(),lastDayOfNextMonth.getMonth(),lastDayOfNextMonth.getDate(),23,59,59,999);
                        break
                }
                if(from !== undefined && from !== null && to !== undefined && to !== null)
                {
                    var condition1 = new DateCondition(DateCompareType.AfterEqualsTo,from,null);
                    var condition2 = new DateCondition(DateCompareType.BeforeEqualsTo,to,null);
                    var condition = new RelationCondition(RelationCompareType.And,condition1,condition2);
                    return condition.evaluate(null,0,0,actual)
                }
            }
            else if(this.expectTypeId === 1)
            {
                expectedNumber = this.getExpectedInt(null,0,0);
                if(expectedNumber !== undefined && expectedNumber !== null)
                    return this.isEqualsYear(expectedNumber,actual)
            }
            else if(this.expectTypeId === 2)
            {
                expectedNumber = this.getExpectedInt(null,0,0);
                if(expectedNumber !== undefined && expectedNumber !== null)
                    return this.isEqualsQuarter(expectedNumber,actual)
            }
            else if(this.expectTypeId === 3)
            {
                expectedNumber = this.getExpectedInt(null,0,0);
                if(expectedNumber !== undefined && expectedNumber !== null)
                    return this.isEqualsMonth(expectedNumber,actual)
            }
            else if(this.expectTypeId === 4)
            {
                expectedNumber = this.getExpectedInt(null,0,0);
                if(expectedNumber !== undefined && expectedNumber !== null)
                    return this.isEqualsWeek(expectedNumber,actual)
            }
            else if(this.expectTypeId === 5)
            {
                expectedNumber = this.getExpectedInt(null,0,0);
                if(expectedNumber !== undefined && expectedNumber !== null)
                    return this.isEqualsDay(expectedNumber,actual)
            }
            return false
        },
        isEqualsYear: function(expected, actualDateTime)
        {
            return expected === actualDateTime.getFullYear()
        },
        isEqualsQuarter: function(expected, actualDateTime)
        {
            switch(expected)
            {
                case QuarterType.Quarter1:
                    return actualDateTime.getMonth() >= 1 && actualDateTime.getMonth() <= 3;
                case QuarterType.Quarter2:
                    return actualDateTime.getMonth() >= 4 && actualDateTime.getMonth() <= 6;
                case QuarterType.Quarter3:
                    return actualDateTime.getMonth() >= 7 && actualDateTime.getMonth() <= 9;
                case QuarterType.Quarter4:
                    return actualDateTime.getMonth() >= 10 && actualDateTime.getMonth() <= 12
            }
            return false
        },
        isEqualsMonth: function(expected, actualDateTime)
        {
            return expected === actualDateTime.getMonth()
        },
        isEqualsWeek: function(expected, actualDateTime)
        {
            return expected === actualDateTime.getDay()
        },
        isEqualsDay: function(expected, actualDateTime)
        {
            return expected === actualDateTime.getDate()
        },
        reset: function()
        {
            this.expected = null;
            this.ignoreBlank = false;
            this.expectTypeId = 0
        },
        toJSON: function()
        {
            return{
                    ignoreBlank: this.ignoreBlank,
                    formula: this.formula,
                    expected: this.expected,
                    expectTypeId: this.expectTypeId,
                    conType: ConditionType.DateExCondition
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank;
            if(settings.formula !== null && settings.formula !== undefined)
                this.formula = settings.formula;
            if(settings.expected !== null && settings.expected !== undefined)
                this.expected = settings.expected;
            if(settings.expectTypeId !== null && settings.expectTypeId !== undefined)
                this.expectTypeId = settings.expectTypeId
        }
    };
    DateExCondition.fromDay = function(day)
    {
        var con = new DateExCondition(day);
        con.expectTypeId = 5;
        return con
    };
    DateExCondition.fromMonth = function(month)
    {
        var con = new DateExCondition(month);
        con.expectTypeId = 3;
        return con
    };
    DateExCondition.fromQuarter = function(quarter)
    {
        var con = new DateExCondition(quarter);
        con.expectTypeId = 2;
        return con
    };
    DateExCondition.fromWeek = function(week)
    {
        var con = new DateExCondition(week);
        con.expectTypeId = 4;
        return con
    };
    DateExCondition.fromYear = function(year)
    {
        var con = new DateExCondition(year);
        con.expectTypeId = 1;
        return con
    };
    UI.DateExCondition = DateExCondition;
    function TextLengthCondition(compareType, expected, formula)
    {
        this.compareType = compareType;
        this.expected = expected;
        this.formula = typeof formula === "string" ? $.trim(formula).replace("=","") : formula;
        this.ignoreBlank = false
    }
    TextLengthCondition.prototype = {
        conditionType: "TextLengthCondition",
        evaluate: function(evaluator, baseRow, baseColumn, actualObj)
        {
            if(actualObj === undefined || actualObj === null || actualObj === "")
                return this.ignoreBlank;
            var actualLen = actualObj === undefined || actualObj === null ? 0 : actualObj.length;
            var expectedLen = this.getExpectedInt(evaluator,baseRow,baseColumn);
            if(typeof expectedLen === "number")
                switch(this.compareType)
                {
                    case GeneralCompareType.EqualsTo:
                        return actualLen === expectedLen;
                    case GeneralCompareType.GreaterThan:
                        return actualLen > expectedLen;
                    case GeneralCompareType.GreaterThanOrEqualsTo:
                        return actualLen >= expectedLen;
                    case GeneralCompareType.LessThan:
                        return actualLen < expectedLen;
                    case GeneralCompareType.LessThanOrEqualsTo:
                        return actualLen <= expectedLen;
                    case GeneralCompareType.NotEqualsTo:
                        return actualLen !== expectedLen
                }
            return false
        },
        getExpectedInt: function(evaluator, baseRow, basecol)
        {
            var obj = this.getExpected(evaluator,baseRow,basecol);
            obj = parseInt(obj,10);
            return isNaN(obj) ? null : obj
        },
        getExpected: function(evaluator, baseRow, basecol)
        {
            if(this.formula && this.formula.length > 0)
            {
                var calcService = evaluator.getCalcService();
                var expr = calcService.parse(this.formula,baseRow,basecol);
                return calcService.evaluateParsedFormula(evaluator._getSheetSource(),expr,baseRow,basecol)
            }
            else
                return this.expected
        },
        reset: function()
        {
            this.expected = null;
            this.ignoreBlank = false;
            this.compareType = GeneralCompareType.EqualsTo
        },
        toJSON: function()
        {
            return{
                    compareType: this.compareType,
                    expected: this.expected,
                    formula: this.formula,
                    conType: ConditionType.TextLengthCondition,
                    ignoreBlank: this.ignoreBlank
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.compareType !== null && settings.compareType !== undefined)
                this.compareType = settings.compareType;
            if(settings.expected !== null && settings.expected !== undefined)
                this.expected = settings.expected;
            if(settings.formula !== null && settings.formula !== undefined)
                this.formula = settings.formula;
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank
        }
    };
    UI.TextLengthCondition = TextLengthCondition;
    function Top10Condition(type, rank, ranges)
    {
        this.expected = rank;
        this.type = type;
        this.isPercent = false;
        this.ignoreBlank = false;
        this.ranges = ranges
    }
    function Top10Rule(type, rank, style)
    {
        this.type = type;
        this.rank = rank;
        this.style = style
    }
    Top10Rule.prototype = new ConditionRuleBase;
    Top10Rule.prototype.createCondition = function()
    {
        return new Top10Condition(this.type,this.rank,this.ranges)
    };
    Top10Rule.prototype.reset = function()
    {
        this.type = Top10ConditionType.Top;
        this.rank = 10;
        this.style = null
    };
    Top10Rule.prototype.toJSON = function()
    {
        return{
                ruleType: RuleType.Top10Rule,
                ranges: this.ranges,
                style: this.style,
                type: this.type,
                rank: this.rank
            }
    };
    Top10Rule.prototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.ranges)
        {
            this.ranges = [];
            for(var i = 0; i < settings.ranges.length; i++)
            {
                var r = settings.ranges[i];
                this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
            }
        }
        if(settings.style)
        {
            this.style = new UI.Style;
            this.style.copyFrom(settings.style)
        }
        if(settings.type !== null && settings.type !== undefined)
            this.type = settings.type;
        if(settings.rank !== null && settings.rank !== undefined)
            this.rank = settings.rank
    };
    UI.Top10Rule = Top10Rule;
    Top10Condition.prototype = {
        conditionType: "Top10Condition",
        adjustOffset: function(row, col){},
        evaluate: function(evaluator, baseRow, baseColumn, actualObj)
        {
            if(actualObj === undefined || actualObj === null || actualObj === "")
                return this.ignoreBlank;
            var expectedRank = this.getExpectedInt(evaluator,baseRow,baseColumn);
            if(expectedRank !== undefined && expectedRank !== null)
            {
                var values = null;
                if(this.type === Top10ConditionType.Top)
                    values = this.getMaxValues(evaluator,expectedRank,this.ranges);
                else if(this.type === Top10ConditionType.Bottom)
                    values = this.getMinValues(evaluator,expectedRank,this.ranges);
                if(values)
                {
                    var dactual = 0;
                    try
                    {
                        dactual = this._toDouble(actualObj)
                    }
                    catch(ex)
                    {
                        return false
                    }
                    if(values.contains(dactual))
                        return true
                }
            }
            return false
        },
        getExpectedInt: function(evaluator, baseRow, baseColumn)
        {
            var obj = this.getExpected(evaluator,baseRow,baseColumn);
            obj = parseInt(obj,10);
            return isNaN(obj) || !isFinite(obj) ? null : obj
        },
        getExpected: function(evaluator, baseRow, basecol)
        {
            if(this.formula && this.formula.length > 0)
            {
                var calcService = evaluator.getCalcService();
                var expr = calcService.parse(this.formula,baseRow,basecol);
                return calcService.evaluateParsedFormula(evaluator._getSheetSource(),expr,baseRow,basecol)
            }
            else
                return this.expected
        },
        getMaxValues: function(evaluator, rank, ranges)
        {
            var values = [];
            if(ranges)
            {
                var min = Number.MAX_VALUE;
                var cnt = 0;
                for(var i = 0; i < ranges.length; i++)
                {
                    var range = ranges[i];
                    for(var r = 0; r < range.rowCount; r++)
                    {
                        var row = r + range.row;
                        for(var c = 0; c < range.colCount; c++)
                        {
                            var column = c + range.col;
                            var o = evaluator.getValue(row,column);
                            var doubleValue = this._toDouble(o);
                            if(o !== undefined && o !== null && doubleValue !== undefined && doubleValue !== null)
                                try
                                {
                                    if(cnt < rank)
                                    {
                                        values.push(doubleValue);
                                        if(doubleValue < min)
                                            min = doubleValue;
                                        cnt++
                                    }
                                    else if(doubleValue > min)
                                    {
                                        values.remove(min);
                                        values.push(doubleValue);
                                        if(values.indexOf(min) < 0)
                                        {
                                            min = doubleValue;
                                            for(var n = 0; n < values.length; n++)
                                                if(values[n] < min)
                                                    min = values[n]
                                        }
                                    }
                                }
                                catch(ex){}
                        }
                    }
                }
            }
            return values
        },
        getMinValues: function(evaluator, rank, ranges)
        {
            var values = [];
            if(ranges)
            {
                var max = -Number.MAX_VALUE;
                var cnt = 0;
                for(var i = 0; i < ranges.length; i++)
                {
                    var range = ranges[i];
                    for(var r = 0; r < range.rowCount; r++)
                    {
                        var row = r + range.row;
                        for(var c = 0; c < range.colCount; c++)
                        {
                            var column = c + range.col;
                            var o = evaluator.getValue(row,column);
                            var doubleValue = this._toDouble(o);
                            if(o !== undefined && o !== null && doubleValue !== undefined && doubleValue !== null)
                                try
                                {
                                    if(cnt < rank)
                                    {
                                        values.push(doubleValue);
                                        if(doubleValue > max)
                                            max = doubleValue;
                                        cnt++
                                    }
                                    else if(doubleValue < max)
                                    {
                                        values.remove(max);
                                        values.push(doubleValue);
                                        if(values.indexOf(max) < 0)
                                        {
                                            max = doubleValue;
                                            for(var n = 0; n < values.length; n++)
                                                if(values[n] > max)
                                                    max = values[n]
                                        }
                                    }
                                }
                                catch(ex){}
                        }
                    }
                }
            }
            return values
        },
        reset: function()
        {
            this.expected = null;
            this.ignoreBlank = false;
            this.type = Top10ConditionType.Top;
            this.isPercent = false;
            this.ranges = null
        },
        _toDouble: function(value)
        {
            if(typeof value === 'number' || value instanceof Date)
                return Calc.Convert.toDouble(value);
            else
                return null
        },
        toJSON: function()
        {
            return{
                    expected: this.expected,
                    formula: this.formula,
                    type: this.type,
                    isPercent: this.isPercent,
                    ignoreBlank: this.ignoreBlank,
                    ranges: this.ranges,
                    conType: ConditionType.Top10Condition
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.expected !== null && settings.expected !== undefined)
                this.expected = settings.expected;
            if(settings.formula !== null && settings.formula !== undefined)
                this.formula = settings.formula;
            if(settings.type !== null && settings.type !== undefined)
                this.type = settings.type;
            if(settings.isPercent !== null && settings.isPercent !== undefined)
                this.isPercent = settings.isPercent;
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank;
            if(settings.ranges)
            {
                this.ranges = [];
                for(var i = 0; i < settings.ranges.length; i++)
                {
                    var r = settings.ranges[i];
                    this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
                }
            }
        }
    };
    UI.Top10Condition = Top10Condition;
    function UniqueCondition(duplicated, ranges)
    {
        this.expected = duplicated;
        this.ranges = ranges
    }
    function UniqueRule(style)
    {
        this.style = style
    }
    UniqueRule.prototype = new ConditionRuleBase;
    UniqueRule.prototype.createCondition = function()
    {
        return new UniqueCondition(false,this.ranges)
    };
    UniqueRule.prototype.toJSON = function()
    {
        return{
                ruleType: RuleType.UniqueRule,
                ranges: this.ranges,
                style: this.style
            }
    };
    UniqueRule.prototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.ranges)
        {
            this.ranges = [];
            for(var i = 0; i < settings.ranges.length; i++)
            {
                var r = settings.ranges[i];
                this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
            }
        }
        if(settings.style)
        {
            this.style = new UI.Style;
            this.style.copyFrom(settings.style)
        }
    };
    UI.UniqueRule = UniqueRule;
    function DuplicateRule(style)
    {
        this.style = style
    }
    DuplicateRule.prototype = new ConditionRuleBase;
    DuplicateRule.prototype.createCondition = function()
    {
        return new UniqueCondition(true,this.ranges)
    };
    DuplicateRule.prototype.toJSON = function()
    {
        return{
                ruleType: RuleType.DuplicateRule,
                ranges: this.ranges,
                style: this.style
            }
    };
    DuplicateRule.prototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.ranges)
        {
            this.ranges = [];
            for(var i = 0; i < settings.ranges.length; i++)
            {
                var r = settings.ranges[i];
                this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
            }
        }
        if(settings.style)
        {
            this.style = new UI.Style;
            this.style.copyFrom(settings.style)
        }
    };
    UI.DuplicateRule = DuplicateRule;
    UniqueCondition.prototype = {
        conditionType: "UniqueCondition",
        ignoreBlank: false,
        adjustOffset: function(row, col){},
        evaluate: function(evaluator, baseRow, baseColumn, actualObj)
        {
            var value = actualObj;
            if(value === undefined || value === null || value === "")
                return this.ignoreBlank;
            var convert = Calc.Convert;
            if(convert.isNumber(value))
                value = convert.toDouble(value);
            var expectDuplicated = this.getExpectedBoolean(evaluator,baseRow,baseColumn);
            if(expectDuplicated !== undefined && expectDuplicated !== null)
            {
                var cached = this.getDuplicated(evaluator,this.ranges);
                if(cached !== undefined && cached !== null)
                    if(cached.contains(value))
                        return expectDuplicated === true;
                    else
                        return expectDuplicated !== true;
                return expectDuplicated !== true
            }
            return false
        },
        getExpectedBoolean: function(evaluator, baseRow, baseColumn)
        {
            var obj = this.getExpected(evaluator,baseRow,baseColumn);
            try
            {
                return Calc.Convert.toBool(obj)
            }
            catch(err)
            {
                return null
            }
        },
        getExpected: function(evaluator, baseRow, baseColumn)
        {
            if(this.formula && this.formula.length > 0)
            {
                var calcService = evaluator.getCalcService();
                var expr = calcService.parse(this.formula,baseRow,baseColumn);
                return calcService.evaluateParsedFormula(evaluator._getSheetSource(),expr,baseRow,baseColumn)
            }
            else
                return this.expected
        },
        getDuplicated: function(evaluator, ranges)
        {
            var map = [],
                duplicated = [];
            if(ranges)
            {
                var length = ranges.length;
                var convert = Calc.Convert;
                for(var i = 0; i < length; i++)
                {
                    var cellrange = ranges[i];
                    for(var r = 0; r < cellrange.rowCount; r++)
                    {
                        var row = r + cellrange.row;
                        for(var c = 0; c < cellrange.colCount; c++)
                        {
                            var column = c + cellrange.col;
                            var o = evaluator.getValue(row,column,3);
                            if(o !== undefined && o !== null)
                            {
                                if(convert.isNumber(o))
                                    o = convert.toDouble(o);
                                if(this._containsKey(map,o))
                                {
                                    if(!duplicated.contains(o))
                                        duplicated.push(o)
                                }
                                else
                                    map.push({
                                        key: o,
                                        value: false
                                    })
                            }
                        }
                    }
                }
            }
            return duplicated
        },
        _containsKey: function(map, key)
        {
            var length = map.length;
            for(var i = 0; i < length; i++)
                if(map[i].key === key)
                    return true;
            return false
        },
        reset: function()
        {
            this.expected = null;
            this.ignoreBlank = false;
            this.ranges = null
        },
        toJSON: function()
        {
            return{
                    expected: this.expected,
                    formula: this.formula,
                    ranges: this.ranges,
                    conType: ConditionType.UniqueCondition,
                    ignoreBlank: this.ignoreBlank
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.expected !== null && settings.expected !== undefined)
                this.expected = settings.expected;
            if(settings.formula !== null && settings.formula !== undefined)
                this.formula = settings.formula;
            if(settings.ranges)
            {
                this.ranges = [];
                for(var i = 0; i < settings.ranges.length; i++)
                {
                    var r = settings.ranges[i];
                    this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
                }
            }
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank
        }
    };
    UI.UniqueCondition = UniqueCondition;
    function AverageCondition(type, ranges)
    {
        this.type = type;
        this.ranges = ranges
    }
    function AverageRule(type, style)
    {
        this.type = type;
        this.style = style
    }
    AverageRule.prototype = new ConditionRuleBase;
    AverageRule.prototype.createCondition = function()
    {
        return new AverageCondition(this.type,this.ranges)
    };
    AverageRule.prototype.reset = function()
    {
        this.type = AverageConditionType.Above;
        this.style = null
    };
    AverageRule.prototype.toJSON = function()
    {
        return{
                ruleType: RuleType.AverageRule,
                ranges: this.ranges,
                style: this.style,
                type: this.type
            }
    };
    AverageRule.prototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.ranges)
        {
            this.ranges = [];
            for(var i = 0; i < settings.ranges.length; i++)
            {
                var r = settings.ranges[i];
                this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
            }
        }
        if(settings.style)
        {
            this.style = new UI.Style;
            this.style.copyFrom(settings.style)
        }
        if(settings.type !== null && settings.type !== undefined)
            this.type = settings.type
    };
    UI.AverageRule = AverageRule;
    AverageCondition.prototype = {
        conditionType: "AverageCondition",
        ignoreBlank: false,
        adjustOffset: function(row, col){},
        evaluate: function(evaluator, baseRow, baseColumn, actualObj)
        {
            if(this.ignoreBlank && (actualObj === undefined || actualObj === null || actualObj === ""))
                return true;
            var convert = Calc.Convert;
            var toDouble = convert.toDouble;
            this._rebuildFormula();
            var averageValue = this.getExpectedDouble(evaluator,baseRow,baseColumn);
            var stddevValue = null;
            var stddevObjValue = this._stdevExpr ? this._getExpectedByExpression(evaluator,this._stdevExpr,baseRow,baseColumn) : null;
            if(stddevObjValue !== undefined && stddevObjValue !== null)
                try
                {
                    stddevValue = toDouble(stddevObjValue)
                }
                catch(err)
                {
                    stddevValue = NaN
                }
            if(convert.isNumber(actualObj))
            {
                var cellValue = toDouble(actualObj);
                if(!isNaN(averageValue))
                    switch(this.type)
                    {
                        case AverageConditionType.Above:
                            return cellValue > averageValue;
                        case AverageConditionType.Below:
                            return cellValue < averageValue;
                        case AverageConditionType.EqualOrAbove:
                            return cellValue >= averageValue;
                        case AverageConditionType.EqualOrBelow:
                            return cellValue <= averageValue;
                        case AverageConditionType.Above1StdDev:
                            return!isNaN(stddevValue) ? cellValue > averageValue + stddevValue : false;
                        case AverageConditionType.Below1StdDev:
                            return!isNaN(stddevValue) ? cellValue < averageValue - stddevValue : false;
                        case AverageConditionType.Above2StdDev:
                            return!isNaN(stddevValue) ? cellValue > averageValue + 2 * stddevValue : false;
                        case AverageConditionType.Below2StdDev:
                            return!isNaN(stddevValue) ? cellValue < averageValue - 2 * stddevValue : false;
                        case AverageConditionType.Above3StdDev:
                            return!isNaN(stddevValue) ? cellValue > averageValue + 3 * stddevValue : false;
                        case AverageConditionType.Below3StdDev:
                            return!isNaN(stddevValue) ? cellValue < averageValue - 3 * stddevValue : false
                    }
            }
            return false
        },
        _rebuildFormula: function()
        {
            if(this.ranges)
            {
                this._expr = this._createExpression("AVERAGE",this.ranges);
                if(this.type === AverageConditionType.Above1StdDev || this.type === AverageConditionType.Below1StdDev || this.type === AverageConditionType.Above2StdDev || this.type === AverageConditionType.Below2StdDev || this.type === AverageConditionType.Above3StdDev || this.type === AverageConditionType.Below3StdDev)
                    this._stdevExpr = this._createExpression("STDEV",this.ranges)
            }
        },
        _createExpression: function(name, parameters)
        {
            var expressions = Calc.Expressions,
                functions = Calc.Functions,
                convert = Calc.Convert;
            var averageFunc = functions.findGlobalFunction(name);
            if(averageFunc)
            {
                var args = [];
                var length = parameters.length;
                for(var i = 0; i < length; i++)
                {
                    var obj = parameters[i];
                    if(obj instanceof expressions.Expression)
                        args[i] = obj;
                    else if(obj instanceof UI.Range)
                        args[i] = new expressions.RangeExpression(obj.row,obj.col,obj.row + obj.rowCount - 1,obj.col + obj.colCount - 1);
                    else if(convert.isNumber(obj) && !isNaN(obj = convert.toDouble(obj)))
                        args[i] = new expressions.DoubleExpression(obj,obj.toString());
                    else
                        throw'NotSupportedException';
                }
                return new expressions.FunctionExpression(averageFunc,args)
            }
            return null
        },
        getExpectedDouble: function(evaluator, baseRow, baseColumn)
        {
            var obj = this.getExpected(evaluator,baseRow,baseColumn);
            try
            {
                return Calc.Convert.toDouble(obj)
            }
            catch(err)
            {
                return NaN
            }
        },
        getExpected: function(evaluator, baseRow, baseColumn)
        {
            var calcService = evaluator.getCalcService();
            var formula = calcService.unparse(this._expr,baseRow,baseColumn);
            this._expr = calcService.parse(formula,baseRow,baseColumn);
            return calcService.evaluateParsedFormula(evaluator._getSheetSource(),this._expr,baseRow,baseColumn)
        },
        _getExpectedByExpression: function(evaluator, expression, baseRow, baseColumn)
        {
            var calcService = evaluator.getCalcService();
            var formula = calcService.unparse(expression,baseRow,baseColumn);
            expression = calcService.parse(formula,baseRow,baseColumn);
            return calcService.evaluateParsedFormula(evaluator._getSheetSource(),expression,baseRow,baseColumn)
        },
        reset: function()
        {
            this.ignoreBlank = false;
            this.type = AverageConditionType.Above;
            this.ranges = null;
            this._expr = null;
            this._stdevExpr = null
        },
        toJSON: function()
        {
            return{
                    type: this.type,
                    ranges: this.ranges,
                    conType: ConditionType.AverageCondition,
                    ignoreBlank: this.ignoreBlank
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.type !== null && settings.type !== undefined)
                this.type = settings.type;
            if(settings.ranges)
            {
                this.ranges = [];
                for(var i = 0; i < settings.ranges.length; i++)
                {
                    var r = settings.ranges[i];
                    this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
                }
            }
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank
        }
    };
    UI.AverageCondition = AverageCondition;
    function ScaleValue(type, value)
    {
        this.type = type;
        this.value = value
    }
    UI.ScaleValue = ScaleValue;
    function ScaleRule(scaleValue1, scaleValue2, scaleValue3)
    {
        this.scales = [scaleValue1,scaleValue2,scaleValue3];
        this.expected = [];
        this.lowestValueCached = null;
        this.highestValueCached = null;
        this.cached = false
    }
    var ScaleRulePrototype = new ConditionRuleBase;
    ScaleRulePrototype.isScaleRule = function()
    {
        return true
    };
    ScaleRulePrototype.createCondition = function()
    {
        return null
    };
    ScaleRulePrototype._calculateLowestValueEx = function(evaluator)
    {
        var rets = (new Top10Condition).getMinValues(evaluator,1,this.ranges);
        return rets && rets.length > 0 ? rets[0] : null
    };
    ScaleRulePrototype._calculateHighestValueEx = function(evaluator)
    {
        var rets = (new Top10Condition).getMaxValues(evaluator,1,this.ranges);
        return rets && rets.length > 0 ? rets[0] : null
    };
    ScaleRulePrototype._cloneRanges = function(ranges)
    {
        var newRanges = [];
        var length = ranges.length;
        for(var i = 0; i < length; i++)
        {
            var r = ranges[i];
            newRanges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
        }
        return newRanges
    };
    ScaleRulePrototype._rangesChanged = function()
    {
        if(!this._oldRanges)
            this._oldRanges = this._cloneRanges(this.ranges);
        if(this._oldRanges.length !== this.ranges.length)
            return true;
        for(var i = 0; i < this._oldRanges.length; i++)
            if(!this._oldRanges[i].equals(this.ranges[i]))
                return true;
        return false
    };
    ScaleRulePrototype._tryCache = function(evaluator)
    {
        if(this._rangesChanged())
        {
            this._clearCache();
            this._oldRanges = this._cloneRanges(this.ranges)
        }
        if(this.cached === false)
        {
            this.lowestValueCached = this._calculateLowestValueEx(evaluator);
            this.highestValueCached = this._calculateHighestValueEx(evaluator);
            this.cached = true
        }
    };
    ScaleRulePrototype._clearCache = function()
    {
        this.lowestValueCached = null;
        this.highestValueCached = null;
        this.cached = false
    };
    ScaleRulePrototype._calculateFormula = function(evaluator, baseRow, baseColumn, formula)
    {
        if(formula && formula.length > 0)
        {
            var calcService = evaluator.getCalcService();
            var expr = calcService.parse(formula,baseRow,baseColumn);
            return calcService.evaluateParsedFormula(evaluator._getSheetSource(),expr,baseRow,baseColumn)
        }
        return null
    };
    ScaleRulePrototype._isFormula = function(val)
    {
        return val && val[0] === "="
    };
    ScaleRulePrototype._trimFormula = function(val)
    {
        return val === undefined || val === null || val === "" ? null : val[0] === "=" ? val.substr(1) : val
    };
    ScaleRulePrototype._calculateValue = function(evaluator, baseRow, baseColumn, value)
    {
        var dValue = null;
        if(this._isFormula(value))
            dValue = this._calculateFormula(evaluator,baseRow,baseColumn,this._trimFormula(value.toString()));
        else
            try
            {
                dValue = Calc.Convert.toDouble(value)
            }
            catch(err)
            {
                dValue = NaN
            }
        return dValue
    };
    ScaleRulePrototype._getHighestValue = function(evaluator)
    {
        this._tryCache(evaluator);
        return this.highestValueCached
    };
    ScaleRulePrototype._getLowestValue = function(evaluator)
    {
        this._tryCache(evaluator);
        return this.lowestValueCached
    };
    ScaleRulePrototype._calculatePercent = function(evaluator, baseRow, baseColumn, value)
    {
        var dValue = this._calculateValue(evaluator,baseRow,baseColumn,value);
        if(!isNaN(dValue))
            if(0 <= dValue && dValue <= 100)
            {
                var min = this._getLowestValue(evaluator);
                var max = this._getHighestValue(evaluator);
                if(min !== undefined && min !== null && max !== undefined && max !== null)
                    return min + (max - min) * dValue / 100.0
            }
        return null
    };
    ScaleRulePrototype._calculatePercentile = function(evaluator, baseRow, baseColumn, value)
    {
        var dValue = this._calculateValue(evaluator,baseRow,baseColumn,value);
        if(!isNaN(dValue))
            if(0 <= dValue && dValue <= 100)
            {
                var total = 0;
                var length = this.ranges.length;
                for(var i = 0; i < length; i++)
                {
                    var exp = this._createExpression("PERCENTILE",[this.ranges[i],dValue / 100.0]);
                    var calcService = evaluator.getCalcService();
                    var formula = calcService.unparse(exp,baseRow,baseColumn);
                    exp = calcService.parse(formula,baseRow,baseColumn);
                    var val = calcService.evaluateParsedFormula(evaluator._getSheetSource(),exp,baseRow,baseColumn);
                    try
                    {
                        total += Calc.Convert.toDouble(val)
                    }
                    catch(InvalidCastException){}
                }
                return total / this.ranges.length
            }
        return null
    };
    ScaleRulePrototype._getActualValue = function(evaluator, baseRow, baseColumn, index)
    {
        var val = this.scales[index];
        if(val)
            switch(val.type)
            {
                case ScaleValueType.Formula:
                    return this._calculateValue(evaluator,baseRow,baseColumn,val.value);
                case ScaleValueType.HighestValue:
                    return this._getHighestValue(evaluator);
                case ScaleValueType.LowestValue:
                    return this._getLowestValue(evaluator);
                case ScaleValueType.Number:
                    return this._calculateValue(evaluator,baseRow,baseColumn,val.value);
                case ScaleValueType.Percent:
                    return this._calculatePercent(evaluator,baseRow,baseColumn,val.value);
                case ScaleValueType.Percentile:
                    return this._calculatePercentile(evaluator,baseRow,baseColumn,val.value);
                case ScaleValueType.Automax:
                    var max = this._getHighestValue(evaluator);
                    return max < 0.0 ? 0.0 : max;
                case ScaleValueType.Automin:
                    var min = this._getLowestValue(evaluator);
                    return min > 0.0 ? 0.0 : min
            }
        return null
    };
    ScaleRulePrototype._evaluate2Scale = function(value, min, max)
    {
        if(value <= min)
            return 0.0;
        if(value >= max)
            return 1.0;
        return(value - min) / (max - min)
    };
    ScaleRulePrototype.evaluate = function(evaluator, baseRow, baseColumn, actual)
    {
        this._tryCache(evaluator);
        if(this.contains(baseRow,baseColumn))
        {
            if(actual === undefined || actual === null)
                return null;
            try
            {
                var numberValue = Calc.Convert.toDouble(actual);
                var minValue = this._getActualValue(evaluator,baseRow,baseColumn,0);
                var midValue = this._getActualValue(evaluator,baseRow,baseColumn,1);
                var maxValue = this._getActualValue(evaluator,baseRow,baseColumn,2);
                if(isNaN(midValue))
                {
                    if(!isNaN(minValue) && !isNaN(maxValue))
                    {
                        if(minValue > maxValue)
                            return null;
                        return this._evaluate2Scale(numberValue,minValue,maxValue)
                    }
                }
                else if(!isNaN(minValue) && !isNaN(maxValue) && !isNaN(midValue))
                {
                    if(minValue > maxValue)
                        return null;
                    if(numberValue < minValue)
                        return 0.0;
                    if(numberValue >= maxValue)
                        return 2.0;
                    var midScale = this._evaluate2Scale(midValue,minValue,maxValue);
                    if(minValue <= numberValue && numberValue <= midValue)
                        return this._evaluate2Scale(numberValue,minValue,midValue);
                    else
                        return 1 + this._evaluate2Scale(numberValue,minValue,maxValue)
                }
            }
            catch(err)
            {
                return null
            }
        }
        return null
    };
    ScaleRulePrototype._evaluateColor = function(value, color1, color2)
    {
        if(0 <= value && value <= 1)
        {
            var Color = UI._Color;
            var minColor = Color.parse(color1);
            var maxColor = Color.parse(color2);
            var a = minColor.a * (1 - value) + maxColor.a * value;
            var r = minColor.r * (1 - value) + maxColor.r * value;
            var g = minColor.g * (1 - value) + maxColor.g * value;
            var b = minColor.b * (1 - value) + maxColor.b * value;
            return new Color(parseFloat(a / 255),parseInt(r,10),parseInt(g,10),parseInt(b,10)).toString()
        }
        return null
    };
    ScaleRulePrototype._createExpression = AverageCondition.prototype._createExpression;
    ScaleRulePrototype._addRows = function(row, rowCount)
    {
        var base = ConditionRuleBase.prototype._addRows;
        base.apply(this,arguments);
        this._clearCache()
    };
    ScaleRulePrototype._addColumns = function(col, colCount)
    {
        var base = ConditionRuleBase.prototype._addColumns;
        base.apply(this,arguments);
        this._clearCache()
    };
    ScaleRulePrototype._removeRows = function(row, rowCount)
    {
        var base = ConditionRuleBase.prototype._removeRows;
        base.apply(this,arguments);
        this._clearCache()
    };
    ScaleRulePrototype._removeColumns = function(col, colCount)
    {
        var base = ConditionRuleBase.prototype._removeColumns;
        base.apply(this,arguments);
        this._clearCache()
    };
    ScaleRule.prototype = ScaleRulePrototype;
    UI.ScaleRule = ScaleRule;
    function TwoScaleRule(minType, minValue, minColor, maxType, maxValue, maxColor)
    {
        var scaleValue1 = new ScaleValue(minType,minValue);
        var scaleValue2 = new ScaleValue(maxType,maxValue);
        ScaleRule.call(this,scaleValue1,null,scaleValue2);
        this.expected[0] = minColor;
        this.expected[2] = maxColor
    }
    var TwoScaleRulePrototype = new ScaleRule;
    TwoScaleRulePrototype.evaluate = function(evaluator, baseRow, baseColumn, actual)
    {
        if(actual !== undefined && actual !== null)
        {
            var numberValue = NaN;
            try
            {
                if(Calc.Convert.isNumber(actual))
                    numberValue = Calc.Convert.toDouble(actual)
            }
            catch(InvalidCastException)
            {
                return null
            }
            if(isNaN(numberValue))
                return null;
            var minValue = this._getActualValue(evaluator,baseRow,baseColumn,0);
            var maxValue = this._getActualValue(evaluator,baseRow,baseColumn,2);
            if(minValue !== undefined && minValue !== null && maxValue !== undefined && maxValue !== null)
            {
                var result = this._evaluate2Scale(numberValue,minValue,maxValue);
                return this._evaluateColor(result,this.expected[0],this.expected[2])
            }
        }
        return null
    };
    TwoScaleRulePrototype._init = function()
    {
        this.scales = new Array(3);
        this.scales[0] = new ScaleValue(ScaleValueType.LowestValue,null);
        this.scales[2] = new ScaleValue(ScaleValueType.HighestValue,null);
        this.expected = new Array(3);
        var Color = UI._Color;
        this.expected[0] = new Color(0,255,255,255).toString();
        this.expected[2] = new Color(255,99,190,123).toString()
    };
    TwoScaleRulePrototype.reset = function()
    {
        this.ranges = null;
        this.condition = null;
        this.style = null;
        this._init()
    };
    TwoScaleRulePrototype.getMinimumType = function()
    {
        return this.scales && this.scales.length === 3 ? this.scales[0].type : null
    };
    TwoScaleRulePrototype.setMinimumType = function(type)
    {
        if(this.scales && this.scales.length === 3)
            this.scales[0].type = type
    };
    TwoScaleRulePrototype.getMinimumValue = function()
    {
        return this.scales && this.scales.length === 3 ? this.scales[0].value : null
    };
    TwoScaleRulePrototype.setMinimumValue = function(value)
    {
        if(this.scales && this.scales.length === 3)
            this.scales[0].value = value
    };
    TwoScaleRulePrototype.getMinimumColor = function()
    {
        return this.expected && this.expected.length === 3 ? this.expected[0] : null
    };
    TwoScaleRulePrototype.setMinimumColor = function(color)
    {
        if(this.expected && this.expected.length === 3)
            this.expected[0] = color
    };
    TwoScaleRulePrototype.getMaximumType = function()
    {
        return this.scales && this.scales.length === 3 ? this.scales[2].type : null
    };
    TwoScaleRulePrototype.setMaximumType = function(type)
    {
        if(this.scales && this.scales.length === 3)
            this.scales[2].type = type
    };
    TwoScaleRulePrototype.getMaximumValue = function()
    {
        return this.scales && this.scales.length === 3 ? this.scales[2].value : null
    };
    TwoScaleRulePrototype.setMaximumValue = function(value)
    {
        if(this.scales && this.scales.length === 3)
            this.scales[2].value = value
    };
    TwoScaleRulePrototype.getMaximumColor = function()
    {
        return this.expected && this.expected.length === 3 ? this.expected[2] : null
    };
    TwoScaleRulePrototype.setMaximumColor = function(color)
    {
        if(this.expected && this.expected.length === 3)
            this.expected[2] = color
    };
    TwoScaleRulePrototype.toJSON = function()
    {
        return{
                ruleType: RuleType.TwoScaleRule,
                ranges: this.ranges,
                minType: this.getMinimumType(),
                minValue: this.getMinimumValue(),
                minColor: this.getMinimumColor(),
                maxType: this.getMaximumType(),
                maxValue: this.getMaximumValue(),
                maxColor: this.getMaximumColor()
            }
    };
    TwoScaleRulePrototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.ranges)
        {
            this.ranges = [];
            for(var i = 0; i < settings.ranges.length; i++)
            {
                var r = settings.ranges[i];
                this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
            }
        }
        if(settings.minType !== null && settings.minType !== undefined)
            this.setMinimumType(settings.minType);
        if(settings.minValue !== null && settings.minValue !== undefined)
            this.setMinimumValue(settings.minValue);
        if(settings.minColor !== null && settings.minColor !== undefined)
            this.setMinimumColor(settings.minColor);
        if(settings.maxType !== null && settings.maxType !== undefined)
            this.setMaximumType(settings.maxType);
        if(settings.maxValue !== null && settings.maxValue !== undefined)
            this.setMaximumValue(settings.maxValue);
        if(settings.maxColor !== null && settings.maxColor !== undefined)
            this.setMaximumColor(settings.maxColor)
    };
    TwoScaleRule.prototype = TwoScaleRulePrototype;
    UI.TwoScaleRule = TwoScaleRule;
    function ThreeScaleRule(minType, minValue, minColor, midType, midValue, midColor, maxType, maxValue, maxColor)
    {
        var scaleValue1 = new ScaleValue(minType,minValue);
        var scaleValue2 = new ScaleValue(midType,midValue);
        var scaleValue3 = new ScaleValue(maxType,maxValue);
        ScaleRule.call(this,scaleValue1,scaleValue2,scaleValue3);
        this.expected[0] = minColor;
        this.expected[1] = midColor;
        this.expected[2] = maxColor;
        this._evaluator = null
    }
    var ThreeScaleRulePrototype = new ScaleRule;
    ThreeScaleRulePrototype.evaluate = function(evaluator, baseRow, baseColumn, actual)
    {
        this._evaluator = evaluator;
        if(actual !== undefined && actual !== null)
        {
            var numberValue = NaN;
            try
            {
                if(Calc.Convert.isNumber(actual))
                    numberValue = Calc.Convert.toDouble(actual)
            }
            catch(InvalidCastException)
            {
                return null
            }
            if(isNaN(numberValue))
                return null;
            var minValue = this._getActualValue(evaluator,baseRow,baseColumn,0);
            var midValue = this._getActualValue(evaluator,baseRow,baseColumn,1);
            var maxValue = this._getActualValue(evaluator,baseRow,baseColumn,2);
            if(minValue !== undefined && minValue !== null && maxValue !== undefined || maxValue !== null && midValue !== undefined && midValue !== null)
            {
                if(minValue > maxValue)
                    return null;
                if(numberValue < minValue)
                    return this.expected[0];
                if(numberValue >= maxValue)
                    return this.expected[2];
                var result;
                if(minValue <= numberValue && numberValue <= midValue)
                {
                    result = this._evaluate2Scale(numberValue,minValue,midValue);
                    return this._evaluateColor(result,this.expected[0],this.expected[1])
                }
                else
                {
                    result = this._evaluate2Scale(numberValue,midValue,maxValue);
                    return this._evaluateColor(result,this.expected[1],this.expected[2])
                }
            }
        }
        return null
    };
    ThreeScaleRulePrototype._init = function()
    {
        this.scales = new Array(3);
        this.scales[0] = new ScaleValue(ScaleValueType.LowestValue,null);
        this.scales[1] = new ScaleValue(ScaleValueType.Percentile,50);
        this.scales[2] = new ScaleValue(ScaleValueType.HighestValue,null);
        this.expected = new Array(3);
        var Color = UI._Color;
        this.expected[0] = new Color(255,248,105,107).toString();
        this.expected[1] = new Color(255,255,235,132).toString();
        this.expected[2] = new Color(255,99,190,123).toString()
    };
    ThreeScaleRulePrototype.reset = function()
    {
        this.ranges = null;
        this.condition = null;
        this.style = null;
        this._init();
        this._evaluator = null
    };
    ThreeScaleRulePrototype.getMinimumType = function()
    {
        return this.scales && this.scales.length === 3 ? this.scales[0].type : null
    };
    ThreeScaleRulePrototype.setMinimumType = function(type)
    {
        if(this.scales && this.scales.length === 3)
            this.scales[0].type = type
    };
    ThreeScaleRulePrototype.getMinimumValue = function()
    {
        return this.scales && this.scales.length === 3 ? this.scales[0].value : null
    };
    ThreeScaleRulePrototype.setMinimumValue = function(value)
    {
        if(this.scales && this.scales.length === 3)
            this.scales[0].value = value
    };
    ThreeScaleRulePrototype.getMinimumColor = function()
    {
        return this.expected && this.expected.length === 3 ? this.expected[0] : null
    };
    ThreeScaleRulePrototype.setMinimumColor = function(color)
    {
        if(this.expected && this.expected.length === 3)
            this.expected[0] = color
    };
    ThreeScaleRulePrototype.getMidpointType = function()
    {
        return this.scales && this.scales.length === 3 ? this.scales[1].type : null
    };
    ThreeScaleRulePrototype.setMidpointType = function(type)
    {
        if(this.scales && this.scales.length === 3)
            this.scales[1].type = type
    };
    ThreeScaleRulePrototype.getMidpointValue = function()
    {
        if(this.scales && this.scales.length === 3)
        {
            if(this._evaluator)
                if(this.scales[1].type === ScaleValueType.HighestValue)
                    return this._getHighestValue(this._evaluator);
                else if(this.scales[1].type === ScaleValueType.LowestValue)
                    return this._getLowestValue(this._evaluator);
            return this.scales[1].value
        }
        return null
    };
    ThreeScaleRulePrototype.setMidpointValue = function(value)
    {
        if(this.scales && this.scales.length === 3)
            this.scales[1].value = value
    };
    ThreeScaleRulePrototype.getMidpointColor = function()
    {
        return this.expected && this.expected.length === 3 ? this.expected[1] : null
    };
    ThreeScaleRulePrototype.setMidpointColor = function(color)
    {
        if(this.expected && this.expected.length === 3)
            this.expected[1] = color
    };
    ThreeScaleRulePrototype.getMaximumType = function()
    {
        return this.scales && this.scales.length === 3 ? this.scales[2].type : null
    };
    ThreeScaleRulePrototype.setMaximumType = function(type)
    {
        if(this.scales && this.scales.length === 3)
            this.scales[2].type = type
    };
    ThreeScaleRulePrototype.getMaximumValue = function()
    {
        return this.scales && this.scales.length === 3 ? this.scales[2].value : null
    };
    ThreeScaleRulePrototype.setMaximumValue = function(value)
    {
        if(this.scales && this.scales.length === 3)
            this.scales[2].value = value
    };
    ThreeScaleRulePrototype.getMaximumColor = function()
    {
        return this.expected && this.expected.length === 3 ? this.expected[2] : null
    };
    ThreeScaleRulePrototype.setMaximumColor = function(color)
    {
        if(this.expected && this.expected.length === 3)
            this.expected[2] = color
    };
    ThreeScaleRulePrototype.toJSON = function()
    {
        return{
                ruleType: RuleType.ThreeScaleRule,
                ranges: this.ranges,
                minType: this.getMinimumType(),
                minValue: this.getMinimumValue(),
                minColor: this.getMinimumColor(),
                midType: this.getMidpointType(),
                midValue: this.getMidpointValue(),
                midColor: this.getMidpointColor(),
                maxType: this.getMaximumType(),
                maxValue: this.getMaximumValue(),
                maxColor: this.getMaximumColor()
            }
    };
    ThreeScaleRulePrototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.ranges)
        {
            this.ranges = [];
            for(var i = 0; i < settings.ranges.length; i++)
            {
                var r = settings.ranges[i];
                this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
            }
        }
        if(settings.minType !== null && settings.minType !== undefined)
            this.setMinimumType(settings.minType);
        if(settings.minValue !== null && settings.minValue !== undefined)
            this.setMinimumValue(settings.minValue);
        if(settings.minColor !== null && settings.minColor !== undefined)
            this.setMinimumColor(settings.minColor);
        if(settings.midType !== null && settings.midType !== undefined)
            this.setMidpointType(settings.midType);
        if(settings.midValue !== null && settings.midValue !== undefined)
            this.setMidpointValue(settings.midValue);
        if(settings.midColor !== null && settings.midColor !== undefined)
            this.setMidpointColor(settings.midColor);
        if(settings.maxType !== null && settings.maxType !== undefined)
            this.setMaximumType(settings.maxType);
        if(settings.maxValue !== null && settings.maxValue !== undefined)
            this.setMaximumValue(settings.maxValue);
        if(settings.maxColor !== null && settings.maxColor !== undefined)
            this.setMaximumColor(settings.maxColor)
    };
    ThreeScaleRule.prototype = ThreeScaleRulePrototype;
    UI.ThreeScaleRule = ThreeScaleRule;
    function DataBarRule(minType, minValue, maxType, maxValue, color)
    {
        var scaleValue1 = new ScaleValue(minType,minValue);
        var scaleValue2 = new ScaleValue(maxType,maxValue);
        ScaleRule.call(this,scaleValue1,null,scaleValue2);
        this._init(color)
    }
    var DataBarRulePrototype = new ScaleRule;
    DataBarRulePrototype._init = function(color)
    {
        this._gradient = true;
        this._color = color;
        this._showBorder = false;
        this._borderColor = "black";
        this._dataBarDirection = BarDirection.LeftToRight;
        this._negativeFillColor = "red";
        this._useNegativeFillColor = true;
        this._negativeBorderColor = "black";
        this._useNegativeBorderColor = false;
        this._axisPosition = DataBarAxisPosition.Automatic;
        this._axisColor = "black";
        this._showBarOnly = false
    };
    DataBarRulePrototype._calcuteMinValue = function(evaluator, baseRow, baseColumn, index, actual)
    {
        var svalue = this.scales[index];
        var tpRange = null;
        if(svalue)
            if(svalue.type === ScaleValueType.Formula || svalue.type === ScaleValueType.Percentile)
            {
                for(var i = 0; i < this.ranges.length; i++)
                    if(this.ranges[i].intersect(baseRow,-1,1,-1))
                    {
                        tpRange = this.ranges[i];
                        break
                    }
                if(tpRange)
                    return this._getActualValue(evaluator,tpRange.row,tpRange.col,0,actual)
            }
            else
                return this._getActualValue(evaluator,baseRow,baseColumn,0,actual);
        return null
    };
    DataBarRulePrototype._calcuteMaxValue = function(evaluator, baseRow, baseColumn, index, actual)
    {
        var svalue = this.scales[index];
        var tpRange = null;
        if(svalue)
            if(svalue.type === ScaleValueType.Formula || svalue.type === ScaleValueType.Percentile)
            {
                for(var i = 0; i < this.ranges.length; i++)
                    if(this.ranges[i].intersect(baseRow,-1,1,-1))
                    {
                        tpRange = this.ranges[i];
                        break
                    }
                if(tpRange)
                    return this._getActualValue(evaluator,tpRange.row,tpRange.col,2,actual)
            }
            else
                return this._getActualValue(evaluator,baseRow,baseColumn,2,actual);
        return null
    };
    DataBarRulePrototype._evaluateNoneScale = function(currentValue, minValue, maxValue, axisScale)
    {
        axisScale = 0.0;
        if(maxValue === minValue)
            if(currentValue < minValue)
                return[0.0,axisScale];
            else if(currentValue > maxValue)
                return[1.0,axisScale];
            else
                return[0.5,axisScale];
        if(currentValue >= maxValue)
            return[1.0,axisScale];
        else if(currentValue <= minValue)
            return[0.0,axisScale];
        else
            return[(currentValue - minValue) / (maxValue - minValue),axisScale]
    };
    DataBarRulePrototype._evaluateMidScale = function(currentValue, minValue, maxValue, axisScale)
    {
        axisScale = 0.5;
        var totalValue = Math.abs(maxValue - minValue);
        if(maxValue > 0 && minValue >= 0)
        {
            if(maxValue === minValue)
                return[0.5,axisScale];
            if(currentValue >= maxValue)
                return[0.5,axisScale];
            else if(currentValue <= minValue)
                return[minValue / maxValue * 0.5,axisScale];
            else
                return[Math.abs(currentValue / maxValue) * 0.5,axisScale]
        }
        else if(maxValue > 0 && minValue < 0)
        {
            var positiveMaxScale = maxValue > Math.abs(minValue) ? 0.5 : maxValue / totalValue;
            var negativeMaxScale = maxValue > Math.abs(minValue) ? minValue / totalValue : -0.5;
            if(currentValue > 0)
                if(currentValue >= maxValue)
                    return[positiveMaxScale,axisScale];
                else
                    return[currentValue / maxValue * positiveMaxScale,axisScale];
            else if(currentValue < 0)
                if(currentValue <= minValue)
                    return[negativeMaxScale,axisScale];
                else
                    return[currentValue / minValue * negativeMaxScale,axisScale];
            else
                return[0.0,axisScale]
        }
        else if(maxValue <= 0 && minValue < 0)
        {
            if(maxValue === minValue)
                return[-0.5,axisScale];
            if(currentValue >= maxValue)
                return[-maxValue / minValue * 0.5,axisScale];
            else if(currentValue <= minValue)
                return[-0.5,axisScale];
            else
                return[-currentValue / minValue * 0.5,axisScale]
        }
        else if(maxValue === 0 && minValue === 0)
            return[0.0,axisScale];
        axisScale = -1;
        return[-1,axisScale]
    };
    DataBarRulePrototype._evaluateAutoScale = function(currentValue, minValue, maxValue, axisScale)
    {
        var totalValue = Math.abs(maxValue - minValue);
        if(maxValue > 0 && minValue >= 0)
        {
            axisScale = 0.0;
            if(minValue === maxValue)
                if(currentValue < minValue)
                    return[0.0,axisScale];
                else if(currentValue > maxValue)
                    return[1.0,axisScale];
                else
                    return[0.5,axisScale];
            if(currentValue <= minValue)
                return[0.0,axisScale];
            else if(currentValue >= maxValue)
                return[1.0,axisScale];
            else
                return[(currentValue - minValue) / totalValue,axisScale]
        }
        else if(maxValue > 0 && minValue < 0)
        {
            axisScale = Math.abs(minValue) / totalValue;
            if(currentValue >= maxValue)
                return[1 - axisScale,axisScale];
            else if(currentValue <= minValue)
                return[-axisScale,axisScale];
            else if(currentValue === 0)
                return[0.0,axisScale];
            else
                return[(currentValue - 0.0) / totalValue,axisScale]
        }
        else if(maxValue <= 0 && minValue < 0)
        {
            axisScale = 1.0;
            if(maxValue === minValue)
                if(currentValue < minValue)
                    return[-1.0,axisScale];
                else if(currentValue > maxValue)
                    return[0.0,axisScale];
                else
                    return[-0.5,axisScale];
            if(currentValue >= maxValue)
                return[-0.0,axisScale];
            else if(currentValue <= minValue)
                return[-1.0,axisScale];
            else
                return[(currentValue - maxValue) / totalValue,axisScale]
        }
        else if(maxValue === 0 && minValue === 0)
        {
            axisScale = 0.5;
            if(currentValue > 0)
                return[0.5,axisScale];
            else if(currentValue < 0)
                return[-0.5,axisScale];
            else
                return[0.0,axisScale]
        }
        axisScale = -1;
        return[-1,axisScale]
    };
    DataBarRulePrototype._evaluateScale = function(currentValue, minValue, maxValue, axisScale)
    {
        if(this._axisPosition === DataBarAxisPosition.Automatic)
            return this._evaluateAutoScale(currentValue,minValue,maxValue,axisScale);
        else if(this._axisPosition === DataBarAxisPosition.CellMidPoint)
            return this._evaluateMidScale(currentValue,minValue,maxValue,axisScale);
        else
            return this._evaluateNoneScale(currentValue,minValue,maxValue,axisScale)
    };
    DataBarRulePrototype.evaluate = function(evaluator, baseRow, baseColumn, actual)
    {
        var value = actual;
        if(value !== null && value !== undefined)
        {
            var numberValue = NaN;
            try
            {
                if(Calc.Convert.isNumber(value))
                    numberValue = Calc.Convert.toDouble(value)
            }
            catch(ex)
            {
                return null
            }
            if(isNaN(numberValue))
                return null;
            var minValue = this._calcuteMinValue(evaluator,baseRow,baseColumn,0,actual);
            var maxValue = this._calcuteMaxValue(evaluator,baseRow,baseColumn,2,actual);
            if(minValue !== null && minValue !== undefined && maxValue !== null && maxValue !== undefined)
            {
                if(minValue > maxValue)
                {
                    var tpNum = maxValue;
                    maxValue = minValue;
                    minValue = tpNum
                }
                var axisScale;
                var resultArray = this._evaluateScale(numberValue,minValue,maxValue,axisScale);
                if(!resultArray || resultArray.length < 2)
                    return null;
                var barScale = resultArray[0];
                axisScale = resultArray[1];
                var fillColor = numberValue < 0 && this._useNegativeFillColor ? this._negativeFillColor : this._color;
                var borderFillColor = numberValue < 0 && this._useNegativeBorderColor ? this._negativeBorderColor : this._borderColor;
                return{
                        fillColor: fillColor,
                        borderColor: borderFillColor,
                        showBorder: this._showBorder,
                        axisColor: this._axisColor,
                        isGradient: this._gradient,
                        direction: this._dataBarDirection,
                        axisLocation: axisScale,
                        scale: barScale,
                        showBarOnly: this._showBarOnly
                    }
            }
        }
        return null
    };
    DataBarRulePrototype.minimumType = function(value)
    {
        if(arguments.length === 0)
            return this.scales && this.scales.length === 3 ? this.scales[0].type : null;
        else
        {
            if(this.scales && this.scales.length === 3)
                this.scales[0].type = value;
            return this
        }
    };
    DataBarRulePrototype.minimumValue = function(value)
    {
        if(arguments.length === 0)
            return this.scales && this.scales.length === 3 ? this.scales[0].value : null;
        else
        {
            if(this.scales && this.scales.length === 3)
                this.scales[0].value = value;
            return this
        }
    };
    DataBarRulePrototype.maximumType = function(value)
    {
        if(arguments.length === 0)
            return this.scales && this.scales.length === 3 ? this.scales[2].type : null;
        else
        {
            if(this.scales && this.scales.length === 3)
                this.scales[2].type = value;
            return this
        }
    };
    DataBarRulePrototype.maximumValue = function(value)
    {
        if(arguments.length === 0)
            return this.scales && this.scales.length === 3 ? this.scales[2].value : null;
        else
        {
            if(this.scales && this.scales.length === 3)
                this.scales[2].value = value;
            return this
        }
    };
    DataBarRulePrototype.gradient = function(value)
    {
        if(arguments.length === 0)
            return this._gradient;
        else
        {
            this._gradient = value;
            return this
        }
    };
    DataBarRulePrototype.color = function(value)
    {
        if(arguments.length === 0)
            return this._color;
        else
        {
            this._color = value;
            return this
        }
    };
    DataBarRulePrototype.showBorder = function(value)
    {
        if(arguments.length === 0)
            return this._showBorder;
        else
        {
            this._showBorder = value;
            return this
        }
    };
    DataBarRulePrototype.borderColor = function(value)
    {
        if(arguments.length === 0)
            return this._borderColor;
        else
        {
            this._borderColor = value;
            return this
        }
    };
    DataBarRulePrototype.dataBarDirection = function(value)
    {
        if(arguments.length === 0)
            return this._dataBarDirection;
        else
        {
            this._dataBarDirection = value;
            return this
        }
    };
    DataBarRulePrototype.negativeFillColor = function(value)
    {
        if(arguments.length === 0)
            return this._negativeFillColor;
        else
        {
            this._negativeFillColor = value;
            return this
        }
    };
    DataBarRulePrototype.useNegativeFillColor = function(value)
    {
        if(arguments.length === 0)
            return this._useNegativeFillColor;
        else
        {
            this._useNegativeFillColor = value;
            return this
        }
    };
    DataBarRulePrototype.negativeBorderColor = function(value)
    {
        if(arguments.length === 0)
            return this._negativeBorderColor;
        else
        {
            this._negativeBorderColor = value;
            return this
        }
    };
    DataBarRulePrototype.useNegativeBorderColor = function(value)
    {
        if(arguments.length === 0)
            return this._useNegativeBorderColor;
        else
        {
            this._useNegativeBorderColor = value;
            return this
        }
    };
    DataBarRulePrototype.axisPosition = function(value)
    {
        if(arguments.length === 0)
            return this._axisPosition;
        else
        {
            this._axisPosition = value;
            return this
        }
    };
    DataBarRulePrototype.axisColor = function(value)
    {
        if(arguments.length === 0)
            return this._axisColor;
        else
        {
            this._axisColor = value;
            return this
        }
    };
    DataBarRulePrototype.showBarOnly = function(value)
    {
        if(arguments.length === 0)
            return this._showBarOnly;
        else
        {
            this._showBarOnly = value;
            return this
        }
    };
    DataBarRulePrototype.toJSON = function()
    {
        return{
                ruleType: RuleType.DataBarRule,
                ranges: this.ranges,
                minType: this.minimumType(),
                minValue: this.minimumValue(),
                maxType: this.maximumType(),
                maxValue: this.maximumValue(),
                gradient: this.gradient(),
                color: this.color(),
                showBorder: this.showBorder(),
                borderColor: this.borderColor(),
                dataBarDirection: this.dataBarDirection(),
                negativeFillColor: this.negativeFillColor(),
                useNegativeFillColor: this.useNegativeFillColor(),
                negativeBorderColor: this.negativeBorderColor(),
                useNegativeBorderColor: this.useNegativeBorderColor(),
                axisPosition: this.axisPosition(),
                axisColor: this.axisColor(),
                showBarOnly: this.showBarOnly()
            }
    };
    DataBarRulePrototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.ranges)
        {
            this.ranges = [];
            for(var i = 0; i < settings.ranges.length; i++)
            {
                var r = settings.ranges[i];
                this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
            }
        }
        if(settings.minType !== null && settings.minType !== undefined)
            this.minimumType(settings.minType);
        if(settings.minValue !== null && settings.minValue !== undefined)
            this.minimumValue(settings.minValue);
        if(settings.maxType !== null && settings.maxType !== undefined)
            this.maximumType(settings.maxType);
        if(settings.maxValue !== null && settings.maxValue !== undefined)
            this.maximumValue(settings.maxValue);
        if(settings.gradient !== null && settings.gradient !== undefined)
            this.gradient(settings.gradient);
        if(settings.color !== null && settings.color !== undefined)
            this.color(settings.color);
        if(settings.showBorder !== null && settings.showBorder !== undefined)
            this.showBorder(settings.showBorder);
        if(settings.borderColor !== null && settings.borderColor !== undefined)
            this.borderColor(settings.borderColor);
        if(settings.dataBarDirection !== null && settings.dataBarDirection !== undefined)
            this.dataBarDirection(settings.dataBarDirection);
        if(settings.negativeFillColor !== null && settings.negativeFillColor !== undefined)
            this.negativeFillColor(settings.negativeFillColor);
        if(settings.useNegativeFillColor !== null && settings.useNegativeFillColor !== undefined)
            this.useNegativeFillColor(settings.useNegativeFillColor);
        if(settings.negativeBorderColor !== null && settings.negativeBorderColor !== undefined)
            this.negativeBorderColor(settings.negativeBorderColor);
        if(settings.useNegativeBorderColor !== null && settings.useNegativeBorderColor !== undefined)
            this.useNegativeBorderColor(settings.useNegativeBorderColor);
        if(settings.axisPosition !== null && settings.axisPosition !== undefined)
            this.axisPosition(settings.axisPosition);
        if(settings.axisColor !== null && settings.axisColor !== undefined)
            this.axisColor(settings.axisColor);
        if(settings.showBarOnly !== null && settings.showBarOnly !== undefined)
            this.showBarOnly(settings.showBarOnly)
    };
    DataBarRule.prototype = DataBarRulePrototype;
    UI.DataBarRule = DataBarRule;
    function IconCriterion(isGreaterThanOrEqualTo, iconValueType, iconValue)
    {
        this.isGreaterThanOrEqualTo = isGreaterThanOrEqualTo;
        this.iconValueType = iconValueType;
        this.iconValue = iconValue
    }
    UI.IconCriterion = IconCriterion;
    function IconSetRule(iconSetType)
    {
        this._init(iconSetType)
    }
    var IconSetRulePrototype = new ScaleRule;
    IconSetRulePrototype._initIconSetType = function(iconSetType)
    {
        this._iconSetType = iconSetType;
        if(this._iconSetType >= IconSetType.ThreeArrowsColored && this._iconSetType <= IconSetType.ThreeSymbolsUncircled)
        {
            this._iconCriteria = new Array(2);
            this._iconCriteria[0] = new IconCriterion(true,IconValueType.Percent,33);
            this._iconCriteria[1] = new IconCriterion(true,IconValueType.Percent,67)
        }
        else if(this._iconSetType >= IconSetType.FourArrowsColored && this._iconSetType <= IconSetType.FourTrafficLights)
        {
            this._iconCriteria = new Array(3);
            this._iconCriteria[0] = new IconCriterion(true,IconValueType.Percent,25);
            this._iconCriteria[1] = new IconCriterion(true,IconValueType.Percent,50);
            this._iconCriteria[2] = new IconCriterion(true,IconValueType.Percent,75)
        }
        else if(this._iconSetType >= IconSetType.FiveArrowsColored && this._iconSetType <= IconSetType.FiveBoxes)
        {
            this._iconCriteria = new Array(4);
            this._iconCriteria[0] = new IconCriterion(true,IconValueType.Percent,20);
            this._iconCriteria[1] = new IconCriterion(true,IconValueType.Percent,40);
            this._iconCriteria[2] = new IconCriterion(true,IconValueType.Percent,60);
            this._iconCriteria[3] = new IconCriterion(true,IconValueType.Percent,80)
        }
        else if(this._iconSetType > IconSetType.FiveBoxes)
            this._iconCriteria = new Array
    };
    IconSetRulePrototype._init = function(iconSetType)
    {
        this._showIconOnly = false;
        this._reverseIconOrder = false;
        this._initIconSetType(iconSetType)
    };
    IconSetRulePrototype._modifyIconIndex = function(index)
    {
        var iconCount = this._iconCriteria.length + 1;
        if(this._reverseIconOrder && iconCount > 2)
            return iconCount - 1 - index;
        return index
    };
    IconSetRulePrototype._getActualValue = function(evaluator, baseRow, baseColumn, index)
    {
        var value = this._iconCriteria[index];
        if(value)
            switch(value.iconValueType)
            {
                case IconValueType.Formula:
                    return this._calculateValue(evaluator,baseRow,baseColumn,value.iconValue);
                case IconValueType.Number:
                    return this._calculateValue(evaluator,baseRow,baseColumn,value.iconValue);
                case IconValueType.Percent:
                    return this._calculatePercent(evaluator,baseRow,baseColumn,value.iconValue);
                case IconValueType.Percentile:
                    return this._calculatePercentile(evaluator,baseRow,baseColumn,value.iconValue)
            }
        return null
    };
    IconSetRulePrototype.evaluate = function(evaluator, baseRow, baseColumn, actual)
    {
        var value = actual;
        if(value === null || value === undefined)
            return null;
        var numberValue = NaN;
        try
        {
            if(Calc.Convert.isNumber(value))
                numberValue = Calc.Convert.toDouble(value)
        }
        catch(ex)
        {
            return null
        }
        if(isNaN(numberValue))
            return null;
        var iconCount = 0;
        if(this._iconSetType >= IconSetType.ThreeArrowsColored && this._iconSetType <= IconSetType.ThreeSymbolsUncircled)
            iconCount = 3;
        else if(this._iconSetType >= IconSetType.FourArrowsColored && this._iconSetType <= IconSetType.FourTrafficLights)
            iconCount = 4;
        else if(this._iconSetType >= IconSetType.FiveArrowsColored && this._iconSetType <= IconSetType.FiveBoxes)
            iconCount = 5;
        if(!this._iconCriteria)
            return 0;
        var lastValue = Number.MAX_VALUE;
        for(var n = iconCount - 1; n > 0; n--)
            if(n < this._iconCriteria.length + 1)
            {
                var criterion = this._iconCriteria[n - 1];
                if(criterion && criterion.iconValue !== null && criterion.iconValue !== undefined)
                {
                    var currentValue = this._getActualValue(evaluator,baseRow,baseColumn,n - 1);
                    if(currentValue !== null && currentValue !== undefined)
                    {
                        if(criterion.isGreaterThanOrEqualTo)
                        {
                            if(numberValue < lastValue && numberValue >= currentValue)
                                return{
                                        iconSetType: this._iconSetType,
                                        iconIndex: this._modifyIconIndex(n),
                                        showIconOnly: this._showIconOnly
                                    }
                        }
                        else if(numberValue < lastValue && numberValue > currentValue)
                            return{
                                    iconSetType: this._iconSetType,
                                    iconIndex: this._modifyIconIndex(n),
                                    showIconOnly: this._showIconOnly
                                }
                    }
                    else
                        return{
                                iconSetType: this._iconSetType,
                                iconIndex: this._modifyIconIndex(0),
                                showIconOnly: this._showIconOnly
                            }
                }
                else
                    return{
                            iconSetType: this._iconSetType,
                            iconIndex: this._modifyIconIndex(0),
                            showIconOnly: this._showIconOnly
                        }
            }
            else
                return{
                        iconSetType: this._iconSetType,
                        iconIndex: this._modifyIconIndex(0),
                        showIconOnly: this._showIconOnly
                    };
        return{
                iconSetType: this._iconSetType,
                iconIndex: this._modifyIconIndex(0),
                showIconOnly: this._showIconOnly
            }
    };
    IconSetRulePrototype.reset = function()
    {
        this.ranges = null;
        this.condition = null;
        this.style = null;
        this._showIconOnly = false;
        this._reverseIconOrder = false;
        this._iconSetType = IconSetType.ThreeArrowsColored;
        this._iconCriteria = null
    };
    IconSetRulePrototype.iconSetType = function(value)
    {
        if(arguments.length === 0)
            return this._iconSetType;
        else
        {
            this._initIconSetType(value);
            return this
        }
    };
    IconSetRulePrototype.reverseIconOrder = function(value)
    {
        if(arguments.length === 0)
            return this._reverseIconOrder;
        else
        {
            this._reverseIconOrder = value;
            return this
        }
    };
    IconSetRulePrototype.showIconOnly = function(value)
    {
        if(arguments.length === 0)
            return this._showIconOnly;
        else
        {
            this._showIconOnly = value;
            return this
        }
    };
    IconSetRulePrototype.iconCriteria = function()
    {
        return this._iconCriteria
    };
    IconSetRulePrototype.toJSON = function()
    {
        return{
                ruleType: RuleType.IconSetRule,
                ranges: this.ranges,
                iconSetType: this.iconSetType(),
                iconCriteria: this.iconCriteria(),
                showIconOnly: this.showIconOnly(),
                reverseIconOrder: this.reverseIconOrder()
            }
    };
    IconSetRulePrototype.fromJSON = function(settings)
    {
        if(!settings)
            return;
        if(settings.ranges)
        {
            this.ranges = [];
            for(var i = 0; i < settings.ranges.length; i++)
            {
                var r = settings.ranges[i];
                this.ranges.push(new UI.Range(r.row,r.col,r.rowCount,r.colCount))
            }
        }
        if(settings.iconSetType !== null && settings.iconSetType !== undefined)
            this._initIconSetType(settings.iconSetType);
        if(settings.iconCriteria !== null && settings.iconCriteria !== undefined)
        {
            var length = settings.iconCriteria.length;
            for(var i = 0; i < length; i++)
            {
                var iconCriterion = settings.iconCriteria[i];
                this._iconCriteria[i] = new IconCriterion(iconCriterion.isGreaterThanOrEqualTo,iconCriterion.iconValueType,iconCriterion.iconValue)
            }
        }
        if(settings.showIconOnly !== null && settings.showIconOnly !== undefined)
            this.showIconOnly(settings.showIconOnly);
        if(settings.reverseIconOrder !== null && settings.reverseIconOrder !== undefined)
            this.reverseIconOrder(settings.reverseIconOrder)
    };
    IconSetRule.prototype = IconSetRulePrototype;
    IconSetRule._getImageSrc = function()
    {
        return"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAQgCAYAAADvxtzfAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO" + "wgAADsIBFShKgAAANyVJREFUeF7tfQl8TFf7/6CvVsXaIkqrtVZqKxq0SCyxxxpr0ailtUYoYilCrLWE" + "qC0h9i2xJyKSEJSiUcQWW4tGFRFEJJlkMs//+Z577yzJzGQm+v5/fd93ns/n+7nnnvN8v2e59z5z7ty5" + "Z1R/u7m4uDR1dXWlFi1aDJOzrDeFfPv2bbJZxJCswGqRnOSYmJg8RZjjgTKxk5MMREVHmRVRyDoBzmhn" + "KBIVFU2RR45QRMThXCIKWckTArCcIhERERQeFk4HDhwwEgFu3bqVnksAllPkwP4DtHfvXgoNDdHlGSKX" + "ACynSEhIKO3YsYO2bt1KGzdtovXB6ykwMNC8ACynCMibNm2k4PUSefXq1ZYFYIYiIK8PDtaRf/zxR6sE" + "dOeG0myFvDxguWUBQzKgI69cSQHLA8jff5l5Ac50MSRbQi6Bnj17FkKmLZCpdjM0UwNlCTJNb3wO6C7X" + "vGBSAKaIKJctAswRjg+HDh2i/fv3U4h8dZoVgBm2JDIyksLDw2nf3n20a9cu2rJlS94CiD6KQNjBg7R3" + "zx7auWMnbd68mdavW2dZwJAM7N4dStu3b6dNGzdSUFAQreRrwqxATnJoaCht27aNNmzYQGsD1/LVuJKW" + "LfM3LZCTrPQ3eH2wLs8QRgI5yTu4v5u4v+vk/ioEQx+dQE6y0t9A7q+hMx+ZdoaxQidgSNb1d60UOA3J" + "wplNEdEJGKoCa9asMUtWjPNF4JF3c4tYIiuGACQnJTMUyYts1hSRfJHt9k+xlLUuHi8DmxPj5KMVLg5y" + "tnUmyEEuROnXKWXN57aJGJIVvFjlbJ1ITrL2N2+9yIp6JkVeBroEpaxuLF1MLwObGZEF7ozR5T33/8RI" + "hNPBzxZUpBcrG0oCogVrmuhFmKy9PZLxjV5kcVUhAnLy/Aoi78WKuvrLGSIpqz7Ti4B8czDRjYG6vGcL" + "P6Dk+e+R9uWldOw/X1ZTLwCDyIuAunqRmwOIEvqQ9noPXZ4hnnGrZKreIPLcv6bOSXutO9E1d6LLbYni" + "WxBdakp00VkS+KFSbgEYRJ4trqIXudKWtPEtZXIjogv1RX7yvPKmBWBChEdaCMhkrSB/StrztUX+U793" + "zQukrnWplzy/vNQKmaxFzb8y+bzUxaezipsWkMjvSWQANf/KZNQcx+Rfqon8pJlvmxjEda4uuprzwJPp" + "hXMLvPixPr0IqMNnX01xmDDSyQsqUPLcMpQ8uxQ99S1GSTOKUNL3henJ1ILmx+B/2XCN4zLFlYaLRTeQ" + "fNbhxMGxx+HDETA5iAgUyhmYF3AkZJqxKde8ctlKAWYUaW8N5cubr9BrHpIAH06ZktskEelsVMhaJmuv" + "9SDt1Q4iH+eE7G5sCF2IPgggQkCuWcs1a68wGZc15+PEkil6U8gIYXASAmg2ggpqBvliY0mAz06ZJpmO" + "zEFUT+5vVLOWydrz9SQBPsVlqp6MMK4jJ3Czr/OAgSznGUIXkXRk/iBRCkWzMdoGZBxewytVFxNBxkeZ" + "UpBztCXnKoQoZRgrcMUqAsZkbrbos5wHR5CFM5sSrXDZiwwUGn20GZAR5g3JiiHwIHbIu7lFAHzQmCKb" + "NUMRfNTZRFZMiPCHbb7Idvs3WIl1Dh4l1xWNKx74tu0zd5BLrCt675vY4VR8zduJxVfYIGJIXnEhiLAt" + "tvytxLcXWyGSk9zuWCedSNEfCie+PfdN8yKG5ICLgdQ6pr2AGwP7yH9r1r8S35xhQsSIfGkNuUS65cLy" + "S2uFSOFpbyS+6VNIL2JIXh6/mh1X0zJGk3BXhovYIh95KIPfG98VSizkLYvwoaISgYw1Ran4yrep2yl3" + "8o9fSZ/u/5zqy/C/vFLkF570Bv1rfCF6w7sgFRptZn7gsPQtWnplBTmFNqRPQj8jp92f8X4AFfZ5wzQh" + "pxX94S1acnUZVdlRl6pur8Ooy/vL0WzrBN6e9yYtvuZP72+uSe9vceKtEy25tpQKjbVySlNkdmFalLCE" + "ym2oRuWCq1FZBvYLjbJS4K0ZhemHG4uoVNBHVHrdR2K7iPcLDi9gnUDhqW+wwA9UfO0HOmC/wDfWCkwq" + "RAtvLsyFAkNV1gn8i0cbA4bjjGYX+JYB8mArBexm+TtF2cWywdHwyxgFNgsoDyuAfAlERUXRkagjdOSI" + "9LTDZoGIQ4cE8E1mvgQOHDwgnnAoTzlsFti9e7cO+RLAk40d2yWYFTD3hTwImzdtok2MzZs26wRyQogo" + "X3+dOXNGOCrAl5HrghgGX0oC8IM/eEIAllNk9epVtGr1avFgQoFZsmI5RQJWBFBAwAqxzZOsWE6Rpf7S" + "N7hWkRXLKWITWTFDEZvJiiki+SLbTW/KIPI1U1jOss0g8FoiIBueCzaLgJjzbDQSQWZegIBZEUMHSwiU" + "HxfkElEETp0+TcdPnKDomGg6FBEhPU8KCRHPWzdsCGaBtbRq5apcIiYEYsQz5/0HJIFtEBDPWyGwklYE" + "GMcGvcApFjgutUAIyC2AQHDwBrkF0uMhky3IC6bGQDeIeUERyUW2xhSBfJFhIOWbDAMx32TYa5FhfLYZ" + "Pwqym3WGyxQjn68jYEi2+QTKSbbpFDZFxjzRKhFTZMwNxRyRY4NFEVNkZW6o4CDPF82K5CQryDlHVJBL" + "BDs5AUdpfrjdaI6YE9z63NcJCkDYvFmaH27irSIgu1g2RSAIc8R1QQL5Eli9ynh+aLOANEeUkC8B/6VL" + "eY64lPzleaLNAosXL9YhXwI5YZOAOcgudrPGHgz7ksxBdrFsfwzuQ3TnTi5YLXB3YHdByAoJ1QH797/u" + "bZ3A7T7ukgDHgqxtOyhz23ax/3v/btYJ3OjRRhAyN26izA0SsH+zZ3vrBK65uwqCek0QqXlupF4TKPav" + "d25pncDldl8IQsayFZSxPIC3AWL/cvum1glccmssCOkLf6CMBYsYP4h95Msuervi4uRw+YtamguNaz6E" + "A4DDBUKa72xK9/VjzBb7yD/a+BOBsHJvvTzw7hsJQuSScw0PEFOjDgtHBWlTpgm8mjzNKD9x5TIKr+5I" + "e0v+q7YQgJ2p/ZHHL80+pZeRh4TTq/ETGBNlID1B5P+hI6v0ZMVO1HD0+Mm5JqVEhEsio8cKpDIE+Ud/" + "82TFoj8o7QGnF4cOClLq8FHG5KIWyIodciwiRJ6HH7C+5py2r3Qhj32lCj2+7jPW+ppz2u4SKtESm2q2" + "W25LXd+SHk1W5f+p18v1LpQS+HlWvkVeBjWjjMshlBL0Rf5EXq7l4JJ+XRIJNCHy1NeBnvqVpuR5ZSl5" + "QUV6trgyPfevQS8CalPKygaCqDx/NCmSNLOIzsESxMNr3uYSefJ9YcnpXCXpxxnna5MWP1S55CI98cUD" + "azyHxaPzO3xl5hRJmvaGTkAbV50F6pD2gjNp4zkyX20vfkYjHiFD4PYo3U+NhMjqxq9USVMLGrfgV24B" + "C9AlV+nhrWELWEDfgqaaRz6qdqonU1SSQB4wGoPVzhJZjAELPJlSgJ5wV5Kmv0U4KniqnzwXR6WCICgi" + "SD//sZ6ebI09W/i+jvzMv6ZtZBiecAvyDx/aTobhNwZP5znmjwxL9itF+SbbLf9WhOHJCGHEyUAaeSiz" + "aM6MhOrVq1OvXr1o2rRpAkgjD2Wyj0nzKlKkiGb8+PEUGxsrgFtfQNlHGXzgK1H05la0aFENfk4NR/zI" + "ecWKFTRr1iyazViwYIH4pTTK4ANfcCSqZPFo6rFjx2jVqlXk5+dHM2fOpKnTptKECRPIy8uLxo4dS/Pn" + "zxc+8AVHoqpUTcuXLy/U8TPjuXPnSuSpEnnMmDE0bNgw6t9/APXt00c8a4AvOOBCwAeDBOUlS5aQr69v" + "DvJQGsDkHh49qEP79uTNLYEvOOBCwB9NxA/b0cRpCnm0XPOA/tSjh0Ru4eJCLfjeCb7ggAsBPzhitOfN" + "m0cTDZo9QCF3aM83XS7UyNmZGtSvL3xRzlxfCAxu3bq1aNaiRYtYmclDQeZmi5o76Mh1atchV24FfMFh" + "Lk4uVRk+LGp80YABQtNRs4eoGWRXHbkGn0w4nPAFB1wIwPxdZGUMZN++fUWfDcnVq1UjrzFewge+4EhU" + "yUow4keMGCEOEe7cO7u7i2a6NGsmtkv5Xhpl8IGvzDGyCoyzzlwjfrcOZ4z2zp07RBp5KIMP4wMQTBmu" + "tnGMRFw87twKQL6QEhkTZB+rzInRUQbSdrPWFi9eXISvd89Jk3xCvL2940aOHBk3ePDQkP79+3t6eHhY" + "PgIcSJynT5+e4OMzmcaNG0cjR42kIUOGiOuiZ8+e1KlTp4Q2bdqYjokcvryYrPGZ7CPIo0aOoiFDh/Dl" + "PIB6MblzZ3dya+NGzZo11TRs1Mg4JnIQcZv+/XTNZF3NTBY19+eae4mTqY2bGzVv3ozPxs/42qit+bjq" + "x/qYyDXHKzWP5JqHDuEoJDe7c+fO1IZrbt68uUSuw1dljepUqVIlKSbOmDGjqb7Zxn1GzW5t2lAzQear" + "EuTqNejDDz8kR46JpUqVaqqaxMNtasBwNbq5tdHXXLeuiAcgI6C+88475ODg4KPiQ+U/Stdng5q5z81E" + "n6Waq9eoQZU+rCTIpd8pTcWKFaO33nrLX8XXtx9GW0/uLJqtG7A6XLMgKzXryFSoUCFfFdc8WN9sJivN" + "/oxrrss1iz5LNb9T+h09+Q3xuxxP1aBBg8p49PJQS2RltA0GrJJM5j4r5DcKCbI+Jrp3cvfHoVJGu658" + "qKQBc6TSBjXLZEAfEznmleAzLP4zpWbus+Fo68hSs4HcMbFx48YV6tSpfdbwUJWWyW++9aZhzeZjYsWK" + "FYvwGTbO0dEx0XjAxC+hbIuJfJI4FS5cuCMfKntMtMYmz91J5mBNuXA4dvpWLigOeZXbBWQHc7Cm/PXN" + "lLICa8rz7GNe5XYB2cEcrCm3299hpkZXgTXleR7nvMrtArKDOVhT/jdYrRAyCzZTNSsQfDj2mH4tFwwF" + "LI6BXYANjubABkdzEHy7/Q1manQVWFOe53HOq9wuIDuYgzXlf4OZugoVWFOOhKV4kGe5XUByMAtryu32" + "d5ip0VXAZuoqVCD4cLR0nOFoMR7YBdjgaA5scDQHwX99M1WzAmvKkbA0BnmW2wUkB7Owptxuf4eZGl0F" + "1pQjYfE451VuF5AczMKa8tc3U8oKrClHwmIf8yq3C0gOZmFNud3+Zjt9+nSRY8eOecbExIQciYqKi4w8" + "EhcRERESFhHhuXPnTstPuaKPH3eOPno0AW+W4YcaypKoeCHk4MGDtP/AgYQ9e/aYfu58NDbWK+boUY1E" + "jiaumSIOH6bwcJDDQKa9e/fhnQ4Nt8T4uXNsbKwbyGKhxegoOnwkUiIfCqeDYUyWl0XFCyF4VW3Hjh2a" + "bdu26Z87Mzle1CzIR+iwIHPNOrKoWZCxhiJ+TrF161bpuTMPWFOFHKnUjGYb1byHdoWE6slbtopfBW3c" + "uLGpKioqxicqWu4zD1juPu+hkF1M3rmLtm8zIlNwcLCPKjIqyl+MtqiZ+wzy/v26mrGi684du2ib1Gzx" + "asrGDZtow/pg/PDFXxUZGemnb/ZB8SuXfQYDJmqWm70JZFHzBrGOZNDaIF8VH+PBumYb9Fki6waMm72F" + "yZvEao7r162ndYFBtHbtWk8IlOGa1YK8T+rzLh15h9xnJotmo2YmBzF5zVo1d0F67sxkf5M1K2RpwGTy" + "OvGm4drVa/XPnXft2lUiNDQ0XiyBC/I2ueZNClmqOShQR45fs2aN8XNnrrXC9h07zhoN2IaN4u1Kw5qZ" + "eJZh+rkzixTZunnrOD5UiQp5nb7mRK55An4lIrtbNh4gp6CgoI7AulXr7M+d/27rues1Xpr0vPyNl2f8" + "tydHXBlh29qKMJAHXf6WAJtFvrw01Kv/xcHU/+IQgQEXB6cNuDhM/4Zdz7hhuX5nppjHuYFeveIGUK+4" + "gRJ+Gaj2OP+V/tezXX7qWa/z6d6P3X/uLX5rZ2idT/X24jJS4H6qt9r9dB89uc3Jrg3cjnV+6hbbhdyO" + "d9a4He+iE2kd29mrzfEupID31a2OuRv/brdptJvHF0faaJpFtSWgKdKRbT2bH2nrpc9jRLVRM0z/6LfB" + "IRePBuHNNQ3DXahhmCuDt5xuoKTDXNT1w1zxYw7z5rTP2eOTvY00tfY2ptr7GhO2tXjLaXWtfc6WyYpV" + "3VnPo3rop5rqIQ2oemh9qhZaX11tZ33ryIp9tK22x0fba2s+3F5H/eG22p3lbNvso511nSvvcjL7E0K7" + "/Q+Yt6pIMa9insXGFAtx8CoWByCNPJTJXqatuHdxZ4cxxRNqza9N3bd7kPfx8QJIIw9l8JHdjY1r8Xp3" + "QhnN8KhRtP0pz0oYwY830XqGso8y+MBXpklWfExxt7ITy2mW3lwuHBf+sUQEzl7nB1C3c33I41x/Gn9l" + "Mv2YuJZmXp5DZSeV04Aj01F78Xg0dWvSDvru1mT68tIg6vkrk8/2oU6ne5BzREuqtK0WtQh3p+o7G1Db" + "g10IHEF2GOPQ9KMZlUXN3170ovanulObE12o5bFOIo2Y8P7WT2jiuen0KiuNttzZSZ2P9iFwwGWB4j4Y" + "pLm3fxCO3572piGnRtOH22tTlZ31RI377oeTYuF/HCH3o73FwIKrcvAq7j/s8Lc08+Yc6n98mHBKzXpF" + "Lbm5bSO6093U+yIvMzuTZp6fLyppF92DwAEXo+/31YFBLDBXFEb/FSsIf6U/ogxNhkgnvnpAnSJ7i/JK" + "3LK1f64ncJjrizEY3CrQjbY93Un1DjQVTb6ZclsQYUf/PEGf7GpMH/AgglxrdxMx2OCIk8thtEOZMhPK" + "qoMSg2nitWlUZden9Pn+tvQo/QnNvbhEV+tH2+uK9NBzowm+4IArHQnui+uaFkK5+09fCkKNnQ1FrR/t" + "qMsDWofJtajlYXfa8mQ7wVf0X7FSk0qVwHH9OnyIOJxjLk2gzw66UtWQ+ixQh2qGNqaRv44XZfCBLzgy" + "XbIi3kUqOIwpdrZxwOcUcGelcF71IIgRKNLIQxl8So0pZSbgSlfiOK4hERdPx42dBJBGHpdNyPOKVIwH" + "yKnY2GIdAaTlbLtZZf+9MXHmrTmiDD7wtSkmunGIc4lpT/3ODKYt7ANfcATZMCbOubeQ+jG5F5O7Mrkj" + "yMc7k8vRDvR5ZGsx8cKFBt9cMREBZciVEdRTNLuvnhzTgZqAfMiF6h74nBqHu3FLd+aOiesfb5T6jJpP" + "9RCR2RU1H0HNrlSHyTX3NKJqfInjCs0VEzHaSs0go89odgNB/oLJzlR1V336kOOD2ZjYk0fb7YTc7CNu" + "3Gwm72fybj3ZYkycnDBDrpnJXHNdJjtxzdWYbHVM7HtmkFSz3Gz02eaY6H3Zh5zDWthj4v+YubpPoGET" + "lmORyeGMh2KxSQl7VF+rashu5g0C7JzIzhHFllegxodb0ldXh1KdjQ2J8+5xmTPDjRHLOCnT9CYLnCwZ" + "+CEBjQ+3EgIuh9sgX80icSh3+rGW8JNpesst0JI8IRDBAtwCXAvdfulL7aM6WyvQShI43FYS2JYfgWtD" + "pC4Igdq2CshdkAUqWdsChyXlQYjg9HDeXqi+8hMjAbdD7S0IsCMXXuWtp6qnqpBqkMqF02c572GFoBrU" + "YEsjkB9yHt6iMDYICJGcNlhVlQlxjBjGbyxq+qo0K2BoPVXm14qzSsCS/ZcIfDthZf4F7KZS4d01oE+f" + "PjR0kCd9NbCfeMer35cDqFcfvkPr0ZO6dOlGHTq0o1ZuralzJ3fhD+gE/P2XC3zJpJ4e3cm9Uwdq2bIl" + "OTdqQnXq1KLKH1Wn9xzLU/GSJfDKMhVQFdQL9O3dh5Yt9ceODu+3rET1vBqS88zPqYanE5Wq9Y5ROdDF" + "vSu2KtWQrwfTgC/7i8wi775NLX5sS/1+5aneuYHU9ee+1OEkT3tiu1DV4TWpwBtijWUB+X1Xleqrr74S" + "r+Uis9Wa9jTg1yH0zeUxNCLem/qeG0Rtj3cjV572fMEzl/cHVJYFClJz12aSAAasU6dOVLlzNep3fhCN" + "uvIdBf21kXYl76ZZt+dTh+M9xLSnMd+Ef3HIjRxqFKeCqgLk6iIL9OvXj1q1akVNFrhQ73Nf0XfXp9Ke" + "5wfoYEoEreRb/k7He9JnPGdyPtSCWh11pxoTa4tW6LrQu3dvaty4MTVf05q6nO5Nnue/pSX3Aijo4UYa" + "d3kKNYtsLyZcEMFY1JrbgFQFVOTq2lwSQP9r165N9b535v52FU4dTvSkTid6UdPIdnxL/AV9sreR6EL7" + "E93ooxHVjbvQpUsXqlKlCpVtXp5aHO0ovsFsFNGKGhxsTvW45tr7moh5cqujnVigO735/tviPGjuKncB" + "7zU7OjqKfjVc+QU1j24nRrwJi+BDphkLolXuP/WkGt+L/xsQwHuA4OPVPCpeXPw1ChUuWZg+W9mM2vGh" + "Azqc7E6dfvKgtrFdqco4Jx0Z0B1GnJI4PZUCoIyrI33i14BqL/mMPvL6mN6qaFwOuHcxuBYKFCjA/VKJ" + "bSHZAfsYbeSJrZyvwOhiUoBjC7g2x7aFvG0mRhyvcKPM0F8I2O1vNu3p00U0EZGe2WHhIZr9++M0obvj" + "MnfsDFFv2eKpzeu5szY62jn78JGE7PBDpNl/gDS795Bm5y7K2rKNsjZsoMy1gQmZq1aZvnfWHIn20hyO" + "1GSHHaJshbxDIW9kchBlrlxNGcuWazIWLTG+d86KjnYTZK5ZIu+lLJmcCXJgEKmZrF4WQBmLl1D6vAWa" + "dN85+ntnJsdLzT5ImtAcNTM5c+UqHTlj/kJK95tLaTNmSvfOdCiqqQY17zNutlIzmq1etlxHzvCbQ+kz" + "ZlLa1O8pbdKkpipN2CEfZcCy5AHLDEaf13GzpZrVi5YyeQFlzAbZl9IFeTKljZvko+I++wuyqHkrZYGs" + "q5nJSrMFmWue9j2lT5pCad9NpFRvb39VVsheP+OaMWByzYJsULMgc81MfuU9nl6NGeurUm/fOdi4ZkOy" + "UrOv1GcfmTyWyV5jKXXUKE8Vn11l1MEb1FLN8qFCn+fxgBn0OV0hSzVT6sjR6pTRo6V7Z/Xadf6i2csx" + "YPJoz56rr1lp9thxgvxq1BiufbT+3pnWrCmR4b88XjpUC/g465stDdgErhlkb0FmxCcPy/Fs7tXixRXS" + "5y08q/RZd6i45jRudppS88gxZ9PGjDF976xdvLhI2gzfcelTpyXqmi1qxoCNTuRBm6D19rbu3jll/Hin" + "1LHjO6aOGtuRB8t+75xPQ+jC3xEDpsOYGRtWvXr1F127dqXvvvtOAGnkoUxyMW2Ob7/99hEsZRAWFiaW" + "LYmPjxdAGnkogw98JYqBFStW7CgWHo2OjqbHjx/L3+3rDXkogw98ZZrOhqGpR48epczMTJmS21AGEfgy" + "Z7hEVamK8PTmGZqYlJQku5q3J0+eiO6Aw1zxEL8pBun06dOyS952/PhxMbDMbQIBz2+//ZYuX74sF+dt" + "Fy9eJHDAFQKYK1+9elUuztsuXbpE4CgCTTF1wXK31hq6K69uKrpQhI9tMv4qITU1VXYxbykpKeJvFcBh" + "ru6XEMO/+OILoZydnS275jaNRkM//fQTwRcciaq3GCym8fPPP5tsCfJAhg98JYqxYV2c2AYNGtD69euF" + "M44McPLkSbHsD8rgI/uaNVwwT6tWrUrt2rUTQBp5cpnVlu/L2W56+4fEROwjjOU7JnKRAM5C/MTM5pjI" + "ZTpgdRKI2BQTudwIn376qW0xUSEaAkuhWR0TFZIhsPSZPSbaY6LdrDd7TJSNy3T4X42JCskQWF/R6pio" + "kAyxcOFC62OiQlKARbjwI3CrYyKX6VC4cGGxvqJNMZHzBbD0F8g2x0QsuIg/MLDHxP9Us8dE2bhMh//2" + "mCivEWsVTMbE2bNn53I0B5MxEXEOUUdxMgeLMRHKb0gLDZqEVTERi/OaaolNMTEwMFA4Ix7aY+J/uv0f" + "x0SELqRtjokIWfIZZwSrYiJOV8Q93jeJPGNi3bp1c5FywmxMxKWsOOUFkzFRXvrZKthjoj0m2u1vsNde" + "jQVE4/VgbDSbBeCoQNm3WcCQ8B8ggAIFyr7NApYIOfdzWV6EnPsiQ4Eph7z2bSbk3P9vEVCQn3275cNe" + "exBBNDqMtprNAnBUoOzbLGDxTMzL/v8LoECBsm+zgCVCzv1clhch577xei9seRFy7guBMTsfvp7A4FV3" + "X0/AcI2bvAg594VAu5EXXk9ABzYUKLBm3275sNceRBCND6ONZrMAHBUo+zYLWD4T87D/AwET14bNAl67" + "rA8wuY2JIzcn2iAARwXy/rC192wTMIyJ2H619I5tAoYxEds+c27YJmC07hdvu025apuAYUzEtpP3JdsE" + "dDCxD0cFpvbtlg977UEE0fgw2mi2C5g4L2wWsBQf8jYmjtr65+sJjNhoPsDkNjgqkPeHBd63TcCwz9h+" + "veJ32wQMYyK2A5bctiCAAgXyvmFMxLbvvJuWBSzFRGx7zrxuWcBSTMS26+QrlgUsxURsO38Xb1nAUkzE" + "tqOX+RgpHHSwYh9EBaLcbvmwHINquzFxjOHFZrMxcfR2fYDJ2+CoQN4fufmBbQI548O3wX/YJmAYE7Ed" + "Gmhwuedp7GgYE7H9eqXB1ZvLUKBA3jeMidgO9DeIF7mMCyzFRGy/XHjLsoClmIitUcARCQXyvqWYiK3H" + "DH2IExm2xERsjSIWErbERGy7TLhsLGBLTMS20ziDfSRsiYnYth9z0VhAh/zs2y23na6oKnKshMozpqQq" + "5GhJVRyANPJQJruZtujiKmcmJPzyeW26NWYo/bUmQABp5KEMPrK7sXGh1/H3imoeLJ1P9McfEn77TYK8" + "jzL4wFemScaqbicqFtO8OhkrHLWXL1P2kSjSHJBfmGNknz0ryuADX3BkukoVU0IVj6bS/fukPfUzZR+S" + "X08MZfL2HZS1cTNlBq2jLN6ne/dEt8AR5GOlVE1Pf/K+1MwLF0m8JLhvP2WFhJJm23Ymb5LeLvxxJWX4" + "+5Mm8ojwBQdcVUwplQ8Gie7/wc0+IsiakBAmyy8Irg2kzB9/pIwl/pSxYCGpeYuWggMumu//x8LZRHd+" + "o+y9+0izK0R6NTF4A6nXBJJ6BZMX4+XAhZQxy4/Sv59B2uvXCRxwIeB3d6YPC9yRyVspcz3IaykzYIUg" + "p+P9Ppmc5jOFtDduEDjM9VUdLaEafLVfV9GsrF2hlLUumDJXryH18hXSW3bzFlC672xKmzZdvJ6Hd97g" + "Cw5OLtUJB1WZ4xWKqTOv8KE7cZLUq1YzeTllLFpMGXPnCXL6VCZPmkyvxk+gTD4S8AUHXHEk0JfLvTqK" + "Q5S1Yyepf1gkkWfOkt7tm8jkcd/xUVhGdPcuwVf0X7GoUqoSnBF/f850cYiy+FCpecTTpk5jsg/3fTpl" + "7T8oyuADX3BkumTRpVUV+BQ9e7GTK6WfOS2ctdeuCSCNPJTBh8mm3+3D1caDOo6RiIsnYciXAuJC4jyu" + "eUKeV6Ri3CInrq0jgLScbbd/q/3ernXju1066c8uE/ZbB7fm97p2WCTv6g3k5G++yUr1Hk/3unUKkrON" + "DORnI0ZohE/XjivkbMn+GtD/Ac51wJSIQlZ8UsaM1d5s16KKXKxSYefpkMFppkRykl96jaXf3duMF0RD" + "yykC3O/RJcwqsmKmRKwmKyZEhg7NMCSnjh1nHRkm+jx8RLahAGDu6BjZ7U6tWxn2WbyabK1ITjL6fLdL" + "h7k5x8SkiCmy0mdTA5tLxPBEMjXaEEka/PUrxSfXiXSrTZv32SHV0qFSRED+vVO7EXK23iDyW8f2Q+Vd" + "kwaR253aDJJ37fZvs3/WPFGbkCCg7Fs9T8w6HEnpPKXBdAZAOisqSpRZnifevSf9yjkzkzJ4aqcIZPJE" + "k7RaqYx9zM4Ts3hymXVMWpAVBIgIsmyZmD+yD3xNzBPvU/qcuaLGrJM/SQylVjaQRXfYx+Q8UXvtuq7J" + "QPZvv8tUoqwzZ43K4JtrnojRVhwMmy1M7o5OwNw8EWsf5OqzQXcgAh+z88SssHBdv5U+AzqR7GwxiObn" + "iXyIsniyaUg2FEEZfPKeJx4My30iHYoQZTbOE68LIG2fJ/7HWlh1x3bh1R0pvEZZ4whkjSnkRzu2kM0i" + "CvnZsWjS3rxF6Rd+tV5EISfHHn2Qfeq0uIPNPn/eOhHDmjUnTgiyAiHy63nzIjry0SjKijlKmbgB3cq3" + "v1hsBneyjOxz51gkLreIjhxzRIR0NW62Bdbxjfc6g/0g0vBlnR73i15kl0pVCDs3p06g7MtXxL2hBL75" + "NIKSt4y0t27RX6sChIhowaGqjk7YeRC0WoT1ND++6dRhrgwpreGBTYo+cgr+aLkQgCki2VeuUtaRKEqb" + "PJVeTeYbT94qQPOf7dktajYiK6aI3AsOvJEVESktYwN4jSNN7Al6tnuXebJiiojmwgXKCj9EqcNHUVZ0" + "DD3btSNvsmK6MeGByk64YbrPeZkicmPUMOtrzmmKSL7IdnsNwwpFtHy5Z/aixSGaOfPjNLNmx2mmfB+i" + "/m6SZ54rE2mXr3LW+i9P0P6wmLLnzScmU9bU7ylr4iTK5NNZ/c3IhMzBg03PE7UBP3plL12myTYkT2Py" + "BB8me5P62xGU8fUQSu83QJPRvbdxRNIGBLgJ8iImz51P2b6zSWNEHklqiUxpHr3pVacumpdt2uvnidn+" + "y+KVmkHOmjadMtHsMTJ5EJO/HEDpTE5z70av2nSgV64t5bXC/P2b6sjcbMOaM9FsJmd8OZDSe3DN7l1l" + "cit6+XkzSmnQqKlKs3CRj+gzasaACTIPGNeskNMMyKlMTv28Ob1s0JhS6jTwUXGf/QVZ1IzR1jdb1Cya" + "rZBbM7kZvWzI5LoN6PnHdfxVWb6z/dBnHXn4CKnP/bhmXZ/bU6pLa3rZBM0GuT6l1KxDL6rV9FVppk4f" + "bDTag4ZKA8bNTuskDZhCFs3mml9IZHpRuZqnSjt5chn1aG915nCpzzhU6R69mMzNbis3uwn3Gc2uo5Cd" + "6NmH1dQpjlXltcK+HeEvnSRKs+U+o2b9gFGKE5OrOtHzytXp+QdVDNYKGzasRFrfAfEYMP1oy2S5ZqnP" + "TP5IkOOTK1fOsVaYh0eFNPcuZ41qVsioWfS5Oj37oMpZJptZK6yxR5GXrq3G8YAlpih9lslcc+KzDypP" + "uF+xonXzxJR69ZxefFy344tqH3dM+aCqfZ6Yl2Fh3hHjA+hQdcfh4dUcH+LTSEK5PRE13rNunW12Tgyv" + "Vi7i6eQpRHvDKDtkH931HID8ewerlXMOr1rWLax6udiwauVMr/CMAnyUqxcsIS0LaFngGV+hLKpmxKH8" + "6uCBwk+m6c1IYCEEwvUC3IKMOQtIG7qfkpcvz1sgQwgYtMBWAakFLBDKAhxYbW8Bj4EYRBZ4PmFiDoEA" + "ywJ/9uuLQYsQh7N6uQtXunUUAmpZ4PEPP5gXgCMfpqvs4InJJ89OXPhcOIvzInmiD/0xBUfE8SGXW7/O" + "9uEqZapKh9Axhsm/YcojFxmbOQFD2+XkZF9n25KBbF9n+zVNWaX3n7XOdtcmTWj+V560YfQYmty1GzWW" + "3vM0gsl1th1LlaLDfn6kwaPjA/spa9cuysST36B1tLCHBxUuVEgnYHKd7WhM92KiKPvCz5R98QxpIsNJ" + "jWew0oq2NLNdB1nAxDrbX7VuTVlhHM5+OUXaZ78TpdwnzaWfKXPDBkHGA9xXywKo/vvvm15neycH0ax9" + "+yn7zAnSJv9O2hf3SPPrKb5/DqR0noxmLFoi7qUDBwwUrci1znYs912sLbt/L2lOHyPNmVjKDN1JGTzA" + "6XPnUQaLZK4Ppv0jRppeZzuYZ2p4VC76vI7v2teulcnzKW32HHHjnblxMy3p3sP0OtvdGjTk0V4vDRju" + "1LlG1JzGN91ovhplLFDt3XfFeWByne1Ib54nL1/BNfPtPh6ZM7CPp+H4EmLLQPGmtYDJdbZLvv02hQz9" + "htSr1kgLEgdLRHXwRgrgpsNHgcV1tjt+Uou2ff01RfGZuLpnL6pRpoxROWBfZ/t/w0aMGOEAyLu225gx" + "Y/wAedc28/b2rjBx4sRUAGk523obPXp00L59+whAWs62zry8vOpNnTpV8+DBAwKQRp5cbNrYoQb3tx9j" + "IeMq/uP62rVrAkgjTy7rB1+ZJpo6nTNfzpkzRywuhb9oPn/+PD18+FCs1AMgjTyUwQe+4ICLwSrCiZNY" + "FAFrAiUkJNCZM2fFGkEghB8Kp5ijMSIPZfCBLzjgilYM4/tGzriA/3S+e/cuhXNoxz8t79u7T2zxT9Ph" + "4WGiDD5cexw4gqwYC5RhJGCNixs3boh/WDYE8lAGH/jKNGPjgnZYOOH27du0afNm8QfJmzdvElvkYSEF" + "+MjuuW3UqFGeG/hTCAsrhYaG0sWLF8QaukjHx18ilMFHds9t3DdfnDh//vkn/fbbb2KwAKSRhzL4yO65" + "jQs3YZBQE4/wS95fDCCNPHkAN8nuuY37d5LxlDGdSaXlbBzq0siTy3JP9xVj9X6WLl+UwUfetZs19mhG" + "mdeLiU9nl8p/TEzyK10heWHF/MfEp34lg9JjvPIXE5/5laiXvKSqRpsUZX1MfDLrnRpP55TslzS75MKn" + "s0tezfh5BtFfG/OOiUyYzs19+Xx1fUrd04vST/Bc8fpqouRo0v7uk3dMvL+4YpEkv5InX4V5Ej0/wbVu" + "IO29WaS9PYq0NwZaFxOT55cqkTS71IW0qJFET0JJe60Laa+4CVgdE1PmOJThriSkH59A9DCQtL/WJPrV" + "ybaYyCdOu5QNLtyNYKIzJUl7tpRtMTHJr4Rn6t4+RPf9iM7XILo307aY+NSvlC9OHkqOpOw/9xMG1qaY" + "yAKbMJBoxdN5777ko2NbTMThTPIr9RTnxvMZxW2PiTgTcQnLu7nMHhPzYRhQS4OapyEmAvKubabERABp" + "Odt6U2IigLScbZ0ZxkQAaeTJxabNXEwEkEaeKGMf+Mq0vGMigDTyUAYf+IIDrumYeF8fEwXujBZxEmXw" + "gS844IpW5IqJV5WY2EZsCVuOkyiDD1/2ceAIsmI5YyLiofZ8TYYTpzk+ch7K4ANfmWZs+pi4nojjIZ0x" + "AMfJlI0teDBL2RYTBX7lNOehDD6ye24zFRMBpJEnnVSlbIuJANLIkwfQ9piINPJEGfvI2bktr5iIMvjI" + "u3b7/2IVvSsWKfM6QbXspHKxZSeWixP/qmmrlZ1Ytt8XK5oSUGaiY6418iwamv3BtEoPVt8LpDX3g+j9" + "7yslc3d0Z2aeVnai47zB4UMp8NF6CnoUTEiXm1hupVxs2cpPKv/BJ/NqZW5+tI3GJ/jQuIRJtPnxNnKa" + "V0tT5rv3LEdmWLmJjlu/PzeDZt+ZT1/+8jX1Y/jdWUDIKzfJEQtQmrcyk8o3bbG2Fa3jUO5+oje1j+1O" + "7WJ7UNeTfWjD4y3UdGVzdKWj7J7D+FA5Ti5/cdmtFfTV2W+oUXhLRisdhsaNpkXXl5LjpPK3nGaYeFyC" + "Q9V9hwctuLOEPg51phohn8ngdOhnVDO0ES29t4LgU25SOR+ZJhkOUeUZVZLXPdhALQ93Fv/lbApto3oQ" + "fCrPqJxaekpF/QcuDlHv0L7ib5jx/9U//L7MJIIebhA+Hjt78Vg4bpXpOO7lrr43pQI1XPqZVYAvH5GH" + "Ml26aMr7lG9gC17rIrPbv8OOOZVxCPu4fANbsNPwZwR4khnh9D791NHFKsAXT0dluvhx39Yr3/H05s4d" + "4tsz8W6fKfCNo/C55jMWz171EepgtYoVohpWT828eIHwF3xZm7dSpsAWgSwFBw8SfNg3+bBTjhjJTfK5" + "7D2ctHyXhpfi1KvXUMaqNeJbfgVavv2FD54KyzS94ZlqeI3yt15EHaas0N2UsXARpctAOnP7Tko9fozC" + "P37vIp4KyzRj4351PNOzI0/prlH67LmUNm0GpeF1vFmzxRj80r87116+qexu2nhAjzzavpkyQ3ZLr6F4" + "f0eZu0IJeRhs2c28hVV7r94xlwaa7GtXKU38PnEaIX2sxWeZByqXN/M/7zkMh+j2vJmUffIn8WtRpPlc" + "mScX5204RJF1KydnxJ2j9HNn6HCdyg9wtsrF1hkO1c892hMQXrWs7RMr8etRPHevXg5ryObP0Gyji8Zu" + "dvtnW9IcB6cns0o0sAWPZhSvKpFnlViZNLsE5QuzSszju/biJ8Wd6sMfbYI2cS0EwvjuvXhs1vnBRNf7" + "MwbI25xQ8rEdQFpG5s8DuRXF9/PtbvHeT2aXOM1NirMFaPljv2JmJp12s9v/ud33VhV5NKWg55MpBUKe" + "TCkYJ6FACPJQJruZtqQpKufHUwomPFtRh1IPDKaMX5YKII08lMFHdje2x9MKej2Z6aBJPzWHKP22hFdX" + "GDy9k/dRBh/4yjTJkiYXcnviW0KjSYySnF8ck677xHlE92cyfIme7hNl8IEvODJdpeJ+xqOplH6T6MkO" + "ogeLmYw36KYT3Z3Ak8vRRLeGSGJpN0S3wBHkR1NVTZN/+ECq+VkkkxcR/YF3174n+h1knsHe4oCT0JcI" + "X8j9yS1jX3DAVT2ZWtAHg0Tpt6Rm/+FHdG8ak79j8giim4OY3Ifoameiy2687SpaCg643PwC/uknuGmv" + "4pk8m5s8lei38US3hzPZk0NYLya5M7kV0cUviH5tQJTyM4EDLreggF9aDDcVAnenMHksk7/hSfdXTO7J" + "5I5E8S2Z/DmT6xOd/4To5S8EDgv4qh5PLTg4ZTv3DV24x7XfGsZkBE8PoisdmNyCyY2Z/ClRnBOnG/FA" + "3iRwcHKp/pysKsMfFGpt8nkivkOnGxx9r3VncnsmuxJdALke0S81ic5V4Up42su+4IArjgT6krKFa+ND" + "RPgS9kpbokvNmcy1/VqHawa5Mgtzq9ISCL6i/4olT1KVwHFNi+Uu4HDiUF1uw/0FuQaLNOTzgg8vl8EH" + "vuDIdMn4HK/A43H2xXpX0jyMlYR4tCnltEgjD2XwYbLpOTOuNnYYx0jExfNyd18BcSFxHtc8Ic8rUrGk" + "ySqnx1MKdQSQlrPtZoul7Clc8+Xmt/oDSMvZ1ltaWMG9GdEqyohREdJytvWWduhfezOO/osApOVs6y09" + "oujejNiipGYgLWdbb+mRpfeqT7xD6hOlCWk523pLi3IMzDxVgQCk5WzrLf3oR7Mzf67Ks7GqhLScbb1l" + "nq71TdYvdSjrXB1CWs623rLON+qcdb4xZcUxOC1nW2+Z8a4umostKfNc82yk5ez/JEuPVg3JiFKdAZCW" + "s603Jt0U1wIDaTnbenvtFtjtNewfNk/kmYiAvG/9PPGv9ZQa60ipx0oLpJ/iicXDtaLM8jyRJw90x0tM" + "a1KOvEmkzSSt+k96dexdniN8xpMuniuyj/l54p8rpDlRvIskIJYsSGOBd1iAZ2fnearzx0Lha2KeyLPU" + "az14auMipjeSgJYo6wULlJbmSedrcxnPm3iSlXue+PKcTG4m5oMpUUWYryFtxp88reEugIwnwHEfm5kn" + "YrQxsbrYlNGEXkY7cPPVpH2VwM5lmczzQ8zUfqlubp7IXbjCM1ImY3b26mhJrvkd3hanjJOO0kwtjsmY" + "9pmdJz7wl6d2DSn9eBk+CEmU/fw0qU++xzXzbO1cNZ6Ez7E0T+TDiEPFU9r04+9S9st40vy1gwW4BSBf" + "50m3VfPEB0tJfbqKIKpPlqOsM1x74hJRlo95IoPT9nniP97wMtDICQH0dKbD8Ke+Dg8ZJDCz6J4ns4pZ" + "924fkxOTfB0iNBEF+UaLrw2OSuqYjsR595JmFHdO8i3qljSzaCxvTb9VhgI6oSIBIRBM2Ze+hoCaxeNQ" + "/mqPu/CTaXozKxAvBO7RWY5Y92ZS1qXp1grw3Sl34fUEHrGA1IV7dC6/LVAEzlayTiDrYCEQInA4eXsh" + "LfQLWUBqQWbcJEsCDvee+ha9mjSrmCftUhVK9nVw4f2zOC80RyvwtN8D58bDpzOLWf9uH743xSFkYgyf" + "A78lzXDI/7t9NENlf7fPkoFsf7fvNU15M+if9W5ftzZVaOGkJrRpoStNHV6HmtR716gcMP1uX5miFLmR" + "J1t3xktTvhs8XxDf5PWgxRM+ocL/KqgTMPlu39GtHiJ40KNgxmZOT+eJRW9p+nO5Hc0ZrbxlaOLdPs8e" + "PA+6PZZnpat5JnKV5wW3iB5v5Rb047ljGwHN5W7UwKm46Xf7QgLciG6NkCacaVdYgCdejzdx83tKU0Bu" + "ASUMpLUzpT9NzfVu38ltPE/C14B3vCWRh2u4RWO45tY8e+PZGb6gu/E17VnW0PS7fZsWfC7Vhhkrvg7E" + "t3rx+CKSyRd4rnylk5iELfSuavrdvh5tKrITvvrEV4FMxKxVnvbRRe7C9b6kTRhE1T+QzgOT7/Zd2MPN" + "vcIfbVc6Sn1G8zGAmOLdHEqhS+oJP8Dku31lS79JcaFMuDGI4SmNCW+1CV/Txjm1dWTA4rt9vdqWp7CV" + "jejnbU1p24K6VLtaUaNywP5un92sskczVI4WP9bzssdTC2wV94b5MdyVPp1fnp4tc3qFuxk523rju5O4" + "jLhllHkrhO9WCuT9+xNDezK5YL/nq52JMn4XSNneVbpPtsZ44Bxwf5R1ew9pf/MWyH5yhu8Ti1+SXSzb" + "4ykF5qFG1Ky9PYIxXKRfRXoRWia7mTbczj3h2Tlq1HIgMULKVXq6oMJDtFB2z20YrLSYSVLtHFC117rJ" + "Ww6unIdBRQtld2MTX0Qs/ojo1U3SXm4tQPJWSvNnBos8W90o85GPSnpwrxj1VBXiE+ZCxnn+LGAnPMnR" + "cijXIiIbprks6244Pfn+X+EyVTI+5sN0hy2uFtF5BfjSgSHy5DT7vNzdj7tSSHpsLg7btDcSoSwErAAG" + "mSfld8R1In1rpXxjlQemFozlgV6jwOyXUtabSvX/AOf1iiJEERHsAAAAAElFTkSuQmCC"
    };
    IconSetRule.getIcon = function(iconSetType, iconIndex)
    {
        var iconArray = [["0,160,16,16","0,180,16,16","0,80,16,16",null,null],["0,100,16,16","0,120,16,16","0,140,16,16",null,null],["0,900,16,16","0,920,16,16","0,880,16,16",null,null],["0,820,16,16","0,840,16,16","0,860,16,16",null,null],["0,780,16,16","0,1000,16,16","0,420,16,16",null,null],["0,680,16,16","0,940,16,16","0,400,16,16",null,null],["0,800,16,16","0,1020,16,16","0,440,16,16",null,null],["0,740,16,16","0,1040,16,16","0,400,16,16",null,null],["0,720,16,16","0,980,16,16","0,380,16,16",null,null],["0,700,16,16","0,960,16,16","0,360,16,16",null,null],["0,160,16,16","0,40,16,16","0,60,16,16","0,80,16,16",null],["0,100,16,16","0,0,16,16","0,20,16,16","0,140,16,16",null],["0,220,16,16","0,340,16,16","0,460,16,16","0,760,16,16",null],["0,600,16,16","0,620,16,16","0,640,16,16","0,660,16,16",null],["0,200,16,16","0,680,16,16","0,940,16,16","0,400,16,16",null],["0,160,16,16","0,40,16,16","0,180,16,16","0,60,16,16","0,80,16,16"],["0,100,16,16","0,0,16,16","0,120,16,16","0,20,16,16","0,140,16,16"],["0,580,16,16","0,600,16,16","0,620,16,16","0,640,16,16","0,660,16,16"],["0,480,16,16","0,500,16,16","0,520,16,16","0,540,16,16","0,560,16,16"],["0,240,16,16","0,260,16,16","0,280,16,16","0,300,16,16","0,320,16,16"]];
        var image = IconSetRule._getImageSrc();
        var imageRect = iconArray[iconSetType][iconIndex];
        if(imageRect)
        {
            var rect = imageRect.split(",");
            return{
                    image: image,
                    x: rect[0],
                    y: rect[1],
                    w: rect[2],
                    h: rect[3]
                }
        }
        return null
    };
    UI.IconSetRule = IconSetRule;
    function ConditionalFormats(worksheet)
    {
        this.worksheet = worksheet;
        this.rules = []
    }
    ConditionalFormats.prototype = {
        rules: null,
        worksheet: null,
        getRule: function(index)
        {
            return this.rules[index]
        },
        count: function()
        {
            return this.rules.length
        },
        _cloneRanges: function(ranges)
        {
            var newRanges = [];
            var length = ranges.length;
            for(var i = 0; i < length; i++)
                newRanges.push(this.worksheet._getActualRange(ranges[i]));
            return newRanges
        },
        addSpecificTextRule: function(comparisionOperator, text, style, ranges)
        {
            var rule = new SpecificTextRule(comparisionOperator,text,style);
            rule.ranges = ranges;
            return this.addRule(rule)
        },
        addCellValueRule: function(comparisionOperator, value1, value2, style, ranges)
        {
            var rule = new CellValueRule(comparisionOperator,value1,value2,style);
            rule.ranges = ranges;
            return this.addRule(rule)
        },
        addDateOccurringRule: function(type, style, ranges)
        {
            var rule = new DateOccurringRule(type,style);
            rule.ranges = ranges;
            return this.addRule(rule)
        },
        addFormulaRule: function(formula, style, ranges)
        {
            var rule = new FormulaRule(formula,style);
            rule.ranges = ranges;
            return this.addRule(rule)
        },
        addTop10Rule: function(type, rank, style, ranges)
        {
            var rule = new Top10Rule(type,rank,style);
            rule.ranges = ranges;
            return this.addRule(rule)
        },
        addUniqueRule: function(style, ranges)
        {
            var rule = new UniqueRule(style);
            rule.ranges = ranges;
            return this.addRule(rule)
        },
        addDuplicateRule: function(style, ranges)
        {
            var rule = new DuplicateRule(style);
            rule.ranges = ranges;
            return this.addRule(rule)
        },
        addAverageRule: function(type, style, ranges)
        {
            var rule = new AverageRule(type,style);
            rule.ranges = ranges;
            return this.addRule(rule)
        },
        add3ScaleRule: function(minType, minValue, minColor, midType, midValue, midColor, maxType, maxValue, maxColor, ranges)
        {
            var rule = new ThreeScaleRule(minType,minValue,minColor,midType,midValue,midColor,maxType,maxValue,maxColor);
            rule.ranges = ranges;
            return this.addRule(rule)
        },
        add2ScaleRule: function(minType, minValue, minColor, maxType, maxValue, maxColor, ranges)
        {
            var rule = new TwoScaleRule(minType,minValue,minColor,maxType,maxValue,maxColor);
            rule.ranges = ranges;
            return this.addRule(rule)
        },
        addDataBarRule: function(minType, minValue, maxType, maxValue, color, ranges)
        {
            var rule = new DataBarRule(minType,minValue,maxType,maxValue,color);
            rule.ranges = ranges;
            return this.addRule(rule)
        },
        addIconSetRule: function(iconSetTye, ranges)
        {
            var rule = new IconSetRule(iconSetTye);
            rule.ranges = ranges;
            return this.addRule(rule)
        },
        addRule: function(rule)
        {
            if(this.worksheet)
            {
                var self = this;
                return this.worksheet._bindToAutoRefresh(function(rule)
                    {
                        if(!rule)
                            throw new Error("rule");
                        rule.ranges = self._cloneRanges(rule.ranges);
                        self.rules.push(rule);
                        return rule
                    })(rule)
            }
        },
        removeRule: function(rule)
        {
            if(this.worksheet)
            {
                var self = this;
                this.worksheet._bindToAutoRefresh(function(rule)
                {
                    if(rule)
                        self.rules.remove(rule)
                })(rule)
            }
        },
        _removeRange: function(srcRange, row, column, rowCount, columnCount)
        {
            if(!srcRange.intersect(row,column,rowCount,columnCount))
                return[srcRange];
            var sourceRowTop = srcRange.row;
            var sourceRowBottom = srcRange.row + srcRange.rowCount - 1;
            var sourceColumnLeft = srcRange.col;
            var sourceColumnRight = srcRange.col + srcRange.colCount - 1;
            var removeRowTop = row;
            var removeRowBottom = row + rowCount - 1;
            var removeColumnLeft = column;
            var removeColumnRight = column + columnCount - 1;
            var newRanges = [];
            if(sourceRowTop !== -1 && sourceColumnLeft !== -1)
                if(removeRowTop !== -1 && removeColumnLeft !== -1)
                {
                    if(removeColumnLeft - sourceColumnLeft > 0 && removeRowTop - sourceRowTop > 0)
                    {
                        var topLeft = new UI.Range(sourceRowTop,sourceColumnLeft,removeRowTop - sourceRowTop,removeColumnLeft - sourceColumnLeft);
                        newRanges.push(topLeft)
                    }
                    if(Math.min(removeColumnRight,sourceColumnRight) - Math.max(removeColumnLeft,sourceColumnLeft) >= 0 && removeRowTop - sourceRowTop > 0)
                    {
                        var topMid = new UI.Range(sourceRowTop,Math.max(removeColumnLeft,sourceColumnLeft),removeRowTop - sourceRowTop,Math.min(removeColumnRight,sourceColumnRight) - Math.max(removeColumnLeft,sourceColumnLeft) + 1);
                        newRanges.push(topMid)
                    }
                    if(sourceColumnRight - removeColumnRight > 0 && removeRowTop - sourceRowTop > 0)
                    {
                        var topRight = new UI.Range(sourceRowTop,removeColumnRight + 1,removeRowTop - sourceRowTop,sourceColumnRight - removeColumnRight);
                        newRanges.push(topRight)
                    }
                    if(removeColumnLeft - sourceColumnLeft > 0 && Math.min(removeRowBottom,sourceRowBottom) - Math.max(removeRowTop,sourceRowTop) >= 0)
                    {
                        var midLeft = new UI.Range(Math.max(removeRowTop,sourceRowTop),sourceColumnLeft,Math.min(removeRowBottom,sourceRowBottom) - Math.max(removeRowTop,sourceRowTop) + 1,removeColumnLeft - sourceColumnLeft);
                        newRanges.push(midLeft)
                    }
                    if(sourceColumnRight - removeColumnRight > 0 && Math.min(removeRowBottom,sourceRowBottom) - Math.max(removeRowTop,sourceRowTop) >= 0)
                    {
                        var midRight = new UI.Range(Math.max(removeRowTop,sourceRowTop),removeColumnRight + 1,Math.min(removeRowBottom,sourceRowBottom) - Math.max(removeRowTop,sourceRowTop) + 1,sourceColumnRight - removeColumnRight);
                        newRanges.push(midRight)
                    }
                    if(removeColumnLeft - sourceColumnLeft > 0 && sourceRowBottom - removeRowBottom > 0)
                    {
                        var bottomLeft = new UI.Range(removeRowBottom + 1,sourceColumnLeft,sourceRowBottom - removeRowBottom,removeColumnLeft - sourceColumnLeft);
                        newRanges.push(bottomLeft)
                    }
                    if(Math.min(removeColumnRight,sourceColumnRight) - Math.max(removeColumnLeft,sourceColumnLeft) >= 0 && sourceRowBottom - removeRowBottom > 0)
                    {
                        var bottomMid = new UI.Range(removeRowBottom + 1,Math.max(removeColumnLeft,sourceColumnLeft),sourceRowBottom - removeRowBottom,Math.min(removeColumnRight,sourceColumnRight) - Math.max(removeColumnLeft,sourceColumnLeft) + 1);
                        newRanges.push(bottomMid)
                    }
                    if(sourceColumnRight - removeColumnRight > 0 && sourceRowBottom - removeRowBottom > 0)
                    {
                        var bottomRight = new UI.Range(removeRowBottom + 1,removeColumnRight + 1,sourceRowBottom - removeRowBottom,sourceColumnRight - removeColumnRight);
                        newRanges.push(bottomRight)
                    }
                }
            if(newRanges.length > 0)
                return newRanges;
            return null
        },
        removeRuleByRange: function(row, column, rowCount, columnCount)
        {
            if(this.worksheet)
            {
                var self = this;
                this.worksheet._bindToAutoRefresh(function(row, column, rowCount, columnCount)
                {
                    var removeRules = [];
                    if(self.rules)
                        for(var i = 0, rulesLength = self.rules.length; i < rulesLength; i++)
                        {
                            var rule = self.rules[i];
                            if(rule && rule.ranges && rule.intersects(row,column,rowCount,columnCount))
                            {
                                var newRanges = [];
                                for(var j = 0, rangesLength = rule.ranges.length; j < rangesLength; j++)
                                {
                                    var range = rule.ranges[j];
                                    var ranges = self._removeRange(range,row,column,rowCount,columnCount);
                                    if(ranges)
                                        newRanges = newRanges.concat(ranges)
                                }
                                if(newRanges.length > 0)
                                    rule.ranges = newRanges;
                                else
                                    removeRules.push(rule)
                            }
                        }
                    for(var k = 0, length = removeRules.length; k < length; k++)
                        self.rules.remove(removeRules[k])
                })(row,column,rowCount,columnCount)
            }
        },
        clearRule: function()
        {
            if(this.worksheet)
            {
                var self = this;
                this.worksheet._bindToAutoRefresh(function()
                {
                    self.rules.length = 0
                })()
            }
        },
        getRules: function(row, column)
        {
            var rulesTemp = [];
            if(this.rules)
                for(var i = 0; i < this.rules.length; i++)
                {
                    var rule = this.rules[i];
                    if(rule)
                        if(rule.intersects(row,column,1,1))
                            rulesTemp.push(rule)
                }
            return rulesTemp
        },
        containsRule: function(rule, row, column)
        {
            if(rule)
                if(this.rules.contains(rule))
                    return rule.contains(row,column);
            return false
        },
        _clearNullRefRules: function()
        {
            if(this.rules)
                for(var n = this.count() - 1; n > -1; n--)
                {
                    var rule = this.rules[n];
                    if(rule.hasNoReference())
                        this.removeRule(rule)
                }
        },
        _addRows: function(row, rowCount)
        {
            if(this.rules && this.worksheet)
            {
                var length = this.rules.length;
                for(var i = 0; i < length; i++)
                {
                    var rule = this.rules[i];
                    if(rule)
                        rule._addRows(row,rowCount)
                }
            }
        },
        _addColumns: function(col, colCount)
        {
            if(this.rules && this.worksheet)
            {
                var length = this.rules.length;
                for(var i = 0; i < length; i++)
                {
                    var rule = this.rules[i];
                    if(rule)
                        rule._addColumns(col,colCount)
                }
            }
        },
        _removeRows: function(row, rowCount)
        {
            if(this.rules && this.worksheet)
            {
                var length = this.rules.length;
                for(var i = 0; i < length; i++)
                {
                    var rule = this.rules[i];
                    if(rule)
                        rule._removeRows(row,rowCount)
                }
            }
        },
        _removeColumns: function(col, colCount)
        {
            if(this.rules && this.worksheet)
            {
                var length = this.rules.length;
                for(var i = 0; i < length; i++)
                {
                    var rule = this.rules[i];
                    if(rule)
                        rule._removeColumns(col,colCount)
                }
            }
        },
        _clearCache: function()
        {
            if(this.rules != null)
                for(var i = 0; i < this.rules.length; i++)
                {
                    var rule = this.rules[i];
                    if(rule instanceof ScaleRule)
                        rule._clearCache()
                }
        },
        toJSON: function()
        {
            var rules = [];
            for(var i = 0; i < this.rules.length; i++)
            {
                var rule = this.rules[i];
                rules.push(rule ? rule.toJSON() : null)
            }
            return{rules: rules}
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.rules)
            {
                this.rules = [];
                for(var i = 0; i < settings.rules.length; i++)
                {
                    var ruleSettings = settings.rules[i];
                    var rule = null;
                    if(ruleSettings)
                    {
                        var dict = this._getRuleTypes();
                        var ruleClass = dict[ruleSettings.ruleType];
                        if(ruleClass)
                        {
                            rule = new ruleClass;
                            rule.fromJSON(ruleSettings)
                        }
                    }
                    if(rule)
                        this.rules.push(rule)
                }
            }
        },
        _getRuleTypes: function()
        {
            if(!this._ruleTypes)
            {
                var dict = {};
                dict[RuleType.ConditionRuleBase] = ConditionRuleBase;
                dict[RuleType.CellValueRule] = CellValueRule;
                dict[RuleType.SpecificTextRule] = SpecificTextRule;
                dict[RuleType.FormulaRule] = FormulaRule;
                dict[RuleType.DateOccurringRule] = DateOccurringRule;
                dict[RuleType.Top10Rule] = Top10Rule;
                dict[RuleType.UniqueRule] = UniqueRule;
                dict[RuleType.DuplicateRule] = DuplicateRule;
                dict[RuleType.AverageRule] = AverageRule;
                dict[RuleType.ScaleValue] = ScaleValue;
                dict[RuleType.TwoScaleRule] = TwoScaleRule;
                dict[RuleType.ThreeScaleRule] = ThreeScaleRule;
                dict[RuleType.DataBarRule] = DataBarRule;
                dict[RuleType.IconSetRule] = IconSetRule;
                this._ruleTypes = dict
            }
            return this._ruleTypes
        }
    };
    UI.ConditionalFormats = ConditionalFormats
})(window,jQuery);
(function(window)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        window.GrapeCity = GrapeCity = {};
    if(typeof GrapeCity.Calc === "undefined")
        GrapeCity.Calc = {};
    var Calc = GrapeCity.Calc;
    if(typeof Calc.Functions === "undefined")
        Calc.Functions = {};
    var Functions = Calc.Functions;
    Functions._builtInFunctions = Functions._builtInFunctions || {};
    if(typeof Functions._defineBuildInFunction === 'undefined')
        Functions._defineBuildInFunction = function(name, fnEvaluate, options)
        {
            if(!name)
                throw"Invalid function name";
            var fn;
            name = name.toUpperCase();
            if(!Functions._builtInFunctions.hasOwnProperty(name))
            {
                fn = new Functions.Function(name,0,255);
                Functions._builtInFunctions[name] = fn
            }
            else
            {
                fn = Functions._builtInFunctions[name];
                if(!fn)
                {
                    Functions._builtInFunctions[name] = new Functions.Function(name,0,255);
                    fn = Functions[name.toUpperCase()]
                }
                else if(!options || !options.override)
                    throw"Attempt to override function while override is not allowed";
            }
            if(fnEvaluate && typeof fnEvaluate === "function")
                fn.evaluate = fnEvaluate;
            if(options)
                for(var prop in options)
                    if(options.hasOwnProperty(prop) && prop !== 'override')
                        fn[prop] = options[prop];
            return fn
        };
    var def = Functions._defineBuildInFunction;
    function acceptsNotSecond(i)
    {
        return i !== 1
    }
    function CriteriaEvaluator(criteriaOperator, criteriaValue)
    {
        this.criteriaOperator = criteriaOperator;
        this.criteriaValue = criteriaValue
    }
    CriteriaEvaluator.prototype = {evaluate: function(databaseValue)
        {
            var result = this.criteriaOperator.evaluate(databaseValue,this.criteriaValue,null);
            return typeof result === "boolean" ? Calc.Convert.toBool(result) : false
        }};
    function DatabaseEnumerator(database, field, criteria)
    {
        this.database = database;
        this.criteria = criteria;
        this.row = 0;
        if(arguments.length === 3)
        {
            this.column = this.columnIndex(database,field);
            if(database.getRowCount() < 2 || database.getColumnCount() < 1)
                throw"InvalidCastException";
            if(criteria.getRowCount() < 2 || criteria.getColumnCount() < 1)
                throw"InvalidCastException";
            if(this.column < 0 || this.database.getColumnCount() <= this.column)
                throw"InvalidCastException";
        }
        else if(arguments.length === 2)
        {
            this.column = -1;
            if(database.getRowCount() < 2 || database.getColumnCount() < 1)
                throw"InvalidCastException";
            if(criteria.getRowCount() < 2 || criteria.getColumnCount() < 1)
                throw"InvalidCastException";
        }
        else
            throw"InvalidNullException";
    }
    DatabaseEnumerator.prototype = {
        columnIndex: function(database, field)
        {
            if(typeof field === "string")
            {
                for(var i = 0; i < database.getColumnCount(); i++)
                {
                    var columnLabel = Calc.Convert.toString(database.getValue(0,i));
                    if(columnLabel && this.ignoreCaseEqual(columnLabel,field))
                        return i
                }
                return-1
            }
            else
                return Calc.Convert.toInt(field) - 1
        },
        current: function()
        {
            if(this.row <= 0 || this.database.getRowCount() <= this.row)
                throw"InvalidOperationException";
            return this.database.getValue(this.row,this.column)
        },
        moveNext: function()
        {
            var found = false;
            while(!found && this.row < this.database.getRowCount())
            {
                this.row++;
                if(this.row < this.database.getRowCount())
                    found = this.rowMeetsCriteria()
            }
            return found
        },
        createEvaluator: function(criteria)
        {
            if(typeof criteria === "string")
            {
                var criteriaText = GrapeCity.Calc.Convert.toString(criteria);
                var textHelper = new GrapeCity.UI._StringHelper(criteriaText);
                var criteriaDouble = 0.0;
                for(var i = 0; i < DatabaseEnumerator.operators.length; i++)
                    if(textHelper.startsWith(DatabaseEnumerator.operators[i].getName()))
                    {
                        criteriaText = criteriaText.substring(DatabaseEnumerator.operators[i].getName().length);
                        if(!isNaN(criteriaDouble = GrapeCity.UI._NumberHelper.parseInvariant(criteriaText)))
                            criteria = criteriaDouble;
                        else if(this.ignoreCaseEqual("true",criteriaText))
                            criteria = true;
                        else if(this.ignoreCaseEqual("false",criteriaText))
                            criteria = false;
                        else
                            criteria = criteriaText;
                        return new CriteriaEvaluator(DatabaseEnumerator.operators[i],criteria)
                    }
                return new CriteriaEvaluator(GrapeCity.Calc.Operators.equal,criteria)
            }
            else if(criteria)
                return new CriteriaEvaluator(GrapeCity.Calc.Operators.equal,criteria);
            return null
        },
        rowMeetsCriteria: function()
        {
            var pass = false;
            for(var i = 1; !pass && i < this.criteria.getRowCount(); i++)
            {
                pass = true;
                for(var j = 0; pass && j < this.criteria.getColumnCount(); j++)
                {
                    var criteriaEva = this.createEvaluator(this.criteria.getValue(i,j));
                    if(criteriaEva)
                    {
                        var k = this.columnIndex(this.database,this.criteria.getValue(0,j));
                        var databaseValue = this.database.getValue(this.row,k);
                        pass = criteriaEva.evaluate(databaseValue)
                    }
                }
            }
            return pass
        },
        reset: function()
        {
            this.row = 0
        },
        ignoreCaseEqual: function(_stringOne, _stringTwo)
        {
            _stringOne = _stringOne.toLowerCase();
            _stringTwo = _stringTwo.toLowerCase();
            return _stringOne === _stringTwo
        }
    };
    DatabaseEnumerator.operators = [GrapeCity.Calc.Operators.equal,GrapeCity.Calc.Operators.notEqual,GrapeCity.Calc.Operators.lessThanOrEqual,GrapeCity.Calc.Operators.greaterThanOrEqual,GrapeCity.Calc.Operators.lessThan,GrapeCity.Calc.Operators.greaterThan];
    function db_daverage(args)
    {
        if(!args[0] || !args[1] || !args[2])
            throw"ArgumentNullException";
        var database = Calc.Convert._toArray(args[0]);
        var field = args[1];
        var criteria = Calc.Convert._toArray(args[2]);
        var sum = 0.0;
        var n = 0.0;
        var enumerator = new DatabaseEnumerator(database,field,criteria);
        while(enumerator.moveNext())
        {
            var obj = enumerator.current();
            if(GrapeCity.Calc.Convert.isNumber(obj))
            {
                var d;
                if(isNaN(d = Calc.Convert.toDouble(obj)))
                    return Calc.Convert.Errors.Value;
                sum += d;
                n++
            }
            else if(Calc.Convert.isError(obj))
                return obj
        }
        if(0.0 === n)
            return Calc.Errors.DivideByZero;
        return Calc.Convert.toResult(sum / n)
    }
    function db_dcount(args)
    {
        if(!args[0] || !args[1] || !args[2])
            throw"ArgumentNullException";
        var database = Calc.Convert._toArray(args[0]);
        var field = args[1];
        var criteria = Calc.Convert._toArray(args[2]);
        var n = 0.0;
        var enumerator = null;
        if(GrapeCity.Calc._Helper._argumentExists(args,1))
        {
            enumerator = new DatabaseEnumerator(database,field,criteria);
            while(enumerator.moveNext())
            {
                var obj = enumerator.current();
                if(GrapeCity.Calc.Convert.isNumber(obj))
                    n++
            }
        }
        else
        {
            enumerator = new DatabaseEnumerator(database,criteria);
            while(enumerator.moveNext())
                n++
        }
        return GrapeCity.Calc.Convert.toResult(n)
    }
    function db_dcounta(args)
    {
        if(!args[0] || !args[1] || !args[2])
            throw"ArgumentNullException";
        var database = Calc.Convert._toArray(args[0]);
        var field = args[1];
        var criteria = Calc.Convert._toArray(args[2]);
        var n = 0.0;
        var enumerator = null;
        if(GrapeCity.Calc._Helper._argumentExists(args,1))
        {
            enumerator = new DatabaseEnumerator(database,field,criteria);
            while(enumerator.moveNext())
            {
                var obj = enumerator.current();
                if(obj)
                    n++
            }
        }
        else
        {
            enumerator = new DatabaseEnumerator(database,criteria);
            while(enumerator.moveNext())
                n++
        }
        return GrapeCity.Calc.Convert.toResult(n)
    }
    function db_dget(args)
    {
        if(!args[0] || !args[1] || !args[2])
            throw"ArgumentNullException";
        var database = Calc.Convert._toArray(args[0]);
        var field = args[1];
        var criteria = Calc.Convert._toArray(args[2]);
        var val = null;
        var enumerator = new DatabaseEnumerator(database,field,criteria);
        if(enumerator.moveNext())
        {
            val = enumerator.current();
            if(enumerator.moveNext())
                return GrapeCity.Calc.Errors.Number
        }
        else
            return GrapeCity.Calc.Errors.Value;
        return val
    }
    function db_dmax(args)
    {
        if(!args[0] || !args[1] || !args[2])
            throw"ArgumentNullException";
        var database = Calc.Convert._toArray(args[0]);
        var field = args[1];
        var criteria = Calc.Convert._toArray(args[2]);
        var any = false;
        var max = -1.79769e+308;
        var enumerator = new DatabaseEnumerator(database,field,criteria);
        while(enumerator.moveNext())
        {
            var obj = enumerator.current();
            if(GrapeCity.Calc.Convert.isNumber(obj))
            {
                var x;
                if(isNaN(x = Calc.Convert.toDouble(obj)))
                    return GrapeCity.Calc.Errors.Value;
                if(!any || x > max)
                    max = x;
                any = true
            }
            else if(Calc.Convert.isError(obj))
                return obj
        }
        if(!any)
            return Calc.Errors.Value;
        return Calc.Convert.toResult(max)
    }
    function db_dmin(args)
    {
        if(!args[0] || !args[1] || !args[2])
            throw"ArgumentNullException";
        var database = Calc.Convert._toArray(args[0]);
        var field = args[1];
        var criteria = Calc.Convert._toArray(args[2]);
        var any = false;
        var min = 1.79769e+308;
        var enumerator = new DatabaseEnumerator(database,field,criteria);
        while(enumerator.moveNext())
        {
            var obj = enumerator.current();
            if(GrapeCity.Calc.Convert.isNumber(obj))
            {
                var val;
                if(isNaN(val = Calc.Convert.toDouble(obj)))
                    return GrapeCity.Calc.Errors.Value;
                if(!any || val < min)
                    min = val;
                any = true
            }
            else if(Calc.Convert.isError(obj))
                return obj
        }
        if(!any)
            return Calc.Errors.Value;
        return Calc.Convert.toResult(min)
    }
    function db_dproduct(args)
    {
        if(!args[0] || !args[1] || !args[2])
            throw"ArgumentNullException";
        var database = Calc.Convert._toArray(args[0]);
        var field = args[1];
        var criteria = Calc.Convert._toArray(args[2]);
        var product = 1.0;
        var enumerator = new DatabaseEnumerator(database,field,criteria);
        while(enumerator.moveNext())
        {
            var obj = enumerator.current();
            if(GrapeCity.Calc.Convert.isNumber(obj))
            {
                var x;
                if(isNaN(x = Calc.Convert.toDouble(obj)))
                    return Calc.Convert.Errors.Value;
                product *= x
            }
            else if(Calc.Convert.isError(obj))
                return obj
        }
        return GrapeCity.Calc.Convert.toResult(product)
    }
    function db_dstdev(args)
    {
        if(!args[0] || !args[1] || !args[2])
            throw"ArgumentNullException";
        var database = Calc.Convert._toArray(args[0]);
        var field = args[1];
        var criteria = Calc.Convert._toArray(args[2]);
        var sumx = 0.0;
        var sumx2 = 0.0;
        var n = 0.0;
        var enumerator = new DatabaseEnumerator(database,field,criteria);
        while(enumerator.moveNext())
        {
            var obj = enumerator.current();
            if(GrapeCity.Calc.Convert.isNumber(obj))
            {
                var x;
                if(isNaN(x = Calc.Convert.toDouble(obj)))
                    return Calc.Convert.Errors.Value;
                sumx += x;
                sumx2 += x * x;
                n++
            }
            else if(Calc.Convert.isError(obj))
                return obj
        }
        if(n <= 1.0)
            return GrapeCity.Calc.Errors.DivideByZero;
        return GrapeCity.Calc.Convert.toResult(Math.sqrt(Math.max(0.0,(n * sumx2 - sumx * sumx) / (n * (n - 1.0)))))
    }
    function db_dstdevp(args)
    {
        if(!args[0] || !args[1] || !args[2])
            throw"ArgumentNullException";
        var database = Calc.Convert._toArray(args[0]);
        var field = args[1];
        var criteria = Calc.Convert._toArray(args[2]);
        var sumx = 0.0;
        var sumx2 = 0.0;
        var n = 0.0;
        var enumerator = new DatabaseEnumerator(database,field,criteria);
        while(enumerator.moveNext())
        {
            var obj = enumerator.current();
            if(GrapeCity.Calc.Convert.isNumber(obj))
            {
                var x;
                if(isNaN(x = Calc.Convert.toDouble(obj)))
                    return Calc.Convert.Errors.Value;
                sumx += x;
                sumx2 += x * x;
                n++
            }
            else if(Calc.Convert.isError(obj))
                return obj
        }
        if(n <= 0.0)
            return GrapeCity.Calc.Errors.DivideByZero;
        return GrapeCity.Calc.Convert.toResult(Math.sqrt(Math.max(0.0,(n * sumx2 - sumx * sumx) / (n * n))))
    }
    function db_dsum(args)
    {
        if(!args[0] || !args[1] || !args[2])
            throw"ArgumentNullException";
        var database = Calc.Convert._toArray(args[0]);
        var field = args[1];
        var criteria = Calc.Convert._toArray(args[2]);
        var sum = 0.0;
        var enumerator = new DatabaseEnumerator(database,field,criteria);
        while(enumerator.moveNext())
        {
            var obj = enumerator.current();
            if(GrapeCity.Calc.Convert.isNumber(obj))
            {
                var x;
                if(isNaN(x = Calc.Convert.toDouble(obj)))
                    return Calc.Convert.Errors.Value;
                sum += x
            }
            else if(Calc.Convert.isError(obj))
                return obj
        }
        return GrapeCity.Calc.Convert.toResult(sum)
    }
    function db_dvar(args)
    {
        if(!args[0] || !args[1] || !args[2])
            throw"ArgumentNullException";
        var database = Calc.Convert._toArray(args[0]);
        var field = args[1];
        var criteria = Calc.Convert._toArray(args[2]);
        var sumx = 0.0;
        var sumx2 = 0.0;
        var n = 0.0;
        var enumerator = new DatabaseEnumerator(database,field,criteria);
        while(enumerator.moveNext())
        {
            var obj = enumerator.current();
            if(GrapeCity.Calc.Convert.isNumber(obj))
            {
                var x;
                if(isNaN(x = Calc.Convert.toDouble(obj)))
                    return Calc.Convert.Errors.Value;
                sumx += x;
                sumx2 += x * x;
                n++
            }
            else if(Calc.Convert.isError(obj))
                return obj
        }
        if(n <= 1.0)
            return GrapeCity.Calc.Errors.DivideByZero;
        return GrapeCity.Calc.Convert.toResult(Math.max(0.0,(n * sumx2 - sumx * sumx) / (n * (n - 1.0))))
    }
    function db_dvarp(args)
    {
        if(!args[0] || !args[1] || !args[2])
            throw"ArgumentNullException";
        var database = Calc.Convert._toArray(args[0]);
        var field = args[1];
        var criteria = Calc.Convert._toArray(args[2]);
        var sumx = 0.0;
        var sumx2 = 0.0;
        var n = 0.0;
        var enumerator = new DatabaseEnumerator(database,field,criteria);
        while(enumerator.moveNext())
        {
            var obj = enumerator.current();
            if(GrapeCity.Calc.Convert.isNumber(obj))
            {
                var x;
                if(isNaN(x = Calc.Convert.toDouble(obj)))
                    return Calc.Convert.Errors.Value;
                sumx += x;
                sumx2 += x * x;
                n++
            }
            else if(Calc.Convert.isError(obj))
                return obj
        }
        if(n <= 1.0)
            return GrapeCity.Calc.Errors.DivideByZero;
        return GrapeCity.Calc.Convert.toResult(Math.max(0.0,(n * sumx2 - sumx * sumx) / (n * n)))
    }
    def("DAVERAGE",db_daverage,{
        minArgs: 3,
        maxArgs: 3,
        acceptsReference: acceptsNotSecond,
        acceptsArray: acceptsNotSecond
    });
    def("DCOUNT",db_dcount,{
        minArgs: 3,
        maxArgs: 3,
        acceptsReference: acceptsNotSecond,
        acceptsArray: acceptsNotSecond
    });
    def("DCOUNTA",db_dcounta,{
        minArgs: 3,
        maxArgs: 3,
        acceptsReference: acceptsNotSecond,
        acceptsArray: acceptsNotSecond
    });
    def("DGET",db_dget,{
        minArgs: 3,
        maxArgs: 3,
        acceptsReference: acceptsNotSecond,
        acceptsArray: acceptsNotSecond
    });
    def("DMAX",db_dmax,{
        minArgs: 3,
        maxArgs: 3,
        acceptsReference: acceptsNotSecond,
        acceptsArray: acceptsNotSecond
    });
    def("DMIN",db_dmin,{
        minArgs: 3,
        maxArgs: 3,
        acceptsReference: acceptsNotSecond,
        acceptsArray: acceptsNotSecond
    });
    def("DPRODUCT",db_dproduct,{
        minArgs: 3,
        maxArgs: 3,
        acceptsReference: acceptsNotSecond,
        acceptsArray: acceptsNotSecond
    });
    def("DSTDEV",db_dstdev,{
        minArgs: 3,
        maxArgs: 3,
        acceptsReference: acceptsNotSecond,
        acceptsArray: acceptsNotSecond
    });
    def("DSTDEVP",db_dstdevp,{
        minArgs: 3,
        maxArgs: 3,
        acceptsReference: acceptsNotSecond,
        acceptsArray: acceptsNotSecond
    });
    def("DSUM",db_dsum,{
        minArgs: 3,
        maxArgs: 3,
        acceptsReference: acceptsNotSecond,
        acceptsArray: acceptsNotSecond
    });
    def("DVAR",db_dvar,{
        minArgs: 3,
        maxArgs: 3,
        acceptsReference: acceptsNotSecond,
        acceptsArray: acceptsNotSecond
    });
    def("DVARP",db_dvarp,{
        minArgs: 3,
        maxArgs: 3,
        acceptsReference: acceptsNotSecond,
        acceptsArray: acceptsNotSecond
    })
})(window);
(function(window)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        window.GrapeCity = GrapeCity = {};
    if(typeof GrapeCity.Calc === "undefined")
        GrapeCity.Calc = {};
    var Calc = GrapeCity.Calc;
    if(typeof Calc.Functions === "undefined")
        Calc.Functions = {};
    var Functions = Calc.Functions;
    Functions._builtInFunctions = Functions._builtInFunctions || {};
    if(typeof Functions._defineBuildInFunction === 'undefined')
        Functions._defineBuildInFunction = function(name, fnEvaluate, options)
        {
            if(!name)
                throw"Invalid function name";
            var fn;
            name = name.toUpperCase();
            if(!Functions._builtInFunctions.hasOwnProperty(name))
            {
                fn = new Functions.Function(name,0,255);
                Functions._builtInFunctions[name] = fn
            }
            else
            {
                fn = Functions._builtInFunctions[name];
                if(!fn)
                {
                    Functions._builtInFunctions[name] = new Functions.Function(name,0,255);
                    fn = Functions[name.toUpperCase()]
                }
                else if(!options || !options.override)
                    throw"Attempt to override function while override is not allowed";
            }
            if(fnEvaluate && typeof fnEvaluate === "function")
                fn.evaluate = fnEvaluate;
            if(options)
                for(var prop in options)
                    if(options.hasOwnProperty(prop) && prop !== 'override')
                        fn[prop] = options[prop];
            return fn
        };
    var def = Functions._defineBuildInFunction;
    function acceptsNotZero(i)
    {
        return i !== 0
    }
    function acceptsOne(i)
    {
        return i === 1
    }
    function acceptsTwo(i)
    {
        return i === 2
    }
    var INFINITY = 1.79769313486231570815E308;
    var MAXNUM = 1.79769313486231570815E308;
    var SQ2OPI = 7.9788456080286535587989E-1;
    var THPIO4 = 2.35619449019234492885;
    var TWOOPI = 6.36619772367581343075535E-1;
    var PIO4 = 7.85398163397448309616E-1;
    var MACHEP = 1.11022302462515654042E-16;
    var EUL = 5.772156649015328606065e-1;
    function _gamma(x)
    {
        var i,
            k,
            m;
        var ga,
            gr,
            z;
        var r = 1.0;
        var g = [1.0,0.5772156649015329,-0.6558780715202538,-0.420026350340952e-1,0.1665386113822915,-0.421977345555443e-1,-0.9621971527877e-2,0.7218943246663e-2,-0.11651675918591e-2,-0.2152416741149e-3,0.1280502823882e-3,-0.201348547807e-4,-0.12504934821e-5,0.1133027232e-5,-0.2056338417e-6,0.6116095e-8,0.50020075e-8,-0.11812746e-8,0.1043427e-9,0.77823e-11,-0.36968e-11,0.51e-12,-0.206e-13,-0.54e-14,0.14e-14];
        if(x > 171.0)
            return 1e308;
        if(x === parseInt(x,10))
            if(x > 0.0)
            {
                ga = 1.0;
                for(i = 2; i < x; i++)
                    ga *= i
            }
            else
                ga = 1e308;
        else
        {
            if(Math.abs(x) > 1.0)
            {
                z = Math.abs(x);
                m = parseInt(z,10);
                r = 1.0;
                for(k = 1; k <= m; k++)
                    r *= z - k;
                z -= m
            }
            else
                z = x;
            gr = g[24];
            for(k = 23; k >= 0; k--)
                gr = gr * z + g[k];
            ga = 1.0 / (gr * z);
            if(Math.abs(x) > 1.0)
            {
                ga *= r;
                if(x < 0.0)
                    ga = -Math.PI / (x * ga * Math.sin(Math.PI * x))
            }
        }
        return ga
    }
    function _lgamma(x)
    {
        var x0,
            x2,
            xp,
            gl,
            gl0;
        var k;
        var n = 0;
        var a = [8.333333333333333e-02,-2.777777777777778e-03,7.936507936507937e-04,-5.952380952380952e-04,8.417508417508418e-04,-1.917526917526918e-03,6.410256410256410e-03,-2.955065359477124e-02,1.796443723688307e-01,-1.39243221690590];
        x0 = x;
        if(x <= 0.0)
            return 1e308;
        else if(x === 1.0 || x === 2.0)
            return 0.0;
        else if(x <= 7.0)
        {
            n = parseInt(7 - x,10);
            x0 = x + n
        }
        x2 = 1.0 / (x0 * x0);
        xp = 2.0 * Math.PI;
        gl0 = a[9];
        for(k = 8; k >= 0; k--)
            gl0 = gl0 * x2 + a[k];
        gl = gl0 / x0 + 0.5 * Math.log(xp) + (x0 - 0.5) * Math.log(x0) - x0;
        if(x <= 7.0)
            for(k = 1; k <= n; k++)
            {
                gl -= Math.log(x0 - 1.0);
                x0 -= 1.0
            }
        return gl
    }
    function _expx2(x, sign)
    {
        var u,
            u1,
            m,
            f;
        x = Math.abs(x);
        if(sign < 0)
            x = -x;
        m = 0.0078125 * Math.floor(128 * x + 0.5);
        f = x - m;
        u = m * m;
        u1 = 2 * m * f + f * f;
        if(sign < 0)
        {
            u = -u;
            u1 = -u1
        }
        if(u + u1 > Math.log(MAXNUM))
            return INFINITY;
        u = Math.exp(u) * Math.exp(u1);
        return u
    }
    function _polevl(x, coef, N)
    {
        var ans;
        var i;
        var k = 0;
        ans = parseFloat(coef[k++]);
        i = N;
        do
            ans = ans * x + parseFloat(coef[k++]);
        while(--i !== 0);
        return ans
    }
    function _p1evl(x, coef, N)
    {
        var ans;
        var i;
        var k = 0;
        ans = x + parseFloat(coef[k++]);
        i = N - 1;
        do
            ans = ans * x + parseFloat(coef[k++]);
        while(--i !== 0);
        return ans
    }
    function _j0(x)
    {
        var PP = [];
        PP[0] = (7.96936729297347051624E-4);
        PP[1] = (8.28352392107440799803E-2);
        PP[2] = (1.23953371646414299388E0);
        PP[3] = (5.44725003058768775090E0);
        PP[4] = (8.74716500199817011941E0);
        PP[5] = (5.30324038235394892183E0);
        PP[6] = (9.99999999999999997821E-1);
        var PQ = [];
        PQ[0] = (9.24408810558863637013E-4);
        PQ[1] = (8.56288474354474431428E-2);
        PQ[2] = (1.25352743901058953537E0);
        PQ[3] = (5.47097740330417105182E0);
        PQ[4] = (8.76190883237069594232E0);
        PQ[5] = (5.30605288235394617618E0);
        PQ[6] = (1.00000000000000000218E0);
        var QP = [];
        QP[0] = -1.13663838898469149931E-2;
        QP[1] = -1.28252718670509318512E0;
        QP[2] = -1.95539544257735972385E1;
        QP[3] = -9.32060152123768231369E1;
        QP[4] = -1.77681167980488050595E2;
        QP[5] = -1.47077505154951170175E2;
        QP[6] = -5.14105326766599330220E1;
        QP[7] = -6.05014350600728481186E0;
        var QQ = [];
        QQ[0] = (6.43178256118178023184E1);
        QQ[1] = (8.56430025976980587198E2);
        QQ[2] = (3.88240183605401609683E3);
        QQ[3] = (7.24046774195652478189E3);
        QQ[4] = (5.93072701187316984827E3);
        QQ[5] = (2.06209331660327847417E3);
        QQ[6] = (2.42005740240291393179E2);
        var YP = [];
        YP[0] = (1.55924367855235737965E4);
        YP[1] = -1.46639295903971606143E7;
        YP[2] = (5.43526477051876500413E9);
        YP[3] = -9.82136065717911466409E11;
        YP[4] = (8.75906394395366999549E13);
        YP[5] = -3.46628303384729719441E15;
        YP[6] = (4.42733268572569800351E16);
        YP[7] = -1.84950800436986690637E16;
        var YQ = [];
        YQ[0] = (1.04128353664259848412E3);
        YQ[1] = (6.26107330137134956842E5);
        YQ[2] = (2.68919633393814121987E8);
        YQ[3] = (8.64002487103935000337E10);
        YQ[4] = (2.02979612750105546709E13);
        YQ[5] = (3.17157752842975028269E15);
        YQ[6] = (2.50596256172653059228E17);
        var DR1 = 5.78318596294678452118E0;
        var DR2 = 3.04712623436620863991E1;
        var RP = [];
        RP[0] = -4.79443220978201773821E9;
        RP[1] = (1.95617491946556577543E12);
        RP[2] = -2.49248344360967716204E14;
        RP[3] = (9.70862251047306323952E15);
        var RQ = [];
        RQ[0] = (4.99563147152651017219E2);
        RQ[1] = (1.73785401676374683123E5);
        RQ[2] = (4.84409658339962045305E7);
        RQ[3] = (1.11855537045356834862E10);
        RQ[4] = (2.11277520115489217587E12);
        RQ[5] = (3.10518229857422583814E14);
        RQ[6] = (3.18121955943204943306E16);
        RQ[7] = (1.71086294081043136091E18);
        var PIO4 = 7.85398163397448309616E-1;
        var SQ2OPI = 7.9788456080286535587989E-1;
        var w,
            z,
            p,
            q,
            xn;
        if(x < 0)
            x = -x;
        if(x <= 5.0)
        {
            z = x * x;
            if(x < 1.0e-5)
                return 1.0 - z / 4.0;
            p = (z - DR1) * (z - DR2);
            p = p * _polevl(z,RP,3) / _p1evl(z,RQ,8);
            return p
        }
        w = 5.0 / x;
        q = 25.0 / (x * x);
        p = _polevl(q,PP,6) / _polevl(q,PQ,6);
        q = _polevl(q,QP,7) / _p1evl(q,QQ,7);
        xn = x - PIO4;
        p = p * Math.cos(xn) - w * q * Math.sin(xn);
        return p * SQ2OPI / Math.sqrt(x)
    }
    function _j1(x)
    {
        var RP = [];
        RP[0] = -8.99971225705559398224E8;
        RP[1] = (4.52228297998194034323E11);
        RP[2] = -7.27494245221818276015E13;
        RP[3] = (3.68295732863852883286E15);
        var RQ = [];
        RQ[0] = (6.20836478118054335476E2);
        RQ[1] = (2.56987256757748830383E5);
        RQ[2] = (8.35146791431949253037E7);
        RQ[3] = (2.21511595479792499675E10);
        RQ[4] = (4.74914122079991414898E12);
        RQ[5] = (7.84369607876235854894E14);
        RQ[6] = (8.95222336184627338078E16);
        RQ[7] = (5.32278620332680085395E18);
        var QP = [];
        QP[0] = (5.10862594750176621635E-2);
        QP[1] = (4.98213872951233449420E0);
        QP[2] = (7.58238284132545283818E1);
        QP[3] = (3.66779609360150777800E2);
        QP[4] = (7.10856304998926107277E2);
        QP[5] = (5.97489612400613639965E2);
        QP[6] = (2.11688757100572135698E2);
        QP[7] = (2.52070205858023719784E1);
        var QQ = [];
        QQ[0] = (7.42373277035675149943E1);
        QQ[1] = (1.05644886038262816351E3);
        QQ[2] = (4.98641058337653607651E3);
        QQ[3] = (9.56231892404756170795E3);
        QQ[4] = (7.99704160447350683650E3);
        QQ[5] = (2.82619278517639096600E3);
        QQ[6] = (3.36093607810698293419E2);
        var PP = [];
        PP[0] = (7.62125616208173112003E-4);
        PP[1] = (7.31397056940917570436E-2);
        PP[2] = (1.12719608129684925192E0);
        PP[3] = (5.11207951146807644818E0);
        PP[4] = (8.42404590141772420927E0);
        PP[5] = (5.21451598682361504063E0);
        PP[6] = (1.00000000000000000254E0);
        var PQ = [];
        PQ[0] = (5.71323128072548699714E-4);
        PQ[1] = (6.88455908754495404082E-2);
        PQ[2] = (1.10514232634061696926E0);
        PQ[3] = (5.07386386128601488557E0);
        PQ[4] = (8.39985554327604159757E0);
        PQ[5] = (5.20982848682361821619E0);
        PQ[6] = (9.99999999999999997461E-1);
        var Z1 = 1.46819706421238932572E1;
        var Z2 = 4.92184563216946036703E1;
        var w,
            z,
            p,
            q,
            xn;
        var SQ2OPI = 7.9788456080286535587989E-1;
        var THPIO4 = 2.35619449019234492885;
        w = x;
        if(x < 0)
            w = -x;
        if(w <= 5.0)
        {
            z = x * x;
            w = _polevl(z,RP,3) / _p1evl(z,RQ,8);
            w = w * x * (z - Z1) * (z - Z2);
            return w
        }
        w = 5.0 / x;
        z = w * w;
        p = _polevl(z,PP,6) / _polevl(z,PQ,6);
        q = _polevl(z,QP,7) / _p1evl(z,QQ,7);
        xn = x - THPIO4;
        p = p * Math.cos(xn) - w * q * Math.sin(xn);
        return p * SQ2OPI / Math.sqrt(x)
    }
    function _jn(n, x)
    {
        var pkm2,
            pkm1,
            pk,
            xk,
            r,
            ans;
        var k,
            sign;
        var MACHEP = 1.11022302462515654042E-16;
        if(n < 0)
        {
            n = -n;
            if((n & 1) === 0)
                sign = 1;
            else
                sign = -1
        }
        else
            sign = 1;
        if(x < 0.0)
        {
            if((n & 1) !== 0)
                sign = -sign;
            x = -x
        }
        if(n === 0)
            return sign * _j0(x);
        if(n === 1)
            return sign * _j1(x);
        if(n === 2)
            return sign * (2.0 * _j1(x) / x - _j0(x));
        if(x < MACHEP)
            return(0.0);
        k = 56;
        pk = 2 * (n + k);
        ans = pk;
        xk = x * x;
        do
        {
            pk -= 2.0;
            ans = pk - xk / ans
        } while(--k > 0);
        ans = x / ans;
        pk = 1.0;
        pkm1 = 1.0 / ans;
        k = n - 1;
        r = 2 * k;
        do
        {
            pkm2 = (pkm1 * r - pk * x) / x;
            pk = pkm1;
            pkm1 = pkm2;
            r -= 2.0
        } while(--k > 0);
        if(Math.abs(pk) > Math.abs(pkm1))
            ans = _j1(x) / pk;
        else
            ans = _j0(x) / pkm1;
        return sign * ans
    }
    function _adoneGoto(ans, x, s)
    {
        ans = Math.exp(-x) * Math.sqrt(Math.PI / (2.0 * x)) * s
    }
    function _asympGoto(x, n, k, pn, pk, z0, fn, t, s, nkf, i, z, ans, nk1f)
    {
        if(x > Math.log(MAXNUM))
            return(0.0);
        k = n;
        pn = 4.0 * k * k;
        pk = 1.0;
        z0 = 8.0 * x;
        fn = 1.0;
        t = 1.0;
        s = t;
        nkf = MAXNUM;
        i = 0;
        do
        {
            z = pn - pk * pk;
            t = t * z / (fn * z0);
            nk1f = Math.abs(t);
            if(i >= n && nk1f > nkf)
                _adoneGoto(ans,x,s);
            nkf = nk1f;
            s += t;
            fn += 1.0;
            pk += 2.0;
            i += 1
        } while(Math.abs(t / s) > MACHEP)
    }
    function _kn(nn, x)
    {
        var k,
            kf,
            nk1f,
            nkf,
            zn,
            t,
            s,
            z0,
            z;
        var ans,
            fn,
            pn,
            pk,
            zmn,
            tlg,
            tox;
        var i,
            n;
        if(nn < 0)
            n = -nn;
        else
            n = nn;
        if(n > 31)
            return MAXNUM;
        if(x <= 0.0)
            return MAXNUM;
        if(x > 9.55)
            _asympGoto(x,n,k,pn,pk,z0,fn,t,s,nkf,i,z,ans,nk1f);
        ans = 0.0;
        z0 = 0.25 * x * x;
        fn = 1.0;
        pn = 0.0;
        zmn = 1.0;
        tox = 2.0 / x;
        if(n > 0)
        {
            pn = -EUL;
            k = 1.0;
            for(i = 1; i < n; i++)
            {
                pn += 1.0 / k;
                k += 1.0;
                fn *= k
            }
            zmn = tox;
            if(n === 1)
                ans = 1.0 / x;
            else
            {
                nk1f = fn / n;
                kf = 1.0;
                s = nk1f;
                z = -z0;
                zn = 1.0;
                for(i = 1; i < n; i++)
                {
                    nk1f = nk1f / (n - i);
                    kf = kf * i;
                    zn *= z;
                    t = nk1f * zn / kf;
                    s += t;
                    if(MAXNUM - Math.abs(t) < Math.abs(s))
                        return MAXNUM;
                    if(tox > 1.0 && MAXNUM / tox < zmn)
                        return MAXNUM;
                    zmn *= tox
                }
                s *= 0.5;
                t = Math.abs(s);
                if(zmn > 1.0 && MAXNUM / zmn < t)
                    return MAXNUM;
                if(t > 1.0 && MAXNUM / t < zmn)
                    return MAXNUM;
                ans = s * zmn
            }
        }
        tlg = 2.0 * Math.log(0.5 * x);
        pk = -EUL;
        if(n === 0)
        {
            pn = pk;
            t = 1.0
        }
        else
        {
            pn = pn + 1.0 / n;
            t = 1.0 / fn
        }
        s = (pk + pn - tlg) * t;
        k = 1.0;
        do
        {
            t *= z0 / (k * (k + n));
            pk += 1.0 / k;
            pn += 1.0 / (k + n);
            s += (pk + pn - tlg) * t;
            k += 1.0
        } while(Math.abs(t / s) > MACHEP);
        s = 0.5 * s / zmn;
        if((n & 1) !== 0)
            s = -s;
        ans += s;
        return ans
    }
    function _y1(x)
    {
        var w,
            z,
            p,
            q,
            xn;
        var QP = [];
        QP[0] = (5.10862594750176621635E-2);
        QP[1] = (4.98213872951233449420E0);
        QP[2] = (7.58238284132545283818E1);
        QP[3] = (3.66779609360150777800E2);
        QP[4] = (7.10856304998926107277E2);
        QP[5] = (5.97489612400613639965E2);
        QP[6] = (2.11688757100572135698E2);
        QP[7] = (2.52070205858023719784E1);
        var QQ = [];
        QQ[0] = (7.42373277035675149943E1);
        QQ[1] = (1.05644886038262816351E3);
        QQ[2] = (4.98641058337653607651E3);
        QQ[3] = (9.56231892404756170795E3);
        QQ[4] = (7.99704160447350683650E3);
        QQ[5] = (2.82619278517639096600E3);
        QQ[6] = (3.36093607810698293419E2);
        var PP = [];
        PP[0] = (7.62125616208173112003E-4);
        PP[1] = (7.31397056940917570436E-2);
        PP[2] = (1.12719608129684925192E0);
        PP[3] = (5.11207951146807644818E0);
        PP[4] = (8.42404590141772420927E0);
        PP[5] = (5.21451598682361504063E0);
        PP[6] = (1.00000000000000000254E0);
        var PQ = [];
        PQ[0] = (5.71323128072548699714E-4);
        PQ[1] = (6.88455908754495404082E-2);
        PQ[2] = (1.10514232634061696926E0);
        PQ[3] = (5.07386386128601488557E0);
        PQ[4] = (8.39985554327604159757E0);
        PQ[5] = (5.20982848682361821619E0);
        PQ[6] = (9.99999999999999997461E-1);
        var YP = [];
        YP[0] = (1.26320474790178026440E9);
        YP[1] = -6.47355876379160291031E11;
        YP[2] = (1.14509511541823727583E14);
        YP[3] = -8.12770255501325109621E15;
        YP[4] = (2.02439475713594898196E17);
        YP[5] = -7.78877196265950026825E17;
        var YQ = [];
        YQ[0] = (5.94301592346128195359E2);
        YQ[1] = (2.35564092943068577943E5);
        YQ[2] = (7.34811944459721705660E7);
        YQ[3] = (1.87601316108706159478E10);
        YQ[4] = (3.88231277496238566008E12);
        YQ[5] = (6.20557727146953693363E14);
        YQ[6] = (6.87141087355300489866E16);
        YQ[7] = (3.97270608116560655612E18);
        if(x <= 5.0)
        {
            if(x <= 0.0)
                return-MAXNUM;
            z = x * x;
            w = x * (_polevl(z,YP,5) / _p1evl(z,YQ,8));
            w += TWOOPI * (_j1(x) * Math.log(x) - 1.0 / x);
            return w
        }
        w = 5.0 / x;
        z = w * w;
        p = _polevl(z,PP,6) / _polevl(z,PQ,6);
        q = _polevl(z,QP,7) / _p1evl(z,QQ,7);
        xn = x - THPIO4;
        p = p * Math.sin(xn) + w * q * Math.cos(xn);
        return p * SQ2OPI / Math.sqrt(x)
    }
    function _y0(x)
    {
        var w,
            z,
            p,
            q,
            xn;
        var PP = [];
        PP[0] = (7.96936729297347051624E-4);
        PP[1] = (8.28352392107440799803E-2);
        PP[2] = (1.23953371646414299388E0);
        PP[3] = (5.44725003058768775090E0);
        PP[4] = (8.74716500199817011941E0);
        PP[5] = (5.30324038235394892183E0);
        PP[6] = (9.99999999999999997821E-1);
        var PQ = [];
        PQ[0] = (9.24408810558863637013E-4);
        PQ[1] = (8.56288474354474431428E-2);
        PQ[2] = (1.25352743901058953537E0);
        PQ[3] = (5.47097740330417105182E0);
        PQ[4] = (8.76190883237069594232E0);
        PQ[5] = (5.30605288235394617618E0);
        PQ[6] = (1.00000000000000000218E0);
        var QP = [];
        QP[0] = -1.13663838898469149931E-2;
        QP[1] = -1.28252718670509318512E0;
        QP[2] = -1.95539544257735972385E1;
        QP[3] = -9.32060152123768231369E1;
        QP[4] = -1.77681167980488050595E2;
        QP[5] = -1.47077505154951170175E2;
        QP[6] = -5.14105326766599330220E1;
        QP[7] = -6.05014350600728481186E0;
        var QQ = [];
        QQ[0] = (6.43178256118178023184E1);
        QQ[1] = (8.56430025976980587198E2);
        QQ[2] = (3.88240183605401609683E3);
        QQ[3] = (7.24046774195652478189E3);
        QQ[4] = (5.93072701187316984827E3);
        QQ[5] = (2.06209331660327847417E3);
        QQ[6] = (2.42005740240291393179E2);
        var YP = [];
        YP[0] = (1.55924367855235737965E4);
        YP[1] = -1.46639295903971606143E7;
        YP[2] = (5.43526477051876500413E9);
        YP[3] = -9.82136065717911466409E11;
        YP[4] = (8.75906394395366999549E13);
        YP[5] = -3.46628303384729719441E15;
        YP[6] = (4.42733268572569800351E16);
        YP[7] = -1.84950800436986690637E16;
        var YQ = [];
        YQ[0] = (1.04128353664259848412E3);
        YQ[1] = (6.26107330137134956842E5);
        YQ[2] = (2.68919633393814121987E8);
        YQ[3] = (8.64002487103935000337E10);
        YQ[4] = (2.02979612750105546709E13);
        YQ[5] = (3.17157752842975028269E15);
        YQ[6] = (2.50596256172653059228E17);
        if(x <= 5.0)
        {
            if(x <= 0.0)
                return-MAXNUM;
            z = x * x;
            w = _polevl(z,YP,7) / _p1evl(z,YQ,7);
            w += TWOOPI * Math.log(x) * _j0(x);
            return w
        }
        w = 5.0 / x;
        z = 25.0 / (x * x);
        p = _polevl(z,PP,6) / _polevl(z,PQ,6);
        q = _polevl(z,QP,7) / _p1evl(z,QQ,7);
        xn = x - PIO4;
        p = p * Math.sin(xn) + w * q * Math.cos(xn);
        return p * SQ2OPI / Math.sqrt(x)
    }
    function _yn(n, x)
    {
        var an,
            anm1,
            anm2,
            r;
        var k,
            sign;
        if(n < 0)
        {
            n = -n;
            if((n & 1) === 0)
                sign = 1;
            else
                sign = -1
        }
        else
            sign = 1;
        if(n === 0)
            return sign * _y0(x);
        if(n === 1)
            return sign * _y1(x);
        if(x <= 0.0)
            return-MAXNUM;
        anm2 = _y0(x);
        anm1 = _y1(x);
        k = 1;
        r = 2 * k;
        do
        {
            an = r * anm1 / x - anm2;
            anm2 = anm1;
            anm1 = an;
            r += 2.0;
            ++k
        } while(k < n);
        return sign * an
    }
    function _fact(n)
    {
        var result = 1.0;
        for(var i = n; i > 1; i--)
            result *= i;
        return result
    }
    function _bessel(num, order, modfied)
    {
        if(order < 0)
            return NaN;
        var Z,
            Zm,
            N1,
            N2,
            n1,
            n2,
            act,
            old;
        var iterMax = 100;
        Z = num * 0.5;
        Zm = Z * Z;
        Z = Math.pow(Z,parseFloat(order));
        N1 = _fact(order);
        n1 = 0.0;
        N2 = 1.0;
        n2 = parseFloat(order);
        act = Z / N1;
        old = act * 0.9;
        if(modfied)
            while(act !== old && iterMax !== 0)
            {
                Z *= Zm;
                n1++;
                N1 *= n1;
                n2++;
                N2 *= n2;
                old = act;
                act += Z / N1 / N2;
                iterMax--
            }
        else
        {
            var add = false;
            while(act !== old && iterMax !== 0)
            {
                Z *= Zm;
                n1++;
                N1 *= n1;
                n2++;
                N2 *= n2;
                old = act;
                if(add)
                    act += Z / N1 / N2;
                else
                    act -= Z / N1 / N2;
                iterMax--;
                add = !add
            }
        }
        return act
    }
    function _stringToLong(s, radix)
    {
        var limit = Math.pow(radix,10.0);
        var number = parseInt(s,radix);
        if(isNaN(number))
            return Calc.Errors.Number;
        if(limit / 2 <= number)
            number -= limit;
        return number
    }
    function _longToString(number, radix, places)
    {
        var buffer;
        if(number < 0)
            number += Math.pow(radix,10.0);
        buffer = number.toString(radix);
        if(buffer.length < places)
        {
            var pos = places - buffer.length;
            for(var i = 0; i < pos; i++)
                buffer = "0" + buffer
        }
        return buffer.toUpperCase()
    }
    function Complex(real, imag)
    {
        this._real = real;
        this._imag = imag
    }
    Complex.prototype = {
        real: function()
        {
            return this._real
        },
        imag: function()
        {
            return this._imag
        },
        toString: function(suffix)
        {
            if(!suffix)
                suffix = "i";
            var sb = new GrapeCity.UI._StringBuilder;
            if(this._real !== 0.0 || this._imag === 0.0)
                sb.append(this._real.toString());
            if(this._imag === -1.0)
                sb.append("-");
            else if(this._real !== 0.0 && this._imag > 0.0)
                sb.append("+");
            if(this._imag !== -1.0 && this._imag !== 0.0 && this._imag !== 1.0)
                sb.append(this._imag.toString());
            if(this._imag !== 0.0)
                sb.append(suffix);
            return sb.toString()
        }
    };
    Complex._parse = function(s)
    {
        var real = 0.0;
        var imag = 0.0;
        var realDigit = false;
        var imagDigit = false;
        var imagSuffix = false;
        var realLen = 0;
        var imagLen = 0;
        var i = 0;
        if(!s)
            throw'Argument Null';
        if(s.length === 0)
            throw'Format';
        if(i < s.length && (s.charAt(i) === '+' || s.charAt(i) === '-'))
            i++;
        while(i < s.length && !isNaN(Number(s.charAt(i))))
        {
            i++;
            realDigit = true
        }
        if(i < s.length && s.charAt(i) === '.')
            i++;
        while(i < s.length && !isNaN(Number(s.charAt(i))))
        {
            i++;
            realDigit = true
        }
        if(i < s.length && (s.charAt(i) === 'E' || s.charAt(i) === 'e'))
        {
            i++;
            realDigit = false;
            if(i < s.length && (s.charAt(i) === '+' || s.charAt(i) === '-'))
                i++;
            while(i < s.length && !isNaN(Number(s.charAt(i))))
            {
                i++;
                realDigit = true
            }
        }
        if(i < s.length && (s.charAt(i) === '+' || s.charAt(i) === '-'))
        {
            realLen = i;
            i++;
            while(i < s.length && !isNaN(Number(s.charAt(i))))
            {
                i++;
                imagDigit = true
            }
            if(i < s.length && s.charAt(i) === '.')
                i++;
            while(i < s.length && !isNaN(Number(s.charAt(i))))
            {
                i++;
                imagDigit = true
            }
            if(i < s.length && (s.charAt(i) === 'E' || s.charAt(i) === 'e'))
            {
                i++;
                imagDigit = false;
                if(i < s.length && (s.charAt(i) === '+' || s.charAt(i) === '-'))
                    i++;
                while(i < s.length && !isNaN(Number(s.charAt(i))))
                {
                    i++;
                    imagDigit = true
                }
            }
            if(i < s.length && (s.charAt(i) === 'i' || s.charAt(i) === 'j'))
            {
                i++;
                imagSuffix = true
            }
            imagLen = i - realLen
        }
        else if(i < s.length && (s.charAt(i) === 'i' || s.charAt(i) === 'j'))
        {
            i++;
            imagLen = i;
            imagDigit = realDigit;
            imagSuffix = true;
            realDigit = false
        }
        else
            realLen = i;
        if(i < s.length)
            throw'Format';
        if(realLen > 0)
            if(realDigit)
                real = parseInt(s.substr(0,realLen),10);
            else
                throw'Format';
        if(imagLen > 0)
        {
            if(!imagSuffix)
                throw'Format';
            if(imagLen === 1)
                imag = 1.0;
            else if(imagLen === 2 && s.charAt(realLen) === '+')
                imag = 1.0;
            else if(imagLen === 2 && s.charAt(realLen) === '-')
                imag = -1.0;
            else if(imagDigit)
                imag = parseInt(s.substr(realLen,imagLen - 1),10);
            else
                throw'Format';
        }
        return new Complex(real,imag)
    };
    function ComplexConvert(){}
    ComplexConvert._toComplex = function(value)
    {
        try
        {
            if(!value)
                return new Complex(0.0,0.0);
            else if(value.constructor === Number)
                return new Complex(parseFloat(value),0.0);
            else if(value.constructor === String)
                return Complex._parse(value);
            else
                throw'Invalid Cast';
        }
        catch(err)
        {
            throw'Invalid Cast';
        }
    };
    ComplexConvert._toResult = function(value, suffix)
    {
        if(isNaN(value.real()) || value.real() === Number.POSITIVE_INFINITY || isNaN(value.imag()) || value.imag() === Number.POSITIVE_INFINITY)
            return Calc.Errors.Number;
        else
            return value.toString(suffix)
    };
    var one_g_to_sg = 0.00006852205001;
    var one_g_to_lbm = 0.002204622915;
    var one_g_to_u = 6.02217e+23;
    var one_g_to_ozm = 0.035273972;
    var one_m_to_Nmi = 1 / 1852.0;
    var one_m_to_in = 10000 / 254.0;
    var one_m_to_ft = one_m_to_in / 12;
    var one_m_to_yd = one_m_to_ft / 3;
    var one_m_to_mi = one_m_to_yd / 1760.0;
    var one_m_to_ang = 1e10;
    var one_m_to_Pica = 2834.645669;
    var one_m_to_km = 0.001;
    var one_yr_to_day = 365.25;
    var one_yr_to_hr = 24 * one_yr_to_day;
    var one_yr_to_mn = 60 * one_yr_to_hr;
    var one_yr_to_sec = 60 * one_yr_to_mn;
    var one_Pa_to_atm = 0.9869233e-5;
    var one_Pa_to_mmHg = 0.00750061708;
    var one_N_to_dyn = 100000;
    var one_N_to_lbf = 0.224808924;
    var one_HP_to_W = 745.701;
    var one_J_to_e = 9999995.193;
    var one_J_to_c = 0.239006249;
    var one_J_to_cal = 0.238846191;
    var one_J_to_eV = 6.2146e+18;
    var one_J_to_HPh = 1.0 / (3600 * one_HP_to_W);
    var one_J_to_Wh = 1.0 / 3600;
    var one_J_to_flb = 23.73042222;
    var one_J_to_BTU = 0.000947815;
    var one_T_to_ga = 10000;
    var C_K_offset = 273.15;
    var one_tsp_to_tbs = 1.0 / 3;
    var one_tsp_to_oz = 1.0 / 6;
    var one_tsp_to_cup = 1.0 / 48;
    var one_tsp_to_pt = 1.0 / 96;
    var one_tsp_to_qt = 1.0 / 192;
    var one_tsp_to_gal = 1.0 / 768;
    var one_tsp_to_l = 0.004929994;
    var one_tsp_to_ukpt = 0.008675585;
    var yotta = 1e+24;
    var zetta = 1e+21;
    var exa = 1e+18;
    var peta = 1e+15;
    var tera = 1e+12;
    var giga = 1e+09;
    var mega = 1e+06;
    var kilo = 1e+03;
    var hecto = 1e+02;
    var deka = 1e+01;
    var deci = 1e-01;
    var centi = 1e-02;
    var milli = 1e-03;
    var micro = 1e-06;
    var nano = 1e-09;
    var pico = 1e-12;
    var femto = 1e-15;
    var atto = 1e-18;
    var zepto = 1e-21;
    var yocto = 1e-24;
    function Eng_convert_unit_t(str, c)
    {
        this.str = str;
        this.c = c
    }
    var _weight_units = [new Eng_convert_unit_t("g",1.0),new Eng_convert_unit_t("sg",one_g_to_sg),new Eng_convert_unit_t("lbm",one_g_to_lbm),new Eng_convert_unit_t("u",one_g_to_u),new Eng_convert_unit_t("ozm",one_g_to_ozm),new Eng_convert_unit_t(null,0.0)];
    var _distance_units = [new Eng_convert_unit_t("m",1.0),new Eng_convert_unit_t("mi",one_m_to_mi),new Eng_convert_unit_t("Nmi",one_m_to_Nmi),new Eng_convert_unit_t("in",one_m_to_in),new Eng_convert_unit_t("ft",one_m_to_ft),new Eng_convert_unit_t("yd",one_m_to_yd),new Eng_convert_unit_t("ang",one_m_to_ang),new Eng_convert_unit_t("Pica",one_m_to_Pica),new Eng_convert_unit_t("km",one_m_to_km),new Eng_convert_unit_t(null,0.0)];
    var _time_units = [new Eng_convert_unit_t("yr",1.0),new Eng_convert_unit_t("day",one_yr_to_day),new Eng_convert_unit_t("hr",one_yr_to_hr),new Eng_convert_unit_t("mn",one_yr_to_mn),new Eng_convert_unit_t("sec",one_yr_to_sec),new Eng_convert_unit_t(null,0.0)];
    var _pressure_units = [new Eng_convert_unit_t("Pa",1.0),new Eng_convert_unit_t("atm",one_Pa_to_atm),new Eng_convert_unit_t("mmHg",one_Pa_to_mmHg),new Eng_convert_unit_t("p",1.0),new Eng_convert_unit_t("at",one_Pa_to_atm),new Eng_convert_unit_t(null,0.0)];
    var _force_units = [new Eng_convert_unit_t("N",1.0),new Eng_convert_unit_t("dyn",one_N_to_dyn),new Eng_convert_unit_t("lbf",one_N_to_lbf),new Eng_convert_unit_t("dy",one_N_to_dyn),new Eng_convert_unit_t(null,0.0)];
    var _energy_units = [new Eng_convert_unit_t("J",1.0),new Eng_convert_unit_t("e",one_J_to_e),new Eng_convert_unit_t("c",one_J_to_c),new Eng_convert_unit_t("cal",one_J_to_cal),new Eng_convert_unit_t("eV",one_J_to_eV),new Eng_convert_unit_t("HPh",one_J_to_HPh),new Eng_convert_unit_t("Wh",one_J_to_Wh),new Eng_convert_unit_t("flb",one_J_to_flb),new Eng_convert_unit_t("BTU",one_J_to_BTU),new Eng_convert_unit_t("ev",one_J_to_eV),new Eng_convert_unit_t("hh",one_J_to_HPh),new Eng_convert_unit_t("wh",one_J_to_Wh),new Eng_convert_unit_t("btu",one_J_to_BTU),new Eng_convert_unit_t(null,0.0)];
    var _power_units = [new Eng_convert_unit_t("HP",1.0),new Eng_convert_unit_t("W",one_HP_to_W),new Eng_convert_unit_t("h",1.0),new Eng_convert_unit_t("w",one_HP_to_W),new Eng_convert_unit_t(null,0.0)];
    var _magnetism_units = [new Eng_convert_unit_t("T",1.0),new Eng_convert_unit_t("ga",one_T_to_ga),new Eng_convert_unit_t(null,0.0)];
    var _liquid_units = [new Eng_convert_unit_t("tsp",1.0),new Eng_convert_unit_t("tbs",one_tsp_to_tbs),new Eng_convert_unit_t("oz",one_tsp_to_oz),new Eng_convert_unit_t("cup",one_tsp_to_cup),new Eng_convert_unit_t("pt",one_tsp_to_pt),new Eng_convert_unit_t("qt",one_tsp_to_qt),new Eng_convert_unit_t("gal",one_tsp_to_gal),new Eng_convert_unit_t("l",one_tsp_to_l),new Eng_convert_unit_t("uk_pt",one_tsp_to_ukpt),new Eng_convert_unit_t("us_pt",one_tsp_to_pt),new Eng_convert_unit_t("lt",one_tsp_to_l),new Eng_convert_unit_t(null,0.0)];
    var _prefixes = [new Eng_convert_unit_t("Y",yotta),new Eng_convert_unit_t("Z",zetta),new Eng_convert_unit_t("E",exa),new Eng_convert_unit_t("P",peta),new Eng_convert_unit_t("T",tera),new Eng_convert_unit_t("G",giga),new Eng_convert_unit_t("M",mega),new Eng_convert_unit_t("k",kilo),new Eng_convert_unit_t("h",hecto),new Eng_convert_unit_t("e",deka),new Eng_convert_unit_t("d",deci),new Eng_convert_unit_t("c",centi),new Eng_convert_unit_t("m",milli),new Eng_convert_unit_t("u",micro),new Eng_convert_unit_t("n",nano),new Eng_convert_unit_t("p",pico),new Eng_convert_unit_t("f",femto),new Eng_convert_unit_t("a",atto),new Eng_convert_unit_t("z",zepto),new Eng_convert_unit_t("y",yocto),new Eng_convert_unit_t(null,0.0)];
    var _c = 0.0;
    var _prefix = 0.0;
    var _v = 0.0;
    var _fixPrefixesList = ["cup","mmHg","J","sec","cel","kel","hh","Wh","wh","flb","BTU"];
    function _fixPrefixes(unitName)
    {
        for(var i = 0; _fixPrefixesList[i].str; i++)
            if(unitName.localeCompare(_fixPrefixesList[i].str) === 0)
                return false;
        return true
    }
    function _get_constant_of_unit(units, prefixes, unit_name)
    {
        var i;
        _c = 0;
        _prefix = 1;
        for(i = 0; units[i].str; i++)
            if(unit_name.localeCompare(units[i].str) === 0)
            {
                _c = units[i].c;
                return true
            }
        var j = 0;
        if(prefixes)
            for(i = 0; prefixes[i].str; i++)
            {
                var u = unit_name.substr(0,1).toLowerCase();
                var p = prefixes[i].str.substr(0,1).toLowerCase();
                if(u === p && _fixPrefixes(unit_name))
                {
                    _prefix = prefixes[i].c;
                    j++
                }
            }
        for(i = 0; units[i].str; i++)
        {
            var u1 = unit_name.substr(1,units[i].str.Length);
            var u2 = units[i].str.substr(0,units[i].str.Length);
            if(u1 === u2 && _fixPrefixes(unit_name))
            {
                _c = units[i].c;
                return true
            }
        }
        return false
    }
    function _convert(units, prefixes, from_unit, to_unit, n)
    {
        var from_c,
            from_prefix,
            to_c,
            to_prefix;
        from_c = 0.0;
        from_prefix = 0.0;
        to_prefix = 0.0;
        to_c = 0.0;
        var from_result = _get_constant_of_unit(units,prefixes,from_unit);
        from_c = _c;
        from_prefix = _prefix;
        var to_result = _get_constant_of_unit(units,prefixes,to_unit);
        to_prefix = _prefix;
        to_c = _c;
        if(from_result)
        {
            if(!to_result)
                return false;
            if(from_c === 0 || to_prefix === 0)
                return false;
            _v = n * from_prefix / from_c * to_c / to_prefix;
            return true
        }
        return false
    }
    function eg_convert(args)
    {
        var n = parseFloat(args[0]);
        if(isNaN(n))
            return Calc.Errors.Value;
        var from_unit = GrapeCity.Calc.Convert.toString(args[1]);
        var to_unit = GrapeCity.Calc.Convert.toString(args[2]);
        if(!from_unit || from_unit === "" || !to_unit || to_unit === "")
            return Calc.Errors.NotAvailable;
        else if((from_unit.localeCompare("C") === 0 || from_unit.localeCompare("cel") === 0) && to_unit.localeCompare("C") === 0 || to_unit.localeCompare("cel") === 0)
            return n;
        else if((from_unit.localeCompare("F") === 0 || from_unit.localeCompare("fah") === 0) && (to_unit.localeCompare("F") === 0 || to_unit.localeCompare("fah") === 0))
            return n;
        else if((from_unit.localeCompare("K") === 0 || from_unit.localeCompare("kel") === 0) && (to_unit.localeCompare("K") === 0 || to_unit.localeCompare("kel") === 0))
            return n;
        else if((from_unit.localeCompare("C") === 0 || from_unit.localeCompare("cel") === 0) && (to_unit.localeCompare("F") === 0 || to_unit.localeCompare("fah") === 0))
            return n * 9 / 5 + 32;
        else if((from_unit.localeCompare("F") === 0 || from_unit.localeCompare("fah") === 0) && (to_unit.localeCompare("C") === 0 || to_unit.localeCompare("cel") === 0))
            return(n - 32) * 5 / 9;
        else if((from_unit.localeCompare("F") === 0 || from_unit.localeCompare("fah") === 0) && (to_unit.localeCompare("F") === 0 || to_unit.localeCompare("fah") === 0))
            return n;
        else if((from_unit.localeCompare("F") === 0 || from_unit.localeCompare("fah") === 0) && (to_unit.localeCompare("K") === 0 || to_unit.localeCompare("kel") === 0))
            return(n - 32) * 5 / 9 + C_K_offset;
        else if((from_unit.localeCompare("K") === 0 || from_unit.localeCompare("kel") === 0) && (to_unit.localeCompare("F") === 0 || to_unit.localeCompare("fah") === 0))
            return(n - C_K_offset) * 9 / 5 + 32;
        else if((from_unit.localeCompare("C") === 0 || from_unit.localeCompare("cel") === 0) && (to_unit.localeCompare("K") === 0 || to_unit.localeCompare("kel") === 0))
            return n + C_K_offset;
        else if((from_unit.localeCompare("K") === 0 || from_unit.localeCompare("kel") === 0) && (to_unit.localeCompare("C") === 0 || to_unit.localeCompare("cel") === 0))
            return n - C_K_offset;
        if(_convert(_weight_units,_prefixes,from_unit,to_unit,n))
            return _v;
        if(_convert(_distance_units,_prefixes,from_unit,to_unit,n))
            return _v;
        if(_convert(_time_units,null,from_unit,to_unit,n))
            return _v;
        if(_convert(_pressure_units,_prefixes,from_unit,to_unit,n))
            return _v;
        if(_convert(_force_units,_prefixes,from_unit,to_unit,n))
            return _v;
        if(_convert(_energy_units,_prefixes,from_unit,to_unit,n))
            return _v;
        if(_convert(_power_units,_prefixes,from_unit,to_unit,n))
            return _v;
        if(_convert(_magnetism_units,_prefixes,from_unit,to_unit,n))
            return _v;
        if(_convert(_liquid_units,_prefixes,from_unit,to_unit,n))
            return _v;
        if(_convert(_magnetism_units,_prefixes,from_unit,to_unit,n))
            return _v;
        return Calc.Errors.NotAvailable
    }
    function eg_besseli(args)
    {
        var x = GrapeCity.Calc.Convert.toDouble(args[0]);
        if(isNaN(x))
            return Calc.Errors.Value;
        var n = GrapeCity.Calc.Convert.toInt(args[1]);
        if(isNaN(n))
            return Calc.Errors.Value;
        if(n < 0)
            return Calc.Errors.Number;
        return GrapeCity.Calc.Convert.toResult(_bessel(x,n,true))
    }
    function eg_besselj(args)
    {
        var x = GrapeCity.Calc.Convert.toDouble(args[0]);
        if(isNaN(x))
            return Calc.Errors.Value;
        var n = GrapeCity.Calc.Convert.toInt(args[1]);
        if(isNaN(n))
            return Calc.Errors.Value;
        if(n < 0)
            return Calc.Errors.Number;
        return GrapeCity.Calc.Convert.toResult(_bessel(x,n,false))
    }
    function eg_besselk(args)
    {
        var x = GrapeCity.Calc.Convert.toDouble(args[0]);
        if(isNaN(x))
            return Calc.Errors.Value;
        var n = GrapeCity.Calc.Convert.toInt(args[1]);
        if(isNaN(n))
            return Calc.Errors.Value;
        if(x <= 0.0)
            return Calc.Errors.Number;
        if(n < 0)
            return Calc.Errors.Number;
        return GrapeCity.Calc.Convert.toResult(_kn(n,x))
    }
    function eg_bessely(args)
    {
        var x = GrapeCity.Calc.Convert.toDouble(args[0]);
        if(isNaN(x))
            return Calc.Errors.Value;
        var n = GrapeCity.Calc.Convert.toInt(args[1]);
        if(isNaN(n))
            return Calc.Errors.Value;
        if(x <= 0.0)
            return Calc.Errors.Number;
        if(n < 0)
            return Calc.Errors.Number;
        return GrapeCity.Calc.Convert.toResult(_yn(n,x))
    }
    function eg_bin2dec(args)
    {
        var number = GrapeCity.Calc.Convert.toString(args[0]);
        var result;
        if(number.length > 10)
            return Calc.Errors.Number;
        result = _stringToLong(number,2);
        if(result.length < number.length)
            return Calc.Errors.Number;
        return result
    }
    function eg_bin2hex(args)
    {
        var number = GrapeCity.Calc.Convert.toString(args[0]);
        var places = GrapeCity.Calc._Helper._argumentExists(args,1) ? GrapeCity.Calc.Convert.toInt(args[1]) : 1;
        var temp;
        var result;
        if(number.length > 10)
            return Calc.Errors.Number;
        if(places < 1 || 10 < places)
            return Calc.Errors.Number;
        temp = _stringToLong(number,2);
        if(temp.length < number.length)
            return Calc.Errors.Number;
        result = _longToString(temp,16,places);
        if(places < result.length && 0 <= temp && GrapeCity.Calc._Helper._argumentExists(args,1))
            return Calc.Errors.Number;
        return result
    }
    function eg_bin2oct(args)
    {
        var number = GrapeCity.Calc.Convert.toString(args[0]);
        var places = GrapeCity.Calc._Helper._argumentExists(args,1) ? GrapeCity.Calc.Convert.toInt(args[1]) : 1;
        var temp;
        var result;
        if(10 < number.length)
            return Calc.Errors.Number;
        if(places < 1 || 10 < places)
            return Calc.Errors.Number;
        temp = _stringToLong(number,2);
        if(temp.length < number.length)
            return Calc.Errors.Number;
        result = _longToString(temp,8,places);
        if(0 <= temp && places < result.length && GrapeCity.Calc._Helper._argumentExists(args,1))
            return Calc.Errors.Number;
        return result
    }
    function eg_dec2bin(args)
    {
        var number = GrapeCity.Calc.Convert.toDouble(args[0]);
        var places = GrapeCity.Calc._Helper._argumentExists(args,1) ? GrapeCity.Calc.Convert.toInt(args[1]) : 1;
        var result;
        if(number < -512 || 511 < number)
            return Calc.Errors.Number;
        if(places < 1 || 10 < places)
            return Calc.Errors.Number;
        result = _longToString(number,2,places);
        if(0 <= result && places < result.length && GrapeCity.Calc._Helper._argumentExists(args,1))
            return Calc.Errors.Number;
        return result
    }
    function eg_dec2hex(args)
    {
        var number = GrapeCity.Calc.Convert.toDouble(args[0]);
        var places = GrapeCity.Calc._Helper._argumentExists(args,1) ? GrapeCity.Calc.Convert.toInt(args[1]) : 1;
        var result;
        if(number < -549755813888 || 549755813887 < number)
            return Calc.Errors.Number;
        if(places < 1 || 10 < places)
            return Calc.Errors.Number;
        result = _longToString(number,16,places);
        if(0 <= result && places < result.length && GrapeCity.Calc._Helper._argumentExists(args,1))
            return Calc.Errors.Number;
        return result
    }
    function eg_dex2oct(args)
    {
        var number = GrapeCity.Calc.Convert.toDouble(args[0]);
        var places = GrapeCity.Calc._Helper._argumentExists(args,1) ? GrapeCity.Calc.Convert.toInt(args[1]) : 1;
        var result;
        if(number < -536870912 || 536870911 < number)
            return Calc.Errors.Number;
        if(places < 1 || 10 < places)
            return Calc.Errors.Number;
        result = _longToString(number,8,places);
        if(0 <= result && places < result.length && GrapeCity.Calc._Helper._argumentExists(args,1))
            return Calc.Errors.Number;
        return result
    }
    function eg_hex2bin(args)
    {
        var number = GrapeCity.Calc.Convert.toString(args[0]);
        var places = GrapeCity.Calc._Helper._argumentExists(args,1) ? GrapeCity.Calc.Convert.toInt(args[1]) : 1;
        var temp;
        var result;
        if(10 < number.length)
            return Calc.Errors.Number;
        if(places < 1 || 10 < places)
            return Calc.Errors.Number;
        temp = _stringToLong(number.toLowerCase(),16);
        if(temp.length < number.length)
            return Calc.Errors.Number;
        if(temp < -512 || 511 < temp)
            return Calc.Errors.Number;
        result = _longToString(temp,2,places);
        if(0 <= temp && places < result.length && GrapeCity.Calc._Helper._argumentExists(args,1))
            return Calc.Errors.Number;
        return result
    }
    function eg_hex2dec(args)
    {
        var number = GrapeCity.Calc.Convert.toString(args[0]);
        var result;
        if(10 < number.length)
            return Calc.Errors.Number;
        result = _stringToLong(number.toLowerCase(),16);
        if(result.length < number.length)
            return Calc.Errors.Number;
        return GrapeCity.Calc.Convert.toResult(result)
    }
    function eg_hex2oct(args)
    {
        var number = GrapeCity.Calc.Convert.toString(args[0]);
        var places = GrapeCity.Calc._Helper._argumentExists(args,1) ? GrapeCity.Calc.Convert.toInt(args[1]) : 1;
        var temp;
        var result;
        if(10 < number.length)
            return Calc.Errors.Number;
        if(places < 1 || 10 < places)
            return Calc.Errors.Number;
        temp = _stringToLong(number,16);
        if(temp.length < number.length)
            return Calc.Errors.Number;
        if(temp < -536870912 || 536870911 < temp)
            return Calc.Errors.Number;
        result = _longToString(temp,8,places);
        if(0 <= temp && places < result.length && GrapeCity.Calc._Helper._argumentExists(args,1))
            return Calc.Errors.Number;
        return result
    }
    function eg_oct2bin(args)
    {
        var number = GrapeCity.Calc.Convert.toString(args[0]);
        var places = GrapeCity.Calc._Helper._argumentExists(args,1) ? GrapeCity.Calc.Convert.toInt(args[1]) : 1;
        var temp;
        var result;
        if(10 < number.length)
            return Calc.Errors.Number;
        if(places < 1 || 10 < places)
            return Calc.Errors.Number;
        temp = _stringToLong(number,8);
        if(temp.length < number.length)
            return Calc.Errors.Number;
        if(temp < -512 || 511 < temp)
            return Calc.Errors.Number;
        result = _longToString(temp,2,places);
        if(0 <= temp && places < result.length && GrapeCity.Calc._Helper._argumentExists(args,1))
            return Calc.Errors.Number;
        return result
    }
    function eg_oct2dec(args)
    {
        var number = GrapeCity.Calc.Convert.toString(args[0]);
        var result;
        if(10 < number.length)
            return Calc.Errors.Number;
        result = _stringToLong(number,8);
        if(result.length < number.length)
            return Calc.Errors.Number;
        return GrapeCity.Calc.Convert.toResult(result)
    }
    function eg_oct2hex(args)
    {
        var number = GrapeCity.Calc.Convert.toString(args[0]);
        var places = GrapeCity.Calc._Helper._argumentExists(args,1) ? GrapeCity.Calc.Convert.toInt(args[1]) : 1;
        var temp;
        var result;
        if(10 < number.length)
            return Calc.Errors.Number;
        if(places < 1 || 10 < places)
            return Calc.Errors.Number;
        temp = _stringToLong(number,8);
        if(temp.length < number.length)
            return Calc.Errors.Number;
        if(temp < -549755813888 || 549755813887 < temp)
            return Calc.Errors.Number;
        result = _longToString(temp,16,places);
        if(0 <= temp && places < result.length && GrapeCity.Calc._Helper._argumentExists(args,1))
            return Calc.Errors.Number;
        return result
    }
    function eg_erf(args)
    {
        var lower = GrapeCity.Calc.Convert.toDouble(args[0]);
        if(isNaN(lower))
            return Calc.Errors.Value;
        var upper = 0.0;
        if(GrapeCity.Calc._Helper._argumentExists(args,1))
        {
            upper = GrapeCity.Calc.Convert.toDouble(args[1]);
            if(isNaN(upper))
                return Calc.Errors.Value
        }
        var ans;
        if(lower < 0 || upper < 0)
            return Calc.Errors.Number;
        if(lower > 27 || upper > 27)
            return Calc.Errors.Number;
        var val = Functions.StatHelper.normsdist([lower * Math.sqrt(2)]);
        if(typeof val === Calc.Errors)
            return NaN;
        ans = parseFloat(val) * 2 - 1;
        if(GrapeCity.Calc._Helper._argumentExists(args,1))
        {
            val = Functions.StatHelper.normsdist([upper * Math.sqrt(2)]);
            if(typeof val === Calc.Errors)
                return NaN;
            var x = parseFloat(val) * 2 - 1;
            ans = x - ans
        }
        return ans
    }
    function eg_erfc(args)
    {
        var x = GrapeCity.Calc.Convert.toDouble(args[0]);
        if(isNaN(x))
            return Calc.Errors.Value;
        if(x < 0)
            return Calc.Errors.Number;
        var val = eg_erf([x]);
        if(typeof val === Calc.Errors)
            return NaN;
        return 1.0 - parseFloat(val)
    }
    function eg_delta(args)
    {
        var num1 = GrapeCity.Calc.Convert.toDouble(args[0]);
        if(isNaN(num1))
            return Calc.Errors.Value;
        var num2 = 0.0;
        if(GrapeCity.Calc._Helper._argumentExists(args,1))
        {
            num2 = GrapeCity.Calc.Convert.toDouble(args[1]);
            if(isNaN(num2))
                return Calc.Errors.Value
        }
        return Functions._MathHelper.approxEqual(num1,num2) ? 1 : 0
    }
    function eg_gestep(args)
    {
        var num = GrapeCity.Calc.Convert.toDouble(args[0]);
        if(isNaN(num))
            return Calc.Errors.Value;
        var step = 0.0;
        if(GrapeCity.Calc._Helper._argumentExists(args,1))
        {
            step = GrapeCity.Calc.Convert.toDouble(args[1]);
            if(isNaN(step))
                return Calc.Errors.Value
        }
        return num >= step ? 1 : 0
    }
    function eg_complex(args)
    {
        var real = GrapeCity.Calc.Convert.toDouble(args[0]);
        var imag = GrapeCity.Calc.Convert.toDouble(args[1]);
        if(isNaN(real) || isNaN(imag))
            return Calc.Errors.Value;
        var suffix = Calc._Helper._argumentExists(args,2) ? Calc.Convert.toString(args[2]) : "i";
        if(suffix !== "i" && suffix !== "j")
            return Calc.Errors.Value;
        return ComplexConvert._toResult(new Complex(real,imag),suffix)
    }
    function eg_imabs(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        var x = num.real();
        var y = num.imag();
        return GrapeCity.Calc.Convert.toResult(Math.sqrt(x * x + y * y))
    }
    function eg_imaginary(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        return GrapeCity.Calc.Convert.toResult(num.imag())
    }
    function eg_imargument(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        var x = num.real();
        var y = num.imag();
        if(x === 0.0 && y === 0.0)
            return Calc.Errors.DivideByZero;
        return GrapeCity.Calc.Convert.toResult(Math.atan2(y,x))
    }
    function eg_imconjugate(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        var x = num.real();
        var y = num.imag();
        return ComplexConvert._toResult(new Complex(x,-y))
    }
    function eg_imcos(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        var x = num.real();
        var y = num.imag();
        return ComplexConvert._toResult(new Complex(Math.cos(x) * Functions._MathHelper.cosh(y),-Math.sin(x) * Functions._MathHelper.sinh(y)))
    }
    function eg_imdiv(args)
    {
        var num0 = ComplexConvert._toComplex(args[0]);
        var num1 = ComplexConvert._toComplex(args[1]);
        var a = num0.real();
        var b = num0.imag();
        var c = num1.real();
        var d = num1.imag();
        if(c === 0.0 && d === 0.0)
            return Calc.Errors.Number;
        return ComplexConvert._toResult(new Complex((a * c + b * d) / (c * c + d * d),(b * c - a * d) / (c * c + d * d)))
    }
    function eg_imexp(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        var x = num.real();
        var y = num.imag();
        return ComplexConvert._toResult(new Complex(Math.exp(x) * Math.cos(y),Math.exp(x) * Math.sin(y)))
    }
    function eg_imln(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        var x = num.real();
        var y = num.imag();
        if(x === 0.0 && y === 0.0)
            return Calc.Errors.Number;
        var abs = Math.sqrt(x * x + y * y);
        var phi = Math.atan2(y,x);
        return ComplexConvert._toResult(new Complex(Math.log(abs),phi))
    }
    function eg_imlog10(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        var x = num.real();
        var y = num.imag();
        if(x === 0.0 && y === 0.0)
            return Calc.Errors.Number;
        var abs = Math.sqrt(x * x + y * y);
        var phi = Math.atan2(y,x);
        var log10e = Functions._MathHelper.log10(Math.E);
        return ComplexConvert._toResult(new Complex(log10e * Math.log(abs),log10e * phi))
    }
    function eg_imlog2(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        var x = num.real();
        var y = num.imag();
        if(x === 0.0 && y === 0.0)
            return Calc.Errors.Number;
        var abs = Math.sqrt(x * x + y * y);
        var phi = Math.atan2(y,x);
        var log2e = Functions._MathHelper.log(Math.E,2);
        return ComplexConvert._toResult(new Complex(log2e * Math.log(abs),log2e * phi))
    }
    function eg_impower(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        var pow = parseFloat(args[1]);
        if(isNaN(pow))
            return Calc.Errors.Value;
        var x = num.real();
        var y = num.imag();
        if(x === 0.0 && y === 0.0)
            if(pow > 0.0)
                return ComplexConvert._toResult(new Complex(0.0,0.0));
            else
                return Calc.Errors.Number;
        var abs = Math.sqrt(x * x + y * y);
        var phi = Math.atan2(y,x);
        return ComplexConvert._toResult(new Complex(Math.pow(abs,pow) * Math.cos(pow * phi),Math.pow(abs,pow) * Math.sin(pow * phi)))
    }
    function eg_improduct(args)
    {
        var real = 1.0;
        var imag = 0.0;
        var num,
            a,
            b,
            c,
            d;
        for(var i = 0; i < args.length; i++)
            if(args[i].constructor === Calc.Array)
            {
                var array = args[i];
                for(var j = 0; j < array.rowCount; j++)
                    for(var k = 0; k < array.columnCount; k++)
                    {
                        num = ComplexConvert._toComplex(array.getValue(j,k));
                        a = real;
                        b = imag;
                        c = num.real();
                        d = num.imag();
                        real = a * c - b * d;
                        imag = a * d + b * c
                    }
            }
            else
            {
                num = ComplexConvert._toComplex(args[i]);
                a = real;
                b = imag;
                c = num.real();
                d = num.imag();
                real = a * c - b * d;
                imag = a * d + b * c
            }
        return ComplexConvert._toResult(new Complex(real,imag))
    }
    function eg_imreal(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        return GrapeCity.Calc.Convert.toResult(num.real())
    }
    function eg_imsin(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        var x = num.real();
        var y = num.imag();
        return ComplexConvert._toResult(new Complex(Math.sin(x) * Functions._MathHelper.cosh(y),Math.cos(x) * Functions._MathHelper.sinh(y)))
    }
    function eg_imsqrt(args)
    {
        var num = ComplexConvert._toComplex(args[0]);
        var x = num.real();
        var y = num.imag();
        if(x === 0.0 && y === 0.0)
            return ComplexConvert._toResult(new Complex(0.0,0.0));
        var abs = Math.sqrt(x * x + y * y);
        var phi = Math.atan2(y,x);
        return ComplexConvert._toResult(new Complex(Math.sqrt(abs) * Math.cos(phi / 2.0),Math.sqrt(abs) * Math.sin(phi / 2.0)))
    }
    function eg_imsub(args)
    {
        var num0 = ComplexConvert._toComplex(args[0]);
        var num1 = ComplexConvert._toComplex(args[1]);
        var a = num0.real();
        var b = num0.imag();
        var c = num1.real();
        var d = num1.imag();
        return ComplexConvert._toResult(new Complex(a - c,b - d))
    }
    function eg_imsum(args)
    {
        var real = 0.0;
        var imag = 0.0;
        var num,
            a,
            b,
            c,
            d;
        for(var i = 0; i < args.length; i++)
            if(args[i].constructor === Calc.Array)
            {
                var array = args[i];
                for(var j = 0; j < array.rowCount; j++)
                    for(var k = 0; k < array.columnCount; k++)
                    {
                        num = ComplexConvert._toComplex(array.getValue(j,k));
                        a = real;
                        b = imag;
                        c = num.real();
                        d = num.imag();
                        real = a + c;
                        imag = b + d
                    }
            }
            else
            {
                num = ComplexConvert._toComplex(args[i]);
                a = real;
                b = imag;
                c = num.real();
                d = num.imag();
                real = a + c;
                imag = b + d
            }
        return ComplexConvert._toResult(new Complex(real,imag))
    }
    def("BESSELI",eg_besseli,{
        minArgs: 2,
        maxArgs: 2
    });
    def("BESSELJ",eg_besselj,{
        minArgs: 2,
        maxArgs: 2
    });
    def("BESSELK",eg_besselk,{
        minArgs: 2,
        maxArgs: 2
    });
    def("BESSELY",eg_bessely,{
        minArgs: 2,
        maxArgs: 2
    });
    def("BIN2DEC",eg_bin2dec,{
        minArgs: 1,
        maxArgs: 1
    });
    def("BIN2HEX",eg_bin2hex,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("BIN2OCT",eg_bin2oct,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("DEC2BIN",eg_dec2bin,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("DEC2HEX",eg_dec2hex,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("DEC2OCT",eg_dex2oct,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("HEX2BIN",eg_hex2bin,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("HEX2DEC",eg_hex2dec,{
        minArgs: 1,
        maxArgs: 1
    });
    def("HEX2OCT",eg_hex2oct,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("OCT2BIN",eg_oct2bin,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("OCT2DEC",eg_oct2dec,{
        minArgs: 1,
        maxArgs: 1
    });
    def("OCT2HEX",eg_oct2hex,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("ERF",eg_erf,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("ERFC",eg_erfc,{
        minArgs: 1,
        maxArgs: 1
    });
    def("DELTA",eg_delta,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("GESTEP",eg_gestep,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne
    });
    def("COMPLEX",eg_complex,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsTwo
    });
    def("IMABS",eg_imabs,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IMAGINARY",eg_imaginary,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IMARGUMENT",eg_imargument,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IMCONJUGATE",eg_imconjugate,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IMCOS",eg_imcos,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IMDIV",eg_imdiv,{
        minArgs: 2,
        maxArgs: 2
    });
    def("IMEXP",eg_imexp,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IMLN",eg_imln,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IMLOG10",eg_imlog10,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IMLOG2",eg_imlog2,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IMPOWER",eg_impower,{
        minArgs: 2,
        maxArgs: 2
    });
    def("IMPRODUCT",eg_improduct,{
        minArgs: 1,
        maxArgs: 255,
        acceptsMissingArgument: acceptsNotZero
    });
    def("IMREAL",eg_imreal,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IMSIN",eg_imsin,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IMSQRT",eg_imsqrt,{
        minArgs: 1,
        maxArgs: 1
    });
    def("IMSUB",eg_imsub,{
        minArgs: 2,
        maxArgs: 2
    });
    def("IMSUM",eg_imsum,{
        minArgs: 1,
        maxArgs: 255,
        acceptsMissingArgument: acceptsNotZero
    });
    def("CONVERT",eg_convert,{
        minArgs: 3,
        maxArgs: 3
    })
})(window);
(function(window)
{
    "use strict";;
    var const_undefined = "undefined";
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === const_undefined)
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.Calc === const_undefined)
        GrapeCity.Calc = {};
    var Calc = GrapeCity.Calc;
    if(typeof Calc.Functions === const_undefined)
        Calc.Functions = {};
    var Functions = Calc.Functions;
    Functions._builtInFunctions = Functions._builtInFunctions || {};
    if(typeof Functions._defineBuildInFunction === const_undefined)
        Functions._defineBuildInFunction = function(name, fnEvaluate, options)
        {
            if(!name)
                throw"Invalid function name";
            var fn;
            name = name.toUpperCase();
            if(!Functions._builtInFunctions.hasOwnProperty(name))
            {
                fn = new Functions.Function(name,0,255);
                Functions._builtInFunctions[name] = fn
            }
            else
            {
                fn = Functions._builtInFunctions[name];
                if(!fn)
                {
                    Functions._builtInFunctions[name] = new Functions.Function(name,0,255);
                    fn = Functions[name.toUpperCase()]
                }
                else if(!options || !options.override)
                    throw"Attempt to override function while override is not allowed";
            }
            if(fnEvaluate && typeof fnEvaluate === "function")
                fn.evaluate = fnEvaluate;
            if(options)
                for(var prop in options)
                    if(options.hasOwnProperty(prop) && prop !== 'override')
                        fn[prop] = options[prop];
            return fn
        };
    var def = Functions._defineBuildInFunction;
    function acceptsNotZero(i)
    {
        return i !== 0
    }
    function acceptsOne(i)
    {
        return i === 1
    }
    function acceptsThree(i)
    {
        return i === 3
    }
    function acceptsThreeFour(i)
    {
        return i === 3 || i === 4
    }
    function acceptsPositive(i)
    {
        return i > 0
    }
    function acceptsFour(i)
    {
        return i === 4
    }
    function acceptsFourFive(i)
    {
        return i === 4 || i === 5
    }
    function acceptsFive(i)
    {
        return i === 5
    }
    function acceptsSix(i)
    {
        return i === 6
    }
    function acceptsEight(i)
    {
        return i === 8
    }
    function acceptsSeven(i)
    {
        return i === 7
    }
    function acceptsZero(i)
    {
        return i === 0
    }
    function acceptsTwo(i)
    {
        return i === 2
    }
    function acceptsNotTwo(i)
    {
        return i !== 2
    }
    function acceptsFiveSix(i)
    {
        return i === 5 || i === 6
    }
    function acceptsFourSixSeven(i)
    {
        return i === 4 || i === 6 || i === 7
    }
    function acceptsThreeFourFive(i)
    {
        return i === 3 || i === 4 || i === 5
    }
    function __isLeapYear(year)
    {
        return year % 400 === 0 || year % 4 === 0 && year % 100 !== 0 || year === 1900
    }
    function __compareDateTime(date1, date2)
    {
        return date1 - date2
    }
    function __toOADate(date)
    {
        return new GrapeCity.UI._DateTimeHelper(date).toOADate()
    }
    function __days_monthly_basis(date_i, date_m, basis)
    {
        var issue_day,
            issue_month,
            issue_year;
        var maturity_day,
            maturity_month,
            maturity_year;
        var months,
            days,
            years;
        var leap_year;
        var maturity,
            issue;
        issue_year = date_i.getFullYear();
        issue_month = date_i.getMonth();
        issue_day = date_i.getDate();
        maturity_year = date_m.getFullYear();
        maturity_month = date_m.getMonth();
        maturity_day = date_m.getDate();
        years = maturity_year - issue_year;
        months = maturity_month - issue_month;
        days = maturity_day - issue_day;
        months = years * 12 + months;
        leap_year = __isLeapYear(issue_year);
        switch(basis)
        {
            case 0:
                var list = [];
                list[0] = date_i;
                list[1] = date_m;
                return Functions._DateTimeHelper.days360(list);
            case 1:
            case 2:
            case 3:
                issue = __toOADate(date_i);
                maturity = __toOADate(date_m);
                return maturity - issue;
            case 4:
                return months * 30 + days;
            default:
                return-1
        }
    }
    function __annual_year_basis(date, basis)
    {
        var leap_year;
        switch(basis)
        {
            case 0:
                return 360;
            case 1:
                leap_year = __isLeapYear(date.getFullYear());
                return leap_year ? 366 : 365;
            case 2:
                return 360;
            case 3:
                return 365;
            case 4:
                return 360;
            default:
                return-1
        }
    }
    function __getRmz(fZins, fZzr, fBw, fZw, nF)
    {
        var fRmz;
        if(fZins === 0.0)
            fRmz = (fBw + fZw) / fZzr;
        else
        {
            var fTerm = Math.pow(1.0 + fZins,fZzr);
            if(nF > 0)
                fRmz = (fZw * fZins / (fTerm - 1.0) + fBw * fZins / (1.0 - 1.0 / fTerm)) / (1.0 + fZins);
            else
                fRmz = fZw * fZins / (fTerm - 1.0) + fBw * fZins / (1.0 - 1.0 / fTerm)
        }
        return-fRmz
    }
    function __getZw(fZins, fZzr, fRmz, fBw, nF)
    {
        var fZw;
        if(fZins === 0.0)
            fZw = fBw + fRmz * fZzr;
        else
        {
            var fTerm = Math.pow(1.0 + fZins,fZzr);
            if(nF > 0)
                fZw = fBw * fTerm + fRmz * (1.0 + fZins) * (fTerm - 1.0) / fZins;
            else
                fZw = fBw * fTerm + fRmz * (fTerm - 1.0) / fZins
        }
        return-fZw
    }
    function __calculate_pvif(rate, nper)
    {
        var ret = Math.pow(1.0 + rate,nper);
        if(isNaN(ret) || !isFinite(ret))
            return GrapeCity.Calc.Errors.Number;
        return ret
    }
    function __calculate_fvifa(rate, nper)
    {
        if(rate === 0.0)
            return nper;
        else
        {
            var x = Math.pow(1.0 + rate,nper);
            var y = x - 1.0;
            var ret = y / rate;
            if(isNaN(ret) || !isFinite(ret))
                return GrapeCity.Calc.Errors.Number;
            return ret
        }
    }
    function __calculate_pmt(rate, nper, pv, fv, type)
    {
        var pvif,
            fvifa;
        pvif = __calculate_pvif(rate,nper);
        fvifa = __calculate_fvifa(rate,nper);
        var x = -pv * pvif - fv;
        var y = 1.0 + rate * Calc.Convert.toDouble(type);
        var z = y * fvifa;
        var ret = x / z;
        if(isNaN(ret) || !isFinite(ret))
            return GrapeCity.Calc.Errors.Number;
        return ret
    }
    function __calculate_interest_part(pv, pmt, rate, per)
    {
        var x = Math.pow(1.0 + rate,per);
        var y = x - 1.0;
        var ret = -(pv * x * rate + pmt * y);
        if(isNaN(ret) || !isFinite(ret))
            return GrapeCity.Calc.Errors.Number;
        return ret
    }
    function __getDaysInMonth(year, month)
    {
        switch(month)
        {
            case 0:
            case 2:
            case 4:
            case 6:
            case 7:
            case 9:
            case 11:
                return 31;
            case 1:
                if(__isLeapYear(year))
                    return 29;
                else
                    return 28;
                break;
            case 3:
            case 5:
            case 8:
            case 10:
                return 30
        }
    }
    function __coup_cd(settlement, maturity, freq, next)
    {
        var months,
            periods;
        var is_eom_special;
        var result = new Date(1,0,1);
        result.setFullYear(1);
        var ndays = 0;
        is_eom_special = maturity.getDate() === __getDaysInMonth(maturity.getFullYear(),maturity.getMonth());
        months = 12 / freq;
        periods = maturity.getFullYear() - settlement.getFullYear();
        if(periods > 0)
            periods = (periods - 1) * freq;
        do
        {
            result = new Date(maturity.getFullYear(),maturity.getMonth(),maturity.getDate());
            periods++;
            result.setMonth(result.getMonth() - periods * months);
            if(is_eom_special)
            {
                ndays = __getDaysInMonth(result.getFullYear(),result.getMonth());
                result = new Date(result.getFullYear(),result.getMonth(),ndays)
            }
        } while(__compareDateTime(settlement,result) < 0);
        if(next)
        {
            result = new Date(maturity.getFullYear(),maturity.getMonth(),maturity.getDate());
            periods--;
            result.setMonth(result.getMonth() - periods * months);
            if(is_eom_special)
            {
                ndays = __getDaysInMonth(result.getFullYear(),result.getMonth());
                result = new Date(result.getFullYear(),result.getMonth(),ndays)
            }
        }
        return result
    }
    function __Days_Between_BASIS_30E_360(from, to)
    {
        var y1,
            m1,
            d1,
            y2,
            m2,
            d2;
        y1 = from.getFullYear();
        m1 = from.getMonth();
        d1 = from.getDate();
        y2 = to.getFullYear();
        m2 = to.getMonth();
        d2 = to.getDate();
        if(d1 === 31)
            d1 = 30;
        if(d2 === 31)
            d2 = 30;
        return(y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1)
    }
    function __Days_Between_BASIS_30Ep_360(from, to)
    {
        var y1,
            m1,
            d1,
            y2,
            m2,
            d2;
        y1 = from.getFullYear();
        m1 = from.getMonth();
        d1 = from.getDate();
        y2 = to.getFullYear();
        m2 = to.getMonth();
        d2 = to.getDate();
        if(d1 === 31)
            d1 = 30;
        if(d2 === 31)
        {
            d2 = 1;
            m2++
        }
        return(y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1)
    }
    function __Days_Between_BASIS_MSRB_30_360_SYM(from, to)
    {
        var y1,
            m1,
            d1,
            y2,
            m2,
            d2;
        y1 = from.getFullYear();
        m1 = from.getMonth();
        d1 = from.getDate();
        y2 = to.getFullYear();
        m2 = to.getMonth();
        d2 = to.getDate();
        if(m1 === 2 && __getDaysInMonth(y1,m1) === d1)
            d1 = 30;
        if(m2 === 2 && __getDaysInMonth(y2,m2) === d2)
            d2 = 30;
        if(d2 === 31 && d1 >= 30)
            d2 = 30;
        if(d1 === 31)
            d1 = 30;
        return(y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1)
    }
    function __Days_Between_BASIS_MSRB_30_360(from, to)
    {
        var y1,
            m1,
            d1,
            y2,
            m2,
            d2;
        y1 = from.getFullYear();
        m1 = from.getMonth();
        d1 = from.getDate();
        y2 = to.getFullYear();
        m2 = to.getMonth();
        d2 = to.getDate();
        if(m1 === 2 && __getDaysInMonth(y1,m1) === d1 && m2 === 2 && __getDaysInMonth(y2,m2) === d2)
        {
            d1 = 30;
            d2 = 30
        }
        if(d2 === 31 && d1 >= 30)
            d2 = 30;
        if(d1 === 31)
            d1 = 30;
        return(y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1)
    }
    function __days_between_basis(from, to, basis)
    {
        var sign = 1;
        if(__compareDateTime(from,to) > 0)
        {
            var tmp = from;
            from = to;
            to = tmp;
            sign = -1
        }
        switch(basis)
        {
            case 1:
            case 2:
            case 3:
                return sign * Calc.Convert.toInt(__toOADate(to) - __toOADate(from));
            case 4:
                return sign * __Days_Between_BASIS_30E_360(from,to);
            case 5:
                return sign * __Days_Between_BASIS_30Ep_360(from,to);
            case 6:
                return sign * __Days_Between_BASIS_MSRB_30_360_SYM(from,to);
            default:
                return sign * __Days_Between_BASIS_MSRB_30_360(from,to)
        }
    }
    function __coupdaybs(settlement, maturity, freq, basis)
    {
        var prev_coupon = __coup_cd(settlement,maturity,freq,false);
        return __days_between_basis(prev_coupon,settlement,basis)
    }
    function __coupdays(settlement, maturity, freq, basis)
    {
        var prev,
            next;
        switch(basis)
        {
            case 0:
            case 2:
            case 4:
            case 5:
                return 360 / freq;
            case 3:
                return 365.0 / freq;
            default:
                next = __coup_cd(settlement,maturity,freq,true);
                prev = __coup_cd(settlement,maturity,freq,false);
                return __days_between_basis(prev,next,1)
        }
    }
    function __coupdaysnc(settlement, maturity, freq, basis)
    {
        var next_coupon = __coup_cd(settlement,maturity,freq,true);
        return __days_between_basis(settlement,next_coupon,basis)
    }
    function __coupncd(settlement, maturity, freq)
    {
        var date = __coup_cd(settlement,maturity,freq,true);
        return __toOADate(date)
    }
    function __coupnum(settlement, maturity, freq)
    {
        var months;
        var coupondate = new Date(maturity.getFullYear(),maturity.getMonth(),maturity.getDate());
        months = maturity.getMonth() - settlement.getMonth() + 12 * (maturity.getFullYear() - settlement.getFullYear());
        coupondate.setMonth(coupondate.getMonth() - months);
        if(maturity.getDate() === __getDaysInMonth(maturity.getFullYear(),maturity.getMonth()))
            while(coupondate.getDate() !== __getDaysInMonth(coupondate.getFullYear(),coupondate.getMonth()))
                coupondate.setDate(coupondate.getDate() + 1.0);
        if(settlement.getDate() >= coupondate.getDate())
            months--;
        return parseInt(1 + months / (12 / freq),10)
    }
    function __couppcd(settlement, maturity, freq)
    {
        var date = __coup_cd(settlement,maturity,freq,false);
        return __toOADate(date)
    }
    function __duration(nSettle, nMat, fCoup, fYield, nFreq, nBase, fNumOfCoups)
    {
        var fDur = 0.0;
        var t,
            p = 0.0;
        var f100 = 100.0;
        var Convert = Calc.Convert;
        fCoup *= f100 / Convert.toDouble(nFreq);
        fYield /= nFreq;
        fYield += 1.0;
        for(t = 1.0; t < fNumOfCoups; t++)
            fDur += t * fCoup / Math.pow(fYield,t);
        fDur += fNumOfCoups * (fCoup + f100) / Math.pow(fYield,fNumOfCoups);
        for(t = 1.0; t < fNumOfCoups; t++)
            p += fCoup / Math.pow(fYield,t);
        p += (fCoup + f100) / Math.pow(fYield,fNumOfCoups);
        fDur /= p;
        fDur /= Convert.toDouble(nFreq);
        return fDur
    }
    function __goal_seek_initialise(data)
    {
        data.havexpos = data.havexneg = false;
        data.xmin = -1.0e10;
        data.xmax = +1.0e10;
        data.precision = 1.0e-10;
        return data
    }
    function __update_data(x, y, data)
    {
        if(y > 0)
        {
            if(data.havexpos)
            {
                if(data.havexneg)
                {
                    if(Math.abs(x - data.xneg) < Math.abs(data.xpos - data.xneg))
                    {
                        data.xpos = x;
                        data.ypos = y
                    }
                }
                else if(y < data.ypos)
                {
                    data.xpos = x;
                    data.ypos = y
                }
            }
            else
            {
                data.xpos = x;
                data.ypos = y;
                data.havexpos = true
            }
            return[false,data]
        }
        else if(y < 0)
        {
            if(data.havexneg)
            {
                if(data.havexpos)
                {
                    if(Math.abs(x - data.xpos) < Math.abs(data.xpos - data.xneg))
                    {
                        data.xneg = x;
                        data.yneg = y
                    }
                }
                else if(-y < -data.yneg)
                {
                    data.xneg = x;
                    data.yneg = y
                }
            }
            else
            {
                data.xneg = x;
                data.yneg = y;
                data.havexneg = true
            }
            return[false,data]
        }
        else
        {
            data.root = x;
            return[true,data]
        }
    }
    function __price(settlement, maturity, rate, yieldParam, redemption, freq, basis)
    {
        var a,
            d,
            e,
            sum,
            den,
            based,
            exponent,
            first_term,
            last_term;
        var k,
            n;
        a = __coupdaybs(settlement,maturity,freq,basis);
        d = __coupdaysnc(settlement,maturity,freq,basis);
        e = __coupdays(settlement,maturity,freq,basis);
        n = parseInt(__coupnum(settlement,maturity,freq),10);
        sum = 0.0;
        den = 100.0 * rate / freq;
        based = 1.0 + yieldParam / freq;
        exponent = d / e;
        for(k = 0; k < n; k++)
            sum += den / Math.pow(based,exponent + k);
        first_term = redemption / Math.pow(based,n - 1.0 + d / e);
        last_term = a / e * den;
        return first_term + sum - last_term
    }
    function __date_ratio(d1, d2, d3, freq, basis)
    {
        var next_coupon,
            prev_coupon;
        var res;
        next_coupon = __coup_cd(d1,d3,freq,true);
        prev_coupon = __coup_cd(d1,d3,freq,false);
        if(__compareDateTime(next_coupon,d2) >= 0)
            return __days_between_basis(d1,d2,basis) / __coupdays(prev_coupon,next_coupon,freq,basis);
        res = __days_between_basis(d1,next_coupon,basis) / __coupdays(prev_coupon,next_coupon,freq,basis);
        while(true)
        {
            prev_coupon = new Date(next_coupon.getFullYear(),next_coupon.getMonth(),next_coupon.getDate());
            next_coupon.setMonth(next_coupon.getMonth() + 12 / freq);
            if(__compareDateTime(next_coupon,d2) >= 0)
            {
                res += __days_between_basis(prev_coupon,d2,basis) / __coupdays(prev_coupon,next_coupon,freq,basis);
                return res
            }
            res += 1.0
        }
    }
    function __calc_oddfprice(settlement, maturity, issue, first_coupon, rate, yieldParam, redemption, freq, basis)
    {
        var a = __days_between_basis(issue,settlement,basis);
        var ds = __days_between_basis(settlement,first_coupon,basis);
        var df = __days_between_basis(issue,first_coupon,basis);
        var e = __coupdays(settlement,maturity,freq,basis);
        var n = parseInt(__coupnum(settlement,maturity,freq),10);
        var scale = 100.0 * rate / freq;
        var f = 1.0 + yieldParam / freq;
        var sum,
            term1,
            term2;
        if(ds > e)
            switch(basis)
            {
                case 0:
                case 4:
                    var cdays = __days_between_basis(first_coupon,maturity,basis);
                    n = 1 + parseInt(Math.ceil(cdays / e),10);
                    break;
                default:
                    var d = new Date(first_coupon.getFullYear(),first_coupon.getMonth(),first_coupon.getDate());
                    var INT_MAXVALUE = 32767;
                    for(n = 0; n < INT_MAXVALUE; n++)
                    {
                        var prev_date = new Date(d.getFullYear(),d.getMonth(),d.getDate());
                        d.setMonth(d.getMonth() + 12 / freq);
                        if(__compareDateTime(d,maturity) >= 0)
                        {
                            n += parseInt(Math.ceil(__days_between_basis(prev_date,maturity,basis)) / __coupdays(prev_date,d,freq,basis),10) + 1;
                            break
                        }
                    }
                    a = e * __date_ratio(issue,settlement,first_coupon,freq,basis);
                    ds = e * __date_ratio(settlement,first_coupon,first_coupon,freq,basis);
                    df = e * __date_ratio(issue,first_coupon,first_coupon,freq,basis);
                    break
            }
        term1 = redemption / Math.pow(f,n - 1.0 + ds / e);
        term2 = df / e / Math.pow(f,ds / e);
        sum = Math.pow(f,-ds / e) * (Math.pow(f,-n) - 1 / f) / (1 / f - 1);
        return term1 + scale * (term2 + sum - a / e)
    }
    function __one_euro(str, prec)
    {
        var subStr = str.substr(0,3);
        var round = Functions._MathHelper.round;
        switch(str[0])
        {
            case'A':
                if(subStr === "ATS")
                    return round(13.7603,prec);
                break;
            case'B':
                if(subStr === "BEF")
                    return round(40.3399,prec);
                break;
            case'D':
                if(subStr === "DEM")
                    return round(1.95583,prec);
                break;
            case'E':
                if(subStr === "ESP")
                    return round((166.386),prec);
                else if(subStr === "EUR")
                    return round((1.0),prec);
                break;
            case'F':
                if(subStr === "FIM")
                    return round((5.94573),prec);
                else if(subStr === "FRF")
                    return round((6.55957),prec);
                break;
            case'G':
                if(subStr === "GRD")
                    return round((340.75),prec);
                break;
            case'I':
                if(subStr === "IEP")
                    return round((0.787564),prec);
                else if(subStr === "ITL")
                    return round((1936.27),prec);
                break;
            case'L':
                if(subStr === "LUX" || subStr === "LUF")
                    return round((40.3399),prec);
                break;
            case'N':
                if(subStr === "NLG")
                    return round((2.20371),prec);
                break;
            case'P':
                if(subStr === "PTE")
                    return round((200.482),prec);
                break;
            default:
                break
        }
        return-1
    }
    function __calcPrecision(str)
    {
        var subStr = str.substr(0,3);
        switch(str[0])
        {
            case'A':
                if(subStr === "ATS")
                    return 2;
                break;
            case'B':
                if(subStr === "BEF")
                    return 0;
                break;
            case'D':
                if(subStr === "DEM")
                    return 2;
                break;
            case'E':
                if(subStr === "ESP")
                    return 0;
                else if(subStr === "EUR")
                    return 2;
                break;
            case'F':
                if(subStr === "FIM")
                    return 2;
                else if(subStr === "FRF")
                    return 2;
                break;
            case'G':
                if(subStr === "GRD")
                    return 2;
                break;
            case'I':
                if(subStr === "IEP")
                    return 2;
                else if(subStr === "ITL")
                    return 0;
                break;
            case'L':
                if(subStr === "LUX" || subStr === "LUF")
                    return 0;
                break;
            case'N':
                if(subStr === "NLG")
                    return 2;
                break;
            case'P':
                if(subStr === "PTE")
                    return 1;
                break;
            default:
                break
        }
        return 2
    }
    function __displayPrecision(str)
    {
        var subStr = str.substr(0,3);
        switch(str[0])
        {
            case'A':
                if(subStr === "ATS")
                    return 2;
                break;
            case'B':
                if(subStr === "BEF")
                    return 0;
                break;
            case'D':
                if(subStr === "DEM")
                    return 2;
                break;
            case'E':
                if(subStr === "ESP")
                    return 0;
                else if(subStr === "EUR")
                    return 2;
                break;
            case'F':
                if(subStr === "FIM")
                    return 2;
                else if(subStr === "FRF")
                    return 2;
                break;
            case'G':
                if(subStr === "GRD")
                    return 2;
                break;
            case'I':
                if(subStr === "IEP")
                    return 2;
                else if(subStr === "ITL")
                    return 0;
                break;
            case'L':
                if(subStr === "LUX" || subStr === "LUF")
                    return 0;
                break;
            case'N':
                if(subStr === "NLG")
                    return 2;
                break;
            case'P':
                if(subStr === "PTE")
                    return 2;
                break;
            default:
                break
        }
        return 2
    }
    Functions._FinancialHelper = {
        days_between_basis: __days_between_basis,
        annual_year_basis: __annual_year_basis
    };
    function fi_fv(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var rate = Convert.toDouble(args[0]);
        var nper = Convert.toDouble(args[1]);
        var pmt = Convert.toDouble(args[2]);
        var pv = _Helper._argumentExists(args,3) ? Convert.toDouble(args[3]) : 0.0;
        var type = _Helper._argumentExists(args,4) ? Convert.toDouble(args[4]) : 0.0;
        if(type !== 0.0)
            type = 1.0;
        if(rate === 0.0)
            return Convert.toResult(-(pmt * nper + pv));
        else
            return Convert.toResult(-(pv * Math.pow(1.0 + rate,nper) + pmt * (1.0 + rate * type) * (Math.pow(1.0 + rate,nper) - 1.0) / rate))
    }
    function fi_fvschedule(args)
    {
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        var fv = Convert.toDouble(args[0]);
        for(var i = 0; i < _ArrayHelper.getLength(args[1]); i++)
        {
            var rate = Convert.toDouble(_ArrayHelper.getValueByIndex(args[1],i));
            fv *= 1.0 + rate
        }
        return fv
    }
    function fi_npv(args)
    {
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        var rate = Convert.toDouble(args[0]);
        var npvResult = 0.0;
        var i = 1;
        var val = 0;
        for(var k = 1; k < args.length; k++)
        {
            if(Convert.isError(args[k]))
                return args[k];
            if(_ArrayHelper.isArrayOrReference(args[k]))
                for(var j = 0; j < _ArrayHelper.getLength(args[k]); j++)
                {
                    var o = _ArrayHelper.getValueByIndex(args[k],j);
                    if(Convert.isError(o))
                        return o;
                    if(Convert.isNumber(o))
                    {
                        val = Convert.toDouble(_ArrayHelper.getValueByIndex(args[k],j));
                        npvResult += val / Math.pow(1.0 + rate,i);
                        i++
                    }
                    else if(Convert.isError(o))
                        return o
                }
            else
            {
                val = Convert.toDouble(args[k]);
                npvResult += val / Math.pow(1.0 + rate,i);
                i++
            }
        }
        return npvResult
    }
    function fi_pv(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var rate = Convert.toDouble(args[0]);
        var nper = Convert.toDouble(args[1]);
        var pmt = Convert.toDouble(args[2]);
        var fv = _Helper._argumentExists(args,3) ? Convert.toDouble(args[3]) : 0.0;
        var type = _Helper._argumentExists(args,4) ? Convert.toDouble(args[4]) : 0.0;
        if(type !== 0.0)
            type = 1.0;
        if(rate === 0.0)
            return Convert.toResult(-(pmt * nper + fv));
        else
        {
            if(rate === -1.0)
                return Calc.Errors.DivideByZero;
            return Convert.toResult(-(fv + pmt * (1.0 + rate * type) * (Math.pow(1.0 + rate,nper) - 1.0) / rate) / Math.pow(1.0 + rate,nper))
        }
    }
    function fi_received(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var investment = Convert.toDouble(args[2]);
        var discount = Convert.toDouble(args[3]);
        var basis = _Helper._argumentExists(args,4) ? Convert.toInt(args[4]) : 0;
        var a,
            d,
            n;
        if(__compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        if(investment <= 0.0 || discount <= 0.0 || basis < 0 || 4 < basis)
            return Calc.Errors.Number;
        a = __days_monthly_basis(settlement,maturity,basis);
        d = __annual_year_basis(settlement,basis);
        if(a <= 0 || d <= 0)
            return Calc.Errors.Number;
        n = 1.0 - discount * a / d;
        if(n <= 0)
            return Calc.Errors.Number;
        return investment / n
    }
    function fi_xnpv(args)
    {
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        var len1 = _ArrayHelper.getLength(args[1]);
        var len2 = _ArrayHelper.getLength(args[2]);
        if(len1 !== len2)
            return Calc.Errors.Number;
        var rate = Convert.toDouble(args[0]);
        var values = new Array(len1);
        var dates = new Array(len2);
        var sum = 0.0;
        for(var k = 0; k < len1; k++)
            values[k] = Convert.toDouble(_ArrayHelper.getValueByIndex(args[1],k));
        for(k = 0; k < len2; k++)
            dates[k] = Convert.toDateTime(_ArrayHelper.getValueByIndex(args[2],k));
        for(var i = 0; i < len1; i++)
            sum += values[i] / Math.pow(1.0 + rate,(__toOADate(dates[i]) - __toOADate(dates[0])) / 365.0);
        return sum
    }
    function __get_cumipmt(fRate, nNumPeriods, fVal, nStart, nEnd, nPayType)
    {
        var fRmz,
            fZinsZ;
        var i;
        fRmz = __getRmz(fRate,nNumPeriods,fVal,0.0,nPayType);
        fZinsZ = 0.0;
        if(nStart === 1)
        {
            if(nPayType <= 0)
                fZinsZ = -fVal;
            nStart++
        }
        for(i = nStart; i <= nEnd; i++)
            if(nPayType > 0)
                fZinsZ += __getZw(fRate,i - 2,fRmz,fVal,1) - fRmz;
            else
                fZinsZ += __getZw(fRate,i - 1,fRmz,fVal,0);
        fZinsZ *= fRate;
        return fZinsZ
    }
    function fi_cumipmt(args)
    {
        var Convert = Calc.Convert;
        var fRate = Convert.toDouble(args[0]);
        var nNumPeriods = Convert.toInt(args[1]);
        var fVal = Convert.toDouble(args[2]);
        var nStartPer = Convert.toInt(args[3]);
        var nEndPer = Convert.toInt(args[4]);
        var nPayType = Convert.toInt(args[5]);
        if(nStartPer < 1 || nEndPer < nStartPer || fRate <= 0.0 || nEndPer > nNumPeriods || nNumPeriods <= 0 || fVal <= 0.0 || nPayType !== 0 && nPayType !== 1)
            return Calc.Errors.Number;
        return __get_cumipmt(fRate,nNumPeriods,fVal,nStartPer,nEndPer,nPayType)
    }
    function __get_cumprinc(fRate, nNumPeriods, fVal, nStart, nEnd, nPayType)
    {
        var fRmz,
            fKapZ;
        var i;
        fRmz = __getRmz(fRate,nNumPeriods,fVal,0.0,nPayType);
        fKapZ = 0.0;
        if(nStart === 1)
        {
            if(nPayType <= 0)
                fKapZ = fRmz + fVal * fRate;
            else
                fKapZ = fRmz;
            nStart++
        }
        for(i = nStart; i <= nEnd; i++)
            if(nPayType > 0)
                fKapZ += fRmz - (__getZw(fRate,i - 2,fRmz,fVal,1) - fRmz) * fRate;
            else
                fKapZ += fRmz - __getZw(fRate,i - 1,fRmz,fVal,0) * fRate;
        return fKapZ
    }
    function fi_cumprinc(args)
    {
        var Convert = Calc.Convert;
        var fRate = Convert.toDouble(args[0]);
        var nNumPeriods = Convert.toInt(args[1]);
        var fVal = Convert.toDouble(args[2]);
        var nStartPer = Convert.toInt(args[3]);
        var nEndPer = Convert.toInt(args[4]);
        var nPayType = Convert.toInt(args[5]);
        if(nStartPer < 1 || nEndPer < nStartPer || fRate <= 0.0 || nEndPer > nNumPeriods || nNumPeriods <= 0 || fVal <= 0.0 || nPayType !== 0 && nPayType !== 1)
            return Calc.Errors.Number;
        return __get_cumprinc(fRate,nNumPeriods,fVal,nStartPer,nEndPer,nPayType)
    }
    function fi_ipmt(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var rate = Convert.toDouble(args[0]);
        var per = Convert.toDouble(args[1]);
        var nper = Convert.toDouble(args[2]);
        var pv = Convert.toDouble(args[3]);
        var fv = _Helper._argumentExists(args,4) ? Convert.toDouble(args[4]) : 0.0;
        var type = _Helper._argumentExists(args,5) ? Convert.toInt(args[5]) : 0;
        if(per < 1.0 || per >= nper + 1.0 || nper < 1.0)
            return Calc.Errors.Number;
        else
        {
            var pmt = __calculate_pmt(rate,nper,pv,fv,type);
            return __calculate_interest_part(pv,pmt,rate,per - 1.0)
        }
    }
    function fi_ispmt(args)
    {
        var Convert = Calc.Convert;
        var rate = Convert.toDouble(args[0]);
        var per = Convert.toInt(args[1]);
        var nper = Convert.toInt(args[2]);
        var pv = Convert.toDouble(args[3]);
        if(nper === 0)
            return Calc.Errors.DivideByZero;
        return Convert.toResult(pv * rate * (Convert.toDouble(per) / Convert.toDouble(nper) - 1.0))
    }
    function fi_pmt(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var rate = Convert.toDouble(args[0]);
        var nper = Convert.toDouble(args[1]);
        var pv = Convert.toDouble(args[2]);
        var fv = _Helper._argumentExists(args,3) ? Convert.toDouble(args[3]) : 0.0;
        var type = _Helper._argumentExists(args,4) ? Convert.toDouble(args[4]) : 0.0;
        if(type !== 0.0)
            type = 1.0;
        if(rate === 0.0)
        {
            if(nper === 0.0)
                return Calc.Errors.DivideByZero;
            return Convert.toResult(-(pv + fv) / nper)
        }
        else
        {
            if(nper === 0.0)
                return Calc.Errors.DivideByZero;
            return Convert.toResult(-(pv * Math.pow(1.0 + rate,nper) + fv) / ((1.0 + rate * type) * (Math.pow(1.0 + rate,nper) - 1.0) / rate))
        }
    }
    function fi_ppmt(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var rate = Convert.toDouble(args[0]);
        var per = Convert.toDouble(args[1]);
        var nper = Convert.toDouble(args[2]);
        var pv = Convert.toDouble(args[3]);
        var fv = _Helper._argumentExists(args,4) ? Convert.toDouble(args[4]) : 0;
        var type = _Helper._argumentExists(args,5) ? Convert.toBool(args[5]) : false;
        if(per < 1.0 || per >= nper + 1.0)
            return Calc.Errors.Number;
        else
        {
            var pmt = __calculate_pmt(rate,nper,pv,fv,type ? 1 : 0);
            var ipmt = __calculate_interest_part(pv,pmt,rate,per - 1.0);
            return pmt - ipmt
        }
    }
    function fi_coupdaybsFunc(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var frequency = Convert.toInt(args[2]);
        var basis = _Helper._argumentExists(args,3) ? Convert.toInt(args[3]) : 0;
        var numError = Calc.Errors.Number;
        if(basis < 0 || basis > 4)
            return numError;
        if(frequency !== 1 && frequency !== 2 && frequency !== 4)
            return numError;
        if(__compareDateTime(settlement,maturity) >= 0)
            return numError;
        return __coupdaybs(settlement,maturity,frequency,basis)
    }
    function fi_coupdaysFunc(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var frequency = Convert.toInt(args[2]);
        var basis = _Helper._argumentExists(args,3) ? Convert.toInt(args[3]) : 0;
        var numError = Calc.Errors.Number;
        if(basis < 0 || basis > 4)
            return numError;
        if(frequency !== 1 && frequency !== 2 && frequency !== 4)
            return numError;
        if(__compareDateTime(settlement,maturity) >= 0)
            return numError;
        return __coupdays(settlement,maturity,frequency,basis)
    }
    function fi_coupdaysncFunc(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var frequency = Convert.toInt(args[2]);
        var basis = _Helper._argumentExists(args,3) ? Convert.toInt(args[3]) : 0;
        if(basis < 0 || basis > 4)
            return Calc.Errors.Number;
        if(frequency !== 1 && frequency !== 2 && frequency !== 4)
            return Calc.Errors.Number;
        if(__compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        return __coupdaysnc(settlement,maturity,frequency,basis)
    }
    function fi_coupncdFunc(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var frequency = Convert.toInt(args[2]);
        var basis = _Helper._argumentExists(args,3) ? Convert.toInt(args[3]) : 0;
        if(basis < 0 || basis > 4)
            return Calc.Errors.Number;
        if(frequency !== 1 && frequency !== 2 && frequency !== 4)
            return Calc.Errors.Number;
        if(__compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        return __coupncd(settlement,maturity,frequency)
    }
    function fi_coupnumFunc(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var frequency = Convert.toInt(args[2]);
        var basis = _Helper._argumentExists(args,3) ? Convert.toInt(args[3]) : 0;
        if(basis < 0 || basis > 4)
            return Calc.Errors.Number;
        if(frequency !== 1 && frequency !== 2 && frequency !== 4)
            return Calc.Errors.Number;
        if(__compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        return __coupnum(settlement,maturity,frequency)
    }
    function fi_couppcdFunc(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var frequency = Convert.toInt(args[2]);
        var basis = GrapeCity.Calc._Helper._argumentExists(args,3) ? Convert.toInt(args[3]) : 0;
        if(basis < 0 || basis > 4)
            return Calc.Errors.Number;
        if(frequency !== 1 && frequency !== 2 && frequency !== 4)
            return Calc.Errors.Number;
        if(__compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        return __couppcd(settlement,maturity,frequency)
    }
    function fi_durationFunc(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var coup = Convert.toDouble(args[2]);
        var yld = Convert.toDouble(args[3]);
        var frequency = Convert.toInt(args[4]);
        var basis = _Helper._argumentExists(args,5) ? Convert.toInt(args[5]) : 0;
        var fNumOfCoups;
        if(basis < 0 || basis > 4)
            return Calc.Errors.Number;
        if(frequency !== 1 && frequency !== 2 && frequency !== 4)
            return Calc.Errors.Number;
        if(__compareDateTime(settlement,maturity) > 0)
            return Calc.Errors.Number;
        fNumOfCoups = __coupnum(settlement,maturity,frequency);
        return __duration(settlement,maturity,coup,yld,frequency,basis,fNumOfCoups)
    }
    function __get_mduration(nSettle, nMat, fCoup, fYield, nFreq, nBase, fNumOfCoups)
    {
        var fRet = __duration(nSettle,nMat,fCoup,fYield,nFreq,nBase,fNumOfCoups);
        fRet /= 1.0 + fYield / Calc.Convert.toDouble(nFreq);
        return fRet
    }
    function fi_mduration(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var coup = Convert.toDouble(args[2]);
        var yld = Convert.toDouble(args[3]);
        var frequency = Convert.toInt(args[4]);
        var basis = _Helper._argumentExists(args,5) ? Convert.toInt(args[5]) : 0;
        var fNumOfCoups;
        if(basis < 0 || basis > 4)
            return Calc.Errors.Number;
        if(frequency !== 1 && frequency !== 2 && frequency !== 4)
            return Calc.Errors.Number;
        if(__compareDateTime(settlement,maturity) > 0)
            return Calc.Errors.Number;
        fNumOfCoups = __coupnum(settlement,maturity,frequency);
        return __get_mduration(settlement,maturity,coup,yld,frequency,basis,fNumOfCoups)
    }
    function fi_nper(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var rate = Convert.toDouble(args[0]);
        var pmt = Convert.toDouble(args[1]);
        var pv = Convert.toDouble(args[2]);
        var fv = _Helper._argumentExists(args,3) ? Convert.toDouble(args[3]) : 0.0;
        var type = _Helper._argumentExists(args,4) ? Convert.toDouble(args[4]) : 0.0;
        if(type !== 0.0)
            type = 1.0;
        if(rate === 0.0)
        {
            if(pmt === 0.0)
                return Calc.Errors.DivideByZero;
            return Convert.toResult(-(pv + fv) / pmt)
        }
        else
        {
            if(rate <= -1.0)
                return Calc.Errors.Number;
            return Convert.toResult(Math.log((pmt * (1.0 + rate * type) - fv * rate) / (pv * rate + pmt * (1.0 + rate * type))) / Math.log(1.0 + rate))
        }
    }
    function __yield_f(yieldParam, y, settlement, maturity, rate, par, redemption, freq, basis)
    {
        y = __price(settlement,maturity,rate,yieldParam,redemption,freq,basis) - par;
        return[true,y]
    }
    function __fake_df(x, dfx, xstep, data, settle, maturity, rate, price, redemption, freq, basis)
    {
        var xl,
            xr;
        var status;
        var yl = 0.0;
        var yr = 0.0;
        xl = x - xstep;
        if(xl < data.xmin)
            xl = x;
        xr = x + xstep;
        if(xr > data.xmax)
            xr = x;
        if(xl === xr)
            return[false,dfx,data];
        var resultArray = __yield_f(xl,yl,settle,maturity,rate,price,redemption,freq,basis);
        yl = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,dfx,data];
        resultArray = __yield_f(xr,yr,settle,maturity,rate,price,redemption,freq,basis);
        yr = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,dfx,data];
        dfx = (yr - yl) / (xr - xl);
        return[true,dfx,data]
    }
    function __goal_seek_newton(data, settle, maturity, rate, price, redem, freq, basis, x0)
    {
        var iterations;
        var status;
        var precision = data.precision / 2.0;
        for(iterations = 0; iterations < 20; iterations++)
        {
            var x1,
                stepsize;
            var y0 = 0.0;
            var df0 = 0.0;
            if(x0 < data.xmin || x0 > data.xmax)
                return[false,data];
            var resultArray = __yield_f(x0,y0,settle,maturity,rate,price,redem,freq,basis);
            y0 = resultArray[1];
            status = resultArray[0];
            if(!status)
                return[status,data];
            resultArray = __update_data(x0,y0,data);
            data = resultArray[1];
            if(resultArray[0])
                return[true,data];
            var xstep;
            if(Math.abs(x0) < 1.0e-10)
                if(data.havexneg && data.havexpos)
                    xstep = Math.abs(data.xpos - data.xneg) / 1.0e6;
                else
                    xstep = (data.xmax - data.xmin) / 1.0e6;
            else
                xstep = Math.abs(x0) / 1.0e6;
            var resultArray3 = __fake_df(x0,df0,xstep,data,settle,maturity,rate,price,redem,freq,basis);
            data = resultArray3[2];
            df0 = resultArray3[1];
            status = resultArray3[0];
            if(!status)
                return[status,data];
            if(df0 === 0)
                return[false,data];
            x1 = x0 - 1.000001 * y0 / df0;
            if(x1 === x0)
            {
                data.root = x0;
                return[true,data]
            }
            stepsize = Math.abs(x1 - x0) / (Math.abs(x0) + Math.abs(x1));
            x0 = x1;
            if(stepsize < precision)
            {
                data.root = x0;
                return[true,data]
            }
        }
        return[false,data]
    }
    function __goal_seek_point(data, settle, maturity, rate, price, redem, freq, basis, x0)
    {
        var status;
        var y0 = 0.0;
        if(x0 < data.xmin || x0 > data.xmax)
            return[false,data];
        var resultArray = __yield_f(x0,y0,settle,maturity,rate,price,redem,freq,basis);
        y0 = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,data];
        resultArray = __update_data(x0,y0,data);
        data = resultArray[1];
        if(resultArray[0])
            return[true,data];
        return[false,data]
    }
    function __replaceGoto(data, settle, maturity, rate, price, redem, freq, basis, stepsize, newton_submethod, xmid, ymid, status, method)
    {
        switch(method)
        {
            case 0:
                xmid = data.xpos - data.ypos * ((data.xneg - data.xpos) / (data.yneg - data.ypos));
                break;
            case 1:
                var det;
                xmid = (data.xpos + data.xneg) / 2.0;
                var resultArray = __yield_f(xmid,ymid,settle,maturity,rate,price,redem,freq,basis);
                ymid = resultArray[1];
                status = resultArray[0];
                if(!status)
                    return[null,data,newton_submethod,xmid,ymid,method];
                if(ymid === 0.0)
                {
                    data = __update_data(xmid,ymid,data)[1];
                    return[true,data,newton_submethod,xmid,ymid,method]
                }
                det = Math.sqrt(ymid * ymid - data.ypos * data.yneg);
                if(det === 0)
                    return[null,data,newton_submethod,xmid,ymid,method];
                xmid += (xmid - data.xpos) * ymid / det;
                break;
            case 3:
                xmid = (data.xpos + data.xneg) / 2.0;
                break;
            case 2:
                var x0 = 0.0;
                var y0 = 0.0;
                var xstep = 0.0;
                var df0 = 0.0;
                if(stepsize > 0.1)
                {
                    method = 3;
                    return __replaceGoto(data,settle,maturity,rate,price,redem,freq,basis,stepsize,newton_submethod,xmid,ymid,status,method)
                }
                switch(newton_submethod++ % 4)
                {
                    case 0:
                        x0 = data.xpos;
                        x0 = data.ypos;
                        break;
                    case 2:
                        x0 = data.xneg;
                        y0 = data.yneg;
                        break;
                    default:
                    case 3:
                    case 1:
                        x0 = (data.xpos + data.xneg) / 2.0;
                        var resultArray2 = __yield_f(x0,y0,settle,maturity,rate,price,redem,freq,basis);
                        y0 = resultArray2[1];
                        status = resultArray2[0];
                        if(!status)
                            return[null,data,newton_submethod,xmid,ymid,method];
                        break
                }
                xstep = Math.abs(data.xpos - data.xneg) / 1e6;
                var resultArray3 = __fake_df(x0,df0,xstep,data,settle,maturity,rate,price,redem,freq,basis);
                data = resultArray3[2];
                df0 = resultArray3[1];
                status = resultArray3[0];
                if(!status)
                    return[null,data,newton_submethod,xmid,ymid,method];
                if(df0 === 0)
                    return[null,data,newton_submethod,xmid,ymid,method];
                xmid = x0 - 1.01 * y0 / df0;
                if(xmid < data.xpos && xmid < data.xneg || xmid > data.xpos && xmid > data.xneg)
                    return[null,data,newton_submethod,xmid,ymid,method];
                break;
            default:
                break
        }
        return[false,data,newton_submethod,xmid,ymid,method]
    }
    function __goal_seek_bisection(data, settle, maturity, rate, price, redem, freq, basis)
    {
        var iterations;
        var stepsize;
        var newton_submethod = 0;
        if(!data.havexpos || !data.havexneg)
            return[false,data];
        stepsize = Math.abs(data.xpos - data.xneg) / (Math.abs(data.xpos) + Math.abs(data.xneg));
        for(iterations = 0; iterations < 100 + 2 * 4; iterations++)
        {
            var xmid = 0.0;
            var ymid = 0.0;
            var status;
            var method;
            method = iterations % 4 === 0 ? 1 : iterations % 4 === 2 ? 2 : 3;
            var resultOfGoto = __replaceGoto(data,settle,maturity,rate,price,redem,freq,basis,stepsize,newton_submethod,xmid,ymid,status,method);
            data = resultOfGoto[1];
            newton_submethod = resultOfGoto[2];
            xmid = resultOfGoto[3];
            ymid = resultOfGoto[4];
            method = resultOfGoto[5];
            if(!resultOfGoto[0])
                continue;
            else if(resultOfGoto[0])
                return[true,data];
            var resultArray4 = __yield_f(xmid,ymid,settle,maturity,rate,price,redem,freq,basis);
            ymid = resultArray4[1];
            status = resultArray4[0];
            if(!status)
                continue;
            var resultArray5 = __update_data(xmid,ymid,data);
            data = resultArray5[1];
            if(resultArray5[0])
                return[true,data];
            stepsize = Math.abs(data.xpos - data.xneg) / (Math.abs(data.xpos) + Math.abs(data.xneg));
            if(stepsize < data.precision)
            {
                if(data.yneg < ymid)
                {
                    ymid = data.yneg;
                    xmid = data.xneg
                }
                if(data.ypos < ymid)
                {
                    ymid = data.ypos;
                    xmid = data.xpos
                }
                data.root = xmid;
                return[true,data]
            }
        }
        return[false,data]
    }
    function fi_yieldFunc(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var rate = Convert.toDouble(args[2]);
        var par = Convert.toDouble(args[3]);
        var redemption = Convert.toDouble(args[4]);
        var freq = Convert.toInt(args[5]);
        var basis = _Helper._argumentExists(args,6) ? Convert.toInt(args[6]) : 0;
        var n;
        if(basis < 0 || basis > 4 || !(freq === 1 || freq === 2 || freq === 4) || __compareDateTime(settlement,maturity) > 0)
            return Calc.Errors.Number;
        if(rate < 0.0 || par < 0.0 || redemption <= 0.0)
            return Calc.Errors.Number;
        n = __coupnum(settlement,maturity,freq);
        if(n <= 1.0)
        {
            var a = __coupdaybs(settlement,maturity,freq,basis);
            var d = __coupdaysnc(settlement,maturity,freq,basis);
            var e = __coupdays(settlement,maturity,freq,basis);
            var coeff = freq * e / d;
            var num = redemption / 100.0 + rate / freq - (par / 100.0 + a / e * rate / freq);
            var den = par / 100.0 + a / e * rate / freq;
            return num / den * coeff
        }
        else
        {
            var data = {};
            var status;
            var yield0 = 0.1;
            data.xmin = 0.0;
            data.xmax = 0.0;
            data.precision = 0.0;
            data.havexpos = false;
            data.xpos = 0.0;
            data.ypos = 0.0;
            data.havexneg = false;
            data.xneg = 0.0;
            data.yneg = 0.0;
            data.root = 0.0;
            data = __goal_seek_initialise(data);
            data.xmin = Math.max(data.xmin,0);
            data.xmax = Math.min(data.xmax,1000);
            var resultArray = __goal_seek_newton(data,settlement,maturity,rate,par,redemption,freq,basis,yield0);
            data = resultArray[1];
            status = resultArray[0];
            if(!status)
            {
                for(yield0 = 1e-10; yield0 < data.xmax; yield0 *= 2)
                    data = __goal_seek_point(data,settlement,maturity,rate,par,redemption,freq,basis,yield0)[1];
                resultArray = __goal_seek_bisection(data,settlement,maturity,rate,par,redemption,freq,basis);
                data = resultArray[1];
                status = resultArray[0]
            }
            if(!status)
                return Calc.Errors.Number;
            return data.root
        }
    }
    function fi_yielddisc(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var fPrice = Convert.toDouble(args[2]);
        var fRedemp = Convert.toDouble(args[3]);
        var nBase = _Helper._argumentExists(args,4) ? Convert.toInt(args[4]) : 0;
        var ret;
        var yfrac;
        if(nBase < 0 || nBase > 4)
            return Calc.Errors.Number;
        if(fRedemp <= 0.0 || fPrice <= 0.0 || __compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        ret = fRedemp / fPrice - 1.0;
        yfrac = Functions._DateTimeHelper.yearfrac([settlement,maturity,nBase]);
        return ret / yfrac
    }
    function __get_yieldmat(nSettle, nMat, nIssue, fRate, fPrice, nBase)
    {
        var yearfrac = Functions._DateTimeHelper.yearfrac;
        var fIssMat = yearfrac([nIssue,nMat,nBase]);
        var fIssSet = yearfrac([nIssue,nSettle,nBase]);
        var fSetMat = yearfrac([nSettle,nMat,nBase]);
        var y = 1.0 + fIssMat * fRate;
        y /= fPrice / 100.0 + fIssSet * fRate;
        y--;
        y /= fSetMat;
        return y
    }
    function fi_yieldmat(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var nSettle = Convert.toDateTime(args[0]);
        var nMat = Convert.toDateTime(args[1]);
        var nIssue = Convert.toDateTime(args[2]);
        var fRate = Convert.toDouble(args[3]);
        var fPrice = Convert.toDouble(args[4]);
        var nBase = _Helper._argumentExists(args,5) ? Convert.toInt(args[5]) : 0;
        if(nBase < 0 || nBase > 4 || fRate < 0.0)
            return Calc.Errors.Number;
        return __get_yieldmat(nSettle,nMat,nIssue,fRate,fPrice,nBase)
    }
    function __odd_yield_f(yieldParam, y, settlement, maturity, issue, first_coupon, rate, price, redemption, freq, basis)
    {
        y = __calc_oddfprice(settlement,maturity,issue,first_coupon,rate,yieldParam,redemption,freq,basis) - price;
        return[true,y]
    }
    function __odd_fake_df(x, dfx, xstep, data, settle, maturity, issue, first_coupon, rate, price, redemption, freq, basis)
    {
        var xl,
            xr;
        var status;
        var yl = 0.0;
        var yr = 0.0;
        xl = x - xstep;
        if(xl < data.xmin)
            xl = x;
        xr = x + xstep;
        if(xr > data.xmax)
            xr = x;
        if(xl === xr)
            return[false,dfx,data];
        var resultArray = __odd_yield_f(xl,yl,settle,maturity,issue,first_coupon,rate,price,redemption,freq,basis);
        yl = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,dfx,data];
        resultArray = __odd_yield_f(xr,yr,settle,maturity,issue,first_coupon,rate,price,redemption,freq,basis);
        yr = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,dfx,data];
        dfx = (yr - yl) / (xr - xl);
        return[true,dfx,data]
    }
    function __odd_goal_seek_newton(data, settle, maturity, issue, first_coupon, rate, price, redem, freq, basis, x0)
    {
        var iterations;
        var status;
        var precision = data.precision / 2.0;
        for(iterations = 0; iterations < 20; iterations++)
        {
            var x1,
                stepsize;
            var y0 = 0.0;
            var df0 = 0.0;
            if(x0 < data.xmin || x0 > data.xmax)
                return[false,data];
            var resultArray = __odd_yield_f(x0,y0,settle,maturity,issue,first_coupon,rate,price,redem,freq,basis);
            y0 = resultArray[1];
            status = resultArray[0];
            if(!status)
                return[status,data];
            resultArray = __update_data(x0,y0,data);
            data = resultArray[1];
            if(resultArray[0])
                return[true,data];
            var xstep;
            if(Math.abs(x0) < 1.0e-10)
                if(data.havexneg && data.havexpos)
                    xstep = Math.abs(data.xpos - data.xneg) / 1.0e6;
                else
                    xstep = (data.xmax - data.xmin) / 1.0e6;
            else
                xstep = Math.abs(x0) / 1.0e6;
            var resultArray3 = __odd_fake_df(x0,df0,xstep,data,settle,maturity,issue,first_coupon,rate,price,redem,freq,basis);
            data = resultArray3[2];
            df0 = resultArray3[1];
            status = resultArray3[0];
            if(!status)
                return[status,data];
            if(df0 === 0)
                return[false,data];
            x1 = x0 - 1.000001 * y0 / df0;
            if(x1 === x0)
            {
                data.root = x0;
                return[true,data]
            }
            stepsize = Math.abs(x1 - x0) / (Math.abs(x0) + Math.abs(x1));
            x0 = x1;
            if(stepsize < precision)
            {
                data.root = x0;
                return[true,data]
            }
        }
        return[false,data]
    }
    function __odd_goal_seek_point(data, settle, maturity, issue, first_coupon, rate, price, redem, freq, basis, x0)
    {
        var status;
        var y0 = 0.0;
        if(x0 < data.xmin || x0 > data.xmax)
            return[false,data];
        var resultArray = __odd_yield_f(x0,y0,settle,maturity,issue,first_coupon,rate,price,redem,freq,basis);
        y0 = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,data];
        resultArray = __update_data(x0,y0,data);
        data = resultArray[1];
        if(resultArray[0])
            return[true,data];
        return[false,data]
    }
    function __odd_replaceGoto(data, settle, maturity, issue, first_coupon, rate, price, redem, freq, basis, stepsize, newton_submethod, xmid, ymid, status, method)
    {
        switch(method)
        {
            case 0:
                xmid = data.xpos - data.ypos * ((data.xneg - data.xpos) / (data.yneg - data.ypos));
                break;
            case 1:
                var det;
                xmid = (data.xpos + data.xneg) / 2.0;
                var resultArray = __odd_yield_f(xmid,ymid,settle,maturity,issue,first_coupon,rate,price,redem,freq,basis);
                ymid = resultArray[1];
                status = resultArray[0];
                if(!status)
                    return[null,data,newton_submethod,xmid,ymid,method];
                if(ymid === 0.0)
                {
                    data = __update_data(xmid,ymid,data)[1];
                    return[true,data,newton_submethod,xmid,ymid,method]
                }
                det = Math.sqrt(ymid * ymid - data.ypos * data.yneg);
                if(det === 0)
                    return[null,data,newton_submethod,xmid,ymid,method];
                xmid += (xmid - data.xpos) * ymid / det;
                break;
            case 3:
                xmid = (data.xpos + data.xneg) / 2.0;
                break;
            case 2:
                var x0 = 0.0;
                var y0 = 0.0;
                var xstep = 0.0;
                var df0 = 0.0;
                if(stepsize > 0.1)
                {
                    method = 3;
                    return __odd_replaceGoto(data,settle,maturity,issue,first_coupon,rate,price,redem,freq,basis,stepsize,newton_submethod,xmid,ymid,status,method)
                }
                switch(newton_submethod++ % 4)
                {
                    case 0:
                        x0 = data.xpos;
                        x0 = data.ypos;
                        break;
                    case 2:
                        x0 = data.xneg;
                        y0 = data.yneg;
                        break;
                    default:
                    case 3:
                    case 1:
                        x0 = (data.xpos + data.xneg) / 2.0;
                        var resultArray2 = __odd_yield_f(x0,y0,settle,maturity,issue,first_coupon,rate,price,redem,freq,basis);
                        y0 = resultArray2[1];
                        status = resultArray2[0];
                        if(!status)
                            return[null,data,newton_submethod,xmid,ymid,method];
                        break
                }
                xstep = Math.abs(data.xpos - data.xneg) / 1e6;
                var resultArray3 = __odd_fake_df(x0,df0,xstep,data,settle,maturity,issue,first_coupon,rate,price,redem,freq,basis);
                data = resultArray3[2];
                df0 = resultArray3[1];
                status = resultArray3[0];
                if(!status)
                    return[null,data,newton_submethod,xmid,ymid,method];
                if(df0 === 0)
                    return[null,data,newton_submethod,xmid,ymid,method];
                xmid = x0 - 1.01 * y0 / df0;
                if(xmid < data.xpos && xmid < data.xneg || xmid > data.xpos && xmid > data.xneg)
                    return[null,data,newton_submethod,xmid,ymid,method];
                break;
            default:
                break
        }
        return[false,data,newton_submethod,xmid,ymid,method]
    }
    function __odd_goal_seek_bisection(data, settle, maturity, issue, first_coupon, rate, price, redem, freq, basis)
    {
        var iterations;
        var stepsize;
        var newton_submethod = 0;
        if(!data.havexpos || !data.havexneg)
            return[false,data];
        stepsize = Math.abs(data.xpos - data.xneg) / (Math.abs(data.xpos) + Math.abs(data.xneg));
        for(iterations = 0; iterations < 100 + 2 * 4; iterations++)
        {
            var xmid = 0.0;
            var ymid = 0.0;
            var status;
            var method;
            method = iterations % 4 === 0 ? 1 : iterations % 4 === 2 ? 2 : 3;
            var resultOfGoto = __odd_replaceGoto(data,settle,maturity,issue,first_coupon,rate,price,redem,freq,basis,stepsize,newton_submethod,xmid,ymid,status,method);
            data = resultOfGoto[1];
            newton_submethod = resultOfGoto[2];
            xmid = resultOfGoto[3];
            ymid = resultOfGoto[4];
            method = resultOfGoto[5];
            if(resultOfGoto[0] === undefined || resultOfGoto[0] === null)
                continue;
            else if(resultOfGoto[0])
                return[true,data];
            var resultArray4 = __odd_yield_f(xmid,ymid,settle,maturity,issue,first_coupon,rate,price,redem,freq,basis);
            ymid = resultArray4[1];
            status = resultArray4[0];
            if(!status)
                continue;
            var resultArray5 = __update_data(xmid,ymid,data);
            data = resultArray5[1];
            if(resultArray5[0])
                return[true,data];
            stepsize = Math.abs(data.xpos - data.xneg) / (Math.abs(data.xpos) + Math.abs(data.xneg));
            if(stepsize < data.precision)
            {
                if(data.yneg < ymid)
                {
                    ymid = data.yneg;
                    xmid = data.xneg
                }
                if(data.ypos < ymid)
                {
                    ymid = data.ypos;
                    xmid = data.xpos
                }
                data.root = xmid;
                return[true,data]
            }
        }
        return[false,data]
    }
    function fi_oddfyield(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var issue = Convert.toDateTime(args[2]);
        var first_coupon = Convert.toDateTime(args[3]);
        var rate = Convert.toDouble(args[4]);
        var price = Convert.toDouble(args[5]);
        var redemption = Convert.toDouble(args[6]);
        var freq = Convert.toInt(args[7]);
        var basis = _Helper._argumentExists(args,8) ? Convert.toInt(args[8]) : 0;
        var yield0 = 0.1;
        var data = {};
        if(basis < 0 || basis > 4 || !(freq === 1 || freq === 2 || freq === 4) || __compareDateTime(issue,settlement) > 0 || __compareDateTime(settlement,first_coupon) > 0 || __compareDateTime(first_coupon,maturity) > 0)
            return Calc.Errors.Number;
        if(rate < 0.0 || price <= 0.0 || redemption <= 0.0)
            return Calc.Errors.Number;
        data.xmin = 0.0;
        data.xmax = 0.0;
        data.precision = 0.0;
        data.havexpos = false;
        data.xpos = 0.0;
        data.ypos = 0.0;
        data.havexneg = false;
        data.xneg = 0.0;
        data.yneg = 0.0;
        data.root = 0.0;
        data = __goal_seek_initialise(data);
        data.xmin = Math.max(data.xmin,0);
        data.xmax = Math.min(data.xmax,1000);
        var resultArray = __odd_goal_seek_newton(data,settlement,maturity,issue,first_coupon,rate,price,redemption,freq,basis,yield0);
        data = resultArray[1];
        var status = resultArray[0];
        if(status)
        {
            for(yield0 = 1e-10; yield0 < data.xmax; yield0 *= 2)
                data = __odd_goal_seek_point(data,settlement,maturity,issue,first_coupon,rate,price,redemption,freq,basis,yield0)[1];
            resultArray = __odd_goal_seek_bisection(data,settlement,maturity,issue,first_coupon,rate,price,redemption,freq,basis);
            data = resultArray[1];
            status = resultArray[0]
        }
        if(!status)
            return Calc.Errors.Number;
        return data.root
    }
    function __calc_oddlyield(settlement, maturity, last_interest, rate, price, redemption, freq, basis)
    {
        var d = new Date(last_interest.getFullYear(),last_interest.getMonth(),last_interest.getDate());
        var x1,
            x2,
            x3;
        do
            d.setMonth(d.getMonth() + 12 / freq);
        while(__compareDateTime(d,maturity) < 0);
        x1 = __date_ratio(last_interest,settlement,d,freq,basis);
        x2 = __date_ratio(last_interest,maturity,d,freq,basis);
        x3 = __date_ratio(settlement,maturity,d,freq,basis);
        return(freq * (redemption - price) + 100 * rate * (x2 - x1)) / (x3 * price + 100 * rate * x1 * x3 / freq)
    }
    function fi_oddlyield(args)
    {
        var Convert = Calc.Convert,
            _Helper = Calc._Helper;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var last_interest = Convert.toDateTime(args[2]);
        var rate = Convert.toDouble(args[3]);
        var pr = Convert.toDouble(args[4]);
        var redemption = Convert.toDouble(args[5]);
        var freq = Convert.toInt(args[6]);
        var basis = _Helper._argumentExists(args,7) ? Convert.toInt(args[7]) : 0;
        if(basis < 0 || basis > 4 || !(freq === 1 || freq === 2 || freq === 4) || __compareDateTime(settlement,maturity) > 0 || __compareDateTime(last_interest,settlement) > 0)
            return Calc.Errors.Number;
        if(rate < 0.0 || pr < 0.0 || redemption <= 0.0)
            return Calc.Errors.Number;
        return __calc_oddlyield(settlement,maturity,last_interest,rate,pr,redemption,freq,basis)
    }
    function fi_tbilleq(args)
    {
        var Convert = Calc.Convert;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var discount = Convert.toDouble(args[2]);
        var dsm;
        if(__compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        if(discount <= 0.0)
            return Calc.Errors.Number;
        dsm = __toOADate(maturity) - __toOADate(settlement);
        if(dsm > 365.0)
            return Calc.Errors.Number;
        var p1 = 365.0 * discount;
        var p2 = 360.0 - discount * dsm;
        if(p2 === 0.0)
            return Calc.Errors.DivideByZero;
        else if(p2 < 0.0)
            return Calc.Errors.Number;
        return Convert.toResult(p1 / p2)
    }
    function fi_tbillyield(args)
    {
        var Convert = Calc.Convert;
        var settlement = Convert.toDateTime(args[0]);
        var maturity = Convert.toDateTime(args[1]);
        var pr = Convert.toDouble(args[2]);
        var dsm;
        if(__compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        if(pr <= 0.0)
            return Calc.Errors.Number;
        dsm = __toOADate(maturity) - __toOADate(settlement);
        if(dsm > 365.0)
            return Calc.Errors.Number;
        return(100.0 - pr) / pr * (360.0 / dsm)
    }
    function __irr_npv(rate, y, vals)
    {
        var n = vals.length;
        var sum = 0.0;
        var f = 1.0;
        var ff = 1.0 / (rate + 1.0);
        var i;
        for(i = 0; i < n; i++)
        {
            sum += vals[i] * f;
            f *= ff
        }
        y = sum;
        return[true,y]
    }
    function __irr_npv_df(rate, y, vals)
    {
        var n = vals.length;
        var sum = 0.0;
        var f = 1.0;
        var ff = 1.0 / (rate + 1.0);
        var i;
        for(i = 1; i < n; i++)
        {
            sum += vals[i] * -i * f;
            f *= ff
        }
        y = sum;
        return[true,y]
    }
    function __irr_goal_seek_newton(data, vals, x0)
    {
        var iterations;
        var precision = data.precision / 2.0;
        for(iterations = 0; iterations < 20; iterations++)
        {
            var x1,
                stepsize;
            var status;
            var y0 = 0.0;
            var df0 = 0.0;
            if(x0 < data.xmin || x0 > data.xmax)
                return[false,data];
            var resultArray = __irr_npv(x0,y0,vals);
            y0 = resultArray[1];
            status = resultArray[0];
            if(!status)
                return[status,data];
            resultArray = __update_data(x0,y0,data);
            data = resultArray[1];
            if(resultArray[0])
                return[true,data];
            resultArray = __irr_npv_df(x0,df0,vals);
            df0 = resultArray[1];
            status = resultArray[0];
            if(!status)
                return[status,data];
            if(df0 === 0)
                return[false,data];
            x1 = x0 - 1.000001 * y0 / df0;
            if(x1 === x0)
            {
                data.root = x0;
                return[true,data]
            }
            stepsize = Math.abs(x1 - x0) / (Math.abs(x0) + Math.abs(x1));
            x0 = x1;
            if(stepsize < precision)
            {
                data.root = x0;
                return[true,data]
            }
        }
        return[false,data]
    }
    function __irr_goal_seek_point(data, vals, x0)
    {
        var status;
        var y0 = 0.0;
        if(x0 < data.xmin || x0 > data.xmax)
            return[false,data];
        var resultArray = __irr_npv(x0,y0,vals);
        y0 = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,data];
        resultArray = __update_data(x0,y0,data);
        data = resultArray[1];
        if(resultArray[0])
            return[true,data];
        return[false,data]
    }
    function _irr_fake_df(x, dfx, xstep, data, vals)
    {
        var xl,
            xr;
        var status;
        var yl = 0.0;
        var yr = 0.0;
        xl = x - xstep;
        if(xl < data.xmin)
            xl = x;
        xr = x + xstep;
        if(xr > data.xmax)
            xr = x;
        if(xl === xr)
            return[false,dfx,data];
        var resultArray = __irr_npv(xl,yl,vals);
        yl = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,dfx,data];
        resultArray = __irr_npv(xr,yr,vals);
        yr = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,dfx,data];
        dfx = (yr - yl) / (xr - xl);
        return[true,dfx,data]
    }
    function __irr_replaceGoto(data, vals, stepsize, newton_submethod, xmid, ymid, status, method)
    {
        switch(method)
        {
            case 0:
                xmid = data.xpos - data.ypos * ((data.xneg - data.xpos) / (data.yneg - data.ypos));
                break;
            case 1:
                var det;
                xmid = (data.xpos + data.xneg) / 2.0;
                var resultArray = __irr_npv(xmid,ymid,vals);
                ymid = resultArray[1];
                status = resultArray[0];
                if(!status)
                    return[null,data,newton_submethod,xmid,ymid,method];
                if(ymid === 0)
                {
                    data = __update_data(xmid,ymid,data)[1];
                    return[true,data,newton_submethod,xmid,ymid,method]
                }
                det = Math.sqrt(ymid * ymid - data.ypos * data.yneg);
                if(det === 0)
                    return[null,data,newton_submethod,xmid,ymid,method];
                xmid += (xmid - data.xpos) * ymid / det;
                break;
            case 3:
                xmid = (data.xpos + data.xneg) / 2.0;
                break;
            case 2:
                var x0,
                    xstep;
                var y0 = 0.0;
                var df0 = 0.0;
                if(stepsize > 0.1)
                {
                    method = 3;
                    return __irr_replaceGoto(data,vals,stepsize,newton_submethod,xmid,ymid,status,method)
                }
                switch(newton_submethod++ % 4)
                {
                    case 0:
                        x0 = data.xpos;
                        x0 = data.ypos;
                        break;
                    case 2:
                        x0 = data.xneg;
                        y0 = data.yneg;
                        break;
                    default:
                    case 3:
                    case 1:
                        x0 = (data.xpos + data.xneg) / 2.0;
                        var resultArray2 = __irr_npv(x0,y0,vals);
                        y0 = resultArray2[1];
                        status = resultArray2[0];
                        if(!status)
                            return[null,data,newton_submethod,xmid,ymid,method];
                        break
                }
                xstep = Math.abs(data.xpos - data.xneg) / 1.0e6;
                var resultArray3 = _irr_fake_df(x0,df0,xstep,data,vals);
                data = resultArray3[2];
                df0 = resultArray3[1];
                status = resultArray3[0];
                if(!status)
                    return[null,data,newton_submethod,xmid,ymid,method];
                if(df0 === 0)
                    return[null,data,newton_submethod,xmid,ymid,method];
                xmid = x0 - 1.01 * y0 / df0;
                if(xmid < data.xpos && xmid < data.xneg || xmid > data.xpos && xmid > data.xneg)
                    return[null,data,newton_submethod,xmid,ymid,method];
                break;
            default:
                return[false,data,newton_submethod,xmid,ymid,method]
        }
        return[false,data,newton_submethod,xmid,ymid,method]
    }
    function __irr_goal_seek_bisection(data, vals)
    {
        var iterations;
        var stepsize;
        var newton_submethod = 0;
        if(!data.havexpos || !data.havexneg)
            return[false,data];
        stepsize = Math.abs(data.xpos - data.xneg) / (Math.abs(data.xpos) + Math.abs(data.xneg));
        for(iterations = 0; iterations < 100 + 15 * 4; iterations++)
        {
            var xmid;
            var ymid = 0.0;
            var status;
            var method = 0;
            method = iterations % 4 === 0 ? 1 : iterations % 4 === 2 ? 2 : 3;
            var resultOfGoto = __irr_replaceGoto(data,vals,stepsize,newton_submethod,xmid,ymid,status,method);
            data = resultOfGoto[1];
            newton_submethod = resultOfGoto[2];
            xmid = resultOfGoto[3];
            ymid = resultOfGoto[4];
            method = resultOfGoto[5];
            if(resultOfGoto[0] === null)
                continue;
            else if(resultOfGoto[0])
                return[true,data];
            var resultArray = __irr_npv(xmid,ymid,vals);
            ymid = resultArray[1];
            status = resultArray[0];
            if(!status)
                continue;
            resultArray = __update_data(xmid,ymid,data);
            data = resultArray[1];
            if(resultArray[0])
                return[true,data];
            stepsize = Math.abs(data.xpos - data.xneg) / (Math.abs(data.xpos) + Math.abs(data.xneg));
            if(stepsize < data.precision)
            {
                if(data.yneg < ymid)
                {
                    ymid = data.yneg;
                    xmid = data.xneg
                }
                if(data.ypos < ymid)
                {
                    ymid = data.ypos;
                    xmid = data.xpos
                }
                data.root = xmid;
                return[true,data]
            }
        }
        return[false,data]
    }
    function fi_irr(args)
    {
        var data = {};
        data.xmin = 0.0;
        data.xmax = 0.0;
        data.precision = 0.0;
        data.havexpos = false;
        data.xpos = 0.0;
        data.ypos = 0.0;
        data.havexneg = false;
        data.xneg = 0.0;
        data.yneg = 0.0;
        data.root = 0.0;
        data = __goal_seek_initialise(data);
        var a1 = args[0];
        var Convert = Calc.Convert,
            _Helper = Calc._Helper,
            _ArrayHelper = Calc._ArrayHelper;
        var guess = _Helper._argumentExists(args,1) ? Convert.toDouble(args[1]) : 0.10;
        if(Math.abs(guess) > 1)
            guess = 0.10;
        var len = _ArrayHelper.getLength(a1);
        if(!_ArrayHelper.isArrayOrReference(a1) || len < 2)
            return Calc.Errors.Number;
        var DOUBLE_MAXVALUE = 1.79769e+308;
        var vals = new Array(len);
        data.xmin = -1;
        data.xmax = Math.min(data.xmax,Math.pow(DOUBLE_MAXVALUE / 1.0e10,1.0 / len) - 1);
        var posVal = false;
        var negVal = false;
        for(var j = 0; j < len; j++)
        {
            var obj = _ArrayHelper.getValueByIndex(a1,j);
            if(Convert.isNumber(obj))
            {
                var dval = Convert.toDouble(obj);
                vals[j] = dval;
                if(dval > 0)
                    posVal = true;
                if(dval < 0)
                    negVal = true
            }
            else if(Convert.isError(obj))
                return obj
        }
        if(!posVal || !negVal)
            return Calc.Errors.Number;
        var resultArray = __irr_goal_seek_newton(data,vals,guess);
        data = resultArray[1];
        var status = resultArray[0];
        if(!status)
        {
            var factor;
            for(factor = 2; !(data.havexneg && data.havexpos) && factor < 100; factor *= 2)
            {
                data = __irr_goal_seek_point(data,vals,guess * factor)[1];
                data = __irr_goal_seek_point(data,vals,guess / factor)[1]
            }
            resultArray = __irr_goal_seek_bisection(data,vals);
            data = resultArray[1];
            status = resultArray[0]
        }
        if(status)
            return data.root;
        else
            return Calc.Errors.Number
    }
    function fi_mirr(args)
    {
        var Convert = Calc.Convert,
            _ArrayHelper = Calc._ArrayHelper;
        var frate = Convert.toDouble(args[1]);
        var rrate = Convert.toDouble(args[2]);
        var pos = 0;
        var neg = 0;
        var n = 0;
        var posnpv = 0.0;
        var negnpv = 0.0;
        var count = _ArrayHelper.getLength(args[0]);
        var vals = new Array(count);
        if(!_ArrayHelper.isArrayOrReference(args[0]))
            return Calc.Errors.DivideByZero;
        var length = _ArrayHelper.getLength(args[0]);
        for(var k = 0; k < length; k++)
        {
            var o = _ArrayHelper.getValueByIndex(args[0],k);
            if(Convert.isNumber(o))
            {
                var val = Convert.toDouble(o);
                vals[k] = val;
                if(val >= 0.0)
                    pos++;
                else
                    neg++
            }
            else if(Convert.isError(o))
                return o
        }
        n = neg + pos;
        for(var i = 0; i < n; i++)
        {
            var v = vals[i];
            if(v >= 0.0)
                posnpv += v / Math.pow(1.0 + rrate,i);
            else
                negnpv += v / Math.pow(1.0 + frate,i)
        }
        if(negnpv === 0.0 || posnpv === 0.0 || rrate <= -1.0)
            return Calc.Errors.DivideByZero;
        var res = Math.pow(-posnpv * Math.pow(1.0 + rrate,n) / (negnpv * (1.0 + rrate)),1.0 / (n - 1.0)) - 1.0;
        return res
    }
    function __xirr_f(rate, y, dates, values)
    {
        var sum = 0.0;
        var n = values.length;
        for(var i = 0; i < n; i++)
        {
            var d = __toOADate(dates[i]) - __toOADate(dates[0]);
            if(d < 0.0)
                return[false,y];
            sum += values[i] / Functions._MathHelper.pow1p(rate,d / 365.0)
        }
        if(!isFinite(sum))
        {
            if(sum === Number.POSITIVE_INFINITY)
                y = 1.79769e+308;
            else if(sum === Number.NEGATIVE_INFINITY)
                y = -1.79769e+308
        }
        else if(isNaN(sum))
            y = 4.94066e-324;
        else
            y = sum;
        return[true,y]
    }
    function __xirr_fake_df(x, dfx, xstep, data, dates, values)
    {
        var xl,
            xr;
        var status;
        var yl = 0.0;
        var yr = 0.0;
        xl = x - xstep;
        if(xl < data.xmin)
            xl = x;
        xr = x + xstep;
        if(xr > data.xmax)
            xr = x;
        if(xl === xr)
            return[false,dfx,data];
        var resultArray = __xirr_f(xl,yl,dates,values);
        yl = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,dfx,data];
        resultArray = __xirr_f(xr,yr,dates,values);
        yr = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,dfx,data];
        dfx = (yr - yl) / (xr - xl);
        return[true,dfx,data]
    }
    function __xirr_goal_seek_newton(data, dates, values, x0)
    {
        var iterations;
        var status;
        var precision = data.precision / 2.0;
        for(iterations = 0; iterations < 20; iterations++)
        {
            var x1,
                stepsize;
            var y0 = 0.0;
            var df0 = 0.0;
            var resultArray = __xirr_f(x0,y0,dates,values);
            y0 = resultArray[1];
            status = resultArray[0];
            if(!status)
                return[status,data];
            resultArray = __update_data(x0,y0,data);
            data = resultArray[1];
            if(resultArray[0])
                return[true,data];
            var xstep;
            if(Math.abs(x0) < 1.0e-10)
                if(data.havexneg && data.havexpos)
                    xstep = Math.abs(data.xpos - data.xneg) / 1.0e6;
                else
                    xstep = (data.xmax - data.xmin) / 1.0e6;
            else
                xstep = Math.abs(x0) / 1.0e6;
            var resultArray3 = __xirr_fake_df(x0,df0,xstep,data,dates,values);
            status = resultArray3[0];
            df0 = resultArray3[1];
            data = resultArray3[2];
            if(!status)
                return[status,data];
            if(df0 === 0)
                return[false,data];
            x1 = x0 - 1.000001 * y0 / df0;
            if(x1 === x0)
            {
                data.root = x0;
                return[true,data]
            }
            stepsize = Math.abs(x1 - x0) / (Math.abs(x0) + Math.abs(x1));
            x0 = x1;
            if(stepsize < precision)
            {
                data.root = x0;
                return[true,data]
            }
        }
        return[false,data]
    }
    function __xirr_goal_seek_point(data, x0, y, dates, values)
    {
        var status;
        var y0 = 0.0;
        if(x0 < data.xmin || x0 > data.xmax)
            return[false,data,y];
        var resultArray = __xirr_f(x0,y,dates,values);
        y = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,data,y];
        resultArray = __update_data(x0,y0,data);
        data = resultArray[1];
        if(resultArray[0])
            return[true,data,y];
        return[false,data,y]
    }
    function __xirr_replaceGoto(data, dates, values, stepsize, newton_submethod, xmid, ymid, status, method)
    {
        switch(method)
        {
            case 0:
                xmid = data.xpos - data.ypos * ((data.xneg - data.xpos) / (data.yneg - data.ypos));
                break;
            case 1:
                var det;
                xmid = (data.xpos + data.xneg) / 2.0;
                var resultArray = __xirr_f(xmid,ymid,dates,values);
                ymid = resultArray[1];
                status = resultArray[0];
                if(!status)
                    return[null,data,newton_submethod,xmid,ymid,method];
                if(ymid === 0.0)
                {
                    data = __update_data(xmid,ymid,data)[1];
                    return[true,data,newton_submethod,xmid,ymid,method]
                }
                det = Math.sqrt(ymid * ymid - data.ypos * data.yneg);
                if(det === 0)
                    return[null,data,newton_submethod,xmid,ymid,method];
                xmid += (xmid - data.xpos) * ymid / det;
                break;
            case 3:
                xmid = (data.xpos + data.xneg) / 2.0;
                break;
            case 2:
                var x0 = 0.0;
                var y0 = 0.0;
                var xstep = 0.0;
                var df0 = 0.0;
                if(stepsize > 0.1)
                {
                    method = 3;
                    return __xirr_replaceGoto(data,dates,values,stepsize,newton_submethod,xmid,ymid,status,method)
                }
                switch(newton_submethod++ % 4)
                {
                    case 0:
                        x0 = data.xpos;
                        x0 = data.ypos;
                        break;
                    case 2:
                        x0 = data.xneg;
                        y0 = data.yneg;
                        break;
                    default:
                    case 3:
                    case 1:
                        x0 = (data.xpos + data.xneg) / 2.0;
                        var resultArray2 = __xirr_f(x0,y0,dates,values);
                        y0 = resultArray2[1];
                        status = resultArray2[0];
                        if(!status)
                            return[null,data,newton_submethod,xmid,ymid,method];
                        break
                }
                xstep = Math.abs(data.xpos - data.xneg) / 1e6;
                var resultArray3 = __xirr_fake_df(x0,df0,xstep,data,dates,values);
                data = resultArray3[2];
                df0 = resultArray3[1];
                status = resultArray3[0];
                if(!status)
                    return[null,data,newton_submethod,xmid,ymid,method];
                if(df0 === 0)
                    return[null,data,newton_submethod,xmid,ymid,method];
                xmid = x0 - 1.01 * y0 / df0;
                if(xmid < data.xpos && xmid < data.xneg || xmid > data.xpos && xmid > data.xneg)
                    return[null,data,newton_submethod,xmid,ymid,method];
                break;
            default:
                break
        }
        return[false,data,newton_submethod,xmid,ymid,method]
    }
    function __xirr_goal_seek_bisection(data, dates, values)
    {
        var iterations;
        var stepsize;
        var newton_submethod = 0;
        if(!data.havexpos || !data.havexneg)
            return[false,data];
        stepsize = Math.abs(data.xpos - data.xneg) / (Math.abs(data.xpos) + Math.abs(data.xneg));
        for(iterations = 0; iterations < 100 + 2 * 4; iterations++)
        {
            var xmid = 0.0;
            var ymid = 0.0;
            var status;
            var method;
            method = iterations % 4 === 0 ? 1 : iterations % 4 === 2 ? 2 : 3;
            var resultOfGoto = __xirr_replaceGoto(data,dates,values,stepsize,newton_submethod,xmid,ymid,status,method);
            data = resultOfGoto[1];
            newton_submethod = resultOfGoto[2];
            xmid = resultOfGoto[3];
            ymid = resultOfGoto[4];
            method = resultOfGoto[5];
            if(!resultOfGoto[0])
                continue;
            else if(resultOfGoto[0])
                return[true,data];
            var resultArray = __xirr_f(xmid,ymid,dates,values);
            ymid = resultArray[1];
            status = resultArray[0];
            if(!status)
                continue;
            resultArray = __update_data(xmid,ymid,data);
            data = resultArray[1];
            if(resultArray[0])
                return[true,data];
            stepsize = Math.abs(data.xpos - data.xneg) / (Math.abs(data.xpos) + Math.abs(data.xneg));
            if(stepsize < data.precision)
            {
                if(data.yneg < ymid)
                {
                    ymid = data.yneg;
                    xmid = data.xneg
                }
                if(data.ypos < ymid)
                {
                    ymid = data.ypos;
                    xmid = data.xpos
                }
                data.root = xmid;
                return[true,data]
            }
        }
        return[false,data]
    }
    function fi_xirr(args)
    {
        var length1 = Calc._ArrayHelper.getLength(args[0]);
        var length2 = Calc._ArrayHelper.getLength(args[1]);
        var values = new Array(length1);
        var dates = new Array(length2);
        var guess = Calc._Helper._argumentExists(args,2) ? Calc.Convert.toDouble(args[2]) : 0.1;
        if(length1 !== length2)
            return Calc.Errors.Number;
        for(var k = 0; k < length1; k++)
            values[k] = Calc.Convert.toDouble(Calc._ArrayHelper.getValueByIndex(args[0],k));
        for(k = 0; k < length2; k++)
            dates[k] = Calc.Convert.toDateTime(Calc._ArrayHelper.getValueByIndex(args[1],k));
        var data = {};
        data.xmin = 0.0;
        data.xmax = 0.0;
        data.precision = 0.0;
        data.havexpos = false;
        data.xpos = 0.0;
        data.ypos = 0.0;
        data.havexneg = false;
        data.xneg = 0.0;
        data.yneg = 0.0;
        data.root = 0.0;
        data = __goal_seek_initialise(data);
        data.xmin = -1;
        data.xmax = Math.min(1000,data.xmax);
        var resultArray = __xirr_goal_seek_newton(data,dates,values,guess);
        data = resultArray[1];
        if(resultArray[0])
            return data.root;
        else
        {
            var i;
            var status;
            for(i = 1; i <= 1024; i += i)
            {
                var ii = Calc.Convert.toDouble(i);
                var y = (-1.0 + 10.0) / (ii + 9.0);
                var resultArray2 = __xirr_goal_seek_point(data,guess,y,dates,values);
                data = resultArray2[1];
                y = resultArray2[2];
                y = ii;
                resultArray2 = __xirr_goal_seek_point(data,guess,y,dates,values);
                data = resultArray2[1];
                y = resultArray2[2];
                var resultArray3 = __xirr_goal_seek_bisection(data,dates,values);
                data = resultArray3[1];
                status = resultArray3[0];
                if(status)
                    return data.root
            }
            return Calc.Errors.Number
        }
    }
    function __get_amordegrc(fCost, nDate, nFirstPer, fRestVal, nPer, fRate, nBase)
    {
        var n;
        var fAmorCoeff,
            fNRate,
            fRest,
            fUsePer;
        fUsePer = 1.0 / fRate;
        if(fUsePer < 3.0)
            fAmorCoeff = 1.0;
        else if(fUsePer < 5.0)
            fAmorCoeff = 1.5;
        else if(fUsePer <= 6.0)
            fAmorCoeff = 2.0;
        else
            fAmorCoeff = 2.5;
        fRate *= fAmorCoeff;
        var o = Functions._DateTimeHelper.yearfrac([nDate,nFirstPer,nBase]);
        if(Calc.Convert.isError(o))
            return 0.0;
        var val = o;
        fNRate = Math.floor(val * fRate * fCost + 0.5);
        fCost -= fNRate;
        fRest = fCost - fRestVal;
        for(n = 0; n < nPer; n++)
        {
            fNRate = Math.floor(fRate * fCost + 0.5);
            fRest -= fNRate;
            if(fRest < 0.0)
                switch(nPer - n)
                {
                    case 0:
                    case 1:
                        return Math.floor(fCost * 0.5 + 0.5);
                    default:
                        return 0.0
                }
            fCost -= fNRate
        }
        return fNRate
    }
    function fi_amordegrc(args)
    {
        var cost = Calc.Convert.toDouble(args[0]);
        var purchased = Calc.Convert.toDateTime(args[1]);
        var firstPer = Calc.Convert.toDateTime(args[2]);
        var salvage = Calc.Convert.toDouble(args[3]);
        var per = Calc.Convert.toInt(args[4]);
        var rate = Calc.Convert.toDouble(args[5]);
        var basis = Calc._Helper._argumentExists(args,6) ? Calc.Convert.toInt(args[6]) : 0;
        var errCheck = 1.0 / rate;
        if(errCheck > 0 && errCheck < 1 || errCheck > 1 && errCheck < 2 || errCheck > 2 && errCheck < 3 || errCheck > 4 && errCheck < 5)
            return Calc.Errors.Number;
        if(__compareDateTime(purchased,firstPer) > 0)
            return Calc.Errors.Number;
        if(basis < 0 || basis > 4 || rate <= 0.0 || basis === 2)
            return Calc.Errors.Number;
        return __get_amordegrc(cost,purchased,firstPer,salvage,per,rate,basis)
    }
    function __get_amorlinc(fCost, nDate, nFirstPer, fRestVal, nPer, fRate, nBase)
    {
        var fOneRate = fCost * fRate;
        var fCostDelta = fCost - fRestVal;
        var o = Functions._DateTimeHelper.yearfrac([nDate,nFirstPer,nBase]);
        if(Calc.Convert.isError(o))
            return 0.0;
        var val = o;
        var f0Rate = val * fRate * fCost;
        var multiplier = (fCost - fRestVal - f0Rate) / fOneRate;
        var nNumOfFullPeriods = Calc.Convert.toInt((fCost - fRestVal - f0Rate) / fOneRate);
        var result;
        if(nPer === 0)
            result = f0Rate;
        else if(nPer <= nNumOfFullPeriods)
            result = fOneRate * (multiplier < 1.0 ? multiplier : 1.0);
        else if(nPer === nNumOfFullPeriods + 1)
            result = fCostDelta - fOneRate * nNumOfFullPeriods - f0Rate;
        else
            result = 0.0;
        return result
    }
    function fi_amorlinc(args)
    {
        var cost = Calc.Convert.toDouble(args[0]);
        var purchased = Calc.Convert.toDateTime(args[1]);
        var firstPer = Calc.Convert.toDateTime(args[2]);
        var salvage = Calc.Convert.toDouble(args[3]);
        var per = Calc.Convert.toInt(args[4]);
        var rate = Calc.Convert.toDouble(args[5]);
        var basis = Calc._Helper._argumentExists(args,6) ? Calc.Convert.toInt(args[6]) : 0;
        if(__compareDateTime(purchased,firstPer) > 0)
            return Calc.Errors.Number;
        if(basis < 0 || basis > 4 || rate <= 0.0 || basis === 2)
            return Calc.Errors.Number;
        return __get_amorlinc(cost,purchased,firstPer,salvage,per,rate,basis)
    }
    function fi_db(args)
    {
        var cost = Calc.Convert.toDouble(args[0]);
        var salvage = Calc.Convert.toDouble(args[1]);
        var life = Calc.Convert.toInt(args[2]);
        var period = Calc.Convert.toInt(args[3]);
        var month = Calc._Helper._argumentExists(args,4) ? Calc.Convert.toInt(args[4]) : 12;
        var lastPeriod = life + (month < 12 ? 1 : 0);
        if(cost < 0.0 || life < 1 || period < 1 || lastPeriod < period || month < 1 || 12 < month)
            return Calc.Errors.Number;
        if(cost === 0.0)
            return 0.0;
        var rate = Functions._MathHelper.round(1.0 - Math.pow(salvage / cost,1.0 / life),3);
        var total = 0.0;
        var result = 0.0;
        for(var i = 1; i <= period; i++)
        {
            if(i === 1)
                result = cost * rate * month / 12.0;
            else if(i === life + 1)
                result = (cost - total) * rate * (12.0 - month) / 12.0;
            else
                result = (cost - total) * rate;
            total += result
        }
        return result
    }
    function fi_ddb(args)
    {
        var cost = Calc.Convert.toDouble(args[0]);
        var salvage = Calc.Convert.toDouble(args[1]);
        var life = Calc.Convert.toInt(args[2]);
        var period = Calc.Convert.toInt(args[3]);
        var factor = Calc._Helper._argumentExists(args,4) ? Calc.Convert.toDouble(args[4]) : 2.0;
        var total = 0.0;
        var result = 0.0;
        if(life <= 0 || cost < 0.0)
            return Calc.Errors.Number;
        if(life < period)
            return Calc.Errors.Number;
        if(factor <= 0.0)
            return Calc.Errors.Number;
        if(period <= 0)
            return Calc.Errors.Number;
        if(cost <= salvage)
            return 0.0;
        for(var i = 1; i <= period; i++)
        {
            result = (cost - total) * (factor / life);
            result = Math.min(result,cost - total - salvage);
            total += result
        }
        return result
    }
    function fi_sln(args)
    {
        var cost = Calc.Convert.toDouble(args[0]);
        var salvage = Calc.Convert.toDouble(args[1]);
        var life = Calc.Convert.toInt(args[2]);
        if(life === 0)
            return Calc.Errors.DivideByZero;
        return(cost - salvage) / life
    }
    function fi_syd(args)
    {
        var cost = Calc.Convert.toDouble(args[0]);
        var salvage = Calc.Convert.toDouble(args[1]);
        var life = Calc.Convert.toInt(args[2]);
        var per = Calc.Convert.toInt(args[3]);
        if(salvage < 0.0 || life < 1 || per <= 0 || per > life)
            return Calc.Errors.Number;
        return(cost - salvage) * (life - per + 1) * 2 / (life * (life + 1))
    }
    function __ScGetGDA(fWert, fRest, fDauer, fPeriode, fFaktor)
    {
        var fGda,
            fZins,
            fAlterWert,
            fNeuerWert;
        fZins = fFaktor / fDauer;
        if(fZins >= 1.0)
        {
            fZins = 1.0;
            if(fPeriode === 1.0)
                fAlterWert = fWert;
            else
                fAlterWert = 0.0
        }
        else
            fAlterWert = fWert * Math.pow(1.0 - fZins,fPeriode - 1.0);
        fNeuerWert = fWert * Math.pow(1.0 - fZins,fPeriode);
        if(fNeuerWert < fRest)
            fGda = fAlterWert - fRest;
        else
            fGda = fAlterWert - fNeuerWert;
        if(fGda < 0.0)
            fGda = 0.0;
        return fGda
    }
    function __ScInterVDB(cost, salvage, life, life1, period, factor)
    {
        var fVdb = 0;
        var fIntEnd = Math.ceil(period);
        var nLoopEnd = Calc.Convert.toInt(fIntEnd);
        var fTerm,
            fLia;
        var fRestwert = cost - salvage;
        var bNowLia = false;
        var fGda;
        var i;
        fLia = 0;
        for(i = 1; i <= nLoopEnd; i++)
        {
            if(!bNowLia)
            {
                fGda = __ScGetGDA(cost,salvage,life,i,factor);
                fLia = fRestwert / (life1 - (i - 1));
                if(fLia > fGda)
                {
                    fTerm = fLia;
                    bNowLia = true
                }
                else
                {
                    fTerm = fGda;
                    fRestwert -= fGda
                }
            }
            else
                fTerm = fLia;
            if(i === nLoopEnd)
                fTerm *= period + 1.0 - fIntEnd;
            fVdb += fTerm
        }
        return fVdb
    }
    function __get_vdb(cost, salvage, life, start_period, end_period, factor, flag)
    {
        var fVdb;
        var fIntStart = Math.floor(start_period);
        var fIntEnd = Math.ceil(end_period);
        var i;
        var nLoopStart = Calc.Convert.toInt(fIntStart);
        var nLoopEnd = Calc.Convert.toInt(fIntEnd);
        fVdb = 0.0;
        if(flag)
            for(i = nLoopStart + 1; i <= nLoopEnd; i++)
            {
                var fTerm;
                fTerm = __ScGetGDA(cost,salvage,life,i,factor);
                if(i === nLoopStart + 1)
                    fTerm *= Math.min(end_period,fIntStart + 1.0) - start_period;
                else if(i === nLoopEnd)
                    fTerm *= end_period + 1.0 - fIntEnd;
                fVdb += fTerm
            }
        else
        {
            var life1 = life;
            var fPart;
            if(start_period !== Math.floor(start_period))
                if(factor > 1.0)
                    if(start_period >= life / 2.0)
                    {
                        fPart = start_period - life / 2.0;
                        start_period = life / 2.0;
                        end_period -= fPart;
                        life1 += 1.0
                    }
            cost -= __ScInterVDB(cost,salvage,life,life1,start_period,factor);
            fVdb = __ScInterVDB(cost,salvage,life,life - start_period,end_period - start_period,factor)
        }
        return fVdb
    }
    function fi_vdb(args)
    {
        var cost = Calc.Convert.toDouble(args[0]);
        var salvage = Calc.Convert.toDouble(args[1]);
        var life = Calc.Convert.toInt(args[2]);
        var start = Calc.Convert.toDouble(args[3]);
        var end = Calc.Convert.toDouble(args[4]);
        var factor = Calc._Helper._argumentExists(args,5) ? Calc.Convert.toDouble(args[5]) : 2.0;
        var noswitch = Calc._Helper._argumentExists(args,6) ? Calc.Convert.toBool(args[6]) : false;
        if(cost < 0.0 || salvage < 0.0 || life < 0 || start < 0 || end < 0 || end < start)
            return Calc.Errors.Number;
        if(cost < salvage && start === 0.0 && end === 1.0)
            return cost - salvage;
        return __get_vdb(cost,salvage,life,start,end,factor,noswitch)
    }
    function fi_accrint(args)
    {
        var maturity = Calc.Convert.toDateTime(args[0]);
        var first_interest = Calc.Convert.toDateTime(args[1]);
        var settlement = Calc.Convert.toDateTime(args[2]);
        var rate = Calc.Convert.toDouble(args[3]);
        var par = Calc._Helper._argumentExists(args,4) ? Calc.Convert.toDouble(args[4]) : 1000.0;
        var freq = Calc.Convert.toInt(args[5]);
        var basis = Calc._Helper._argumentExists(args,6) ? Calc.Convert.toInt(args[6]) : 0;
        var calc_method = Calc._Helper._argumentExists(args,7) ? Calc.Convert.toBool(args[7]) : true;
        var a,
            d,
            coefficient,
            x;
        if(rate <= 0.0 || par <= 0.0)
            return Calc.Errors.Number;
        if(basis < 0 || 4 < basis)
            return Calc.Errors.Number;
        if(!(freq === 1 || freq === 2 || freq === 4))
            return Calc.Errors.Number;
        if(__compareDateTime(maturity,settlement) >= 0)
            return Calc.Errors.Number;
        a = __days_monthly_basis(maturity,settlement,basis);
        d = __annual_year_basis(maturity,basis);
        if(a < 0 || d <= 0)
            return Calc.Errors.Number;
        coefficient = par * rate / Calc.Convert.toDouble(freq);
        x = a / d;
        return coefficient * Calc.Convert.toDouble(freq) * x
    }
    function fi_accrintm(args)
    {
        var issue = Calc.Convert.toDateTime(args[0]);
        var maturity = Calc.Convert.toDateTime(args[1]);
        var rate = Calc.Convert.toDouble(args[2]);
        var par = Calc._Helper._argumentExists(args,3) ? Calc.Convert.toDouble(args[3]) : 1000.0;
        var basis = Calc._Helper._argumentExists(args,4) ? Calc.Convert.toInt(args[4]) : 0;
        var a,
            d;
        if(rate <= 0.0 || par <= 0.0 || basis < 0 || 4 < basis)
            return Calc.Errors.Number;
        if(__compareDateTime(issue,maturity) > 0)
            return Calc.Errors.Number;
        a = __days_monthly_basis(issue,maturity,basis);
        d = __annual_year_basis(issue,basis);
        if(a < 0 || d <= 0)
            return Calc.Errors.Number;
        return par * rate * a / d
    }
    function fi_disc(args)
    {
        var settlement = Calc.Convert.toDateTime(args[0]);
        var maturity = Calc.Convert.toDateTime(args[1]);
        var par = Calc.Convert.toDouble(args[2]);
        var redemption = Calc.Convert.toDouble(args[3]);
        var basis = Calc._Helper._argumentExists(args,4) ? Calc.Convert.toInt(args[4]) : 0;
        var dsm,
            b;
        if(__compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        if(par <= 0.0 || redemption <= 0.0 || basis < 0 || 4 < basis)
            return Calc.Errors.Number;
        b = __annual_year_basis(settlement,basis);
        dsm = __days_monthly_basis(settlement,maturity,basis);
        if(dsm <= 0 || b <= 0)
            return Calc.Errors.Number;
        return(redemption - par) / redemption * (b / dsm)
    }
    function fi_effect(args)
    {
        var rate = Calc.Convert.toDouble(args[0]);
        var nper = Calc.Convert.toInt(args[1]);
        if(rate <= 0.0 || nper < 1)
            return Calc.Errors.Number;
        return Math.pow(1.0 + rate / nper,nper) - 1.0
    }
    function fi_intrate(args)
    {
        var settlement = Calc.Convert.toDateTime(args[0]);
        var maturity = Calc.Convert.toDateTime(args[1]);
        var investment = Calc.Convert.toDouble(args[2]);
        var redemption = Calc.Convert.toDouble(args[3]);
        var basis = Calc._Helper._argumentExists(args,4) ? Calc.Convert.toInt(args[4]) : 0;
        var a,
            d;
        if(__compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        if(investment <= 0.0 || redemption <= 0.0 || basis < 0 || 4 < basis)
            return Calc.Errors.Number;
        a = __days_monthly_basis(settlement,maturity,basis);
        d = __annual_year_basis(settlement,basis);
        if(a <= 0 || d <= 0)
            return Calc.Errors.Number;
        return(redemption - investment) / investment * (d / a)
    }
    function fi_nominal(args)
    {
        var rate = Calc.Convert.toDouble(args[0]);
        var nper = Calc.Convert.toInt(args[1]);
        if(rate <= 0.0 || nper < 1)
            return Calc.Errors.Number;
        return nper * (Math.pow(1.0 + rate,1.0 / nper) - 1.0)
    }
    function __rate_f(rate, y, user_data)
    {
        if(rate > -1.0 && rate !== 0)
        {
            var data = user_data;
            var x = Math.pow(1.0 + rate,data.nper);
            var z = (Math.pow(1.0 + rate,data.nper) - 1.0) / rate;
            y = data.pv * x + data.pmt * (1.0 + rate * data.type) * z + data.fv;
            return[true,y,user_data]
        }
        else
            return[false,y,user_data]
    }
    function __rate_df(rate, y, user_data)
    {
        if(rate > -1.0 && rate !== 0.0)
        {
            var data = user_data;
            var x = Math.pow(1.0 + rate,data.nper - 1.0);
            var z = (Math.pow(1.0 + rate,data.nper) - 1.0) / rate;
            y = -data.pmt * z / rate + x * data.nper * (data.pv + data.pmt * (data.type + 1.0 / rate));
            return[true,y,user_data]
        }
        else
            return[false,y,user_data]
    }
    function __rate_goal_seek_newton(data, user_data, x0)
    {
        var iterations;
        var precision = data.precision / 2.0;
        for(iterations = 0; iterations < 100; iterations++)
        {
            var x1,
                stepsize;
            var status;
            var y0 = 0.0;
            var df0 = 0.0;
            if(x0 < data.xmin || x0 > data.xmax)
                return[false,data,user_data];
            var resultArray = __rate_f(x0,y0,user_data);
            user_data = resultArray[2];
            y0 = resultArray[1];
            status = resultArray[0];
            if(!status)
                return[status,data,user_data];
            var resultArray2 = __update_data(x0,y0,data);
            data = resultArray2[1];
            if(resultArray2[0])
                return[true,data,user_data];
            resultArray = __rate_df(x0,df0,user_data);
            user_data = resultArray[2];
            df0 = resultArray[1];
            status = resultArray[0];
            if(!status)
                return[status,data,user_data];
            if(df0 === 0)
                return[false,data,user_data];
            x1 = x0 - 1.000001 * y0 / df0;
            if(x1 === x0)
            {
                data.root = x0;
                return[true,data,user_data]
            }
            stepsize = Math.abs(x1 - x0) / (Math.abs(x0) + Math.abs(x1));
            x0 = x1;
            if(stepsize < precision)
            {
                data.root = x0;
                return[true,data,user_data]
            }
        }
        return[false,data,user_data]
    }
    function __rate_goal_seek_point(data, user_data, x0)
    {
        var status;
        var y0 = 0.0;
        if(x0 < data.xmin || x0 > data.xmax)
            return[false,data,user_data];
        var resultArray = __rate_f(x0,y0,user_data);
        user_data = resultArray[2];
        y0 = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,data,user_data];
        var resultArray2 = __update_data(x0,y0,data);
        data = resultArray2[1];
        if(resultArray2[0])
            return[true,data,user_data];
        return[false,data,user_data]
    }
    function __rate_fake_df(x, dfx, xstep, data, user_data)
    {
        var xl,
            xr;
        var status;
        var yl = 0.0;
        var yr = 0.0;
        xl = x - xstep;
        if(xl < data.xmin)
            xl = x;
        xr = x + xstep;
        if(xr > data.xmax)
            xr = x;
        if(xl === xr)
            return[false,dfx,data,user_data];
        var resultArray = __rate_f(xl,yl,user_data);
        user_data = resultArray[2];
        yl = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,dfx,data,user_data];
        resultArray = __rate_f(xr,yr,user_data);
        user_data = resultArray[2];
        yr = resultArray[1];
        status = resultArray[0];
        if(!status)
            return[status,dfx,data,user_data];
        dfx = (yr - yl) / (xr - xl);
        return[true,dfx,data,user_data]
    }
    function __rate_replaceGoto(data, user_data, stepsize, newton_submethod, xmid, ymid, status, method)
    {
        switch(method)
        {
            case 0:
                xmid = data.xpos - data.ypos * ((data.xneg - data.xpos) / (data.yneg - data.ypos));
                break;
            case 1:
                var det;
                xmid = (data.xpos + data.xneg) / 2.0;
                var resultArray = __rate_f(xmid,ymid,user_data);
                user_data = resultArray[2];
                ymid = resultArray[1];
                status = resultArray[0];
                if(!status)
                    return[null,data,user_data,newton_submethod,xmid,ymid,method];
                if(ymid === 0)
                {
                    data = __update_data(xmid,ymid,data)[1];
                    return[true,data,user_data,newton_submethod,xmid,ymid,method]
                }
                det = Math.sqrt(ymid * ymid - data.ypos * data.yneg);
                if(det === 0)
                    return[null,data,user_data,newton_submethod,xmid,ymid,method];
                xmid += (xmid - data.xpos) * ymid / det;
                break;
            case 3:
                xmid = (data.xpos + data.xneg) / 2.0;
                break;
            case 2:
                var x0,
                    xstep;
                var y0 = 0.0;
                var df0 = 0.0;
                if(stepsize > 0.1)
                {
                    method = 3;
                    return __rate_replaceGoto(data,user_data,stepsize,newton_submethod,xmid,ymid,status,method)
                }
                switch(newton_submethod++ % 4)
                {
                    case 0:
                        x0 = data.xpos;
                        x0 = data.ypos;
                        break;
                    case 2:
                        x0 = data.xneg;
                        y0 = data.yneg;
                        break;
                    default:
                    case 3:
                    case 1:
                        x0 = (data.xpos + data.xneg) / 2.0;
                        resultArray = __rate_f(x0,y0,user_data);
                        user_data = resultArray[2];
                        y0 = resultArray[1];
                        status = resultArray[0];
                        if(!status)
                            return[null,data,user_data,newton_submethod,xmid,ymid,method];
                        break
                }
                xstep = Math.abs(data.xpos - data.xneg) / 1.0e6;
                var resultArray4 = __rate_fake_df(x0,df0,xstep,data,user_data);
                user_data = resultArray4[3];
                data = resultArray4[2];
                df0 = resultArray4[1];
                status = resultArray4[0];
                if(!status)
                    return[null,data,user_data,newton_submethod,xmid,ymid,method];
                if(df0 === 0)
                    return[null,data,user_data,newton_submethod,xmid,ymid,method];
                xmid = x0 - 1.01 * y0 / df0;
                if(xmid < data.xpos && xmid < data.xneg || xmid > data.xpos && xmid > data.xneg)
                    return[null,data,user_data,newton_submethod,xmid,ymid,method];
                break;
            default:
                return[false,data,user_data,newton_submethod,xmid,ymid,method]
        }
        return[false,data,user_data,newton_submethod,xmid,ymid,method]
    }
    function __rate_goal_seek_bisection(data, user_data)
    {
        var iterations;
        var stepsize;
        var newton_submethod = 0;
        if(!data.havexpos || !data.havexneg)
            return[false,data,user_data];
        stepsize = Math.abs(data.xpos - data.xneg) / (Math.abs(data.xpos) + Math.abs(data.xneg));
        for(iterations = 0; iterations < 100 + 15 * 4; iterations++)
        {
            var xmid;
            var ymid = 0.0;
            var status;
            var method = 0;
            method = iterations % 4 === 0 ? 1 : iterations % 4 === 2 ? 2 : 3;
            var resultOfGoto = __rate_replaceGoto(data,user_data,stepsize,newton_submethod,xmid,ymid,status,method);
            data = resultOfGoto[1];
            user_data = resultOfGoto[2];
            newton_submethod = resultOfGoto[3];
            xmid = resultOfGoto[4];
            ymid = resultOfGoto[5];
            method = resultOfGoto[6];
            if(!resultOfGoto[0])
                continue;
            else if(resultOfGoto[0])
                return[true,data,user_data];
            var resultArray = __rate_f(xmid,ymid,user_data);
            user_data = resultArray[2];
            ymid = resultArray[1];
            status = resultArray[0];
            if(!status)
                continue;
            var resultArray2 = __update_data(xmid,ymid,data);
            data = resultArray2[1];
            if(resultArray2[0])
                return[true,data,user_data];
            stepsize = Math.abs(data.xpos - data.xneg) / (Math.abs(data.xpos) + Math.abs(data.xneg));
            if(stepsize < data.precision)
            {
                if(data.yneg < ymid)
                {
                    ymid = data.yneg;
                    xmid = data.xneg
                }
                if(data.ypos < ymid)
                {
                    ymid = data.ypos;
                    xmid = data.xpos
                }
                data.root = xmid;
                return[true,data,user_data]
            }
        }
        return[false,data,user_data]
    }
    function fi_rate(args)
    {
        var data = {};
        var nper = Calc.Convert.toDouble(args[0]);
        var pmt = Calc.Convert.toDouble(args[1]);
        var pv = Calc.Convert.toDouble(args[2]);
        var fv = Calc._Helper._argumentExists(args,3) ? Calc.Convert.toDouble(args[3]) : 0.0;
        var type = Calc._Helper._argumentExists(args,4) ? Calc.Convert.toInt(args[4]) : 0;
        var guess = Calc._Helper._argumentExists(args,5) ? Calc.Convert.toDouble(args[5]) : 0.1;
        if(nper <= 0)
            return Calc.Errors.Number;
        if(type < 0)
            return Calc.Errors.Value;
        if(type > 1)
            type = 1;
        data.xmin = 0.0;
        data.xmax = 0.0;
        data.precision = 0.0;
        data.havexpos = false;
        data.xpos = 0.0;
        data.ypos = 0.0;
        data.havexneg = false;
        data.xneg = 0.0;
        data.yneg = 0.0;
        data.root = 0.0;
        data = __goal_seek_initialise(data);
        data.xmin = Math.max(data.xmin,-Math.pow(1.7976931348623158e+308 / 1.0e10,1.0 / nper) + 1.0);
        data.xmax = Math.min(data.xmax,Math.pow(1.7976931348623158e+308 / 1.0e10,1.0 / nper) - 1.0);
        var udata = {};
        udata.nper = nper;
        udata.pmt = pmt;
        udata.pv = pv;
        udata.fv = fv;
        udata.type = Calc.Convert.toInt(type);
        var resultArray = __rate_goal_seek_newton(data,udata,guess);
        udata = resultArray[2];
        data = resultArray[1];
        var status = resultArray[0];
        if(!status)
        {
            var factor;
            for(factor = 2; !(data.havexneg && data.havexpos) && factor < 100; factor *= 2)
            {
                resultArray = __rate_goal_seek_point(data,udata,guess * factor);
                udata = resultArray[2];
                data = resultArray[1];
                resultArray = __rate_goal_seek_point(data,udata,guess / factor);
                udata = resultArray[2];
                data = resultArray[1]
            }
            resultArray = __rate_goal_seek_bisection(data,udata);
            udata = resultArray[2];
            data = resultArray[1];
            status = resultArray[0]
        }
        if(status)
            return data.root;
        else
            return Calc.Errors.Number
    }
    function fi_dollarde(args)
    {
        var fractionalDollar = Calc.Convert.toDouble(args[0]);
        var fraction = Calc.Convert.toInt(args[1]);
        if(fraction === 0.0)
            return Calc.Errors.DivideByZero;
        if(fraction < 0.0)
            return Calc.Errors.Number;
        var integerPart = fractionalDollar < 0.0 ? Math.ceil(fractionalDollar) : Math.floor(fractionalDollar);
        var decimalPart = fractionalDollar - integerPart;
        var power10 = Math.pow(10.0,Math.ceil(Functions._MathHelper.log10(fraction)));
        return Calc.Convert.toResult(integerPart + decimalPart * power10 / fraction)
    }
    function fi_dollarfr(args)
    {
        var decimalDollar = Calc.Convert.toDouble(args[0]);
        var fraction = Calc.Convert.toInt(args[1]);
        if(fraction === 0.0)
            return Calc.Errors.DivideByZero;
        if(fraction < 0.0)
            return Calc.Errors.Number;
        var integerPart = decimalDollar < 0.0 ? Math.ceil(decimalDollar) : Math.floor(decimalDollar);
        var decimalPart = decimalDollar - integerPart;
        var power10 = Math.pow(10.0,Math.ceil(Functions._MathHelper.log10(fraction)));
        return Calc.Convert.toResult(integerPart + decimalPart * fraction / power10)
    }
    function fi_price(args)
    {
        var settlement = Calc.Convert.toDateTime(args[0]);
        var maturity = Calc.Convert.toDateTime(args[1]);
        var rate = Calc.Convert.toDouble(args[2]);
        var yieldVar = Calc.Convert.toDouble(args[3]);
        var redem = Calc.Convert.toDouble(args[4]);
        var frequency = Calc.Convert.toInt(args[5]);
        var basis = Calc._Helper._argumentExists(args,6) ? Calc.Convert.toInt(args[6]) : 0;
        if(yieldVar < 0.0 || rate < 0.0 || redem === 0.0)
            return Calc.Errors.Number;
        if(basis < 0 || basis > 4)
            return Calc.Errors.Number;
        if(frequency !== 1 && frequency !== 2 && frequency !== 4)
            return Calc.Errors.Number;
        if(__compareDateTime(settlement,maturity) > 0)
            return Calc.Errors.Number;
        return __price(settlement,maturity,rate,yieldVar,redem,frequency,basis)
    }
    function fi_pricedisc(args)
    {
        var settlement = Calc.Convert.toDateTime(args[0]);
        var maturity = Calc.Convert.toDateTime(args[1]);
        var discount = Calc.Convert.toDouble(args[2]);
        var redemption = Calc.Convert.toDouble(args[3]);
        var basis = Calc._Helper._argumentExists(args,4) ? Calc.Convert.toInt(args[4]) : 0;
        var a,
            d;
        if(__compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        if(discount <= 0.0 || redemption <= 0.0 || basis < 0 || 4 < basis)
            return Calc.Errors.Number;
        a = __days_monthly_basis(settlement,maturity,basis);
        d = __annual_year_basis(settlement,basis);
        if(a <= 0 || d <= 0)
            return Calc.Errors.Number;
        return redemption - discount * redemption * a / d
    }
    function fi_pricemat(args)
    {
        var settlement = Calc.Convert.toDateTime(args[0]);
        var maturity = Calc.Convert.toDateTime(args[1]);
        var issue = Calc.Convert.toDateTime(args[2]);
        var rate = Calc.Convert.toDouble(args[3]);
        var yld = Calc.Convert.toDouble(args[4]);
        var basis = Calc._Helper._argumentExists(args,5) ? Calc.Convert.toInt(args[5]) : 0;
        var a,
            b,
            dsm,
            dim,
            n;
        if(__compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        if(rate < 0.0 || yld < 0.0 || basis < 0 || 4 < basis)
            return Calc.Errors.Number;
        dsm = __days_monthly_basis(settlement,maturity,basis);
        dim = __days_monthly_basis(issue,maturity,basis);
        a = __days_monthly_basis(issue,settlement,basis);
        b = __annual_year_basis(settlement,basis);
        if(a <= 0.0 || b <= 0.0 || dsm <= 0.0 || dim <= 0.0)
            return Calc.Errors.Number;
        n = 1.0 + dsm / b * yld;
        if(n === 0.0)
            return Calc.Errors.Number;
        return(100.0 + dim / b * rate * 100.0) / n - a / b * rate * 100.0
    }
    function fi_oddfprice(args)
    {
        var settlement = Calc.Convert.toDateTime(args[0]);
        var maturity = Calc.Convert.toDateTime(args[1]);
        var issue = Calc.Convert.toDateTime(args[2]);
        var first_coupon = Calc.Convert.toDateTime(args[3]);
        var rate = Calc.Convert.toDouble(args[4]);
        var yieldVar = Calc.Convert.toDouble(args[5]);
        var redemption = Calc.Convert.toDouble(args[6]);
        var freq = Calc.Convert.toInt(args[7]);
        var basis = Calc._Helper._argumentExists(args,8) ? Calc.Convert.toInt(args[8]) : 0;
        if(basis < 0 || basis > 4 || !(freq === 1 || freq === 2 || freq === 4) || __compareDateTime(issue,settlement) > 0 || __compareDateTime(settlement,first_coupon) > 0 || __compareDateTime(first_coupon,maturity) > 0)
            return Calc.Errors.Number;
        if(rate < 0.0 || yieldVar < 0.0 || redemption <= 0.0)
            return Calc.Errors.Number;
        return __calc_oddfprice(settlement,maturity,issue,first_coupon,rate,yieldVar,redemption,freq,basis)
    }
    function __calc_oddlprice(settlement, maturity, last_interest, rate, yieldParam, redemption, freq, basis)
    {
        var d = new Date(last_interest.getFullYear(),last_interest.getMonth(),last_interest.getDate());
        var x1,
            x2,
            x3;
        do
            d.setMonth(d.getMonth() + 12 / freq);
        while(__compareDateTime(d,maturity) < 0);
        x1 = __date_ratio(last_interest,settlement,d,freq,basis);
        x2 = __date_ratio(last_interest,maturity,d,freq,basis);
        x3 = __date_ratio(settlement,maturity,d,freq,basis);
        return(redemption * freq + 100 * rate * (x2 - x1 * (1 + yieldParam * x3 / freq))) / (yieldParam * x3 + freq)
    }
    function fi_oddlprice(args)
    {
        var settlement = Calc.Convert.toDateTime(args[0]);
        var maturity = Calc.Convert.toDateTime(args[1]);
        var last_interest = Calc.Convert.toDateTime(args[2]);
        var rate = Calc.Convert.toDouble(args[3]);
        var yieldVar = Calc.Convert.toDouble(args[4]);
        var redemption = Calc.Convert.toDouble(args[5]);
        var freq = Calc.Convert.toInt(args[6]);
        var basis = Calc._Helper._argumentExists(args,7) ? Calc.Convert.toInt(args[7]) : 0;
        if(basis < 0 || basis > 4 || !(freq === 1 || freq === 2 || freq === 4) || __compareDateTime(settlement,maturity) > 0 || __compareDateTime(last_interest,settlement) > 0)
            return Calc.Errors.Number;
        if(rate < 0.0 || yieldVar < 0.0 || redemption <= 0.0)
            return Calc.Errors.Number;
        return __calc_oddlprice(settlement,maturity,last_interest,rate,yieldVar,redemption,freq,basis)
    }
    function fi_tbillprice(args)
    {
        var settlement = Calc.Convert.toDateTime(args[0]);
        var maturity = Calc.Convert.toDateTime(args[1]);
        var discount = Calc.Convert.toDouble(args[2]);
        var dsm;
        if(__compareDateTime(settlement,maturity) >= 0)
            return Calc.Errors.Number;
        if(discount <= 0.0)
            return Calc.Errors.Number;
        dsm = __toOADate(maturity) - __toOADate(settlement);
        if(dsm > 365.0)
            return Calc.Errors.Number;
        return 100.0 * (1.0 - discount * dsm / 360.0)
    }
    function fi_euro(args)
    {
        var val = Calc.Convert.toString(args[0]);
        var v = __one_euro(val,2);
        if(v >= 0)
            return v;
        else
            return Calc.Errors.Number
    }
    function fi_euroconvert(args)
    {
        var n = Calc.Convert.toDouble(args[0]);
        var str1 = Calc.Convert.toString(args[1]);
        var str2 = Calc.Convert.toString(args[2]);
        var fullprec = Calc._Helper._argumentExists(args,3) ? Calc.Convert.toBool(args[3]) : false;
        var calcPrec = Calc._Helper._argumentExists(args,4) ? Calc.Convert.toInt(args[4]) : 3;
        var dispPrec = 0;
        if(calcPrec < 3)
            return Calc.Errors.Value;
        if(!fullprec)
            dispPrec = __displayPrecision(str2);
        if(!Calc._Helper._argumentExists(args,4))
            calcPrec = __calcPrecision(str1);
        var ret = 0.0;
        var c1 = __one_euro(str1,calcPrec);
        var c2 = __one_euro(str2,calcPrec);
        if(c1 >= 0.0 && c2 >= 0.0)
            ret = n * c2 / c1;
        else
            return Calc.Errors.Value;
        if(!fullprec)
            ret = Functions._MathHelper.round(ret,dispPrec);
        return ret
    }
    def("FV",fi_fv,{
        minArgs: 3,
        maxArgs: 5,
        acceptsMissingArgument: acceptsThreeFour
    });
    def("FVSCHEDULE",fi_fvschedule,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsOne,
        acceptsReference: acceptsOne
    });
    def("NPV",fi_npv,{
        minArgs: 2,
        acceptsArray: acceptsPositive,
        acceptsReference: acceptsPositive
    });
    def("PV",fi_pv,{
        minArgs: 3,
        maxArgs: 5,
        acceptsMissingArgument: acceptsThreeFour
    });
    def("RECEIVED",fi_received,{
        minArgs: 4,
        maxArgs: 5,
        acceptsMissingArgument: acceptsFour
    });
    def("XNPV",fi_xnpv,{
        minArgs: 3,
        maxArgs: 3,
        acceptsArray: acceptsNotZero,
        acceptsReference: acceptsNotZero
    });
    def("CUMIPMT",fi_cumipmt,{
        minArgs: 6,
        maxArgs: 6
    });
    def("CUMPRINC",fi_cumprinc,{
        minArgs: 6,
        maxArgs: 6
    });
    def("IPMT",fi_ipmt,{
        minArgs: 4,
        maxArgs: 6,
        acceptsMissingArgument: acceptsFourFive
    });
    def("ISPMT",fi_ispmt,{
        minArgs: 4,
        maxArgs: 4
    });
    def("PMT",fi_pmt,{
        minArgs: 3,
        maxArgs: 5,
        acceptsMissingArgument: acceptsThreeFour
    });
    def("PPMT",fi_ppmt,{
        minArgs: 4,
        maxArgs: 6,
        acceptsMissingArgument: acceptsFourFive
    });
    def("COUPDAYBS",fi_coupdaybsFunc,{
        minArgs: 3,
        maxArgs: 4,
        acceptsMissingArgument: acceptsThree
    });
    def("COUPDAYS",fi_coupdaysFunc,{
        minArgs: 3,
        maxArgs: 4,
        acceptsMissingArgument: acceptsThree
    });
    def("COUPDAYSNC",fi_coupdaysncFunc,{
        minArgs: 3,
        maxArgs: 4,
        acceptsMissingArgument: acceptsThree
    });
    def("COUPNCD",fi_coupncdFunc,{
        minArgs: 3,
        maxArgs: 4,
        acceptsMissingArgument: acceptsThree
    });
    def("COUPNUM",fi_coupnumFunc,{
        minArgs: 3,
        maxArgs: 4,
        acceptsMissingArgument: acceptsThree
    });
    def("COUPPCD",fi_couppcdFunc,{
        minArgs: 3,
        maxArgs: 4,
        acceptsMissingArgument: acceptsThree
    });
    def("DURATION",fi_durationFunc,{
        minArgs: 5,
        maxArgs: 6,
        acceptsMissingArgument: acceptsFive
    });
    def("MDURATION",fi_mduration,{
        minArgs: 5,
        maxArgs: 6,
        acceptsMissingArgument: acceptsFive
    });
    def("NPER",fi_nper,{
        minArgs: 3,
        maxArgs: 5,
        acceptsMissingArgument: acceptsThreeFour
    });
    def("YIELD",fi_yieldFunc,{
        minArgs: 6,
        maxArgs: 7,
        acceptsMissingArgument: acceptsSix
    });
    def("YIELDDISC",fi_yielddisc,{
        minArgs: 4,
        maxArgs: 5,
        acceptsMissingArgument: acceptsFour
    });
    def("YIELDMAT",fi_yieldmat,{
        minArgs: 5,
        maxArgs: 6,
        acceptsMissingArgument: acceptsFive
    });
    def("ODDFYIELD",fi_oddfyield,{
        minArgs: 8,
        maxArgs: 9,
        acceptsMissingArgument: acceptsEight
    });
    def("ODDLYIELD",fi_oddlyield,{
        minArgs: 7,
        maxArgs: 8,
        acceptsMissingArgument: acceptsSeven
    });
    def("TBILLEQ",fi_tbilleq,{
        minArgs: 3,
        maxArgs: 3
    });
    def("TBILLYIELD",fi_tbillyield,{
        minArgs: 3,
        maxArgs: 3
    });
    def("IRR",fi_irr,{
        minArgs: 1,
        maxArgs: 2,
        acceptsMissingArgument: acceptsOne,
        acceptsArray: acceptsZero,
        acceptsReference: acceptsZero
    });
    def("MIRR",fi_mirr,{
        minArgs: 3,
        maxArgs: 3,
        acceptsArray: acceptsZero,
        acceptsReference: acceptsZero
    });
    def("XIRR",fi_xirr,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsTwo,
        acceptsArray: acceptsNotTwo,
        acceptsReference: acceptsNotTwo
    });
    def("AMORDEGRC",fi_amordegrc,{
        minArgs: 6,
        maxArgs: 7,
        acceptsMissingArgument: acceptsSix
    });
    def("AMORLINC",fi_amorlinc,{
        minArgs: 6,
        maxArgs: 7,
        acceptsMissingArgument: acceptsSix
    });
    def("DB",fi_db,{
        minArgs: 4,
        maxArgs: 5,
        acceptsMissingArgument: acceptsFour
    });
    def("DDB",fi_ddb,{
        minArgs: 4,
        maxArgs: 5,
        acceptsMissingArgument: acceptsFour
    });
    def("SLN",fi_sln,{
        minArgs: 3,
        maxArgs: 3
    });
    def("SYD",fi_syd,{
        minArgs: 4,
        maxArgs: 4
    });
    def("VDB",fi_vdb,{
        minArgs: 5,
        maxArgs: 7,
        acceptsMissingArgument: acceptsFiveSix
    });
    def("ACCRINT",fi_accrint,{
        minArgs: 6,
        maxArgs: 8,
        acceptsMissingArgument: acceptsFourSixSeven
    });
    def("ACCRINTM",fi_accrintm,{
        minArgs: 3,
        maxArgs: 5,
        acceptsMissingArgument: acceptsThreeFour
    });
    def("DISC",fi_disc,{
        minArgs: 4,
        maxArgs: 5,
        acceptsMissingArgument: acceptsFour
    });
    def("EFFECT",fi_effect,{
        minArgs: 2,
        maxArgs: 2
    });
    def("INTRATE",fi_intrate,{
        minArgs: 4,
        maxArgs: 5,
        acceptsMissingArgument: acceptsFour
    });
    def("NOMINAL",fi_nominal,{
        minArgs: 2,
        maxArgs: 2
    });
    def("RATE",fi_rate,{
        minArgs: 3,
        maxArgs: 6,
        acceptsMissingArgument: acceptsThreeFourFive
    });
    def("DOLLARDE",fi_dollarde,{
        minArgs: 2,
        maxArgs: 2
    });
    def("DOLLARFR",fi_dollarfr,{
        minArgs: 2,
        maxArgs: 2
    });
    def("PRICE",fi_price,{
        minArgs: 6,
        maxArgs: 7,
        acceptsMissingArgument: acceptsSix
    });
    def("PRICEDISC",fi_pricedisc,{
        minArgs: 4,
        maxArgs: 5,
        acceptsMissingArgument: acceptsFour
    });
    def("PRICEMAT",fi_pricemat,{
        minArgs: 5,
        maxArgs: 6,
        acceptsMissingArgument: acceptsFive
    });
    def("ODDFPRICE",fi_oddfprice,{
        minArgs: 8,
        maxArgs: 9,
        acceptsMissingArgument: acceptsEight
    });
    def("ODDLPRICE",fi_oddlprice,{
        minArgs: 7,
        maxArgs: 8,
        acceptsMissingArgument: acceptsSeven
    });
    def("TBILLPRICE",fi_tbillprice,{
        minArgs: 3,
        maxArgs: 3
    });
    def("EURO",fi_euro,{
        minArgs: 1,
        maxArgs: 1
    });
    def("EUROCONVERT",fi_euroconvert,{
        minArgs: 3,
        maxArgs: 5,
        acceptsMissingArgument: acceptsThreeFour
    })
})(window);
(function(window, $)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        window.GrapeCity = GrapeCity = {};
    if(typeof GrapeCity.Calc === "undefined")
        GrapeCity.Calc = {};
    var Calc = GrapeCity.Calc;
    if(typeof Calc.Functions === "undefined")
        Calc.Functions = {};
    var Functions = Calc.Functions;
    Functions._builtInFunctions = Functions._builtInFunctions || {};
    if(typeof Functions._defineBuildInFunction === 'undefined')
        Functions._defineBuildInFunction = function(name, fnEvaluate, options)
        {
            if(!name)
                throw"Invalid function name";
            var fn,
                prop;
            name = name.toUpperCase();
            if(!Functions._builtInFunctions.hasOwnProperty(name))
            {
                fn = new Functions.Function(name,0,255);
                Functions._builtInFunctions[name] = fn
            }
            else
            {
                fn = Functions._builtInFunctions[name];
                if(!fn)
                {
                    Functions._builtInFunctions[name] = new Functions.Function(name,0,255);
                    fn = Functions[name.toUpperCase()]
                }
                else if(!options || !options.override)
                    throw"Attempt to override function while override is not allowed";
            }
            if(fnEvaluate && typeof fnEvaluate === "function")
                fn.evaluate = fnEvaluate;
            if(options)
                for(prop in options)
                    if(options.hasOwnProperty(prop) && prop !== 'override')
                        fn[prop] = options[prop];
            return fn
        };
    var def = Functions._defineBuildInFunction;
    var _mlow,
        _mhigh,
        _mcurrent,
        _morig;
    var _bsup,
        _bstarted;
    function value_area_get_x_y(v, x, y)
    {
        if(!v)
            return null;
        if(Calc._ArrayHelper.isArrayOrReference(v))
        {
            if(x > Calc._ArrayHelper.getColumnCount(v))
            {
                var xx = x;
                x = y;
                y = xx
            }
            var val = y * Calc._ArrayHelper.getColumnCount(v) + x;
            return Calc._ArrayHelper.getValueByIndex(v,val,0)
        }
        else
            return v
    }
    function find_compare_type_valid(find, val)
    {
        if(!val)
            return false;
        if(Calc.Convert.isNumber(find) && Calc.Convert.isNumber(val))
            return true;
        if(typeof find === 'bool' && typeof val === 'bool')
            return true;
        if(typeof find === 'string' && typeof val === 'string')
            return true;
        return false
    }
    function find_bound_walk(l, h, start, up, reset)
    {
        if(l < 0)
            return-1;
        if(h < 0)
            return-1;
        if(h < l)
            return-1;
        if(start < l)
            return-1;
        if(start > h)
            return-1;
        if(reset)
        {
            _mlow = l;
            _mhigh = h;
            _mcurrent = start;
            _morig = start;
            _bsup = up;
            _bstarted = up;
            return _mcurrent
        }
        if(_bsup)
        {
            _mcurrent++;
            if(_mcurrent > _mhigh && _bsup === _bstarted)
            {
                _mcurrent = _morig - 1;
                _bsup = false
            }
            else if(_mcurrent > _mhigh && _bsup !== _bstarted)
                return-1
        }
        else
        {
            _mcurrent--;
            if(_mcurrent < _mlow && _bsup === _bstarted)
            {
                _mcurrent = _morig + 1;
                _bsup = true
            }
            else if(_mcurrent < _mlow && _bsup !== _bstarted)
                return-1
        }
        return _mcurrent
    }
    function value_compare(a, b, case_sensitive)
    {
        if(a === b)
            return 0;
        var date = null;
        if(typeof a === "string")
            if(!b && a.toString().length === 0)
                return 0;
            else if(Calc.Convert.isNumber(b))
                return 1;
            else if(typeof b === 'bool')
                return 2;
            else if(typeof b === 'string')
            {
                var t;
                if(case_sensitive)
                    t = a.toString().localeCompare(b.toString());
                else
                    t = a.toLowerCase().localeCompare(b.toLowerCase());
                if(t === 0)
                    return 0;
                else if(t > 0)
                    return 1;
                else
                    return 2
            }
            else if(b instanceof Date)
            {
                date = GrapeCity.UI._DateTimeHelper._parseDate(a);
                if(!isNaN(date))
                    if(date === b)
                        return 0;
                    else if(date > b)
                        return 1;
                    else
                        return 2;
                return 1
            }
            else
                return-1;
        else if(typeof b === 'string')
            if(!a && b.toString().length === 0)
                return 0;
            else if(Calc.Convert.isNumber(a))
                return 2;
            else if(typeof a === 'bool')
                return 1;
            else if(a instanceof Date)
            {
                date = GrapeCity.UI._DateTimeHelper._parseDate(a);
                if(!isNaN(date))
                    if(date === b)
                        return 0;
                    else if(date > b)
                        return 1;
                    else
                        return 2;
                return 2
            }
            else
                return-1;
        if(typeof a === 'bool' && Calc.Convert.isNumber(b))
            return 1;
        if(typeof b === 'bool' && Calc.Convert.isNumber(a))
            return 2;
        var ax = Calc.Convert.toDouble(a);
        var bx = Calc.Convert.toDouble(b);
        if(ax === bx)
            return 0;
        else if(ax < bx)
            return 2;
        else
            return 1
    }
    function value_area_fetch_x_y(v, x, y)
    {
        var res = value_area_get_x_y(v,x,y);
        if(res)
            return res;
        return 0
    }
    function find_index_bisection(find, data, type, height)
    {
        var comp = -1;
        var high,
            low = 0,
            prev = -1,
            mid = -1;
        if(height)
            high = Calc._ArrayHelper.getRowCount(data);
        else
            high = Calc._ArrayHelper.getColumnCount(data);
        high--;
        if(high < low)
            return-1;
        while(low <= high)
        {
            var v = null;
            var start;
            if(type >= 1 !== (comp === 2))
                prev = mid;
            mid = Calc.Convert.toInt((low + high) / 2);
            mid = find_bound_walk(low,high,mid,type >= 0,true);
            start = mid;
            var realVal = find;
            if(find instanceof Calc.Array)
                realVal = find.getValue(0,0);
            else if(find instanceof Calc._SheetRangeReference)
                realVal = find.getValue(0,0,0,0);
            else if(find instanceof Calc.Reference)
                realVal = find.getValue(0,0,0);
            while(!find_compare_type_valid(realVal,v) && mid !== -1)
            {
                var rev = false;
                if(height)
                    v = value_area_get_x_y(data,0,mid);
                else
                    v = value_area_get_x_y(data,mid,0);
                if(find_compare_type_valid(realVal,v))
                    break;
                mid = find_bound_walk(0,0,0,false,false);
                if(!rev && type >= 0 && mid < start)
                {
                    high = mid;
                    rev = true
                }
                else if(!rev && type < 0 && mid > start)
                {
                    low = mid;
                    rev = true
                }
            }
            if(mid === -1 && type >= 1 !== (comp === 2))
                return prev;
            else if(mid === -1)
                return-1;
            comp = value_compare(realVal,v,false);
            if(type >= 1 && comp === 1)
                low = mid + 1;
            else if(type >= 1 && comp === 2)
                high = mid - 1;
            else if(type <= -1 && comp === 1)
                high = mid - 1;
            else if(type <= -1 && comp === 2)
                low = mid + 1;
            else if(comp === 0)
            {
                while(type <= -1 && mid > low || type >= 0 && mid < high)
                {
                    var adj = 0;
                    if(type >= 0)
                        adj = mid + 1;
                    else
                        adj = mid - 1;
                    if(height)
                        v = value_area_fetch_x_y(data,0,adj);
                    else
                        v = value_area_fetch_x_y(data,adj,0);
                    if(!v)
                        return-1;
                    if(!find_compare_type_valid(realVal,v))
                        break;
                    comp = value_compare(realVal,v,false);
                    if(comp !== 0)
                        break;
                    mid = adj
                }
                return mid
            }
        }
        if(type >= 1 !== (comp === 2))
            return mid;
        return prev
    }
    function find_index_linear(find, data, type, height)
    {
        var index_val = null;
        var comp;
        var length,
            lp,
            index = -1;
        if(height)
            length = Calc._ArrayHelper.getRowCount(data);
        else
            length = Calc._ArrayHelper.getColumnCount(data);
        for(lp = 0; lp < length; lp++)
        {
            var v;
            if(height)
                v = value_area_fetch_x_y(data,0,lp);
            else
                v = value_area_fetch_x_y(data,lp,0);
            if(!v)
                return-1;
            var realVal = find;
            if(find instanceof Calc.Array)
                realVal = find.getValue(0,0);
            else if(find instanceof Calc.Reference)
                realVal = find.getValue(0,0,0);
            if(!find_compare_type_valid(realVal,v))
                continue;
            comp = value_compare(realVal,v,false);
            if(type >= 1 && comp === 1)
            {
                comp = -1;
                if(index >= 0)
                    comp = value_compare(v,index_val,false);
                if(index < 0 || index >= 0 && comp === 1)
                {
                    index = lp;
                    index_val = v
                }
            }
            else if(type <= -1 && comp === 2)
            {
                comp = -1;
                if(index >= 0)
                    comp = value_compare(v,index_val,false);
                if(index < 0 || index >= 0 && comp === 2)
                {
                    index = lp;
                    index_val = v
                }
            }
            else if(comp === 0)
                return lp
        }
        return index
    }
    function CreateStringcomparisonRegexPattern(s)
    {
        if(!s || s === "")
            return s;
        var regExpKeys = [/\(/,/\[/,/\{/,/\\/,/\^/,/\$/,/\|/,/\)/,/\+/,/\./];
        for(var i in regExpKeys)
            if(i)
                s = s.replace(regExpKeys[i],regExpKeys[i].source);
        s = s.replace("~?","{113E2532-EAF5-444c-A5CB-3D7446971C4D}");
        s = s.replace("~*","{E21523B3-0F1F-458f-B547-23D25713D0EC}");
        s = s.replace("?",".");
        s = s.replace("*","((.|\\n)*)");
        s = s.replace("{113E2532-EAF5-444c-A5CB-3D7446971C4D}","\\?");
        s = s.replace("{E21523B3-0F1F-458f-B547-23D25713D0EC}","\\*");
        return s.toString()
    }
    function acceptsAny(i)
    {
        return true
    }
    function acceptsNotZero(i)
    {
        return i !== 0
    }
    function acceptsOne(i)
    {
        return i === 1
    }
    function acceptsMoreThanOne(i)
    {
        return i >= 1
    }
    function acceptsTwo(i)
    {
        return i === 2
    }
    function acceptsZeroTwo(i)
    {
        return i === 0 || i === 2
    }
    function acceptsZeroOdd(i)
    {
        return i === 0 || i % 2 === 1
    }
    function acceptsThree(i)
    {
        return i === 3
    }
    function isVolatile()
    {
        return true
    }
    function acceptsOneTwo(i)
    {
        return i === 1 || i === 2
    }
    function acceptsTwoThreeFour(i)
    {
        return i === 2 || i === 3 || i === 4
    }
    function acceptsOneTwoThree(i)
    {
        return i === 1 || i === 2 || i === 3
    }
    function acceptsThreeFour(i)
    {
        return i === 3 || i === 4
    }
    function isBranch()
    {
        return true
    }
    function acceptsZero(i)
    {
        return i === 0
    }
    function isContextSensitive()
    {
        return true
    }
    function __iterate(obj, fn, ctx)
    {
        if(GrapeCity.Calc.Convert.isError(obj))
        {
            ctx.value = obj;
            return false
        }
        else if(GrapeCity.Calc.Convert._isCalcReference(obj))
        {
            for(var r = 0; r < obj.getRowCount(0); r++)
                for(var c = 0; c < obj.getColumnCount(0); c++)
                    if(!fn(obj.getValue(0,r,c),ctx))
                        return false
        }
        else if(GrapeCity.Calc.Convert._isCalcArray(obj))
        {
            for(var i = 0; i < obj.length(); i++)
                if(!fn(obj.getValueByIndex(i),ctx))
                    return false
        }
        else if($.isArray(obj))
            $.each(obj,function(i, v)
            {
                return fn(v,ctx)
            });
        else if(!fn(obj,ctx))
            return false;
        return true
    }
    function findTestArgument()
    {
        return 0
    }
    function findBranchArgument(test)
    {
        if(GrapeCity.Calc.Convert.isError(test))
            return-1;
        try
        {
            return GrapeCity.Calc.Convert.toInt(test)
        }
        catch(err){}
        return-1
    }
    function _checkArgumentsLength(args)
    {
        if(!args)
            throw'Invalid arguments';
        else if(args.length < 1 || args.length > 1)
            throw'Invalid arguments';
    }
    var CellInfoType = {
            row: 0,
            column: 1
        };
    function CellInfoReference(source, row, column, rowCount, columnCount, type)
    {
        this._source = source;
        this._ranges = [new GrapeCity.UI.Range(row,column,rowCount,columnCount)];
        this._row = row;
        this._column = column;
        this._rowCount = rowCount;
        this._columnCount = columnCount;
        this._type = type
    }
    CellInfoReference.prototype = new Calc._ConcreteReference;
    CellInfoReference.prototype.type = function()
    {
        return this._type
    };
    CellInfoReference.prototype.getValue = function(area, rowOffset, columnOffset)
    {
        switch(this._type)
        {
            case CellInfoType.row:
                return this.getRow(0) + 1 + rowOffset;
            case CellInfoType.column:
                return this.getColumn(0) + 1 + columnOffset;
            default:
                return this.getActualValue(area,rowOffset,columnOffset)
        }
    };
    function TransposedArray(array)
    {
        this._array = array
    }
    TransposedArray.prototype = new Calc.Array;
    TransposedArray.prototype.rowCount = function()
    {
        return Calc._ArrayHelper.getColumnCount(this._array)
    };
    TransposedArray.prototype.columnCount = function()
    {
        return Calc._ArrayHelper.getRowCount(this._array)
    };
    TransposedArray.prototype.getValue = function(row, column)
    {
        return Calc._ArrayHelper.getValue(this._array,column,row)
    };
    function eg_row(args, context)
    {
        _checkArgumentsLength([args]);
        if(!context)
            return Calc.Errors.value;
        var isArrayFormula = context.arrayFormulaMode;
        var reference = Calc._Helper._argumentExists(args,0) ? args[0] : context.getReference(context.source,context.row,context.column,context.rowCount,context.columnCount);
        if(!reference || reference.getRangeCount() !== 1)
            return Calc.Errors.value;
        if(isArrayFormula)
            return new CellInfoReference(reference.getSource(),reference.getRow(0),reference.getColumn(0),reference.getRowCount(0),reference.getColumnCount(0),CellInfoType.row);
        return reference.getRow(0) + 1
    }
    function eg_column(args, context)
    {
        _checkArgumentsLength([args]);
        if(!context)
            return Calc.Errors.value;
        var isArrayFormula = context.arrayFormulaMode;
        var reference = Calc._Helper._argumentExists(args,0) ? args[0] : context.getReference(context.source,context.row,context.column,context.rowCount,context.columnCount);
        if(!reference || reference.getRangeCount() !== 1)
            return Calc.Errors.value;
        if(isArrayFormula)
            return new CellInfoReference(reference.getSource(),reference.getRow(0),reference.getColumn(0),reference.getRowCount(0),reference.getColumnCount(0),CellInfoType.column);
        return reference.getColumn(0) + 1
    }
    function eg_rows(args)
    {
        _checkArgumentsLength([args]);
        return Calc._ArrayHelper.getRowCount(args[0])
    }
    function eg_columns(args)
    {
        _checkArgumentsLength([args]);
        return Calc._ArrayHelper.getColumnCount(args[0])
    }
    function eg_transpose(args)
    {
        _checkArgumentsLength([args]);
        return new TransposedArray(args[0])
    }
    function eg_hlookup(args)
    {
        var lookVal = args[0];
        var array = args[1];
        var row_index = Calc.Convert.toInt(args[2]);
        var approx = Calc._Helper._argumentExists(args,3) ? Calc.Convert.toBool(args[3]) : true;
        var index = -1;
        if(row_index <= 0)
            return Calc.Errors.Value;
        if(row_index > Calc._ArrayHelper.getRowCount(array))
            return Calc.Errors.Reference;
        if(approx)
            index = find_index_bisection(args[0],args[1],1,false);
        else
            index = find_index_linear(args[0],args[1],0,false);
        if(index >= 0)
        {
            var v = value_area_fetch_x_y(args[1],index,row_index - 1);
            return v
        }
        return Calc.Errors.NotAvailable
    }
    function eg_vlookup(args)
    {
        var lookVal = args[0];
        var array = args[1];
        var col_index = Calc.Convert.toInt(args[2]);
        var approx = Calc._Helper._argumentExists(args,3) ? Calc.Convert.toBool(args[3]) : true;
        var index = -1;
        if(col_index <= 0)
            return Calc.Errors.Value;
        if(col_index > Calc._ArrayHelper.getColumnCount(array))
            return Calc.Errors.Reference;
        if(approx)
            index = find_index_bisection(args[0],args[1],1,true);
        else
            index = find_index_linear(args[0],args[1],0,true);
        if(index >= 0)
        {
            var v = value_area_fetch_x_y(args[1],col_index - 1,index);
            return v
        }
        return Calc.Errors.NotAvailable
    }
    function eg_lookup(args)
    {
        var index = -1;
        var result;
        var width = Calc._ArrayHelper.getColumnCount(args[1]);
        var height = Calc._ArrayHelper.getRowCount(args[1]);
        if(args.length > 2)
            result = args[2];
        else
        {
            var val = null;
            if(width > height)
            {
                val = eg_hlookup([args[0],args[1],height]);
                if(val instanceof Calc.Error)
                    return Calc.Errors.NotAvailable;
                else
                    return val
            }
            else
            {
                val = eg_vlookup([args[0],args[1],width]);
                if(val instanceof Calc.Error)
                    return Calc.Errors.NotAvailable;
                else
                    return val
            }
        }
        if(result)
        {
            var width2 = Calc._ArrayHelper.getColumnCount(result);
            var height2 = Calc._ArrayHelper.getRowCount(result);
            if(width2 > 1 && height2 > 1)
                return Calc.Errors.NotAvailable
        }
        else
            result = args[1];
        index = find_index_bisection(args[0],args[1],1,width > height ? false : true);
        if(index >= 0)
        {
            var v = null;
            width = Calc._ArrayHelper.getColumnCount(result);
            height = Calc._ArrayHelper.getRowCount(result);
            if(width > height)
                v = value_area_fetch_x_y(result,index,height - 1);
            else
                v = value_area_fetch_x_y(result,width - 1,index);
            return v
        }
        return Calc.Errors.NotAvailable
    }
    function eg_choose(args)
    {
        var index = Calc.Convert.toInt(args[0]);
        if(index < 1 || args.length <= index)
            return Calc.Errors.Value;
        if(!args[index])
            return 0.0;
        else
            return args[index]
    }
    function assertOrder(v, ascending)
    {
        var length = Calc._ArrayHelper.getLength(v);
        for(var i = 0; i < length; i++)
            if(i > 0)
            {
                var a = Calc._ArrayHelper.getValue(v,i - 1);
                var b = Calc._ArrayHelper.getValue(v,i);
                var ret = value_compare(a,b,false);
                if(ret === 2 && !ascending)
                    return false
            }
        return true
    }
    function eg_match(args)
    {
        var width = Calc._ArrayHelper.getColumnCount(args[1]);
        var height = Calc._ArrayHelper.getRowCount(args[1]);
        var matchType = Calc._Helper._argumentExists(args,2) ? Calc.Convert.toInt(args[2]) : 1;
        if(width > 1 && height > 1)
            return Calc.Errors.NotAvailable;
        if(matchType === 1 && !assertOrder(args[1],true))
            return Calc.Errors.NotAvailable;
        else if(matchType === -1 && !assertOrder(args[1],false))
            return Calc.Errors.NotAvailable;
        var result = -1;
        if(matchType === 1)
            result = find_index_bisection(args[0],args[1],1,height > 1);
        else if(matchType === 0)
            result = find_index_linear(args[0],args[1],0,height > 1);
        else if(matchType === -1)
            result = find_index_bisection(args[0],args[1],-1,height > 1);
        if(result === -1)
            return Calc.Errors.NotAvailable;
        return result + 1
    }
    function _appendR1C1Number(sb, prefix, coord, relative)
    {
        sb.append(prefix);
        if(relative)
        {
            if(coord !== 0)
            {
                sb.append("[");
                sb.append(coord.toString());
                sb.append("]")
            }
        }
        else
            sb.append(coord)
    }
    function _appendA1Number(sb, coord, relative)
    {
        if(!relative)
            sb.append("$");
        sb.append(coord);
        return sb
    }
    function _appendA1Letter(sb, coord, relative)
    {
        if(!relative)
            sb.append("$");
        var position = sb.toString().length;
        for(; coord > 0.1; coord = (coord - 1) / 26)
            sb.insert(String.fromCharCode('A'.charCodeAt(0) + (coord - 1) % 26),position);
        return sb
    }
    function _appendExternalName(sb, name)
    {
        if(name && 0 < name.length)
        {
            var needQuotes = !GrapeCity.Calc.Parser._isLetter(name[0]) && name[0] !== '_';
            for(var i = 1; !needQuotes && i < name.length; i++)
                needQuotes = !GrapeCity.Calc.Parser._isLetterOrDigit(name[i]) && name[i] !== '_';
            if(needQuotes)
            {
                sb.append("'");
                sb.append(name.replace("'","''"));
                sb.append("'")
            }
            else
                sb.append(name);
            sb.append("!")
        }
        return sb
    }
    function eg_address(args)
    {
        var row = Calc.Convert.toInt(args[0]);
        var col = Calc.Convert.toInt(args[1]);
        var absNum = Calc._Helper._argumentExists(args,2) ? Calc.Convert.toInt(args[2]) : 1;
        var a1 = Calc._Helper._argumentExists(args,3) ? Calc.Convert.toBool(args[3]) : true;
        var sheetText = Calc._Helper._argumentExists(args,4) ? Calc.Convert.toString(args[4]) : "";
        var rowRelative = absNum === 3 || absNum === 4 || absNum === 7 || absNum === 8;
        var colRelative = absNum === 2 || absNum === 4 || absNum === 6 || absNum === 8;
        var sb = new GrapeCity.UI._StringBuilder;
        if(row < 1 && (a1 || !rowRelative) || row > Calc.Parser.maxRowCount)
            return Calc.Errors.Value;
        if(col < 1 && (a1 || !colRelative) || col > Calc.Parser.maxColumnCount)
            return Calc.Errors.Value;
        if(absNum < 1 || 8 < absNum)
            return Calc.Errors.Value;
        _appendExternalName(sb,sheetText);
        if(a1)
        {
            _appendA1Letter(sb,col,colRelative);
            _appendA1Number(sb,row,rowRelative)
        }
        else
        {
            _appendR1C1Number(sb,"R",row,rowRelative);
            _appendR1C1Number(sb,"C",col,colRelative)
        }
        return sb.toString()
    }
    function eg_index(args)
    {
        var array = args[0];
        var arrayRowCount = Calc._ArrayHelper.getRowCount(array);
        var arrayColumnCount = Calc._ArrayHelper.getColumnCount(array);
        if(args.length === 2)
        {
            var index = Calc._Helper._argumentExists(args,1) ? Calc.Convert.toInt(args[1]) : 0;
            if(arrayRowCount !== 1 && arrayColumnCount !== 1)
                return Calc.Errors.Reference;
            if(index < 0)
                return Calc.Errors.Value;
            if(arrayRowCount * arrayColumnCount < index)
                return Calc.Errors.Reference;
            if(index === 0)
                return new Calc._SliceArray(array,0,0,arrayRowCount,arrayColumnCount);
            else
                return Calc._ArrayHelper.getValue(array,index - 1)
        }
        else
        {
            var row = Calc._Helper._argumentExists(args,1) ? Calc.Convert.toInt(args[1]) : 0;
            var column = Calc._Helper._argumentExists(args,2) ? Calc.Convert.toInt(args[2]) : 0;
            var area = Calc._Helper._argumentExists(args,3) ? Calc.Convert.toInt(args[3]) : 1;
            if(row < 0 || column < 0 || area < 1)
                return Calc.Errors.Value;
            if(arrayRowCount < row || arrayColumnCount < column || 1 < area)
                return Calc.Errors.Reference;
            if(row === 0 && column === 0)
                return new Calc._SliceArray(array,0,0,arrayRowCount,arrayColumnCount);
            else if(row === 0)
                return new Calc._SliceArray(array,0,column - 1,arrayRowCount,1);
            else if(column === 0)
                return new Calc._SliceArray(array,row - 1,0,1,arrayColumnCount);
            else
                return Calc._ArrayHelper.getValue(array,row - 1,column - 1)
        }
    }
    function eg_offset(args)
    {
        var reference = args[0];
        if(!reference || reference.getRangeCount() !== 1)
            return Calc.Errors.Value;
        var rows = Calc.Convert.toInt(args[1]);
        var columns = Calc.Convert.toInt(args[2]);
        var height = Calc._Helper._argumentExists(args,3) ? Calc.Convert.toInt(args[3]) : reference.getRowCount(0);
        var width = Calc._Helper._argumentExists(args,4) ? Calc.Convert.toInt(args[4]) : reference.getColumnCount(0);
        var source = reference.getSource();
        var offsetRow = reference.getRow(0) + rows;
        var offsetColumn = reference.getColumn(0) + columns;
        if(height <= 0 || width <= 0)
            return Calc.Errors.Reference;
        if(offsetRow < source.getRow(0) || source.getRow(0) + source.getRowCount(0) < offsetRow + height)
            return Calc.Errors.Reference;
        if(offsetColumn < source.getColumn(0) || source.getColumn(0) + source.getColumnCount(0) < offsetColumn + width)
            return Calc.Errors.Reference;
        return new Calc._ConcreteReference(source,[{
                    row: offsetRow,
                    column: offsetColumn,
                    rowCount: height,
                    columnCount: width
                }])
    }
    def("ADDRESS",eg_address,{
        minArgs: 2,
        maxArgs: 5,
        acceptsMissingArgument: acceptsTwoThreeFour
    });
    def("INDEX",eg_index,{
        minArgs: 2,
        maxArgs: 4,
        acceptsReference: acceptsZero,
        acceptsArray: acceptsZero,
        acceptsMissingArgument: acceptsOneTwoThree
    });
    def("OFFSET",eg_offset,{
        minArgs: 3,
        maxArgs: 5,
        acceptsMissingArgument: acceptsThreeFour,
        acceptsReference: acceptsZero,
        acceptsArray: acceptsZero,
        isVolatile: isVolatile
    });
    def("ROW",eg_row,{
        minArgs: 0,
        maxArgs: 1,
        acceptsReference: acceptsAny,
        isContextSensitive: isContextSensitive
    });
    def("COLUMN",eg_column,{
        minArgs: 0,
        maxArgs: 1,
        acceptsMissingArgument: acceptsZero,
        acceptsReference: acceptsAny,
        isContextSensitive: isContextSensitive
    });
    def("ROWS",eg_rows,{
        minArgs: 1,
        maxArgs: 1,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("COLUMNS",eg_columns,{
        minArgs: 1,
        maxArgs: 1,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("TRANSPOSE",eg_transpose,{
        minArgs: 1,
        maxArgs: 1,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("LOOKUP",eg_lookup,{
        minArgs: 2,
        maxArgs: 3,
        acceptsArray: acceptsNotZero,
        acceptsReference: acceptsNotZero
    });
    def("HLOOKUP",eg_hlookup,{
        minArgs: 3,
        maxArgs: 4,
        acceptsMissingArgument: acceptsThree,
        acceptsArray: acceptsOne,
        acceptsReference: acceptsOne
    });
    def("VLOOKUP",eg_vlookup,{
        minArgs: 3,
        maxArgs: 4,
        acceptsMissingArgument: acceptsThree,
        acceptsArray: acceptsOne,
        acceptsReference: acceptsOne
    });
    def("CHOOSE",eg_choose,{
        minArgs: 2,
        maxArgs: 255,
        acceptsError: acceptsMoreThanOne,
        isBranch: isBranch,
        findTestArgument: findTestArgument,
        findBranchArgument: findBranchArgument
    });
    def("MATCH",eg_match,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsTwo,
        acceptsReference: acceptsOne,
        acceptsArray: acceptsOne
    })
})(window,jQuery);
(function(window)
{
    "use strict";;
    var const_undefined = "undefined";
    var const_boolean = "boolean";
    var const_string = "string";
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === const_undefined)
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.Calc === const_undefined)
        GrapeCity.Calc = {};
    var Calc = GrapeCity.Calc;
    if(typeof Calc.Functions === const_undefined)
        Calc.Functions = {};
    var Functions = Calc.Functions;
    Functions._builtInFunctions = Functions._builtInFunctions || {};
    if(typeof Functions._defineBuildInFunction === const_undefined)
        Functions._defineBuildInFunction = function(name, fnEvaluate, options)
        {
            if(name === undefined || name === null)
                throw"Invalid function name";
            var fn;
            name = name.toUpperCase();
            if(!Functions._builtInFunctions.hasOwnProperty(name))
            {
                fn = new Functions.Function(name,0,255);
                Functions._builtInFunctions[name] = fn
            }
            else
            {
                fn = Functions._builtInFunctions[name];
                if(!fn)
                {
                    Functions._builtInFunctions[name] = new Functions.Function(name,0,255);
                    fn = Functions[name.toUpperCase()]
                }
                else if(!options || !options.override)
                    throw"Attempt to override function while override is not allowed";
            }
            if(fnEvaluate && typeof fnEvaluate === "function")
                fn.evaluate = fnEvaluate;
            if(options)
                for(var prop in options)
                    if(options.hasOwnProperty(prop) && prop !== 'override')
                        fn[prop] = options[prop];
            return fn
        };
    var def = Functions._defineBuildInFunction;
    function acceptsAny(i)
    {
        return true
    }
    function acceptAboveZero(i)
    {
        return i > 0
    }
    function acceptsSecond(i)
    {
        return i === 1
    }
    function acceptsSecondOrThirdOrFourth(i)
    {
        return i === 1 || i === 2 || i === 3
    }
    function acceptsThird(i)
    {
        return i === 2
    }
    function acceptsFirstOrThird(i)
    {
        return i === 0 || i === 2
    }
    function acceptsFirstOrOne(i)
    {
        return i === 0 || i === 1
    }
    function acceptsFirstOrOdd(i)
    {
        return i === 0 || i % 2 === 1
    }
    function acceptsFirst(i)
    {
        return i === 0
    }
    function acceptsFirstOrSecondOrThird(i)
    {
        return i === 0 || i === 1 || i === 2
    }
    function acceptsEven(i)
    {
        return i % 2 === 0
    }
    function acceptsFourth(i)
    {
        return i === 3
    }
    function acceptsNotFourth(i)
    {
        return i !== 3
    }
    function acceptsFourthOrFifth(i)
    {
        return i === 3 || i === 4
    }
    function __maxIncludeSubtotals(args, includeSubtotals)
    {
        var any = false;
        var max = 0.0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
            {
                for(var j = 0; j < arrayHelper.getLength(args[i]); j++)
                    if(includeSubtotals || !arrayHelper.isSubtotalByIndex(args[i],j))
                    {
                        var obj = arrayHelper.getValueByIndex(args[i],j);
                        if(convert.isNumber(obj))
                        {
                            var val = convert.toDouble(obj);
                            if(!any || val > max)
                                max = val;
                            any = true
                        }
                        else if(convert.isError(obj))
                            return obj
                    }
            }
            else
            {
                var val1 = convert.toDouble(args[i]);
                if(isNaN(val1))
                    return Calc.Errors.Value;
                if(!any || val1 > max)
                    max = val1;
                any = true
            }
        }
        return max
    }
    function st_max(args)
    {
        return __maxIncludeSubtotals(args,true)
    }
    function st_maxa(args)
    {
        var any = false,
            max = 0.0,
            val;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
                for(var j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    var obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj) || typeof obj === const_boolean)
                    {
                        val = convert.toDouble(obj);
                        if(!any || val > max)
                            max = val;
                        any = true
                    }
                    else if(typeof obj === const_string)
                    {
                        val = 0.0;
                        if(!any || val > max)
                            max = val;
                        any = true
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(val = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                if(!any || val > max)
                    max = val;
                any = true
            }
        }
        return max
    }
    function __minIncludeSubtotals(args, includeSubtotals)
    {
        var any = false,
            min = 0.0,
            val;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
            {
                for(var j = 0; j < arrayHelper.getLength(args[i]); j++)
                    if(includeSubtotals || !arrayHelper.isSubtotalByIndex(args[i],j))
                    {
                        var obj = arrayHelper.getValueByIndex(args[i],j);
                        if(convert.isNumber(obj))
                        {
                            val = convert.toDouble(obj);
                            if(!any || val < min)
                                min = val;
                            any = true
                        }
                        else if(convert.isError(obj))
                            return obj
                    }
            }
            else
            {
                if(isNaN(val = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                if(!any || val < min)
                    min = val;
                any = true
            }
        }
        return min
    }
    function st_min(args)
    {
        return __minIncludeSubtotals(args,true)
    }
    function st_mina(args)
    {
        var any = false,
            min = 0.0,
            val;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
                for(var j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    var obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj) || typeof obj === const_boolean)
                    {
                        val = convert.toDouble(obj);
                        if(!any || val < min)
                            min = val;
                        any = true
                    }
                    else if(typeof obj === const_string)
                    {
                        val = 0.0;
                        if(!any || val < min)
                            min = val;
                        any = true
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(val = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                if(!any || val < min)
                    min = val;
                any = true
            }
        }
        return min
    }
    function st_large(args)
    {
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        if(convert.isError(args[0]))
            return args[0];
        if(convert.isError(args[1]))
            return args[1];
        var k = convert.toInt(args[1]);
        var list = [],
            x;
        if(arrayHelper.isArrayOrReference(args[0]))
            for(var i = 0; i < arrayHelper.getLength(args[0]); i++)
            {
                var obj = arrayHelper.getValueByIndex(args[0],i);
                if(convert.isNumber(obj))
                {
                    x = convert.toDouble(obj);
                    list.push(x)
                }
                else if(convert.isError(obj))
                    return obj
            }
        else
        {
            if(isNaN(x = convert.toDouble(args[0])))
                return Calc.Errors.Value;
            list.push(x)
        }
        list.sort(function(x, y)
        {
            return x - y
        });
        if(k <= 0 || list.length < k)
            return Calc.Errors.Number;
        return list[list.length - k]
    }
    function st_small(args)
    {
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        if(convert.isError(args[0]))
            return args[0];
        if(convert.isError(args[1]))
            return args[1];
        var k;
        if(isNaN(k = convert.toInt(args[1])))
            return Calc.Errors.Value;
        var list = [],
            x;
        if(arrayHelper.isArrayOrReference(args[0]))
            for(var i = 0; i < arrayHelper.getLength(args[0]); i++)
            {
                var obj = arrayHelper.getValueByIndex(args[0],i);
                if(convert.isNumber(obj))
                {
                    x = convert.toDouble(obj);
                    list.push(x)
                }
                else if(convert.isError(obj))
                    return obj
            }
        else
        {
            if(isNaN(x = convert.toDouble(args[0])))
                return Calc.Errors.Value;
            list.push(x)
        }
        list.sort(function(x, y)
        {
            return x - y
        });
        if(k <= 0 || list.length < k)
            return Calc.Errors.Number;
        return list[k - 1]
    }
    function __averageIncludeSubtotals(args, includeSubtotals)
    {
        var sum = 0.0;
        var n = 0.0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
            {
                for(var r = 0; r < arrayHelper.getRangeCount(args[i]); r++)
                    for(var j = 0; j < arrayHelper.getLength(args[i],r); j++)
                        if(includeSubtotals || !arrayHelper.isSubtotal(args[i],j,r))
                        {
                            var obj = arrayHelper.getValueByIndex(args[i],j,r);
                            if(convert.isNumber(obj))
                            {
                                sum += convert.toDouble(obj);
                                n++
                            }
                            else if(convert.isError(obj))
                                return obj
                        }
            }
            else
            {
                var x;
                if(isNaN(x = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                sum += x;
                n++
            }
        }
        if(n === 0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(sum / n)
    }
    function st_average(args)
    {
        return __averageIncludeSubtotals(args,true)
    }
    function st_averagea(args)
    {
        var sum = 0.0;
        var n = 0.0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
                for(var j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    var obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj) || typeof obj === const_boolean)
                    {
                        sum += convert.toDouble(obj);
                        n++
                    }
                    else if(typeof obj === const_string)
                    {
                        sum += 0.0;
                        n++
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                var x;
                if(isNaN(x = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                sum += x;
                n++
            }
        }
        if(n === 0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(sum / n)
    }
    function __averageifImp(range, criteria, sumRange)
    {
        var sum = 0.0;
        var n = 0.0;
        var crit = Functions._MathHelper.parseCriteria(criteria);
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        if(arrayHelper.getRowCount(range) !== arrayHelper.getRowCount(sumRange) || arrayHelper.getColumnCount(range) !== arrayHelper.getColumnCount(sumRange))
            return Calc.Errors.Value;
        for(var j = 0; j < arrayHelper.getLength(range); j++)
        {
            var val = arrayHelper.getValueByIndex(range,j);
            if(crit && crit(val))
            {
                var obj = arrayHelper.getValueByIndex(sumRange,j);
                if(convert.isNumber(obj))
                {
                    sum += convert.toDouble(obj);
                    n++
                }
                else if(convert.isError(obj))
                    return obj
            }
        }
        if(n === 0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(sum / n)
    }
    function st_averageif(args)
    {
        var range = args[0];
        var criteria = args[1];
        var sumRange = Calc._Helper._argumentExists(args,2) ? args[2] : args[0];
        var convert = Calc.Convert;
        var critieriaRef = args[1];
        var rowCount,
            colCount,
            result,
            r,
            c;
        if(convert._isCalcReference(critieriaRef))
        {
            rowCount = critieriaRef.getRowCount(0);
            colCount = critieriaRef.getColumnCount(0);
            result = [];
            for(r = 0; r < rowCount; r++)
            {
                result[r] = new Array(colCount);
                for(c = 0; c < colCount; c++)
                    result[r][c] = __averageifImp(range,critieriaRef.getValue(0,r,c),sumRange)
            }
            return new Calc._ConcreteArray(result)
        }
        var criteriaArray = args[1];
        if(convert._isCalcArray(criteriaArray))
        {
            rowCount = criteriaArray.getRowCount();
            colCount = criteriaArray.getColumnCount();
            result = [];
            for(r = 0; r < rowCount; r++)
            {
                result[r] = new Array(colCount);
                for(c = 0; c < colCount; c++)
                    result[r][c] = __averageifImp(range,criteriaArray.getValue(r,c),sumRange)
            }
            return new Calc._ConcreteArray(result)
        }
        return __averageifImp(range,criteria,sumRange)
    }
    function st_averageifs(args)
    {
        var sum = 0.0;
        var n = 0.0;
        var sumRange = args[0];
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var length = arrayHelper.getLength(args[0]);
        var obj;
        for(var i = 0; i < length; i++)
        {
            var condition = true;
            for(var j = 1; j < args.length; j = j + 2)
            {
                var range = args[j];
                var criteria = args[j + 1];
                var crit = Functions._MathHelper.parseCriteria(criteria);
                obj = arrayHelper.getValueByIndex(range,i);
                condition = crit(obj);
                if(!condition)
                    break
            }
            if(condition)
            {
                obj = arrayHelper.getValueByIndex(sumRange,i);
                if(convert.isNumber(obj))
                {
                    sum += convert.toDouble(obj);
                    n++
                }
                else if(convert.isError(obj))
                    return obj
            }
        }
        if(n === 0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(sum / n)
    }
    function st_median(args)
    {
        var list = [],
            x;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
                for(var j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    var obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj))
                    {
                        x = convert.toDouble(obj);
                        list.push(x)
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(x = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                list.push(x)
            }
        }
        list.sort(function(x, y)
        {
            return x - y
        });
        if(list.length === 0)
            return Calc.Errors.Number;
        if(list.length % 2 === 0)
            return(convert.toDouble(list[list.length / 2 - 1]) + convert.toDouble(list[list.length / 2])) / 2.0;
        else
            return list[parseInt(list.length / 2,10)]
    }
    function st_mode(args)
    {
        var mode = null;
        var modeCount = 0;
        var list = [];
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var i,
            j,
            x;
        for(i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
                for(j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    var obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj))
                    {
                        x = convert.toDouble(obj);
                        list.push(x)
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(x = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                list.push(x)
            }
        }
        for(i = 0; i < list.length; i++)
        {
            var count = 0;
            for(j = 0; j < list.length; j++)
                if(j !== i && convert.toDouble(list[j]) === convert.toDouble(list[i]))
                    count++;
            if(count > modeCount)
            {
                modeCount = count;
                mode = list[i]
            }
        }
        if(modeCount === 0)
            return Calc.Errors.NotAvailable;
        return mode
    }
    function st_geomean(args)
    {
        var y;
        var prod = 1.0;
        var n = 0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
                for(var j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    var obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj))
                    {
                        y = convert.toDouble(obj);
                        if(y <= 0)
                            return Calc.Errors.Number;
                        prod *= y;
                        n++
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(y = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                if(y <= 0)
                    return Calc.Errors.Number;
                prod *= y;
                n++
            }
        }
        if(n <= 0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(Math.pow(prod,1.0 / n))
    }
    function st_harmean(args)
    {
        var y;
        var sum = 0.0;
        var n = 0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
                for(var j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    var obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj))
                    {
                        y = convert.toDouble(obj);
                        if(y <= 0.0)
                            return Calc.Errors.Number;
                        sum += 1 / y;
                        n++
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(y = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                if(y <= 0.0)
                    return Calc.Errors.Number;
                sum += 1 / y;
                n++
            }
        }
        if(sum === 0.0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(n / sum)
    }
    function st_trimmean(args)
    {
        var percent;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        if(isNaN(percent = convert.toDouble(args[1])))
            return Calc.Errors.Value;
        var sum = 0.0;
        var list = [],
            i,
            x;
        if(percent < 0.0 || 1.0 <= percent)
            return Calc.Errors.Number;
        if(convert.isError(args[0]))
            return args[0];
        if(arrayHelper.isArrayOrReference(args[0]))
            for(i = 0; i < arrayHelper.getLength(args[0]); i++)
            {
                var obj = arrayHelper.getValueByIndex(args[0],i);
                if(convert.isNumber(obj))
                {
                    x = convert.toDouble(obj);
                    list.push(x)
                }
                else if(convert.isError(obj))
                    return obj
            }
        else
        {
            if(isNaN(x = convert.toDouble(args[0])))
                return Calc.Errors.Value;
            list.push(x)
        }
        list.sort(function(x, y)
        {
            return x - y
        });
        var n = parseInt(list.length * percent / 2,10);
        for(i = n; i < list.length - n; i++)
            sum += convert.toDouble(list[i]);
        return sum / (list.length - 2 * n)
    }
    function st_frequency(args)
    {
        var convert = Calc.Convert;
        var dataArray = convert._toArray(args[0]);
        var binsArray = convert._toArray(args[1]);
        var binsCount = 0,
            i,
            j,
            element;
        var rowCount1 = binsArray.getRowCount();
        var columnCount1 = binsArray.getColumnCount();
        for(i = 0; i < rowCount1; i++)
            for(j = 0; j < columnCount1; j++)
            {
                element = binsArray.getValue(i,j);
                if(convert.isError(element))
                    return element;
                if(convert.isNumber(element))
                    binsCount++
            }
        var rowCount2 = dataArray.getRowCount();
        var columnCount2 = dataArray.getColumnCount();
        for(i = 0; i < rowCount2; i++)
            for(j = 0; j < columnCount2; j++)
            {
                element = dataArray.getValue(i,j);
                if(convert.isError(element))
                    return element
            }
        var bins = new Array(binsCount);
        var results = new Array(binsCount + 1);
        binsCount = 0;
        for(i = 0; i < rowCount1; i++)
            for(j = 0; j < columnCount1; j++)
            {
                element = binsArray.getValue(i,j);
                if(convert.isNumber(element))
                    bins[binsCount++] = convert.toDouble(element)
            }
        bins.sort(function(x, y)
        {
            return x - y
        });
        var resultsLength = results.length;
        for(i = 0; i < resultsLength; i++)
            results[i] = 0;
        for(i = 0; i < rowCount2; i++)
            for(j = 0; j < columnCount2; j++)
            {
                element = dataArray.getValue(i,j);
                if(convert.isNumber(element))
                {
                    var number = convert.toDouble(element);
                    var found = false;
                    for(var k = 0; !found && k < binsCount; k++)
                        if(number <= bins[k])
                        {
                            results[k]++;
                            found = true
                        }
                    if(!found)
                        results[binsCount]++
                }
            }
        return new Calc._OneDimensionalArray(results)
    }
    function st_rank(args)
    {
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var number;
        if(isNaN(number = convert.toDouble(args[0])))
            return Calc.Errors.Value;
        var array = args[1];
        var order = 0.0;
        if(Calc._Helper._argumentExists(args,2))
            if(isNaN(order = convert.toDouble(args[2])))
                return Calc.Errors.Value;
        var lessThanCount = 0;
        var equalToCount = 0;
        var greaterThanCount = 0;
        for(var i = 0; i < arrayHelper.getLength(array); i++)
        {
            var obj = arrayHelper.getValueByIndex(args[1],i);
            if(convert.isNumber(obj))
            {
                var x = convert.toDouble(obj);
                if(x < number)
                    lessThanCount++;
                else if(number < x)
                    greaterThanCount++;
                else
                    equalToCount++
            }
        }
        if(equalToCount === 0)
            return Calc.Errors.NotAvailable;
        return order === 0 ? greaterThanCount + 1 : lessThanCount + 1
    }
    function st_kurt(args)
    {
        var x;
        var sumx = 0.0;
        var sumx2 = 0.0;
        var sum4 = 0.0;
        var mean;
        var stdev;
        var n = 0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var i,
            j,
            obj;
        for(i = 0; i < args.length; i++)
            if(arrayHelper.isArrayOrReference(args[i]))
                for(j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj))
                    {
                        x = convert.toDouble(obj);
                        sumx += x;
                        sumx2 += x * x;
                        n++
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(x = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                sumx += x;
                sumx2 += x * x;
                n++
            }
        if(n <= 3)
            return Calc.Errors.DivideByZero;
        mean = sumx / n;
        stdev = Math.sqrt((n * sumx2 - sumx * sumx) / (n * (n - 1)));
        if(stdev === 0.0)
            return Calc.Errors.DivideByZero;
        for(i = 0; i < args.length; i++)
            if(arrayHelper.isArrayOrReference(args[i]))
                for(j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj))
                    {
                        x = convert.toDouble(obj);
                        sum4 += Math.pow((x - mean) / stdev,4.0)
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(x = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                sum4 += Math.pow((x - mean) / stdev,4.0)
            }
        return convert.toResult(n * (n + 1) * sum4 / ((n - 1) * (n - 2) * (n - 3)) - 3 * (n - 1) * (n - 1) / ((n - 2) * (n - 3)))
    }
    function st_percentile(args)
    {
        var k;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        if(isNaN(k = convert.toDouble(args[1])))
            return Calc.Errors.Value;
        var list = [],
            x;
        if(convert.isError(args[0]))
            return args[0];
        if(arrayHelper.isArrayOrReference(args[0]))
            for(var i = 0; i < arrayHelper.getLength(args[0]); i++)
            {
                var obj = arrayHelper.getValueByIndex(args[0],i);
                if(convert.isNumber(obj))
                {
                    x = convert.toDouble(obj);
                    list.push(x)
                }
                else if(convert.isError(obj))
                    return obj
            }
        else
        {
            if(isNaN(x = convert.toDouble(args[0])))
                return Calc.Errors.Value;
            list.push(x)
        }
        list.sort(function(x, y)
        {
            return x - y
        });
        if(list.length === 0)
            return Calc.Errors.Number;
        if(k < 0 || 1 < k)
            return Calc.Errors.Number;
        var index = k * (list.length - 1);
        var rem = index % 1.0;
        index = parseInt(index,10);
        if(rem === 0.0)
            return list[index];
        else
            return convert.toDouble(list[index]) + rem * (convert.toDouble(list[index + 1]) - convert.toDouble(list[index]))
    }
    function st_percentrank(args)
    {
        var array = args[0];
        var xval;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        if(isNaN(xval = convert.toDouble(args[1])))
            return Calc.Errors.Value;
        var significance = 3;
        if(Calc._Helper._argumentExists(args,2))
            if(isNaN(significance = convert.toInt(args[2])))
                return Calc.Errors.Value;
        var x,
            pr;
        x = xval;
        var smaller = 0;
        var greater = 0;
        var equal = 0;
        var smaller_x = x;
        var greater_x = x;
        if(significance < 1)
            return Calc.Errors.Number;
        for(var i = 0; i < arrayHelper.getLength(array); i++)
        {
            var arrayo = arrayHelper.getValueByIndex(array,i);
            if(arrayo !== undefined && arrayo !== null)
            {
                var arrayi;
                if(isNaN(arrayi = convert.toDouble(arrayo)))
                    return Calc.Errors.Value;
                if(arrayi < x)
                {
                    smaller++;
                    if(smaller_x === x || smaller_x < arrayi)
                        smaller_x = arrayi
                }
                else if(arrayi > x)
                {
                    greater++;
                    if(greater_x === x || greater_x > arrayi)
                        greater_x = arrayi
                }
                else
                    equal++
            }
        }
        if(smaller + equal === 0 || greater + equal === 0)
            return Calc.Errors.NotAvailable;
        else if(greater === 0 && smaller === 0 && equal !== 0)
            return 1.0;
        smaller = convert.toDouble(smaller);
        greater = convert.toDouble(greater);
        equal = convert.toDouble(equal);
        if(equal === 1)
            pr = smaller / (smaller + greater);
        else if(equal === 0)
        {
            var a = (x - smaller_x) / (greater_x - smaller_x);
            pr = (smaller + a - 1.0) / (greater + smaller - 1.0)
        }
        else
            pr = (smaller + 0.5 * equal) / (smaller + equal + greater);
        return Functions._MathHelper.round(pr,significance > 15 ? 15 : significance)
    }
    function st_quartile(args)
    {
        var array = args[0];
        var convert = Calc.Convert;
        var quart = convert.toInt(args[1]);
        var k = 0.0;
        switch(quart)
        {
            case 0:
                k = 0.00;
                break;
            case 1:
                k = 0.25;
                break;
            case 2:
                k = 0.50;
                break;
            case 3:
                k = 0.75;
                break;
            case 4:
                k = 1.00;
                break;
            default:
                return Calc.Errors.Number
        }
        return st_percentile([array,k])
    }
    function __countIncludeSubtotals(args, includeSubtotals)
    {
        var n = 0;
        var convert = Calc.Convert;
        var rowCount,
            columnCount,
            row,
            column;
        for(var i = 0; i < args.length; i++)
            if(convert._isCalcArray(args[i]))
            {
                columnCount = args[i].getColumnCount();
                rowCount = args[i].getRowCount();
                for(column = 0; column < columnCount; column++)
                    for(row = 0; row < rowCount; row++)
                        if(convert.isNumber(args[i].getValue(row,column)))
                            n++
            }
            else if(convert._isCalcReference(args[i]))
            {
                var rangeCount = args[i].getRangeCount();
                columnCount = 0;
                rowCount = 0;
                for(var area = 0; area < rangeCount; area++)
                {
                    columnCount = args[i].getColumnCount(area);
                    for(column = 0; column < columnCount; column++)
                    {
                        rowCount = args[i].getRowCount(area);
                        for(row = 0; row < rowCount; row++)
                        {
                            var o;
                            if(args[i] instanceof Calc._SheetRangeReference)
                            {
                                for(var sheetIndex = 0; sheetIndex < args[i].getSheetCount(); sheetIndex++)
                                    if(includeSubtotals || !args[i].isSubtotal(sheetIndex,area,row,column))
                                        o = args[i].getValue(sheetIndex,area,row,column)
                            }
                            else if(includeSubtotals || !args[i].isSubtotal(area,row,column))
                                o = args[i].getValue(area,row,column);
                            if(convert.isNumber(o))
                                n++
                        }
                    }
                }
            }
            else if(convert.isNumber(args[i]))
                n++;
        return convert.toResult(n)
    }
    function st_count(args)
    {
        return __countIncludeSubtotals(args,true)
    }
    function __countaIncludeSubtotals(args, includeSubtotals)
    {
        var n = 0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
            if(arrayHelper.isArrayOrReference(args[i]))
            {
                for(var r = 0; r < arrayHelper.getRangeCount(args[i]); r++)
                    for(var j = 0; j < arrayHelper.getLength(args[i],r); j++)
                        if(includeSubtotals || !arrayHelper.isSubtotal(args[i],j,r))
                        {
                            var obj = arrayHelper.getValue(args[i],j,r);
                            if(obj !== undefined && obj !== null)
                                n++
                        }
            }
            else if(args[i] !== undefined && args[i] !== null)
                n++;
        return convert.toResult(n)
    }
    function st_counta(args)
    {
        return __countaIncludeSubtotals(args,true)
    }
    function st_countblank(args)
    {
        var count = 0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var r = 0; r < arrayHelper.getRangeCount(args[0]); r++)
            for(var i = 0; i < arrayHelper.getLength(args[0],r); i++)
                if(arrayHelper.getValue(args[0],i,r) === null)
                    count++;
        return convert.toDouble(count)
    }
    function __countifImp(range, criteria)
    {
        var count = 0.0;
        var crit = Functions._MathHelper.parseCriteria(criteria);
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var r = 0; r < arrayHelper.getRangeCount(range); r++)
            for(var i = 0; i < arrayHelper.getLength(range,r); i++)
            {
                var obj = arrayHelper.getValue(range,i,r);
                if(crit && crit(obj))
                    count++
            }
        return convert.toResult(count)
    }
    function st_countif(args)
    {
        var range = args[0];
        var criteria = args[1];
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var r,
            c;
        if(arrayHelper.isArrayOrReference(criteria))
        {
            var rowCount = arrayHelper.getRowCount(criteria);
            var colCount = arrayHelper.getColumnCount(criteria);
            var counts = [];
            for(r = 0; r < rowCount; r++)
            {
                counts[r] = [colCount];
                for(c = 0; c < colCount; c++)
                {
                    var val = __countifImp(range,arrayHelper.getValue(criteria,r,c));
                    if(convert.isError(val))
                        return val;
                    var d;
                    if(isNaN(d = convert.toDouble(val)))
                        return Calc.Errors.Value;
                    counts[r][c] = d
                }
            }
            var count = 0.0;
            for(r = 0; r < rowCount; r++)
                for(c = 0; c < colCount; c++)
                    count += counts[r][c];
            return count
        }
        return __countifImp(range,criteria)
    }
    function st_countifs(args)
    {
        var count = 0.0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var length = arrayHelper.getLength(args[0]);
        for(var i = 0; i < length; i++)
        {
            var condition = true;
            for(var j = 0; j < args.length; j = j + 2)
            {
                var range = args[j];
                var criteria = args[j + 1];
                var crit = Functions._MathHelper.parseCriteria(criteria);
                var obj = arrayHelper.getValueByIndex(range,i);
                condition = crit && crit(obj);
                if(!condition)
                    break
            }
            if(condition)
                count++
        }
        return convert.toResult(count)
    }
    function __dev1(args, stc, includeSubtotals)
    {
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(var i = 0; i < args.length; i++)
            if(convert.isError(args[i]))
                return args[i];
            else if(arrayHelper.isArrayOrReference(args[i]))
            {
                for(var j = 0; j < arrayHelper.getLength(args[i]); j++)
                    if(includeSubtotals || !arrayHelper.isSubtotalByIndex(args[i],j))
                    {
                        var obj = arrayHelper.getValueByIndex(args[i],j);
                        if(convert.isNumber(obj))
                        {
                            var x = convert.toDouble(obj);
                            stc.sumx += x;
                            stc.sumx2 += x * x;
                            stc.n++
                        }
                        else if(convert.isError(obj))
                            return obj
                    }
            }
            else
            {
                var x2;
                if(isNaN(x2 = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                stc.sumx += x2;
                stc.sumx2 += x2 * x2;
                stc.n++
            }
    }
    function __dev2(args, stc)
    {
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var x;
        for(var i = 0; i < args.length; i++)
            if(convert.isError(args[i]))
                return args[i];
            else if(arrayHelper.isArrayOrReference(args[i]))
                for(var j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    var obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj) || typeof obj === const_boolean)
                    {
                        x = convert.toDouble(obj);
                        stc.sumx += x;
                        stc.sumx2 += x * x;
                        stc.n++
                    }
                    else if(typeof obj === const_string)
                    {
                        x = 0.0;
                        stc.sumx += x;
                        stc.sumx2 += x * x;
                        stc.n++
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(x = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                stc.sumx += x;
                stc.sumx2 += x * x;
                stc.n++
            }
    }
    function st_avedev(args)
    {
        var x;
        var mean;
        var sumx = 0.0;
        var sumdev = 0.0;
        var n = 0,
            i,
            j,
            obj;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
                for(j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj))
                    {
                        x = convert.toDouble(obj);
                        sumx += x;
                        n++
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(x = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                sumx += x;
                n++
            }
        }
        mean = sumx / n;
        for(i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
                for(j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj))
                    {
                        x = convert.toDouble(obj);
                        sumdev += Math.abs(x - mean)
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(x = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                sumdev += Math.abs(x - mean)
            }
        }
        if(n === 0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(sumdev / n)
    }
    function __stdevIncludeSubtotals(args, includeSubtotals)
    {
        var data = {
                sumx: 0.0,
                sumx2: 0.0,
                n: 0.0
            };
        var flag = __dev1(args,data,includeSubtotals);
        var convert = Calc.Convert;
        if(convert.isError(flag))
            return flag;
        if(data.n <= 1.0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(Math.sqrt(Math.max(0.0,(data.n * data.sumx2 - data.sumx * data.sumx) / (data.n * (data.n - 1.0)))))
    }
    function st_stdev(args)
    {
        return __stdevIncludeSubtotals(args,true)
    }
    function st_stdeva(args)
    {
        var data = {
                sumx: 0.0,
                sumx2: 0.0,
                n: 0.0
            };
        var flag = __dev2(args,data);
        var convert = Calc.Convert;
        if(convert.isError(flag))
            return flag;
        if(data.n <= 1.0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(Math.sqrt(Math.max(0.0,(data.n * data.sumx2 - data.sumx * data.sumx) / (data.n * (data.n - 1.0)))))
    }
    function __stdevpIncludeSubtotals(args, includeSubtotals)
    {
        var data = {
                sumx: 0.0,
                sumx2: 0.0,
                n: 0.0
            };
        var flag = __dev1(args,data,includeSubtotals);
        var convert = Calc.Convert;
        if(convert.isError(flag))
            return flag;
        if(data.n <= 0.0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(Math.sqrt(Math.max(0.0,(data.n * data.sumx2 - data.sumx * data.sumx) / (data.n * data.n))))
    }
    function st_stdevp(args)
    {
        return __stdevpIncludeSubtotals(args,true)
    }
    function st_stdevpa(args)
    {
        var data = {
                sumx: 0.0,
                sumx2: 0.0,
                n: 0.0
            };
        var flag = __dev2(args,data);
        var convert = Calc.Convert;
        if(convert.isError(flag))
            return flag;
        if(data.n <= 0.0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(Math.sqrt(Math.max(0.0,(data.n * data.sumx2 - data.sumx * data.sumx) / (data.n * data.n))))
    }
    function __varrIncludeSubtotals(args, includeSubtotals)
    {
        var data = {
                sumx: 0.0,
                sumx2: 0.0,
                n: 0.0
            };
        var flag = __dev1(args,data,includeSubtotals);
        var convert = Calc.Convert;
        if(convert.isError(flag))
            return flag;
        if(data.n <= 1.0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(Math.max(0.0,(data.n * data.sumx2 - data.sumx * data.sumx) / (data.n * (data.n - 1.0))))
    }
    function st_varr(args)
    {
        return __varrIncludeSubtotals(args,true)
    }
    function st_vara(args)
    {
        var data = {
                sumx: 0.0,
                sumx2: 0.0,
                n: 0.0
            };
        var flag = __dev2(args,data);
        var convert = Calc.Convert;
        if(convert.isError(flag))
            return flag;
        if(data.n <= 1.0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(Math.max(0.0,(data.n * data.sumx2 - data.sumx * data.sumx) / (data.n * (data.n - 1.0))))
    }
    function __varpIncludeSubtotals(args, includeSubtotals)
    {
        var data = {
                sumx: 0.0,
                sumx2: 0.0,
                n: 0.0
            };
        var flag = __dev1(args,data,includeSubtotals);
        var convert = Calc.Convert;
        if(convert.isError(flag))
            return flag;
        if(data.n <= 0.0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(Math.max(0.0,(data.n * data.sumx2 - data.sumx * data.sumx) / (data.n * data.n)))
    }
    function st_varp(args)
    {
        return __varpIncludeSubtotals(args,true)
    }
    function st_varpa(args)
    {
        var data = {
                sumx: 0.0,
                sumx2: 0.0,
                n: 0.0
            };
        var flag = __dev2(args,data);
        var convert = Calc.Convert;
        if(convert.isError(flag))
            return flag;
        if(data.n <= 0.0)
            return Calc.Errors.DivideByZero;
        return convert.toResult(Math.max(0.0,(data.n * data.sumx2 - data.sumx * data.sumx) / (data.n * data.n)))
    }
    function st_covar(args)
    {
        var x;
        var y;
        var meanx;
        var meany;
        var sumx = 0.0;
        var sumy = 0.0;
        var sumprod = 0.0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var n = arrayHelper.getLength(args[0]);
        if(n === 0)
            return Calc.Errors.DivideByZero;
        if(n !== arrayHelper.getLength(args[1]))
            return Calc.Errors.NotAvailable;
        var count = 0,
            i,
            obj1,
            obj2;
        if(arrayHelper.isArrayOrReference(args[0]))
            for(i = 0; i < n; i++)
            {
                obj1 = arrayHelper.getValueByIndex(args[0],i);
                obj2 = arrayHelper.getValueByIndex(args[1],i);
                if(convert.isNumber(obj1) && convert.isNumber(obj2))
                {
                    x = convert.toDouble(obj1);
                    y = convert.toDouble(obj2);
                    sumx += x;
                    sumy += y;
                    count++
                }
                else if(convert.isError(obj1))
                    return obj1;
                else if(convert.isError(obj2))
                    return obj2
            }
        else
        {
            if(isNaN(x = convert.toDouble(args[0])) || isNaN(y = convert.toDouble(args[1])))
                return Calc.Errors.Value;
            sumx += x;
            sumy += y;
            count++
        }
        meanx = sumx / count;
        meany = sumy / count;
        if(arrayHelper.isArrayOrReference(args[0]))
            for(i = 0; i < n; i++)
            {
                obj1 = arrayHelper.getValueByIndex(args[0],i);
                obj2 = arrayHelper.getValueByIndex(args[1],i);
                if(convert.isNumber(obj1) && convert.isNumber(obj2))
                {
                    x = convert.toDouble(obj1);
                    y = convert.toDouble(obj2);
                    sumprod += (x - meanx) * (y - meany)
                }
                else if(convert.isError(obj1))
                    return obj1;
                else if(convert.isError(obj2))
                    return obj2
            }
        else
        {
            if(isNaN(x = convert.toDouble(args[0])) || isNaN(y = convert.toDouble(args[1])))
                return Calc.Errors.Value;
            sumprod += (x - meanx) * (y - meany)
        }
        if(count <= 1)
            return Calc.Errors.DivideByZero;
        return convert.toResult(sumprod / count)
    }
    function st_devsp(args)
    {
        var x;
        var mean;
        var sumx = 0.0;
        var sumdevsq = 0.0;
        var n = 0,
            i,
            j,
            obj;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(i = 0; i < args.length; i++)
        {
            if(convert.isError(args[i]))
                return args[i];
            if(arrayHelper.isArrayOrReference(args[i]))
                for(j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj))
                    {
                        x = convert.toDouble(arrayHelper.getValueByIndex(args[i],j));
                        sumx += x;
                        n++
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(x = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                sumx += x;
                n++
            }
        }
        mean = sumx / n;
        for(i = 0; i < args.length; i++)
            if(arrayHelper.isArrayOrReference(args[i]))
                for(j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj))
                    {
                        x = convert.toDouble(arrayHelper.getValueByIndex(args[i],j));
                        sumdevsq += (x - mean) * (x - mean)
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(x = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                sumdevsq += (x - mean) * (x - mean)
            }
        return convert.toResult(sumdevsq)
    }
    function st_normsdist(args)
    {
        var z;
        var convert = Calc.Convert;
        if(isNaN(z = convert.toDouble(args[0])))
            return Calc.Errors.Value;
        var Z_MAX = 6.0;
        var y;
        var x;
        var w;
        if(z === 0.0)
            x = 0.0;
        else
        {
            y = 0.5 * Math.abs(z);
            if(y >= Z_MAX * 0.5)
                x = 1.0;
            else if(y < 1.0)
            {
                w = y * y;
                x = ((((((((0.000124818987 * w - 0.001075204047) * w + 0.005198775019) * w - 0.019198292004) * w + 0.059054035642) * w - 0.151968751364) * w + 0.319152932694) * w - 0.531923007300) * w + 0.797884560593) * y * 2.0
            }
            else
            {
                y -= 2.0;
                x = (((((((((((((-0.000045255659 * y + 0.000152529290) * y - 0.000019538132) * y - 0.000676904986) * y + 0.001390604284) * y - 0.000794620820) * y - 0.002034254874) * y + 0.006549791214) * y - 0.010557625006) * y + 0.011630447319) * y - 0.009279453341) * y + 0.005353579108) * y - 0.002141268741) * y + 0.000535310849) * y + 0.999936657524
            }
        }
        return z > 0.0 ? (x + 1.0) * 0.5 : (1.0 - x) * 0.5
    }
    function st_normdist(args)
    {
        var x;
        var mean;
        var stdev;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toDouble(args[0])) || isNaN(mean = convert.toDouble(args[1])) || isNaN(stdev = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        var cumulative;
        if(isNaN(cumulative = convert.toBool(args[3])))
            return Calc.Errors.Value;
        if(stdev <= 0.0)
            return Calc.Errors.Number;
        if(cumulative)
        {
            var list = [];
            list[0] = (x - mean) / stdev;
            return st_normsdist(list)
        }
        else
            return convert.toResult(Math.exp(-((x - mean) * (x - mean)) / (2.0 * stdev * stdev)) / (Math.sqrt(2 * Math.PI) * stdev))
    }
    function st_norminv(args)
    {
        var prob;
        var mean;
        var stdev;
        var convert = Calc.Convert;
        if(isNaN(prob = convert.toDouble(args[0])) || isNaN(mean = convert.toDouble(args[1])) || isNaN(stdev = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        var q;
        var r;
        var val;
        if(prob < 0.0 || 1.0 < prob)
            return Calc.Errors.Number;
        if(stdev <= 0.0)
            return Calc.Errors.Number;
        q = prob - 0.5;
        if(Math.abs(q) <= 0.42)
        {
            r = q * q;
            val = q * (((-25.44106049637 * r + 41.39119773534) * r - 18.61500062529) * r + 2.50662823884) / ((((3.13082909833 * r - 21.06224101826) * r + 23.08336743743) * r + -8.47351093090) * r + 1.0)
        }
        else
        {
            r = prob;
            if(q > 0.0)
                r = 1.0 - prob;
            if(r > 2.2204460492503131e-016)
            {
                r = Math.sqrt(-Math.log(r));
                val = (((2.32121276858 * r + 4.85014127135) * r - 2.29796479134) * r - 2.78718931138) / ((1.63706781897 * r + 3.54388924762) * r + 1.0);
                if(q < 0.0)
                    val = -val
            }
            else if(r > 1e-300)
            {
                val = -2.0 * Math.log(prob);
                r = Math.log(6.283185307179586476925286766552 * val);
                r = r / val + (2.0 - r) / (val * val) + (-14.0 + 6.0 * r - r * r) / (2.0 * val * val * val);
                val = Math.sqrt(val * (1 - r));
                if(q < 0.0)
                    val = -val;
                return val
            }
            else if(q < 0.0)
                return-1.79769e+308;
            else
                return 1.79769e+308
        }
        var x = (val - 0.0) / 1.0;
        var denom = 0.398942280401432677939946059934 * Math.exp(-0.5 * x * x) / 1.0;
        var o = st_normdist([val,0.0,1.0,true]);
        if(convert.isError(o))
            return o;
        val = val - (o - prob) / denom;
        return mean + stdev * val
    }
    function st_normsinv(args)
    {
        var prob;
        var convert = Calc.Convert;
        if(isNaN(prob = convert.toDouble(args[0])))
            return Calc.Errors.Value;
        if(prob < 0.0 || 1.0 < prob)
            return Calc.Errors.Number;
        return st_norminv([prob,0.0,1.0])
    }
    function st_confidence(args)
    {
        var x;
        var stdev;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toDouble(args[0])) || isNaN(stdev = convert.toDouble(args[1])))
            return Calc.Errors.Value;
        var size = convert.toInt(args[2]);
        if(x <= 0.0 || x >= 1.0)
            return Calc.Errors.Number;
        if(stdev <= 0.0)
            return Calc.Errors.Number;
        if(size < 1)
            return Calc.Errors.Number;
        var list = [];
        list[0] = x / 2.0;
        var o = st_normsinv(list);
        if(convert.isError(o))
            return o;
        o = convert.toDouble(o);
        return-o * (stdev / Math.sqrt(size))
    }
    function __lanczos(p)
    {
        var x,
            tmp,
            ser;
        x = p;
        tmp = x + 5.5;
        tmp = tmp - (x + 0.5) * Math.log(tmp);
        ser = 1.000000000190015 + 76.18009172947146 / (p + 1.0);
        ser -= 86.50532032941678 / (p + 2.0);
        ser += 24.01409824083091 / (p + 3.0);
        ser -= 1.231739572450155 / (p + 4.0);
        ser += 0.001208650973866179 / (p + 5.0);
        ser -= 5.395239384953E-06 / (p + 6.0);
        var pt1 = Math.log(2.506628274631001 * ser / x);
        return pt1 - tmp
    }
    function __betacf(a, b, x)
    {
        var m,
            m2;
        var retval,
            aa,
            c,
            d,
            del,
            qab,
            qam,
            qap;
        var ITMAX = 300;
        var FPMIN = 1.0E-50;
        var EPSILON = 1.0E-20;
        qab = a + b;
        qap = a + 1.0;
        qam = a - 1.0;
        c = 1.0;
        d = 1.0 - qab * x / qap;
        if(Math.abs(d) < FPMIN)
            d = FPMIN;
        d = 1.0 / d;
        retval = d;
        for(m = 1; m <= ITMAX; m++)
        {
            m2 = m + m;
            aa = (b - m) * m * x / ((qam + m2) * (a + m2));
            d = 1.0 + aa * d;
            if(Math.abs(d) < FPMIN)
                d = FPMIN;
            c = 1.0 + aa / c;
            if(Math.abs(c) < FPMIN)
                c = FPMIN;
            d = 1.0 / d;
            retval *= d * c;
            aa = 0.0 - (a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
            d = 1.0 + aa * d;
            if(Math.abs(d) < FPMIN)
                d = FPMIN;
            c = 1.0 + aa / c;
            if(Math.abs(c) < FPMIN)
                c = FPMIN;
            d = 1.0 / d;
            del = d * c;
            retval *= del;
            if(Math.abs(del - 1.0) < EPSILON)
                break
        }
        return retval
    }
    function st_betadist(args)
    {
        var convert = Calc.Convert,
            helper = Calc._Helper;
        for(var i = 0; i < args.length; i++)
            if(convert.isError(args[i]))
                return args[i];
        var prob,
            alpha,
            beta;
        if(isNaN(prob = convert.toDouble(args[0])) || isNaN(alpha = convert.toDouble(args[1])) || isNaN(beta = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        var a = 0.0;
        if(helper._argumentExists(args,3))
            if(isNaN(a = convert.toDouble(args[3])))
                return Calc.Errors.Value;
        var b = 1.0;
        if(helper._argumentExists(args,4))
            if(isNaN(b = convert.toDouble(args[4])))
                return Calc.Errors.Value;
        var retval;
        var bt;
        if(alpha <= 0.0 || beta <= 0.0)
            return Calc.Errors.Number;
        if(prob < a || b < prob || a === b)
            return Calc.Errors.Number;
        var x = (prob - a) / (b - a);
        var badBetacf = false;
        var pt1 = __lanczos(alpha + beta);
        var pt2 = __lanczos(alpha);
        var pt3 = __lanczos(beta);
        var pt4 = Math.log(x);
        var pt5 = Math.log(1.0 - x);
        bt = Math.exp(pt1 - pt2 - pt3 + alpha * pt4 + beta * pt5);
        if(x < (alpha + 1.0) / (alpha + beta + 2.0))
            retval = bt * __betacf(beta,alpha,1.0 - x) / beta;
        else
            retval = 1.0 - bt * __betacf(beta,alpha,1.0 - x) / beta;
        if(badBetacf)
            retval = -1.0;
        return convert.toResult(retval)
    }
    function __d1mach(i)
    {
        var list = [];
        list[0] = 2.0;
        switch(i)
        {
            case 1:
                return 2.2250738585072014e-308;
            case 2:
                return 1.7976931348623158e+308;
            case 3:
                return Math.pow(2.0,-53.0);
            case 4:
                return Math.pow(2.0,1.0 - 53.0);
            case 5:
                return Functions._MathHelper.log10(list);
            default:
                return 0.0
        }
    }
    function __chebyshev_init(dos, nos, eta)
    {
        var i,
            ii;
        var err;
        if(nos < 1)
            return 0;
        err = 0.0;
        i = 0;
        var convert = Calc.Convert;
        for(ii = 1; ii <= nos; ii++)
        {
            i = nos - ii;
            err += Math.abs(convert.toDouble(dos[i]));
            if(err > eta)
                return i
        }
        return i
    }
    function __chebyshev_eval(x, a, n)
    {
        var b0,
            b1,
            b2,
            twox,
            i;
        if(n < 1 || n > 1000)
            return 0.0 / 0.0;
        if(x < -1.1 || x > 1.1)
            return 0.0 / 0.0;
        twox = x * 2;
        b2 = b1 = 0;
        b0 = 0;
        var convert = Calc.Convert;
        for(i = 1; i <= n; i++)
        {
            b2 = b1;
            b1 = b0;
            b0 = twox * b1 - b2 + convert.toDouble(a[n - i])
        }
        return(b0 - b2) * 0.5
    }
    function __lgammacor(x)
    {
        var algmcs = [];
        algmcs[0] = +0.1666389480451863247205729650822e+0;
        algmcs[1] = -0.1384948176067563840732986059135e-4;
        algmcs[2] = +0.9810825646924729426157171547487e-8;
        algmcs[3] = -0.1809129475572494194263306266719e-10;
        algmcs[4] = +0.6221098041892605227126015543416e-13;
        algmcs[5] = -0.3399615005417721944303330599666e-15;
        algmcs[6] = +0.2683181998482698748957538846666e-17;
        algmcs[7] = -0.2868042435334643284144622399999e-19;
        algmcs[8] = +0.3962837061046434803679306666666e-21;
        algmcs[9] = -0.6831888753985766870111999999999e-23;
        algmcs[10] = +0.1429227355942498147573333333333e-24;
        algmcs[11] = -0.3547598158101070547199999999999e-26;
        algmcs[12] = +0.1025680058010470912000000000000e-27;
        algmcs[13] = -0.3401102254316748799999999999999e-29;
        algmcs[14] = +0.1276642195630062933333333333333e-30;
        var nalgm = 0,
            xbig = 0.0,
            xmax = 0.0,
            tmp;
        if(nalgm === 0)
        {
            nalgm = __chebyshev_init(algmcs,15,__d1mach(3));
            xbig = 1.0 / Math.sqrt(__d1mach(3));
            xmax = Math.exp(Math.min(Math.log(__d1mach(2) / 12.0),-Math.log(12.0 * __d1mach(1))))
        }
        if(x < 10.0)
            return 0.0 / 0.0;
        else if(x >= xmax)
            return 2.2204460492503131e-016 * 2.2204460492503131e-016;
        else if(x < xbig)
        {
            tmp = 10.0 / x;
            return __chebyshev_eval(tmp * tmp * 2.0 - 1.0,algmcs,nalgm) / x
        }
        else
            return 1.0 / (x * 12.0)
    }
    function __logrelerr(x)
    {
        var alnrcs = [];
        alnrcs[0] = +0.10378693562743769800686267719098e+1;
        alnrcs[1] = -0.13364301504908918098766041553133e+0;
        alnrcs[2] = +0.19408249135520563357926199374750e-1;
        alnrcs[3] = -0.30107551127535777690376537776592e-2;
        alnrcs[4] = +0.48694614797154850090456366509137e-3;
        alnrcs[5] = -0.81054881893175356066809943008622e-4;
        alnrcs[6] = +0.13778847799559524782938251496059e-4;
        alnrcs[7] = -0.23802210894358970251369992914935e-5;
        alnrcs[8] = +0.41640416213865183476391859901989e-6;
        alnrcs[9] = -0.73595828378075994984266837031998e-7;
        alnrcs[10] = +0.13117611876241674949152294345011e-7;
        alnrcs[11] = -0.23546709317742425136696092330175e-8;
        alnrcs[12] = +0.42522773276034997775638052962567e-9;
        alnrcs[13] = -0.77190894134840796826108107493300e-10;
        alnrcs[14] = +0.14075746481359069909215356472191e-10;
        alnrcs[15] = -0.25769072058024680627537078627584e-11;
        alnrcs[16] = +0.47342406666294421849154395005938e-12;
        alnrcs[17] = -0.87249012674742641745301263292675e-13;
        alnrcs[18] = +0.16124614902740551465739833119115e-13;
        alnrcs[19] = -0.29875652015665773006710792416815e-14;
        alnrcs[20] = +0.55480701209082887983041321697279e-15;
        alnrcs[21] = -0.10324619158271569595141333961932e-15;
        alnrcs[22] = +0.19250239203049851177878503244868e-16;
        alnrcs[23] = -0.35955073465265150011189707844266e-17;
        alnrcs[24] = +0.67264542537876857892194574226773e-18;
        alnrcs[25] = -0.12602624168735219252082425637546e-18;
        alnrcs[26] = +0.23644884408606210044916158955519e-19;
        alnrcs[27] = -0.44419377050807936898878389179733e-20;
        alnrcs[28] = +0.83546594464034259016241293994666e-21;
        alnrcs[29] = -0.15731559416479562574899253521066e-21;
        alnrcs[30] = +0.29653128740247422686154369706666e-22;
        alnrcs[31] = -0.55949583481815947292156013226666e-23;
        alnrcs[32] = +0.10566354268835681048187284138666e-23;
        alnrcs[33] = -0.19972483680670204548314999466666e-24;
        alnrcs[34] = +0.37782977818839361421049855999999e-25;
        alnrcs[35] = -0.71531586889081740345038165333333e-26;
        alnrcs[36] = +0.13552488463674213646502024533333e-26;
        alnrcs[37] = -0.25694673048487567430079829333333e-27;
        alnrcs[38] = +0.48747756066216949076459519999999e-28;
        alnrcs[39] = -0.92542112530849715321132373333333e-29;
        alnrcs[40] = +0.17578597841760239233269760000000e-29;
        alnrcs[41] = -0.33410026677731010351377066666666e-30;
        alnrcs[42] = +0.63533936180236187354180266666666e-31;
        var nlnrel = 0,
            xmin = 0.0;
        if(nlnrel === 0)
        {
            nlnrel = __chebyshev_init(alnrcs,43,0.1 * __d1mach(3));
            xmin = -1.0 + Math.sqrt(__d1mach(4))
        }
        if(x <= -1)
            return 0.0 / 0.0;
        if(Math.abs(x) <= 0.375)
            return x * (1.0 - x * __chebyshev_eval(x / 0.375,alnrcs,nlnrel));
        else
            return Math.log(x + 1.0)
    }
    function __gamma(x)
    {
        var i,
            k,
            m,
            ga,
            gr,
            z,
            r = 1.0;
        var g = [1.0,0.5772156649015329,-0.6558780715202538,-0.420026350340952e-1,0.1665386113822915,-0.421977345555443e-1,-0.9621971527877e-2,0.7218943246663e-2,-0.11651675918591e-2,-0.2152416741149e-3,0.1280502823882e-3,-0.201348547807e-4,-0.12504934821e-5,0.1133027232e-5,-0.2056338417e-6,0.6116095e-8,0.50020075e-8,-0.11812746e-8,0.1043427e-9,0.77823e-11,-0.36968e-11,0.51e-12,-0.206e-13,-0.54e-14,0.14e-14];
        if(x > 171.0)
            return 1e308;
        var convert = Calc.Convert;
        if(x === convert.toInt(x))
            if(x > 0.0)
            {
                ga = 1.0;
                for(i = 2; i < x; i++)
                    ga *= i
            }
            else
                ga = 1e308;
        else
        {
            if(Math.abs(x) > 1.0)
            {
                z = Math.abs(x);
                m = convert.toInt(z);
                r = 1.0;
                for(k = 1; k <= m; k++)
                    r *= z - k;
                z -= m
            }
            else
                z = x;
            gr = g[24];
            for(k = 23; k >= 0; k--)
                gr = gr * z + g[k];
            ga = 1.0 / (gr * z);
            if(Math.abs(x) > 1.0)
            {
                ga *= r;
                if(x < 0.0)
                    ga = -Math.PI / (x * ga * Math.sin(Math.PI * x))
            }
        }
        return ga
    }
    function __lgamma(x)
    {
        var x0,
            x2,
            xp,
            gl,
            gl0,
            k,
            n = 0;
        var a = [8.333333333333333e-02,-2.777777777777778e-03,7.936507936507937e-04,-5.952380952380952e-04,8.417508417508418e-04,-1.917526917526918e-03,6.410256410256410e-03,-2.955065359477124e-02,1.796443723688307e-01,-1.39243221690590];
        x0 = x;
        var convert = Calc.Convert;
        if(x <= 0.0)
            return 1e308;
        else if(x === 1.0 || x === 2.0)
            return 0.0;
        else if(x <= 7.0)
        {
            n = convert.toInt(7 - x);
            x0 = x + n
        }
        x2 = 1.0 / (x0 * x0);
        xp = 2.0 * Math.PI;
        gl0 = a[9];
        for(k = 8; k >= 0; k--)
            gl0 = gl0 * x2 + a[k];
        gl = gl0 / x0 + 0.5 * Math.log(xp) + (x0 - 0.5) * Math.log(x0) - x0;
        if(x <= 7.0)
            for(k = 1; k <= n; k++)
            {
                gl -= Math.log(x0 - 1.0);
                x0 -= 1.0
            }
        return gl
    }
    function st_gammaln(args)
    {
        var x;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toDouble(args[0])))
            return Calc.Errors.Value;
        if(x <= 0.0)
            return Calc.Errors.Number;
        return __lgamma(x)
    }
    function __lbeta(a, b)
    {
        var corr,
            p,
            q;
        p = q = a;
        if(b < p)
            p = b;
        if(b > q)
            q = b;
        if(p < 0)
            return 0.0 / 0.0;
        else if(p === 0)
            return 1.79769e+308;
        if(p >= 10.0)
        {
            corr = __lgammacor(p) + __lgammacor(q) - __lgammacor(p + q);
            return Math.log(q) * -0.5 + 0.918938533204672741780329736406 + corr + (p - 0.5) * Math.log(p / (p + q)) + q * __logrelerr(-p / (p + q))
        }
        else if(q >= 10)
        {
            corr = __lgammacor(q) - __lgammacor(p + q);
            var list = [];
            list[0] = p;
            var val = st_gammaln(list);
            var convert = Calc.Convert;
            if(convert.isError(val))
                return 0.0 / 0.0;
            return convert.toDouble(val) + corr + p - p * Math.log(p + q) + (q - 0.5) * __logrelerr(-p / (p + q))
        }
        else
        {
            var a1 = __gamma(p);
            var a2 = __gamma(q);
            var a3 = __gamma(p + q);
            return Math.log(a1 * (a2 / a3))
        }
    }
    function __pbeta_raw(x, pin, qin)
    {
        var ans,
            c,
            finsum,
            p,
            ps,
            p1,
            q,
            term,
            xb,
            xi,
            y;
        var n,
            i,
            ib;
        var eps = 0.0;
        var alneps = 0.0;
        var sml = 0.0;
        var alnsml = 0.0;
        if(eps === 0.0)
        {
            eps = __d1mach(3);
            alneps = Math.log(eps);
            sml = __d1mach(1);
            alnsml = Math.log(sml)
        }
        y = x;
        p = pin;
        q = qin;
        if(p / (p + q) < x)
        {
            y = 1.0 - y;
            p = qin;
            q = pin
        }
        if((p + q) * y / (p + 1.0) < eps)
        {
            ans = 0.0;
            xb = p * Math.log(Math.max(y,sml)) - Math.log(p) - __lbeta(p,q);
            if(xb > alnsml && y !== 0.0)
                ans = Math.exp(xb);
            if(y !== x || p !== pin)
                ans = 1.0 - ans
        }
        else
        {
            ps = q - Math.floor(q);
            if(ps === 0.0)
                ps = 1.0;
            xb = p * Math.log(y) - __lbeta(ps,p) - Math.log(p);
            ans = 0.0;
            var convert = Calc.Convert;
            if(xb >= alnsml)
            {
                ans = Math.exp(xb);
                term = ans * p;
                if(ps !== 1.0)
                {
                    n = convert.toInt(Math.max(alneps / Math.log(y),4.0));
                    for(i = 1; i <= n; i++)
                    {
                        xi = i;
                        term = term * (xi - ps) * y / xi;
                        ans = ans + term / (p + xi)
                    }
                }
            }
            if(q > 1.0)
            {
                xb = p * Math.log(y) + q * Math.log(1.0 - y) - __lbeta(p,q) - Math.log(q);
                ib = convert.toInt(Math.max(xb / alnsml,0.0));
                term = Math.exp(xb - ib * alnsml);
                c = 1.0 / (1.0 - y);
                p1 = q * c / (p + q - 1.0);
                finsum = 0;
                n = convert.toInt(q);
                if(q === n)
                    n = n - 1;
                for(i = 1; i <= n; i++)
                {
                    if(p1 <= 1 && term / eps <= finsum)
                        break;
                    xi = i;
                    term = (q - xi + 1.0) * c * term / (p + q - xi);
                    if(term > 1.0)
                    {
                        ib = ib - 1;
                        term = term * sml
                    }
                    if(ib === 0)
                        finsum = finsum + term
                }
                ans = ans + finsum
            }
            if(y !== x || p !== pin)
                ans = 1.0 - ans;
            ans = Math.max(Math.min(ans,1.0),0.0)
        }
        return ans
    }
    function __pbeta(x, pin, qin)
    {
        if(pin <= 0 || qin <= 0)
            return 0.0 / 0.0;
        if(x <= 0)
            return 0;
        if(x >= 1)
            return 1;
        return __pbeta_raw(x,pin,qin)
    }
    function st_betainv(args)
    {
        var convert = Calc.Convert,
            helper = Calc._Helper;
        for(var i = 0; i < args.length; i++)
            if(convert.isError(args[i]))
                return args[i];
        var prob,
            alpha,
            beta;
        if(isNaN(prob = convert.toDouble(args[0])) || isNaN(alpha = convert.toDouble(args[1])) || isNaN(beta = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        var aa = 0.0;
        if(helper._argumentExists(args,3))
            if(isNaN(aa = convert.toDouble(args[3])))
                return Calc.Errors.Value;
        var bb = 1.0;
        if(helper._argumentExists(args,4))
            if(isNaN(bb = convert.toDouble(args[4])))
                return Calc.Errors.Value;
        if(prob <= 0.0 || 1.0 <= prob)
            return Calc.Errors.Number;
        if(alpha <= 0.0 || beta <= 0.0)
            return Calc.Errors.Number;
        var const1 = 2.30753,
            const2 = 0.27061,
            const3 = 0.99229,
            const4 = 0.04481,
            zero = 0.0,
            fpu = 3.0e-308,
            acu_min = 1.0e-300,
            lower = fpu;
        var upper = 1.0 - 2.22e-16,
            five = 5.0,
            six = 6.0,
            two = 2.0;
        var swap_tail,
            i_pb,
            i_inn,
            a,
            adj,
            logbeta,
            g,
            h,
            pp,
            prev,
            qq,
            r,
            s,
            t,
            tx,
            w,
            y,
            yprev,
            acu,
            xinbta;
        xinbta = prob;
        if(alpha < zero || beta < zero || prob < zero || prob > 1.0)
            return 0.0 / 0.0;
        if(prob === zero || prob === 1.0)
            return prob;
        logbeta = __lbeta(alpha,beta);
        if(prob <= 0.5)
        {
            a = prob;
            pp = alpha;
            qq = beta;
            swap_tail = 0
        }
        else
        {
            a = 1.0 - prob;
            pp = beta;
            qq = alpha;
            swap_tail = 1
        }
        r = Math.sqrt(-Math.log(a * a));
        y = r - (const1 + const2 * r) / (1.0 + (const3 + const4 * r) * r);
        if(pp > 1.0 && qq > 1.0)
        {
            r = (y * y - 3.0) / 6.0;
            s = 1.0 / (pp + pp - 1.0);
            t = 1.0 / (qq + qq - 1.0);
            h = 2.0 / (s + t);
            w = y * Math.sqrt(h + r) / h - (t - s) * (r + five / six - two / (3.0 * h));
            xinbta = pp / (pp + qq * Math.exp(w + w))
        }
        else
        {
            r = qq + qq;
            t = 1.0 / (9.0 * qq);
            t = r * Math.pow(1.0 - t + y * Math.sqrt(t),3.0);
            if(t <= zero)
                xinbta = 1.0 - Math.exp((Math.log((1.0 - a) * qq) + logbeta) / qq);
            else
            {
                t = (4.0 * pp + r - two) / t;
                if(t <= 1.0)
                    xinbta = Math.exp((Math.log(a * pp) + logbeta) / pp);
                else
                    xinbta = 1.0 - two / (t + 1.0)
            }
        }
        r = 1.0 - pp;
        t = 1.0 - qq;
        yprev = zero;
        adj = 1.0;
        if(xinbta < lower)
            xinbta = lower;
        else if(xinbta > upper)
            xinbta = upper;
        acu = Math.max(acu_min,Math.pow(10.0,-13.0 - 2.5 / (pp * pp) - 0.5 / (a * a)));
        tx = prev = zero;
        for(i_pb = 0; i_pb < 1000; i_pb++)
        {
            y = __pbeta_raw(xinbta,pp,qq);
            y = (y - a) * Math.exp(logbeta + r * Math.log(xinbta) + t * Math.log(1.0 - xinbta));
            if(y * yprev <= zero)
                prev = Math.max(Math.abs(adj),fpu);
            g = 1.0;
            for(i_inn = 0; i_inn < 1000; i_inn++)
            {
                adj = g * y;
                if(Math.abs(adj) < prev)
                {
                    tx = xinbta - adj;
                    if(tx >= zero && tx <= 1.0)
                    {
                        if(prev <= acu)
                        {
                            if(swap_tail !== 0)
                                xinbta = 1.0 - xinbta;
                            return convert.toResult((bb - aa) * xinbta + aa)
                        }
                        if(Math.abs(y) <= acu)
                        {
                            if(swap_tail !== 0)
                                xinbta = 1.0 - xinbta;
                            return convert.toResult((bb - aa) * xinbta + aa)
                        }
                        if(tx !== zero && tx !== 1.0)
                            break
                    }
                }
                g /= 3.0
            }
            var xtrunc = tx;
            if(xtrunc === xinbta)
            {
                if(swap_tail !== 0)
                    xinbta = 1.0 - xinbta;
                return convert.toResult((bb - aa) * xinbta + aa)
            }
            xinbta = tx;
            yprev = y
        }
        if(swap_tail !== 0)
            xinbta = 1.0 - xinbta;
        return convert.toResult((bb - aa) * xinbta + aa)
    }
    function st_binomdist(args)
    {
        var x,
            n;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toInt(args[0])) || isNaN(n = convert.toInt(args[1])))
            return Calc.Errors.Value;
        var p;
        if(isNaN(p = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        var cum;
        if(isNaN(cum = convert.toBool(args[3])))
            return Calc.Errors.Value;
        if(x < 0 || n < 0 || n < x || p < 0.0 || 1.0 < p)
            return Calc.Errors.Number;
        var q,
            factor,
            i;
        if(!cum)
        {
            q = 1.0 - p;
            factor = Math.pow(q,n);
            if(factor === 0.0)
            {
                factor = Math.pow(p,n);
                if(factor === 0.0)
                    return Calc.Errors.Number;
                else
                {
                    for(i = 0; i < n - x && factor > 0.0; i++)
                        factor *= convert.toDouble(n - i) / convert.toDouble((i + 1) * q / p);
                    return factor
                }
            }
            else
            {
                for(i = 0; i < x && factor > 0.0; i++)
                    factor *= convert.toDouble(n - i) / convert.toDouble((i + 1) * p / q);
                return factor
            }
        }
        else if(n === x)
            return 1.0;
        else
        {
            q = 1.0 - p;
            factor = Math.pow(q,n);
            var sum;
            if(factor === 0.0)
            {
                factor = Math.pow(p,n);
                if(factor === 0.0)
                    return Calc.Errors.Number;
                else
                {
                    sum = 1.0 - factor;
                    for(i = 0; i < n - x && factor > 0.0; i++)
                    {
                        factor *= convert.toDouble(n - i) / convert.toDouble((i + 1) * q / p);
                        sum -= factor
                    }
                    if(sum < 0.0)
                        return 0.0;
                    else
                        return sum
                }
            }
            else
            {
                sum = factor;
                for(i = 0; i < x && factor > 0.0; i++)
                {
                    factor *= convert.toDouble(n - i) / convert.toDouble((i + 1) * p / q);
                    sum += factor
                }
                return sum
            }
        }
    }
    function st_negbinomdist(args)
    {
        var x,
            r;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toInt(args[0])) || isNaN(r = convert.toInt(args[1])))
            return Calc.Errors.Value;
        var p;
        if(isNaN(p = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        if(p < 0.0 || p >= 1.0)
            return Calc.Errors.Number;
        if(x + r - 1 <= 0)
            return Calc.Errors.Number;
        var o = Functions._MathHelper.combin(x + r - 1,r - 1);
        if(convert.isError(o))
            return o;
        var pt1 = convert.toDouble(o);
        var pt2 = Math.pow(p,r);
        var pt3 = Math.pow(1.0 - p,x);
        return convert.toResult(pt1 * pt2 * pt3)
    }
    function st_critbinom(args)
    {
        var n;
        var convert = Calc.Convert;
        if(isNaN(n = convert.toInt(args[0])))
            return Calc.Errors.Value;
        var p,
            alpha;
        if(isNaN(p = convert.toDouble(args[1])) || isNaN(alpha = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        if(n < 0 || p < 0.0 || 1.0 < p || alpha <= 0.0 || 1.0 <= alpha)
            return Calc.Errors.Number;
        else
        {
            var q = 1.0 - p;
            var factor = Math.pow(q,n);
            var sum,
                i;
            if(factor === 0.0)
            {
                factor = Math.pow(p,n);
                if(factor === 0.0)
                    return Calc.Errors.Number;
                else
                {
                    sum = 1.0 - factor;
                    for(i = 0; i < n && sum >= alpha; i++)
                    {
                        factor *= convert.toDouble(n - i) / convert.toDouble((i + 1) * q / p);
                        sum -= factor
                    }
                    return convert.toDouble(n - i)
                }
            }
            else
            {
                sum = factor;
                for(i = 0; i < n && sum < alpha; i++)
                {
                    factor *= convert.toDouble(n - i) / convert.toDouble((i + 1) * p / q);
                    sum += factor
                }
                return convert.toDouble(i)
            }
        }
    }
    function st_chidist(args)
    {
        var convert = Calc.Convert;
        for(var i = 0; i < args.length; i++)
            if(convert.isError(args[i]))
                return args[i];
        var x,
            df;
        if(isNaN(x = convert.toDouble(args[0])) || isNaN(df = convert.toDouble(args[1])))
            return Calc.Errors.Value;
        if(x < 0.0)
            return Calc.Errors.Number;
        if(df < 1.0 || df > Math.pow(10.0,10.0))
            return Calc.Errors.Number;
        var LOG_SQRT_PI = Math.log(Math.sqrt(Math.PI));
        var I_SQRT_PI = 1.0 / Math.sqrt(Math.PI);
        var e,
            s,
            z,
            c,
            a,
            y = 0.0,
            x1 = x;
        a = 0.5 * x1;
        var even = df % 2 === 0;
        if(df > 1.0)
            y = Math.exp(-a);
        var list = [];
        list[0] = -Math.sqrt(x1);
        var o = st_normsdist(list);
        if(convert.isError(o))
            return o;
        var zz = convert.toDouble(o);
        s = even ? y : 2.0 * zz;
        if(df > 2)
        {
            x1 = 0.5 * (df - 1.0);
            z = even ? 1.0 : 0.5;
            if(a > 20.0)
            {
                e = even ? 0.0 : LOG_SQRT_PI;
                c = Math.log(a);
                while(z <= x1)
                {
                    e = Math.log(z) + e;
                    s += Math.exp(c * z - a - e);
                    z += 1.0
                }
                return s
            }
            else
            {
                e = even ? 1.0 : I_SQRT_PI / Math.sqrt(a);
                c = 0.0;
                while(z <= x1)
                {
                    e = e * (a / z);
                    c = c + e;
                    z += 1.0
                }
                return c * y + s
            }
        }
        else
            return s
    }
    function st_gammadist(args)
    {
        var x,
            alpha,
            beta;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toDouble(args[0])) || isNaN(alpha = convert.toDouble(args[1])) || isNaN(beta = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        var cum;
        if(isNaN(cum = convert.toBool(args[3])))
            return Calc.Errors.Value;
        if(x < 0.0 || alpha <= 0.0 || beta <= 0.0)
            return Calc.Errors.Number;
        if(cum === false)
        {
            var pt1 = Math.pow(beta,alpha);
            if(isNaN(pt1) || !isFinite(pt1))
                return Calc.Errors.DivideByZero;
            var pt2 = 1.0 / (pt1 * __gamma(alpha));
            var pt3 = Math.pow(x,alpha - 1.0);
            var pt4 = Math.exp(-(x / beta));
            var pt5 = pt3 * pt4;
            return pt2 * pt5
        }
        else
        {
            var pn1,
                pn2,
                pn3,
                pn4,
                pn5,
                pn6,
                arg,
                c,
                rn,
                a,
                b,
                an;
            var sum,
                o;
            var third = 1.0 / 3.0;
            var xbig = 1.0e+8;
            var oflo = 1.0e+37;
            var plimit = 1000.0e0;
            var elimit = -88.0e0;
            x = x / beta;
            if(x <= 0.0)
                return Calc.Errors.Number;
            if(alpha > plimit)
            {
                pn1 = Math.sqrt(alpha) * 3.0 * (Math.pow(x / alpha,third) + 1.0 / (alpha * 9.0) - 1.0);
                o = st_normdist([pn1,0.0,1.0,true]);
                if(convert.isError(o))
                    return o;
                return convert.toDouble(o)
            }
            if(x > xbig)
                return 1.0;
            if(x <= 1.0 || x < alpha)
            {
                var list = [];
                list[0] = alpha + 1.0;
                o = st_gammaln(list);
                if(convert.isError(o))
                    return o;
                arg = alpha * Math.log(x) - x - convert.toDouble(o);
                c = 1.0;
                sum = 1.0;
                a = alpha;
                do
                {
                    a = a + 1.0;
                    c = c * x / a;
                    sum = sum + c
                } while(c > 2.2204460492503131e-016);
                arg = arg + Math.log(sum);
                sum = 0.0;
                if(arg >= elimit)
                    sum = Math.exp(arg)
            }
            else
            {
                var list1 = [];
                list1[0] = alpha;
                o = st_gammaln(list1);
                if(convert.isError(o))
                    return o;
                arg = alpha * Math.log(x) - x - convert.toDouble(o);
                a = 1.0 - alpha;
                b = a + x + 1.0;
                c = 0.0;
                pn1 = 1.0;
                pn2 = x;
                pn3 = x + 1.0;
                pn4 = x * b;
                sum = pn3 / pn4;
                for(; ; )
                {
                    a = a + 1.0;
                    b = b + 2.0;
                    c = c + 1.0;
                    an = a * c;
                    pn5 = b * pn3 - an * pn1;
                    pn6 = b * pn4 - an * pn2;
                    if(Math.abs(pn6) > 0.0)
                    {
                        rn = pn5 / pn6;
                        if(Math.abs(sum - rn) <= Math.min(2.2204460492503131e-016,2.2204460492503131e-016 * rn))
                            break;
                        sum = rn
                    }
                    pn1 = pn3;
                    pn2 = pn4;
                    pn3 = pn5;
                    pn4 = pn6;
                    if(Math.abs(pn5) >= oflo)
                    {
                        pn1 = pn1 / oflo;
                        pn2 = pn2 / oflo;
                        pn3 = pn3 / oflo;
                        pn4 = pn4 / oflo
                    }
                }
                arg = arg + Math.log(sum);
                sum = 1.0;
                if(arg >= elimit)
                    sum = 1.0 - Math.exp(arg)
            }
            return sum
        }
    }
    function st_gammainv(args)
    {
        var prob,
            alpha,
            beta;
        var convert = Calc.Convert;
        if(isNaN(prob = convert.toDouble(args[0])) || isNaN(alpha = convert.toDouble(args[1])) || isNaN(beta = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        var C7 = 4.67,
            C8 = 6.66,
            C9 = 6.73,
            C10 = 13.32,
            C11 = 60.0,
            C12 = 70.0,
            C13 = 84.0,
            C14 = 105.0;
        var C15 = 120.0,
            C16 = 127.0,
            C17 = 140.0,
            C18 = 1175.0,
            C19 = 210.0,
            C20 = 252.0,
            C21 = 2264.0;
        var C22 = 294.0,
            C23 = 346.0,
            C24 = 420.0,
            C25 = 462.0,
            C26 = 606.0,
            C27 = 672.0,
            C28 = 707.0;
        var C29 = 735.0,
            C30 = 889.0,
            C31 = 932.0,
            C32 = 966.0,
            C33 = 1141.0,
            C34 = 1182.0,
            C35 = 1278.0;
        var C36 = 1740.0,
            C37 = 2520.0,
            C38 = 5040.0,
            EPS0 = 5.0e-7,
            EPS1 = 1.0e-2,
            EPS2 = 5.0e-7;
        var MAXIT = 20.0,
            pMIN = 0.000002,
            pMAX = 0.999998;
        var a,
            b,
            c,
            ch,
            g,
            p1,
            v;
        var p2,
            q,
            s1,
            s2,
            s3,
            s4,
            s5,
            s6,
            t,
            x;
        var i;
        if(prob < 0.0 || 1.0 < prob || alpha <= 0.0 || beta <= 0.0)
            return Calc.Errors.Number;
        if(prob < pMIN)
            return 0.0;
        if(prob > pMAX)
            return 1.79769e+308;
        v = 2.0 * alpha;
        c = alpha - 1.0;
        var list = [];
        list[0] = alpha;
        var o = st_gammaln(list);
        if(convert.isError(o))
            return o;
        g = convert.toDouble(o);
        if(v < -1.24 * Math.log(prob))
        {
            ch = Math.pow(prob * alpha * Math.exp(g + alpha * 0.69314718055994530942),1.0 / alpha);
            if(ch < EPS0)
                return 0.0 / 0.0
        }
        else if(v > 0.32)
        {
            o = st_norminv([prob,0.0,1.0]);
            if(convert.isError(o))
                return o;
            x = convert.toDouble(o);
            p1 = 0.222222 / v;
            ch = v * Math.pow(x * Math.sqrt(p1) + 1.0 - p1,3.0);
            if(ch > 2.2 * v + 6)
                ch = -2.0 * (Math.log(1.0 - prob) - c * Math.log(0.5 * ch) + g)
        }
        else
        {
            ch = 0.4;
            a = Math.log(1.0 - prob) + g + c * 0.69314718055994530942;
            do
            {
                q = ch;
                p1 = 1.0 + ch * (C7 + ch);
                p2 = ch * (C9 + ch * (C8 + ch));
                t = -0.5 + (C7 + 2 * ch) / p1 - (C9 + ch * (C10 + 3.0 * ch)) / p2;
                ch -= (1.0 - Math.exp(a + 0.5 * ch) * p2 / p1) / t
            } while(Math.abs(q / ch - 1.0) > EPS1)
        }
        for(i = 1; i <= MAXIT; i++)
        {
            q = ch;
            p1 = 0.5 * ch;
            o = st_gammadist([p1,alpha,1.0,true]);
            if(convert.isError(o))
                return o;
            p2 = prob - convert.toDouble(o);
            t = p2 * Math.exp(alpha * 0.69314718055994530942 + g + p1 - c * Math.log(ch));
            b = t / ch;
            a = 0.5 * t - b * c;
            s1 = (C19 + a * (C17 + a * (C14 + a * (C13 + a * (C12 + C11 * a))))) / C24;
            s2 = (C24 + a * (C29 + a * (C32 + a * (C33 + C35 * a)))) / C37;
            s3 = (C19 + a * (C25 + a * (C28 + C31 * a))) / C37;
            s4 = (C20 + a * (C27 + C34 * a) + c * (C22 + a * (C30 + C36 * a))) / C38;
            s5 = (C13 + C21 * a + c * (C18 + C26 * a)) / C37;
            s6 = (C15 + c * (C23 + C16 * c)) / C38;
            ch = ch + t * (1 + 0.5 * t * s1 - b * c * (s1 - b * (s2 - b * (s3 - b * (s4 - b * (s5 - b * s6))))));
            if(Math.abs(q / ch - 1) > EPS2)
                return 0.5 * beta * ch
        }
        return 0.5 * beta * ch
    }
    function st_chiinv(args)
    {
        var convert = Calc.Convert;
        for(var i = 0; i < args.length; i++)
            if(convert.isError(args[i]))
                return args[i];
        var x;
        if(isNaN(x = convert.toDouble(args[0])))
            return Calc.Errors.Value;
        var df;
        if(isNaN(df = convert.toInt(args[1])))
            return Calc.Errors.Value;
        if(x < 0.0 || x > 1.0)
            return Calc.Errors.Number;
        if(df < 1.0 || df > Math.pow(10.0,10.0))
            return Calc.Errors.Number;
        var p = 1.0 - x;
        var o = st_gammainv([p,0.5 * df,2.0]);
        return convert.toDouble(o)
    }
    function st_chitest(args)
    {
        var sum = 0.0;
        var df;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var r = arrayHelper.getRowCount(args[0]);
        var c = arrayHelper.getColumnCount(args[0]);
        if(r !== arrayHelper.getRowCount(args[1]) || c !== arrayHelper.getColumnCount(args[1]))
            return Calc.Errors.NotAvailable;
        if(r > 1 && c > 1)
            df = (r - 1) * (c - 1);
        else if(r > 1 && c === 1)
            df = r - 1;
        else if(r === 1 && c > 1)
            df = c - 1;
        else
            return Calc.Errors.NotAvailable;
        for(var i = 0; i < r; i++)
            for(var j = 0; j < c; j++)
            {
                var item0 = arrayHelper.getValue(args[0],i,j);
                if(convert.isError(item0))
                    return item0;
                var item1 = arrayHelper.getValue(args[1],i,j);
                if(convert.isError(item1))
                    return item1;
                var a,
                    e;
                if(isNaN(a = convert.toDouble(item0)) || isNaN(e = convert.toDouble(item1)))
                    return Calc.Errors.Value;
                if(e === 0)
                    return Calc.Errors.DivideByZero;
                sum += (a - e) * (a - e) / e
            }
        return st_chidist([sum,df])
    }
    function st_correl(args)
    {
        var sumx = 0.0,
            sumy = 0.0,
            sumx2 = 0.0,
            sumy2 = 0.0,
            sumprod = 0.0,
            meanx,
            meany,
            stdevx,
            stdevy;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var n = arrayHelper.getLength(args[0]);
        if(n !== arrayHelper.getLength(args[1]))
            return Calc.Errors.NotAvailable;
        var count = 0,
            i,
            x,
            y,
            objx,
            objy;
        for(i = 0; i < n; i++)
        {
            objx = arrayHelper.getValueByIndex(args[0],i);
            objy = arrayHelper.getValueByIndex(args[1],i);
            if(convert.isNumber(objx) && convert.isNumber(objy))
            {
                x = convert.toDouble(objx);
                y = convert.toDouble(objy);
                sumx += x;
                sumy += y;
                sumx2 += x * x;
                sumy2 += y * y;
                count++
            }
            else if(convert.isError(objx))
                return objx;
            else if(convert.isError(objy))
                return objy
        }
        if(count <= 1)
            return Calc.Errors.DivideByZero;
        meanx = sumx / count;
        meany = sumy / count;
        stdevx = Math.sqrt((count * sumx2 - sumx * sumx) / (count * (count - 1)));
        stdevy = Math.sqrt((count * sumy2 - sumy * sumy) / (count * (count - 1)));
        if(stdevx === 0.0 || stdevy === 0.0)
            return Calc.Errors.DivideByZero;
        for(i = 0; i < n; i++)
        {
            objx = arrayHelper.getValueByIndex(args[0],i);
            objy = arrayHelper.getValueByIndex(args[1],i);
            if(convert.isNumber(objx) && convert.isNumber(objy))
            {
                x = convert.toDouble(objx);
                y = convert.toDouble(objy);
                sumprod += (x - meanx) * (y - meany)
            }
        }
        return convert.toResult(sumprod / convert.toDouble((count - 1) * stdevx * stdevy))
    }
    function st_expondist(args)
    {
        var convert = Calc.Convert;
        for(var i = 0; i < args.length; i++)
            if(convert.isError(args[i]))
                return args[i];
        var x,
            lambda;
        if(isNaN(x = convert.toDouble(args[0])) || isNaN(lambda = convert.toDouble(args[1])))
            return Calc.Errors.Value;
        var cum;
        if(isNaN(cum = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        if(x < 0.0)
            return Calc.Errors.Number;
        if(lambda <= 0.0)
            return Calc.Errors.Number;
        var exp = Math.exp(-lambda * x);
        return convert.toResult(cum ? 1.0 - exp : lambda * exp)
    }
    function st_fdist(args)
    {
        var x,
            df1,
            df2;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toDouble(args[0])) || isNaN(df1 = convert.toDouble(args[1])) || isNaN(df2 = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        if(x < 0.0 || df1 < 1.0 || df1 >= Math.pow(10.0,10.0) || df2 < 1.0 || df2 >= Math.pow(10.0,10.0))
            return Calc.Errors.Number;
        var a1 = df1 * x / (df1 * x + df2);
        var a2 = 0.5 * df1;
        var a3 = 0.5 * df2;
        var o = st_betadist([a1,a2,a3]);
        if(convert.isError(o))
            return o;
        return 1.0 - convert.toDouble(o)
    }
    function st_finv(args)
    {
        var f,
            df1,
            df2;
        var convert = Calc.Convert;
        if(isNaN(f = convert.toDouble(args[0])) || isNaN(df1 = convert.toDouble(args[1])) || isNaN(df2 = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        if(f < 0.0 || 1.0 < f || df1 < 1.0 || df1 >= Math.pow(10.0,10.0) || df2 < 1.0 || df2 >= Math.pow(10.0,10.0))
            return Calc.Errors.Number;
        var x = 1.0 - f;
        var o = st_betainv([1.0 - x,df2 / 2.0,df1 / 2.0]);
        if(convert.isError(o))
            return o;
        return(1.0 / convert.toDouble(o) - 1.0) * (df2 / df1)
    }
    function st_fisher(args)
    {
        var x;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toDouble(args[0])))
            return Calc.Errors.Value;
        if(x <= -1.0 || 1.0 <= x)
            return Calc.Errors.Number;
        return Math.log((1.0 + x) / (1.0 - x)) / 2.0
    }
    function st_fisherinv(args)
    {
        var y;
        var convert = Calc.Convert;
        if(isNaN(y = convert.toDouble(args[0])))
            return Calc.Errors.Value;
        var pt1 = Math.exp(2.0 * y) - 1.0;
        var pt2 = Math.exp(2.0 * y) + 1.0;
        if(!isFinite(pt1) && pt1 > 0 && !isFinite(pt2) && pt2 > 0)
            return 1.0;
        return pt1 / pt2
    }
    function __stat(closure, array)
    {
        var x,
            dx,
            dm;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var y = arrayHelper.getLength(array);
        for(var i = 0; i < arrayHelper.getLength(array); i++)
        {
            var obj = arrayHelper.getValueByIndex(array,i);
            if(convert.isNumber(obj))
            {
                x = convert.toDouble(arrayHelper.getValueByIndex(array,i));
                dx = x - closure.M;
                dm = dx / (closure.N + 1);
                closure.M += dm;
                closure.Q += closure.N * dx * dm;
                closure.N++;
                closure.sum += x
            }
        }
    }
    function st_ftest(args)
    {
        var array1 = args[0];
        var array2 = args[1];
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var i,
            item;
        for(i = 0; i < arrayHelper.getLength(array1); i++)
        {
            item = arrayHelper.getValueByIndex(array1,i);
            if(convert.isError(item))
                return item
        }
        for(i = 0; i < arrayHelper.getLength(array2); i++)
        {
            item = arrayHelper.getValueByIndex(array2,i);
            if(convert.isError(item))
                return item
        }
        var cl = {
                N: 0,
                M: 0.0,
                Q: 0.0,
                afun_flag: false,
                sum: 0.0
            };
        var var1,
            var2,
            p,
            dof1,
            dof2;
        __stat(cl,array1);
        dof1 = cl.N - 1;
        if(cl.N === 1.0)
            return Calc.Errors.DivideByZero;
        var1 = cl.Q / (cl.N - 1.0);
        if(var1 === 0.0)
            return Calc.Errors.DivideByZero;
        cl.N = 0;
        cl.M = 0.0;
        cl.Q = 0.0;
        cl.afun_flag = false;
        cl.sum = 0.0;
        __stat(cl,array2);
        dof2 = cl.N - 1;
        if(cl.N === 1.0)
            return Calc.Errors.DivideByZero;
        var2 = cl.Q / (cl.N - 1.0);
        if(var2 === 0.0)
            return Calc.Errors.DivideByZero;
        var o = st_fdist([var1 / var2,dof1,dof2]);
        if(convert.isError(o))
            return o;
        p = (1.0 - convert.toDouble(o)) * 2;
        if(p > 1.0)
            p = 2.0 - p;
        return p
    }
    function st_hypgeomdist(args)
    {
        var convert = Calc.Convert;
        var a1 = convert.toInt(args[0]);
        var a2 = convert.toInt(args[1]);
        var a3 = convert.toInt(args[2]);
        var a4 = convert.toInt(args[3]);
        if(a1 < 0.0 || a1 > Math.min(a2,a3))
            return Calc.Errors.Number;
        if(a1 < Math.max(0.0,a2 - a4 + a3))
            return Calc.Errors.Number;
        if(a2 < 0.0 || a2 > a4)
            return Calc.Errors.Number;
        if(a3 < 0.0 || a3 > a4)
            return Calc.Errors.Number;
        if(a4 < 0.0)
            return Calc.Errors.Number;
        var o = Functions._MathHelper.combin(a3,a1);
        if(convert.isError(o))
            return o;
        var pt1 = convert.toDouble(o);
        o = Functions._MathHelper.combin(a4 - a3,a2 - a1);
        if(convert.isError(o))
            return o;
        var pt2 = convert.toDouble(o);
        o = Functions._MathHelper.combin(a4,a2);
        if(convert.isError(o))
            return o;
        var pt3 = convert.toDouble(o);
        return convert.toResult(pt1 * pt2 / pt3)
    }
    function st_lognormdist(args)
    {
        var x,
            mean,
            stdev;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toDouble(args[0])) || isNaN(mean = convert.toDouble(args[1])) || isNaN(stdev = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        if(x <= 0.0 || stdev <= 0.0)
            return Calc.Errors.Number;
        var list = [];
        list[0] = (Math.log(x) - mean) / stdev;
        return st_normsdist(list)
    }
    function st_loginv(args)
    {
        var prob,
            mean,
            stdev;
        var convert = Calc.Convert;
        if(isNaN(prob = convert.toDouble(args[0])) || isNaN(mean = convert.toDouble(args[1])) || isNaN(stdev = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        if(stdev <= 0.0 || prob < 0.0 || prob > 1.0)
            return Calc.Errors.Number;
        var list = [];
        list[0] = prob;
        var o = st_normsinv(list);
        if(convert.isError(o))
            return o;
        var p = convert.toDouble(o);
        return convert.toResult(Math.exp(mean + stdev * p))
    }
    function st_pearson(args)
    {
        var sumx = 0.0,
            sumy = 0.0,
            sumx2 = 0.0,
            sumy2 = 0.0,
            sumxy = 0.0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var n = arrayHelper.getLength(args[0]);
        if(n !== arrayHelper.getLength(args[1]))
            return Calc.Errors.NotAvailable;
        var count = 0;
        for(var i = 0; i < n; i++)
        {
            var obj1 = arrayHelper.getValueByIndex(args[0],i);
            var obj2 = arrayHelper.getValueByIndex(args[1],i);
            if(convert.isNumber(obj1) && convert.isNumber(obj2))
            {
                var x = convert.toDouble(obj1);
                var y = convert.toDouble(obj2);
                sumx += x;
                sumy += y;
                sumx2 += x * x;
                sumy2 += y * y;
                sumxy += x * y;
                count++
            }
        }
        if(count * sumx2 - sumx * sumx === 0 || count * sumy2 - sumy * sumy === 0)
            return Calc.Errors.DivideByZero;
        return(count * sumxy - sumx * sumy) / Math.sqrt((count * sumx2 - sumx * sumx) * (count * sumy2 - sumy * sumy))
    }
    function st_rsq(args)
    {
        var arrayY = args[0];
        var arrayX = args[1];
        var sumx = 0.0,
            sumy = 0.0,
            sumx2 = 0.0,
            sumy2 = 0.0,
            sumxy = 0.0,
            n = 0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var length = arrayHelper.getLength(arrayX);
        if(length !== arrayHelper.getLength(arrayY))
            return Calc.Errors.NotAvailable;
        for(var i = 0; i < length; i++)
        {
            var valueX = arrayHelper.getValueByIndex(arrayX,i);
            var valueY = arrayHelper.getValueByIndex(arrayY,i);
            if(convert.isNumber(valueX) && convert.isNumber(valueY))
            {
                var x = convert.toDouble(valueX);
                var y = convert.toDouble(valueY);
                sumx += x;
                sumy += y;
                sumx2 += x * x;
                sumy2 += y * y;
                sumxy += x * y;
                n++
            }
        }
        var divisor = Math.sqrt((n * sumx2 - sumx * sumx) * (n * sumy2 - sumy * sumy));
        if(divisor === 0.0)
            return Calc.Errors.DivideByZero;
        var r = (n * sumxy - sumx * sumy) / divisor;
        return convert.toResult(r * r)
    }
    function __fact(x)
    {
        var result = 1.0;
        for(var i = x; i > 1; i--)
            result *= i;
        return result
    }
    function st_poisson(args)
    {
        var x;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toInt(args[0])))
            return Calc.Errors.Value;
        var mean;
        if(isNaN(mean = convert.toDouble(args[1])))
            return Calc.Errors.Value;
        var cumulative;
        if(isNaN(cumulative = convert.toBool(args[2])))
            return Calc.Errors.Value;
        var result = 0.0;
        if(x <= 0 || mean <= 0.0)
            return Calc.Errors.Number;
        if(cumulative)
            for(var i = 0; i <= x; i++)
                result += Math.exp(-mean) * Math.pow(mean,i) / __fact(i);
        else
            result = Math.exp(-mean) * Math.pow(mean,x) / __fact(x);
        return convert.toResult(result)
    }
    function st_prob(args)
    {
        var lower;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        if(isNaN(lower = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        var upper = lower;
        if(Calc._Helper._argumentExists(args,3))
            if(isNaN(upper = convert.toDouble(args[3])))
                return Calc.Errors.Value;
        var sum = 0.0;
        var total_sum = 0.0;
        var n = arrayHelper.getLength(args[0]);
        if(n !== arrayHelper.getLength(args[1]))
            return Calc.Errors.NotAvailable;
        for(var i = 0; i < n; i++)
            if(arrayHelper.getValueByIndex(args[0],i) !== null && arrayHelper.getValueByIndex(args[1],i) !== null)
            {
                var x,
                    prob;
                if(isNaN(x = convert.toDouble(arrayHelper.getValueByIndex(args[0],i))) || isNaN(prob = convert.toDouble(arrayHelper.getValueByIndex(args[1],i))))
                    return Calc.Errors.Value;
                if(prob <= 0.0 || 1.0 < prob)
                    return Calc.Errors.Number;
                if(lower <= x && x <= upper)
                    sum += prob;
                total_sum += prob
            }
        if(total_sum !== 1.0)
            return Calc.Errors.Number;
        return sum
    }
    function st_skew(args)
    {
        if(args[0] === undefined || args[0] === null)
            throw'Invalid arguments';
        var sumx = 0.0,
            sumx2 = 0.0,
            sumskew = 0.0,
            meanx,
            stdev,
            n = 0;
        var i,
            j,
            x,
            obj;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        for(i = 0; i < args.length; i++)
            if(arrayHelper.isArrayOrReference(args[i]))
                for(j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj))
                    {
                        x = convert.toDouble(obj);
                        sumx += x;
                        sumx2 += x * x;
                        n++
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else if(convert.isError(args[i]))
                return args[i];
            else
            {
                var x1;
                if(isNaN(x1 = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                sumx += x1;
                sumx2 += x1 * x1;
                n++
            }
        if(n <= 2)
            return Calc.Errors.DivideByZero;
        meanx = sumx / n;
        stdev = Math.sqrt((n * sumx2 - sumx * sumx) / (n * (n - 1)));
        if(stdev === 0.0)
            return Calc.Errors.DivideByZero;
        for(i = 0; i < args.length; i++)
            if(arrayHelper.isArrayOrReference(args[i]))
                for(j = 0; j < arrayHelper.getLength(args[i]); j++)
                {
                    obj = arrayHelper.getValueByIndex(args[i],j);
                    if(convert.isNumber(obj))
                    {
                        x = convert.toDouble(obj);
                        sumskew += Math.pow((x - meanx) / stdev,3.0)
                    }
                    else if(convert.isError(obj))
                        return obj
                }
            else
            {
                if(isNaN(x = convert.toDouble(args[i])))
                    return Calc.Errors.Value;
                sumskew += Math.pow((x - meanx) / stdev,3.0)
            }
        return convert.toResult(n * sumskew / ((n - 1) * (n - 2)))
    }
    function st_standardize(args)
    {
        var x,
            mean,
            stdev;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toDouble(args[0])) || isNaN(mean = convert.toDouble(args[1])) || isNaN(stdev = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        if(stdev <= 0.0)
            return Calc.Errors.Number;
        return(x - mean) / stdev
    }
    function st_tdist(args)
    {
        var x;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toDouble(args[0])))
            return Calc.Errors.Value;
        var df,
            tails;
        if(isNaN(df = convert.toInt(args[1])) || isNaN(tails = convert.toInt(args[2])))
            return Calc.Errors.Value;
        if(df < 1 || tails !== 1 && tails !== 2 || x < 0)
            return Calc.Errors.Number;
        var f = df;
        var a = x / Math.sqrt(f);
        var b = f / (f + x * x);
        var im2 = f - 2.0;
        var ioe = f % 2.0;
        var s = 1.0,
            c = 1.0;
        f = 1.0;
        var ks = 2.0 + ioe;
        var fk = ks;
        if(im2 >= 2.0)
            for(var i = convert.toInt(ks); i <= im2; i = i + 2)
            {
                c = c * b * (fk - 1.0) / fk;
                s += c;
                if(s === f)
                    break;
                f = s;
                fk += 2
            }
        if(ioe !== 1.0)
            return tails * (1.0 - (0.5 + 0.5 * a * Math.sqrt(b) * s));
        if(df === 1.0)
            s = 0.0;
        return tails * (1.0 - (0.5 + (a * b * s + Math.atan(a)) * 0.3183098862))
    }
    function st_tinv(args)
    {
        var val;
        var convert = Calc.Convert;
        if(isNaN(val = convert.toDouble(args[0])))
            return Calc.Errors.Value;
        var ndf;
        if(isNaN(ndf = convert.toInt(args[1])))
            return Calc.Errors.Value;
        var eps = 1.0e-12;
        if(val < 0.0 || 1.0 < val || ndf < 1 || ndf > Math.pow(10,10))
            return Calc.Errors.Number;
        var a,
            b,
            c,
            d,
            prob,
            P,
            q,
            x,
            y,
            neg;
        var p1 = val / 2;
        var p2 = p1;
        if(ndf > 1.0e20)
        {
            var list = [];
            list[0] = p1;
            return st_normsinv(list)
        }
        if(p2 < 0.5)
        {
            neg = 0;
            P = 2.0 * p2
        }
        else
        {
            neg = 1;
            P = 2.0 * (1.0 - p2)
        }
        if(Math.abs(ndf - 2.0) < eps)
            if(P > 0.0)
                q = Math.sqrt(2.0 / (P * (2.0 - P)) - 2.0);
            else
                q = 1.79769e+308;
        else if(convert.toDouble(ndf) < 1.0 + eps)
            if(P > 0.0)
            {
                prob = (P + 1.0) * 1.57079632679489661923;
                q = -Math.tan(prob)
            }
            else
                q = 1.79769e+308;
        else
        {
            a = 1.0 / (ndf - 0.5);
            b = 48.0 / (a * a);
            c = ((20700.0 * a / b - 98.0) * a - 16.0) * a + 96.36;
            d = ((94.5 / (b + c) - 3.0) / b + 1.0) * Math.sqrt(a * 1.57079632679489661923) * ndf;
            y = Math.pow(d * P,2.0 / ndf);
            if(y > 0.05 + a)
            {
                var list1 = [];
                list1[0] = 0.5 * P;
                var o = st_normsinv(list1);
                if(convert.isError(o))
                    return o;
                x = convert.toDouble(o);
                y = x * x;
                if(ndf < 5.0)
                    c = c + 0.3 * (ndf - 4.5) * (x + 0.6);
                c = (((0.05 * d * x - 5.0) * x - 7.0) * x - 2.0) * x + b + c;
                y = (((((0.4 * y + 6.3) * y + 36) * y + 94.5) / c - y - 3.0) / b + 1.0) * x;
                y = a * y * y;
                if(y > 0.002)
                    y = Math.exp(y) - 1.0;
                else
                    y = 0.5 * y * y + y
            }
            else
                y = ((1.0 / (((ndf + 6.0) / (ndf * y) - 0.089 * d - 0.822) * (ndf + 2.0) * 3.0) + 0.5 / (ndf + 4.0)) * y - 1.0) * (ndf + 1.0) / (ndf + 2.0) + 1.0 / y;
            q = Math.sqrt(ndf * y)
        }
        if(neg !== 0.0)
            q = -q;
        return convert.toResult(q)
    }
    function __stat1(closure, array)
    {
        var x,
            dx,
            dm;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var length = arrayHelper.getLength(array);
        for(var i = 0; i < length; i++)
        {
            x = convert.toDouble(arrayHelper.getValueByIndex(array,i));
            dx = x - closure.M;
            dm = dx / (closure.N + 1);
            closure.M += dm;
            closure.Q += closure.N * dx * dm;
            closure.N++;
            closure.sum += x
        }
    }
    function __gammaln(value)
    {
        var coefficient = [76.180091729471457,-86.505320329416776,24.014098240830911,-1.231739572450155,1.208650973866179e-03,-5.395239384953E-06];
        var y = value;
        var d = value + 5.5;
        d -= (value + 0.5) * Math.log(d);
        var ser = 1.0000000001900149;
        for(var i = 0; i <= 5; i++)
            ser += coefficient[i] / ++y;
        return-d + Math.log(2.5066282746310006 * ser / value)
    }
    function __iterate(array1, array2)
    {
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var lengthArray1 = arrayHelper.getLength(array1);
        var lengthArray2 = arrayHelper.getLength(array2);
        if(lengthArray1 !== lengthArray2)
            return Calc.Errors.NotAvailable;
        var temp = new Array(lengthArray1);
        for(var i = 0; i < lengthArray1; i++)
        {
            var x1,
                x2;
            if(isNaN(x1 = convert.toDouble(arrayHelper.getValueByIndex(array1,i))) || isNaN(x2 = convert.toDouble(arrayHelper.getValueByIndex(array2,i))))
                return Calc.Errors.Value;
            temp[i] = x1 - x2
        }
        return temp
    }
    function __betaregularized(a, b, x)
    {
        var bt = x === 0.0 || x === 1.0 ? 0.0 : Math.exp(__gammaln(a + b) - __gammaln(a) - __gammaln(b) + a * Math.log(x) + b * Math.log(1.0 - x));
        var swap = x < (a + 1.0) / (a + b + 2.0);
        if(swap)
        {
            var temp = a;
            a = b;
            b = temp;
            x = 1.0 - x
        }
        var FP_MIN = 4.4501477170144028E-308;
        var MAX_ITERATIONS = 100;
        var qab = a + b;
        var qaq = a + 1.0;
        var qam = a - 1.0;
        var c = 1.0;
        var d = 1.0 - qab * x / qaq;
        if(Math.abs(d) < FP_MIN)
            d = FP_MIN;
        d = 1.0 / d;
        var h = d;
        var convert = Calc.Convert;
        for(var i = 1, i2 = 2; i <= MAX_ITERATIONS; i++, i2 += 2)
        {
            var aa = i * (b - i) * x / ((qam + i2) * (a + i2));
            d = 1.0 + aa * d;
            if(Math.abs(d) < FP_MIN)
                d = FP_MIN;
            c = 1.0 + aa / c;
            if(Math.abs(c) < FP_MIN)
                c = FP_MIN;
            d = 1.0 / d;
            h *= d * c;
            aa = -(a + i) * (qab + i) * x / ((a + i2) * (qaq + i2));
            d = 1.0 + aa * d;
            if(Math.abs(d) < FP_MIN)
                d = FP_MIN;
            c = 1.0 + aa / c;
            if(Math.abs(c) < FP_MIN)
                c = FP_MIN;
            d = 1.0 / d;
            var del = d * c;
            h *= del;
            if(Math.abs(convert.toDouble(del - 1.0)) < 4.94066e-324)
            {
                var result = bt * h / a;
                return swap ? 1.0 - result : result
            }
        }
        return Calc.Errors.Number
    }
    function st_ttest(args)
    {
        var array1 = args[0];
        var array2 = args[1];
        var args2 = args[2];
        var args3 = args[3];
        var convert = Calc.Convert;
        if(!convert.isNumber(args2) || !convert.isNumber(args3))
            return Calc.Errors.Value;
        var tails = convert.toInt(args2);
        var type = convert.toInt(args3);
        var mean1,
            mean2,
            x;
        var s,
            var1,
            var2,
            dof;
        var cl = {
                N: 0,
                M: 0,
                Q: 0,
                sum: 0,
                afun_flag: false
            };
        var n1,
            n2;
        if(tails !== 1 && tails !== 2 || type < 1 || 3 < type)
            return Calc.Errors.Number;
        if(type === 1)
        {
            var sum,
                dx,
                dm,
                M,
                Q,
                N;
            array1 = __iterate(array1,array2);
            if(convert.isError(array1))
                return array1;
            dx = dm = M = Q = N = sum = 0;
            var length1 = array1.length;
            for(var i = 0; i < length1; i++)
            {
                var array1i;
                if(isNaN(array1i = convert.toDouble(array1[i])))
                    return Calc.Errors.Value;
                dx = array1i - M;
                dm = dx / (N + 1.0);
                M += dm;
                Q += N * dx * dm;
                N++;
                sum += array1i
            }
            if(N - 1.0 === 0 || N === 0.0)
                return Calc.Errors.DivideByZero;
            s = Math.sqrt(Q / (N - 1.0));
            if(isNaN(s) || !isFinite(s))
                return Calc.Errors.Number;
            mean1 = sum / N;
            x = mean1 / (s / Math.sqrt(N));
            dof = N - 1.0
        }
        else
        {
            cl.N = 0;
            cl.M = 0.0;
            cl.Q = 0.0;
            cl.afun_flag = false;
            cl.sum = 0.0;
            __stat1(cl,array1);
            var1 = cl.Q / (cl.N - 1.0);
            mean1 = cl.sum / cl.N;
            n1 = cl.N;
            cl.N = 0;
            cl.M = 0.0;
            cl.Q = 0.0;
            cl.afun_flag = false;
            cl.sum = 0.0;
            __stat1(cl,array2);
            var2 = cl.Q / (cl.N - 1.0);
            mean2 = cl.sum / cl.N;
            n2 = cl.N;
            if(type !== 2)
            {
                var c = var1 / n1 / (var1 / n1 + var2 / n2);
                dof = 1.0 / (c * c / convert.toDouble(n1 - 1) + (1.0 - c) * (1.0 - c) / convert.toDouble(n2 - 1))
            }
            else
                dof = convert.toDouble(n1 + n2 - 2);
            x = (mean1 - mean2) / Math.sqrt(var1 / convert.toDouble(n1) + var2 / convert.toDouble(n2))
        }
        x = Math.abs(x);
        var value = __betaregularized(0.5 * dof,0.5,dof / (dof + x * x));
        if(convert.isError(value))
            return value;
        return 0.5 * tails * convert.toDouble(value)
    }
    function st_ztest(args)
    {
        var val;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        if(isNaN(val = convert.toDouble(args[1])))
            return Calc.Errors.Value;
        var sigma = 0.0;
        if(Calc._Helper._argumentExists(args,2))
            if(isNaN(sigma = convert.toDouble(args[2])))
                return Calc.Errors.Value;
        var sumx = 0.0,
            sumx2 = 0.0,
            meanx,
            stdev,
            n = 0,
            x;
        if(arrayHelper.isArrayOrReference(args[0]))
            for(var i = 0; i < arrayHelper.getLength(args[0]); i++)
            {
                var obj = arrayHelper.getValueByIndex(args[0],i);
                if(convert.isNumber(obj))
                {
                    x = convert.toDouble(obj);
                    sumx += x;
                    sumx2 += x * x;
                    n++
                }
                else if(convert.isError(obj))
                    return obj
            }
        else
        {
            if(isNaN(x = convert.toDouble(args[0])))
                return Calc.Errors.Value;
            sumx += x;
            sumx2 += x * x;
            n++
        }
        if(n === 0)
            return Calc.Errors.NotAvailable;
        if(n === 1)
            return Calc.Errors.DivideByZero;
        meanx = sumx / n;
        stdev = Calc._Helper._argumentExists(args,2) ? sigma : Math.sqrt((n * sumx2 - sumx * sumx) / (n * (n - 1)));
        if(stdev === 0.0)
            return Calc.Errors.DivideByZero;
        var subArgs = [];
        subArgs[0] = (meanx - val) / (stdev / Math.sqrt(n));
        var o = st_normsdist(subArgs);
        if(convert.isError(o))
            return o;
        return convert.toResult(1.0 - convert.toDouble(o))
    }
    function st_weibull(args)
    {
        var x,
            alpha,
            beta;
        var convert = Calc.Convert;
        if(isNaN(x = convert.toDouble(args[0])) || isNaN(alpha = convert.toDouble(args[1])) || isNaN(beta = convert.toDouble(args[2])))
            return Calc.Errors.Value;
        var cum;
        if(isNaN(cum = convert.toBool(args[3])))
            return Calc.Errors.Value;
        if(x < 0.0 || alpha <= 0 || beta <= 0)
            return Calc.Errors.Number;
        if(cum)
            return convert.toResult(1.0 - Math.exp(-Math.pow(x / beta,alpha)));
        else
            return convert.toResult(alpha / Math.pow(beta,alpha) * Math.pow(x,alpha - 1.0) * Math.exp(-Math.pow(x / beta,alpha)))
    }
    function st_permut(args)
    {
        var convert = Calc.Convert;
        var n = convert.toDouble(convert.toInt(args[0]));
        var k = convert.toDouble(convert.toInt(args[1]));
        var result = 1.0;
        if(n < 0.0 || k < 0.0 || n < k)
            return Calc.Errors.Number;
        for(var i = n - k + 1.0; i <= n; i++)
            result *= i;
        return convert.toResult(result)
    }
    function st_forecast(args)
    {
        var num;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        if(isNaN(num = convert.toDouble(args[0])))
            return Calc.Errors.Value;
        var y,
            x,
            sumy = 0.0,
            sumx = 0.0,
            sumx2 = 0.0,
            sumxy = 0.0,
            a,
            b;
        var n = arrayHelper.getLength(args[1]);
        if(n !== arrayHelper.getLength(args[2]))
            return Calc.Errors.NotAvailable;
        var count = 0;
        for(var i = 0; i < n; i++)
        {
            var obj1 = arrayHelper.getValueByIndex(args[1],i);
            var obj2 = arrayHelper.getValueByIndex(args[2],i);
            if(convert.isNumber(obj1) && convert.isNumber(obj2))
            {
                y = convert.toDouble(obj1);
                x = convert.toDouble(obj2);
                sumy += y;
                sumx += x;
                sumx2 += x * x;
                sumxy += x * y;
                count++
            }
            else if(convert.isError(obj1))
                return obj1;
            else if(convert.isError(obj2))
                return obj2
        }
        if(count === 0)
            return Calc.Errors.DivideByZero;
        if(count * sumx2 - sumx * sumx === 0.0)
            return Calc.Errors.DivideByZero;
        b = (count * sumxy - sumx * sumy) / (count * sumx2 - sumx * sumx);
        a = sumy / count - b * (sumx / count);
        return convert.toResult(a + b * num)
    }
    function st_intercept(args)
    {
        var y,
            x,
            sumy = 0.0,
            sumx = 0.0,
            sumx2 = 0.0,
            sumxy = 0.0,
            b;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var n = arrayHelper.getLength(args[0]);
        if(n !== arrayHelper.getLength(args[1]))
            return Calc.Errors.NotAvailable;
        var count = 0;
        for(var i = 0; i < n; i++)
        {
            var obj1 = arrayHelper.getValueByIndex(args[0],i);
            var obj2 = arrayHelper.getValueByIndex(args[1],i);
            if(convert.isNumber(obj1) && convert.isNumber(obj2))
            {
                y = convert.toDouble(obj1);
                x = convert.toDouble(obj2);
                sumy += y;
                sumx += x;
                sumx2 += x * x;
                sumxy += x * y;
                count++
            }
            else if(convert.isError(obj1))
                return obj1;
            else if(convert.isError(obj2))
                return obj2
        }
        if(count === 0)
            return Calc.Errors.DivideByZero;
        if(count * sumx2 - sumx * sumx === 0.0)
            return Calc.Errors.DivideByZero;
        b = (count * sumxy - sumx * sumy) / (count * sumx2 - sumx * sumx);
        return convert.toResult(sumy / count - b * (sumx / count))
    }
    function st_linest(args)
    {
        var convert = Calc.Convert,
            helper = Calc._Helper;
        var knownY = convert._toArray(args[0]);
        var knownX = new Calc.Array;
        if(helper._argumentExists(args,1))
            knownX = convert._toArray(args[1]);
        else
            knownX = knownY;
        var constant = helper._argumentExists(args,2) ? convert.toBool(args[2]) : true;
        var stats = helper._argumentExists(args,3) ? convert.toBool(args[3]) : false;
        var d,
            i,
            j,
            k,
            m,
            n,
            x,
            y,
            mm,
            nn,
            result,
            found,
            temp,
            val,
            se2;
        for(i = 0; i < knownY.getRowCount(); i++)
            for(j = 0; j < knownY.getColumnCount(); j++)
                if(convert.isError(knownY.getValue(i,j)))
                    return knownY.getValue(i,j);
                else if(!convert.isNumber(knownY.getValue(i,j)))
                    return Calc.Errors.Value;
        for(i = 0; i < knownX.getRowCount(); i++)
            for(j = 0; j < knownX.getColumnCount(); j++)
            {
                if(convert.isError(knownX.getValue(i,j)))
                    return knownX.getValue(i,j);
                if(!convert.isNumber(knownX.getValue(i,j)))
                    return Calc.Errors.Value
            }
        if(knownY.getRowCount() === knownX.getRowCount() && knownY.getColumnCount() === knownX.getColumnCount() && (constant || !stats))
        {
            n = convert.toDouble(knownX.getRowCount() * knownX.getColumnCount());
            var sumx = 0.0,
                sumx2 = 0.0,
                sumy = 0.0,
                sumy2 = 0.0,
                sumxy = 0.0,
                b;
            for(i = 0; i < knownX.getRowCount(); i++)
                for(j = 0; j < knownX.getColumnCount(); j++)
                {
                    if(isNaN(x = convert.toDouble(knownX.getValue(i,j))) || isNaN(y = convert.toDouble(knownY.getValue(i,j))))
                        return Calc.Errors.Value;
                    sumx += x;
                    sumx2 += x * x;
                    sumy += y;
                    sumy2 += y * y;
                    sumxy += x * y
                }
            if(constant)
            {
                m = (n * sumxy - sumx * sumy) / (n * sumx2 - sumx * sumx);
                b = (sumy * sumx2 - sumx * sumxy) / (n * sumx2 - sumx * sumx)
            }
            else
            {
                m = sumxy / sumx2;
                b = 0.0
            }
            result = [];
            result[0] = [];
            result[0][0] = m;
            result[0][1] = b;
            if(stats)
            {
                result[1] = [];
                result[2] = [];
                result[3] = [];
                result[4] = [];
                var nSumx2MinusSumxSumx = n * sumx2 - sumx * sumx;
                var nSumy2MinusSumySumy = n * sumy2 - sumy * sumy;
                var nSumxyMinusSumxSumy = n * sumxy - sumx * sumy;
                var ssresid = sumy2 - b * sumy - m * sumxy;
                var r2 = nSumxyMinusSumxSumy * nSumxyMinusSumxSumy / (nSumx2MinusSumxSumx * nSumy2MinusSumySumy);
                if(n < 3)
                {
                    result[1][0] = Calc.Errors.Number;
                    result[1][1] = Calc.Errors.Number;
                    result[2][1] = Calc.Errors.Number;
                    result[3][0] = Calc.Errors.Number
                }
                else
                {
                    result[1][0] = Math.sqrt(ssresid * n / (nSumx2MinusSumxSumx * (n - 2.0)));
                    result[1][1] = Math.sqrt(ssresid * sumx2 / (nSumx2MinusSumxSumx * (n - 2.0)));
                    result[2][1] = Math.sqrt((nSumy2MinusSumySumy - nSumxyMinusSumxSumy * nSumxyMinusSumxSumy / nSumx2MinusSumxSumx) / (n * (n - 2.0)));
                    if(r2 === 1.0)
                        result[3][0] = Calc.Errors.Number;
                    else
                        result[3][0] = r2 * (n - 2.0) / (1.0 - r2)
                }
                result[2][0] = r2;
                result[3][1] = n - 2.0;
                result[4][0] = nSumy2MinusSumySumy / n - ssresid;
                result[4][1] = ssresid
            }
            return result
        }
        else if(knownY.getColumnCount() === 1 && knownY.getRowCount() === knownX.getRowCount() || knownY.getRowCount() === 1 && knownY.getColumnCount() === knownX.getColumnCount())
        {
            y = [];
            x = [];
            if(knownY.getColumnCount() === 1)
            {
                n = knownX.getRowCount();
                m = knownX.getColumnCount();
                for(i = 0; i < n; i++)
                {
                    if(isNaN(d = convert.toDouble(knownY.getValue(i,0))))
                        return Calc.Errors.Value;
                    y[i] = d
                }
                for(i = 0; i < n; i++)
                {
                    x[i] = [];
                    for(j = 0; j < m; j++)
                    {
                        if(isNaN(d = convert.toDouble(knownX.getValue(i,j))))
                            return Calc.Errors.Value;
                        x[i][j] = d
                    }
                }
            }
            else
            {
                n = knownX.getColumnCount();
                m = knownX.getRowCount();
                x = [];
                y = [];
                for(i = 0; i < n; i++)
                {
                    if(isNaN(d = convert.toDouble(knownY.getValue(0,i))))
                        return Calc.Errors.Value;
                    y[i] = d
                }
                for(i = 0; i < n; i++)
                {
                    x[i] = [];
                    for(j = 0; j < m; j++)
                    {
                        if(isNaN(d = convert.toDouble(knownX.getValue(j,i))))
                            return Calc.Errors.Value;
                        x[i][j] = d
                    }
                }
            }
            var q = [];
            for(k = 0; k < m + 1; k++)
                q[k] = [];
            for(mm = 0; mm < m + 1; mm++)
                for(nn = 0; nn < m + 2; nn++)
                    q[mm][nn] = 0;
            var e = [];
            for(mm = 0; mm < m + 2; mm++)
                e[mm] = 0;
            var v = stats ? [] : null;
            for(k = 0; k < n; k++)
            {
                e[m + 1] = e[m + 1] + y[k] * y[k];
                q[0][m + 1] = q[0][m + 1] + y[k];
                e[0] = q[0][m + 1];
                for(i = 0; i < m; i++)
                {
                    q[0][i + 1] = q[0][i + 1] + x[k][i];
                    q[i + 1][0] = q[0][i + 1];
                    q[i + 1][m + 1] = q[i + 1][m + 1] + x[k][i] * y[k];
                    e[i + 1] = q[i + 1][m + 1];
                    for(j = i; j < m; j++)
                    {
                        q[j + 1][i + 1] = q[j + 1][i + 1] + x[k][i] * x[k][j];
                        q[i + 1][j + 1] = q[j + 1][i + 1]
                    }
                }
            }
            q[0][0] = n;
            if(stats)
            {
                for(mm = 0; mm < m + 1; mm++)
                {
                    v[mm] = [];
                    for(nn = 0; nn < m + 1; nn++)
                        v[mm][nn] = 0
                }
                for(i = 0; i < m + 1; i++)
                    v[i][i] = 1.0
            }
            if(constant)
                for(i = 0; i < m + 1; i++)
                {
                    if(q[i][i] === 0.0)
                    {
                        found = false;
                        for(j = i + 1; !found && j < m + 1; j++)
                            if(q[j][i] !== 0.0)
                            {
                                for(k = 0; k < m + 2; k++)
                                {
                                    temp = q[i][k];
                                    q[i][k] = q[j][k];
                                    q[j][k] = temp
                                }
                                if(stats)
                                    for(k = 0; k < m + 1; k++)
                                    {
                                        temp = v[i][k];
                                        v[i][k] = v[j][k];
                                        v[j][k] = temp
                                    }
                                found = true
                            }
                        if(!found)
                            return Calc.Errors.Number
                    }
                    val = 1.0 / q[i][i];
                    for(k = 0; k < m + 2; k++)
                        q[i][k] = q[i][k] * val;
                    if(stats)
                        for(k = 0; k < m + 1; k++)
                            v[i][k] = v[i][k] * val;
                    for(j = 0; j < m + 1; j++)
                        if(j !== i)
                        {
                            val = -q[j][i];
                            for(k = 0; k < m + 2; k++)
                                q[j][k] = q[j][k] + val * q[i][k];
                            if(stats)
                                for(k = 0; k < m + 1; k++)
                                    v[j][k] = v[j][k] + val * v[i][k]
                        }
                }
            else
                for(i = 1; i < m + 1; i++)
                {
                    if(q[i][i] === 0.0)
                    {
                        found = false;
                        for(j = i + 1; !found && j < m + 1; j++)
                            if(q[j][i] !== 0.0)
                            {
                                for(k = 0; k < m + 2; k++)
                                {
                                    temp = q[i][k];
                                    q[i][k] = q[j][k];
                                    q[j][k] = temp
                                }
                                if(stats)
                                    for(k = 0; k < m + 1; k++)
                                    {
                                        temp = v[i][k];
                                        v[i][k] = v[j][k];
                                        v[j][k] = temp
                                    }
                                found = true
                            }
                        if(!found)
                            return Calc.Errors.Number
                    }
                    val = 1.0 / q[i][i];
                    for(k = 1; k < m + 2; k++)
                        q[i][k] = q[i][k] * val;
                    if(stats)
                        for(k = 1; k < m + 1; k++)
                            v[i][k] = v[i][k] * val;
                    for(j = 1; j < m + 1; j++)
                        if(j !== i)
                        {
                            val = -q[j][i];
                            for(k = 1; k < m + 2; k++)
                                q[j][k] = q[j][k] + val * q[i][k];
                            if(stats)
                                for(k = 1; k < m + 1; k++)
                                    v[j][k] = v[j][k] + val * v[i][k]
                        }
                    q[0][m + 1] = 0.0
                }
            result = [stats ? 5 : 1,m + 1];
            result[0] = [];
            for(i = 0; i < m + 1; i++)
                result[0][i] = q[m - i][m + 1];
            if(stats)
            {
                for(mm = 1; mm < 5; mm++)
                {
                    result[mm] = [];
                    for(nn = 0; nn < m + 1; nn++)
                        result[mm][nn] = 0
                }
                var sqr,
                    sqt,
                    sqe;
                sqt = e[m + 1] - e[0] * e[0] / n;
                sqr = e[m + 1];
                for(i = 0; i < m + 1; i++)
                    sqr -= q[i][m + 1] * e[i];
                sqe = sqt - sqr;
                if(sqt === 0.0)
                    result[2][0] = Calc.Errors.Number;
                else
                    result[2][0] = sqe / sqt;
                result[4][0] = sqe;
                result[4][1] = sqr;
                if(constant)
                {
                    if(n - m - 1 === 0)
                    {
                        result[2][1] = Calc.Errors.Number;
                        for(i = 0; i < m + 1; i++)
                            result[1][i] = Calc.Errors.Number
                    }
                    else
                    {
                        se2 = sqr / (n - m - 1);
                        for(i = 0; i < m + 1; i++)
                            result[1][m - i] = Math.sqrt(se2 * v[i][i]);
                        result[2][1] = Math.sqrt(se2)
                    }
                    if(sqr === 0.0)
                        result[3][0] = Calc.Errors.Number;
                    else
                        result[3][0] = convert.toDouble(n - m - 1) * sqe / (sqr * convert.toDouble(m));
                    result[3][1] = convert.toDouble(n - m - 1)
                }
                else
                {
                    if(n - m === 0)
                    {
                        for(i = 0; i < m + 1; i++)
                            result[1][i] = Calc.Errors.Number;
                        result[2][1] = Calc.Errors.Number
                    }
                    else
                    {
                        se2 = sqr / convert.toDouble(n - m);
                        result[1][m] = Calc.Errors.NotAvailable;
                        for(i = 1; i < m + 1; i++)
                            result[1][m - i] = Math.sqrt(se2 * v[i][i]);
                        result[2][1] = Math.sqrt(se2)
                    }
                    if(sqr === 0.0)
                        result[3][0] = Calc.Errors.Number;
                    else
                        result[3][0] = convert.toDouble(n - m) * sqe / (sqr * convert.toDouble(m));
                    result[3][1] = convert.toDouble(n - m)
                }
                for(i = 2; i < 5; i++)
                    for(j = 2; j < m + 1; j++)
                        result[i][j] = Calc.Errors.NotAvailable
            }
            return result
        }
        return Calc.Errors.Number
    }
    function st_slope(args)
    {
        var arrayY = args[0];
        var arrayX = args[1];
        var sumy = 0.0,
            sumx = 0.0,
            sumx2 = 0.0,
            sumxy = 0.0,
            n = 0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var length = arrayHelper.getLength(args[0]);
        if(length !== arrayHelper.getLength(args[1]))
            return Calc.Errors.NotAvailable;
        for(var i = 0; i < length; i++)
        {
            var valueY = arrayHelper.getValueByIndex(arrayY,i);
            var valueX = arrayHelper.getValueByIndex(arrayX,i);
            if(convert.isNumber(valueY) && convert.isNumber(valueX))
            {
                var y = convert.toDouble(valueY);
                var x = convert.toDouble(valueX);
                sumy += y;
                sumx += x;
                sumx2 += x * x;
                sumxy += x * y;
                n++
            }
        }
        if(n * sumx2 - sumx * sumx === 0.0)
            return Calc.Errors.DivideByZero;
        return convert.toResult((n * sumxy - sumx * sumy) / (n * sumx2 - sumx * sumx))
    }
    function st_trend(args)
    {
        var convert = Calc.Convert,
            helper = Calc._Helper;
        var knownY = convert._toArray(args[0]);
        var knownX = new Calc.Array;
        if(helper._argumentExists(args,1))
            knownX = convert._toArray(args[1]);
        else
            knownX = knownY;
        var newX = helper._argumentExists(args,2) ? convert._toArray(args[2]) : knownX;
        var constant = helper._argumentExists(args,3) ? convert.toBool(args[3]) : true;
        var i,
            j;
        for(i = 0; i < knownY.getRowCount(); i++)
            for(j = 0; j < knownY.getColumnCount(); j++)
                if(!convert.isNumber(knownY.getValue(i,j)))
                    return Calc.Errors.Value;
        for(i = 0; i < knownX.getRowCount(); i++)
            for(j = 0; j < knownX.getColumnCount(); j++)
                if(!convert.isNumber(knownX.getValue(i,j)))
                    return Calc.Errors.Value;
        for(i = 0; i < newX.getRowCount(); i++)
            for(j = 0; j < newX.getColumnCount(); j++)
                if(!convert.isNumber(newX.getValue(i,j)))
                    return Calc.Errors.Value;
        var d,
            k,
            m,
            n,
            x,
            y,
            result,
            found,
            val,
            temp;
        if(knownY.getRowCount() === knownX.getRowCount() && knownY.getColumnCount() === knownX.getColumnCount())
        {
            n = knownX.getRowCount() * knownX.getColumnCount();
            var sumx = 0.0,
                sumx2 = 0.0,
                sumy = 0.0,
                sumxy = 0.0,
                b;
            for(i = 0; i < knownX.getRowCount(); i++)
                for(j = 0; j < knownX.getColumnCount(); j++)
                {
                    if(isNaN(x = convert.toDouble(knownX.getValue(i,j))) || isNaN(y = convert.toDouble(knownY.getValue(i,j))))
                        return Calc.Errors.Value;
                    sumx += x;
                    sumx2 += x * x;
                    sumy += y;
                    sumxy += x * y
                }
            if(constant)
            {
                m = (n * sumxy - sumx * sumy) / (n * sumx2 - sumx * sumx);
                b = (sumy * sumx2 - sumx * sumxy) / (n * sumx2 - sumx * sumx)
            }
            else
            {
                m = sumxy / sumx2;
                b = 0.0
            }
            result = [];
            for(i = 0; i < newX.getRowCount(); i++)
            {
                result[i] = [];
                for(j = 0; j < newX.getColumnCount(); j++)
                {
                    if(isNaN(x = convert.toDouble(newX.getValue(i,j))))
                        return Calc.Errors.Value;
                    result[i][j] = m * x + b
                }
            }
            return result
        }
        else if(knownY.getColumnCount() === 1 && knownY.getRowCount() === knownX.getRowCount() || knownY.getRowCount() === 1 && knownY.getColumnCount() === knownX.getColumnCount())
        {
            y = [];
            x = [];
            if(knownY.getColumnCount() === 1)
            {
                n = knownX.getRowCount();
                m = knownX.getColumnCount();
                for(i = 0; i < n; i++)
                {
                    if(isNaN(d = convert.toDouble(knownY.getValue(i,0))))
                        return Calc.Errors.Value;
                    y[i] = d
                }
                for(i = 0; i < n; i++)
                {
                    x[i] = [];
                    for(j = 0; j < m; j++)
                    {
                        if(isNaN(d = convert.toDouble(knownX.getValue(i,j))))
                            return Calc.Errors.Value;
                        x[i][j] = d
                    }
                }
            }
            else
            {
                n = knownX.getColumnCount();
                m = knownX.getRowCount();
                x = [];
                y = [];
                for(i = 0; i < n; i++)
                {
                    if(isNaN(d = convert.toDouble(knownY.getValue(0,i))))
                        return Calc.Errors.Value;
                    y[i] = d
                }
                for(i = 0; i < n; i++)
                {
                    x[i] = [];
                    for(j = 0; j < m; j++)
                    {
                        if(isNaN(d = convert.toDouble(knownX.getValue(j,i))))
                            return Calc.Errors.Value;
                        x[i][j] = d
                    }
                }
            }
            var q = [];
            for(k = 0; k < m + 1; k++)
                q[k] = [];
            for(var mm = 0; mm < m + 1; mm++)
                for(var nn = 0; nn < m + 2; nn++)
                    q[mm][nn] = 0;
            for(k = 0; k < n; k++)
            {
                q[0][m + 1] = q[0][m + 1] + y[k];
                for(i = 0; i < m; i++)
                {
                    q[0][i + 1] = q[0][i + 1] + x[k][i];
                    q[i + 1][0] = q[0][i + 1];
                    q[i + 1][m + 1] = q[i + 1][m + 1] + x[k][i] * y[k];
                    for(j = i; j < m; j++)
                    {
                        q[j + 1][i + 1] = q[j + 1][i + 1] + x[k][i] * x[k][j];
                        q[i + 1][j + 1] = q[j + 1][i + 1]
                    }
                }
            }
            q[0][0] = n;
            if(constant)
                for(i = 0; i < m + 1; i++)
                {
                    if(q[i][i] === 0.0)
                    {
                        found = false;
                        for(j = i + 1; !found && j < m + 1; j++)
                            if(q[j][i] !== 0.0)
                            {
                                for(k = 0; k < m + 2; k++)
                                {
                                    temp = q[i][k];
                                    q[i][k] = q[j][k];
                                    q[j][k] = temp
                                }
                                found = true
                            }
                        if(!found)
                            return Calc.Errors.NotAvailable
                    }
                    val = 1.0 / q[i][i];
                    for(k = 0; k < m + 2; k++)
                        q[i][k] = q[i][k] * val;
                    for(j = 0; j < m + 1; j++)
                        if(j !== i)
                        {
                            val = -q[j][i];
                            for(k = 0; k < m + 2; k++)
                                q[j][k] = q[j][k] + val * q[i][k]
                        }
                }
            else
                for(i = 1; i < m + 1; i++)
                {
                    if(q[i][i] === 0.0)
                    {
                        found = false;
                        for(j = i + 1; !found && j < m + 1; j++)
                            if(q[j][i] !== 0.0)
                            {
                                for(k = 0; k < m + 2; k++)
                                {
                                    temp = q[i][k];
                                    q[i][k] = q[j][k];
                                    q[j][k] = temp
                                }
                                found = true
                            }
                        if(!found)
                            return Calc.Errors.NotAvailable
                    }
                    val = 1.0 / q[i][i];
                    for(k = 1; k < m + 2; k++)
                        q[i][k] = q[i][k] * val;
                    for(j = 1; j < m + 1; j++)
                        if(j !== i)
                        {
                            val = -q[j][i];
                            for(k = 1; k < m + 2; k++)
                                q[j][k] = q[j][k] + val * q[i][k]
                        }
                    q[0][m + 1] = 0.0
                }
            if(knownY.getColumnCount() === 1)
            {
                result = [];
                for(i = 0; i < newX.getRowCount(); i++)
                {
                    result[i] = [];
                    val = q[0][m + 1];
                    for(j = 0; j < m; j++)
                    {
                        if(isNaN(d = convert.toDouble(newX.getValue(i,j))))
                            return Calc.Errors.Value;
                        val += q[j + 1][m + 1] * d
                    }
                    result[i][0] = val
                }
                return result
            }
            else
            {
                result = [];
                result[0] = [];
                for(i = 0; i < newX.getColumnCount; i++)
                {
                    val = q[0][m + 1];
                    for(j = 0; j < m; j++)
                    {
                        if(isNaN(d = convert.toDouble(newX.getValue(j,i))))
                            return Calc.Errors.Value;
                        val += q[j + 1][m + 1] * d
                    }
                    result[0][i] = val
                }
                return result
            }
        }
        return Calc.Errors.NotAvailable
    }
    function st_growth(args)
    {
        var convert = Calc.Convert,
            helper = Calc._Helper;
        var knownY = convert._toArray(args[0]);
        var knownX = [];
        if(helper._argumentExists(args,1))
            knownX = convert._toArray(args[1]);
        else
            knownX = knownY;
        var newX = helper._argumentExists(args,2) ? convert._toArray(args[2]) : knownX;
        var constant = helper._argumentExists(args,3) ? convert.toBool(args[3]) : true;
        var d,
            k,
            i,
            j,
            m,
            n,
            x,
            y,
            mm,
            nn,
            result,
            s,
            val,
            L;
        for(i = 0; i < knownY.getRowCount(); i++)
            for(j = 0; j < knownY.getColumnCount(); j++)
            {
                val = knownY.getValue(i,j);
                if(convert.isError(val))
                    return val;
                else if(!convert.isNumber(val))
                    return Calc.Errors.Value;
                else if(convert.toDouble(val) <= 0.0)
                    return Calc.Errors.Number
            }
        for(i = 0; i < knownX.getRowCount(); i++)
            for(j = 0; j < knownX.getColumnCount(); j++)
            {
                val = knownX.getValue(i,j);
                if(convert.isError(val))
                    return val;
                else if(!convert.isNumber(val))
                    return Calc.Errors.Value
            }
        for(i = 0; i < newX.getRowCount(); i++)
            for(j = 0; j < newX.getColumnCount(); j++)
            {
                val = newX.getValue(i,j);
                if(convert.isError(val))
                    return val;
                else if(!convert.isNumber(val))
                    return Calc.Errors.Value
            }
        if(knownY.getRowCount() === knownX.getRowCount() && knownY.getColumnCount() === knownX.getColumnCount())
        {
            n = knownX.getRowCount() * knownX.getColumnCount();
            var sumx = 0.0,
                sumx2 = 0.0,
                sumy = 0.0,
                sumxy = 0.0,
                b;
            for(i = 0; i < knownX.getRowCount(); i++)
                for(j = 0; j < knownX.getColumnCount(); j++)
                {
                    if(isNaN(x = convert.toDouble(knownX.getValue(i,j))) || isNaN(y = convert.toDouble(knownY.getValue(i,j))))
                        return Calc.Errors.Value;
                    y = Math.log(y);
                    sumx += x;
                    sumx2 += x * x;
                    sumy += y;
                    sumxy += x * y
                }
            if(constant)
            {
                m = (n * sumxy - sumx * sumy) / (n * sumx2 - sumx * sumx);
                b = (sumy * sumx2 - sumx * sumxy) / (n * sumx2 - sumx * sumx)
            }
            else
            {
                m = sumxy / sumx2;
                b = 0.0
            }
            result = [];
            for(i = 0; i < newX.getRowCount(); i++)
            {
                result[i] = [];
                for(j = 0; j < newX.getColumnCount(); j++)
                {
                    if(isNaN(x = convert.toDouble(newX.getValue(i,j))))
                        return Calc.Errors.Value;
                    result[i][j] = Math.exp(m * x + b)
                }
            }
            return result
        }
        else if(knownY.getColumnCount() === 1 && knownY.getRowCount() === knownX.getRowCount() || knownY.getRowCount() === 1 && knownY.getColumnCount() === knownX.getColumnCount())
        {
            y = [];
            x = [];
            if(knownY.getColumnCount() === 1)
            {
                n = knownX.getRowCount();
                m = knownX.getColumnCount();
                for(i = 0; i < n; i++)
                {
                    if(isNaN(d = convert.toDouble(knownY.getValue(i,0))))
                        return Calc.Errors.Value;
                    y[i] = Math.log(d)
                }
                for(i = 0; i < n; i++)
                {
                    x[i] = [];
                    for(j = 0; j < m; j++)
                    {
                        if(isNaN(d = convert.toDouble(knownX.getValue(i,j))))
                            return Calc.Errors.Value;
                        x[i][j] = d
                    }
                }
            }
            else
            {
                n = knownX.getColumnCount();
                m = knownX.getRowCount();
                x = [];
                y = [];
                for(i = 0; i < n; i++)
                {
                    if(isNaN(d = convert.toDouble(knownY.getValue(0,i))))
                        return Calc.Errors.Value;
                    y[i] = d
                }
                for(i = 0; i < n; i++)
                {
                    x[i] = [];
                    for(j = 0; j < m; j++)
                    {
                        if(isNaN(d = convert.toDouble(knownX.getValue(j,i))))
                            return Calc.Errors.Value;
                        x[i][j] = d
                    }
                }
            }
            var q = [];
            for(mm = 0; mm < m + 1; mm++)
            {
                q[mm] = [];
                for(nn = 0; nn < m + 2; nn++)
                    q[mm][nn] = 0
            }
            var e = [];
            for(mm = 0; mm < m + 2; mm++)
                e[mm] = 0;
            for(k = 0; k < n; k++)
            {
                e[m + 1] = e[m + 1] + y[k] * y[k];
                q[0][m + 1] = q[0][m + 1] + y[k];
                e[0] = q[0][m + 1];
                for(i = 0; i < m; i++)
                {
                    q[0][i + 1] = q[0][i + 1] + x[k][i];
                    q[i + 1][0] = q[0][i + 1];
                    q[i + 1][m + 1] = q[i + 1][m + 1] + x[k][i] * y[k];
                    e[i + 1] = q[i + 1][m + 1];
                    for(j = i; j < m; j++)
                    {
                        q[j + 1][i + 1] = q[j + 1][i + 1] + x[k][i] * x[k][j];
                        q[i + 1][j + 1] = q[j + 1][i + 1]
                    }
                }
            }
            q[0][0] = n;
            if(constant)
                for(s = 0; s < m + 1; s++)
                {
                    i = s;
                    while(i < m + 1 && q[i][s] === 0.0)
                        i++;
                    if(i >= m + 1)
                        return Calc.Errors.NotAvailable;
                    for(L = 0; L < m + 2; L++)
                    {
                        val = q[s][L];
                        q[s][L] = q[i][L];
                        q[i][L] = val
                    }
                    val = 1.0 / q[s][s];
                    for(L = 0; L < m + 2; L++)
                        q[s][L] = q[s][L] * val;
                    for(i = 0; i < m + 1; i++)
                        if(i !== s)
                        {
                            val = -q[i][s];
                            for(L = 0; L < m + 2; L++)
                                q[i][L] = q[i][L] + val * q[s][L]
                        }
                }
            else
                for(s = 1; s < m + 1; s++)
                {
                    i = s;
                    while(i < m + 1 && q[i][s] === 0.0)
                        i++;
                    if(i >= m + 1)
                        return Calc.Errors.NotAvailable;
                    for(L = 1; L < m + 2; L++)
                    {
                        val = q[s][L];
                        q[s][L] = q[i][L];
                        q[i][L] = val
                    }
                    val = 1.0 / q[s][s];
                    q[s][L] = q[s][L] * val;
                    for(i = 1; i < m + 1; i++)
                        if(i !== s)
                        {
                            val = -q[i][s];
                            q[i][L] = q[i][L] + val * q[s][L]
                        }
                    q[0][m + 1] = 0.0
                }
            if(knownY.getColumnCount() === 1)
            {
                result = [];
                for(i = 0; i < newX.getRowCount(); i++)
                {
                    result[i] = [];
                    val = q[0][m + 1];
                    for(j = 0; j < m; j++)
                    {
                        if(isNaN(d = convert.toDouble(newX.getValue(i,j))))
                            return Calc.Errors.Value;
                        val += q[j + 1][m + 1] * d
                    }
                    result[i][0] = Math.exp(val)
                }
                return result
            }
            else
            {
                result = [];
                result[0] = [];
                for(i = 0; i < newX.getColumnCount(); i++)
                {
                    val = q[0][m + 1];
                    for(j = 0; j < m; j++)
                    {
                        if(isNaN(d = convert.toDouble(newX.getValue(j,i))))
                            return Calc.Errors.Value;
                        val += q[j + 1][m + 1] * d
                    }
                    result[0][i] = Math.exp(val)
                }
                return result
            }
        }
        return Calc.Errors.NotAvailable
    }
    function st_logest(args)
    {
        var convert = Calc.Convert,
            helper = Calc._Helper;
        var knownY = convert._toArray(args[0]);
        var knownX = helper._argumentExists(args,1) ? convert._toArray(args[1]) : knownY;
        var constant = helper._argumentExists(args,2) ? convert.toBool(args[2]) : true;
        var stats = helper._argumentExists(args,3) ? convert.toBool(args[3]) : false;
        var i,
            j;
        for(i = 0; i < knownY.getRowCount(); i++)
            for(j = 0; j < knownY.getColumnCount(); j++)
                if(convert.isError(knownY.getValue(i,j)))
                    return knownY.getValue(i,j);
                else if(!convert.isNumber(knownY.getValue(i,j)))
                    return Calc.Errors.Value;
        for(i = 0; i < knownX.getRowCount(); i++)
            for(j = 0; j < knownX.getColumnCount(); j++)
                if(convert.isError(knownX.getValue(i,j)))
                    return knownX.getValue(i,j);
                else if(!convert.isNumber(knownX.getValue(i,j)))
                    return Calc.Errors.Value;
        var d,
            k,
            m,
            n,
            x,
            y,
            mm,
            nn,
            result,
            val,
            temp,
            found,
            se2;
        if(knownY.getRowCount() === knownX.getRowCount() && knownY.getColumnCount() === knownX.getColumnCount() && (constant || !stats))
        {
            n = knownX.getRowCount() * knownX.getColumnCount();
            var sumx = 0.0,
                sumx2 = 0.0,
                sumy = 0.0,
                sumy2 = 0.0,
                sumxy = 0.0,
                b;
            for(i = 0; i < knownX.getRowCount(); i++)
                for(j = 0; j < knownX.getColumnCount(); j++)
                {
                    if(isNaN(x = convert.toDouble(knownX.getValue(i,j))) || isNaN(y = convert.toDouble(knownY.getValue(i,j))))
                        return Calc.Errors.Value;
                    y = Math.log(y);
                    sumx += x;
                    sumx2 += x * x;
                    sumy += y;
                    sumy2 += y * y;
                    sumxy += x * y
                }
            if(constant)
            {
                m = (n * sumxy - sumx * sumy) / (n * sumx2 - sumx * sumx);
                b = (sumy * sumx2 - sumx * sumxy) / (n * sumx2 - sumx * sumx)
            }
            else
            {
                m = sumxy / sumx2;
                b = 0.0
            }
            result = [];
            result[0] = [];
            result[0][0] = Math.exp(m);
            result[0][1] = Math.exp(b);
            if(stats)
            {
                result[1] = [];
                result[2] = [];
                result[3] = [];
                result[4] = [];
                var nSumx2MinusSumxSumx = n * sumx2 - sumx * sumx;
                var nSumy2MinusSumySumy = n * sumy2 - sumy * sumy;
                var nSumxyMinusSumxSumy = n * sumxy - sumx * sumy;
                var ssresid = sumy2 - b * sumy - m * sumxy;
                var r2 = nSumxyMinusSumxSumy * nSumxyMinusSumxSumy / (nSumx2MinusSumxSumx * nSumy2MinusSumySumy);
                if(n < 3)
                {
                    result[1][0] = Calc.Errors.Number;
                    result[1][1] = Calc.Errors.Number;
                    result[2][1] = Calc.Errors.Number;
                    result[3][0] = Calc.Errors.Number
                }
                else
                {
                    result[1][0] = Math.sqrt(ssresid * n / (nSumx2MinusSumxSumx * (n - 2.0)));
                    result[1][1] = Math.sqrt(ssresid * sumx2 / (nSumx2MinusSumxSumx * (n - 2.0)));
                    result[2][1] = Math.sqrt((nSumy2MinusSumySumy - nSumxyMinusSumxSumy * nSumxyMinusSumxSumy / nSumx2MinusSumxSumx) / (n * (n - 2.0)));
                    if(r2 === 1.0)
                        result[3][0] = Calc.Errors.Number;
                    else
                        result[3][0] = r2 * (n - 2.0) / (1.0 - r2)
                }
                result[2][0] = r2;
                result[3][1] = n - 2.0;
                result[4][0] = nSumy2MinusSumySumy / n - ssresid;
                result[4][1] = ssresid
            }
            return result
        }
        else if(knownY.getColumnCount() === 1 && knownY.getRowCount() === knownX.getRowCount() || knownY.getRowCount() === 1 && knownY.getColumnCount() === knownX.getColumnCount())
        {
            y = [];
            x = [];
            if(knownY.getColumnCount() === 1)
            {
                n = knownX.getRowCount();
                m = knownX.getColumnCount();
                for(i = 0; i < n; i++)
                {
                    if(isNaN(d = convert.toDouble(knownY.getValue(i,0))))
                        return Calc.Errors.Value;
                    y[i] = Math.log(d)
                }
                for(i = 0; i < n; i++)
                {
                    x[i] = [];
                    for(j = 0; j < m; j++)
                    {
                        if(isNaN(d = convert.toDouble(knownX.getValue(i,j))))
                            return Calc.Errors.Value;
                        x[i][j] = d
                    }
                }
            }
            else
            {
                n = knownX.getColumnCount();
                m = knownX.getRowCount();
                x = [];
                y = [];
                for(i = 0; i < n; i++)
                {
                    if(isNaN(d = convert.toDouble(knownY.getValue(0,i))))
                        return Calc.Errors.Value;
                    y[i] = Math.log(d)
                }
                for(i = 0; i < n; i++)
                {
                    x[i] = [];
                    for(j = 0; j < m; j++)
                    {
                        if(isNaN(d = convert.toDouble(knownX.getValue(j,i))))
                            return Calc.Errors.Value;
                        x[i][j] = d
                    }
                }
            }
            var q = [];
            for(mm = 0; mm < m + 1; mm++)
            {
                q[mm] = [];
                for(nn = 0; nn < m + 2; nn++)
                    q[mm][nn] = 0
            }
            var e = [];
            for(mm = 0; mm < m + 2; mm++)
                e[mm] = 0;
            var v = stats ? [] : null;
            for(k = 0; k < n; k++)
            {
                e[m + 1] = e[m + 1] + y[k] * y[k];
                q[0][m + 1] = q[0][m + 1] + y[k];
                e[0] = q[0][m + 1];
                for(i = 0; i < m; i++)
                {
                    q[0][i + 1] = q[0][i + 1] + x[k][i];
                    q[i + 1][0] = q[0][i + 1];
                    q[i + 1][m + 1] = q[i + 1][m + 1] + x[k][i] * y[k];
                    e[i + 1] = q[i + 1][m + 1];
                    for(j = i; j < m; j++)
                    {
                        q[j + 1][i + 1] = q[j + 1][i + 1] + x[k][i] * x[k][j];
                        q[i + 1][j + 1] = q[j + 1][i + 1]
                    }
                }
            }
            q[0][0] = n;
            if(stats)
            {
                for(mm = 0; mm < m + 1; mm++)
                {
                    v[mm] = [];
                    for(nn = 0; nn < m + 1; nn++)
                        v[mm][nn] = 0
                }
                for(i = 0; i < m + 1; i++)
                    v[i][i] = 1.0
            }
            if(constant)
                for(i = 0; i < m + 1; i++)
                {
                    if(q[i][i] === 0.0)
                    {
                        found = false;
                        for(j = i + 1; !found && j < m + 1; j++)
                            if(q[j][i] !== 0.0)
                            {
                                for(k = 0; k < m + 2; k++)
                                {
                                    temp = q[i][k];
                                    q[i][k] = q[j][k];
                                    q[j][k] = temp
                                }
                                if(stats)
                                    for(k = 0; k < m + 1; k++)
                                    {
                                        temp = v[i][k];
                                        v[i][k] = v[j][k];
                                        v[j][k] = temp
                                    }
                                found = true
                            }
                        if(!found)
                            return Calc.Errors.Number
                    }
                    val = 1.0 / q[i][i];
                    for(k = 0; k < m + 2; k++)
                        q[i][k] = q[i][k] * val;
                    if(stats)
                        for(k = 0; k < m + 1; k++)
                            v[i][k] = v[i][k] * val;
                    for(j = 0; j < m + 1; j++)
                        if(j !== i)
                        {
                            val = -q[j][i];
                            for(k = 0; k < m + 2; k++)
                                q[j][k] = q[j][k] + val * q[i][k];
                            if(stats)
                                for(k = 0; k < m + 1; k++)
                                    v[j][k] = v[j][k] + val * v[i][k]
                        }
                }
            else
                for(i = 1; i < m + 1; i++)
                {
                    if(q[i][i] === 0.0)
                    {
                        found = false;
                        for(j = i + 1; !found && j < m + 1; j++)
                            if(q[j][i] !== 0.0)
                            {
                                for(k = 0; k < m + 2; k++)
                                {
                                    temp = q[i][k];
                                    q[i][k] = q[j][k];
                                    q[j][k] = temp
                                }
                                if(stats)
                                    for(k = 0; k < m + 1; k++)
                                    {
                                        temp = v[i][k];
                                        v[i][k] = v[j][k];
                                        v[j][k] = temp
                                    }
                                found = true
                            }
                        if(!found)
                            return Calc.Errors.Number
                    }
                    val = 1.0 / q[i][i];
                    for(k = 1; k < m + 2; k++)
                        q[i][k] = q[i][k] * val;
                    if(stats)
                        for(k = 1; k < m + 1; k++)
                            v[i][k] = v[i][k] * val;
                    for(j = 1; j < m + 1; j++)
                        if(j !== i)
                        {
                            val = -q[j][i];
                            for(k = 1; k < m + 2; k++)
                                q[j][k] = q[j][k] + val * q[i][k];
                            if(stats)
                                for(k = 1; k < m + 1; k++)
                                    v[j][k] = v[j][k] + val * v[i][k]
                        }
                    q[0][m + 1] = 0.0
                }
            result = [];
            result[0] = [];
            for(i = 0; i < m + 1; i++)
                result[0][i] = Math.exp(q[m - i][m + 1]);
            if(stats)
            {
                result[1] = [];
                result[2] = [];
                result[3] = [];
                result[4] = [];
                var sqr,
                    sqt,
                    sqe;
                sqt = e[m + 1] - e[0] * e[0] / n;
                sqr = e[m + 1];
                for(i = 0; i < m + 1; i++)
                    sqr -= q[i][m + 1] * e[i];
                sqe = sqt - sqr;
                if(sqt === 0.0)
                    result[2][0] = Calc.Errors.Number;
                else
                    result[2][0] = sqe / sqt;
                result[4][0] = sqe;
                result[4][1] = sqr;
                if(constant)
                {
                    if(n - m - 1 === 0)
                    {
                        result[2][1] = Calc.Errors.Number;
                        for(i = 0; i < m + 1; i++)
                            result[1][i] = Calc.Errors.Number
                    }
                    else
                    {
                        se2 = sqr / (n - m - 1);
                        for(i = 0; i < m + 1; i++)
                            result[1][m - i] = Math.sqrt(se2 * v[i][i]);
                        result[2][1] = Math.sqrt(se2)
                    }
                    if(sqr === 0.0)
                        result[3][0] = Calc.Errors.Number;
                    else
                        result[3][0] = convert.toDouble(n - m - 1) * sqe / (sqr * convert.toDouble(m));
                    result[3][1] = convert.toDouble(n - m - 1)
                }
                else
                {
                    if(n - m === 0)
                    {
                        for(i = 0; i < m + 1; i++)
                            result[1][i] = Calc.Errors.Number;
                        result[2][1] = Calc.Errors.Number
                    }
                    else
                    {
                        se2 = sqr / convert.toDouble(n - m);
                        result[1][m] = Calc.Errors.NotAvailable;
                        for(i = 1; i < m + 1; i++)
                            result[1][m - i] = Math.sqrt(se2 * v[i][i]);
                        result[2][1] = Math.sqrt(se2)
                    }
                    if(sqr === 0.0)
                        result[3][0] = Calc.Errors.Number;
                    else
                        result[3][0] = convert.toDouble(n - m) * sqe / (sqr * convert.toDouble(m));
                    result[3][1] = convert.toDouble(n - m)
                }
                for(i = 2; i < 5; i++)
                    for(j = 2; j < m + 1; j++)
                        result[i][j] = Calc.Errors.NotAvailable
            }
            return result
        }
        return Calc.Errors.Number
    }
    function st_steyx(args)
    {
        var x,
            y,
            sumy = 0.0,
            sumy2 = 0.0,
            sumx = 0.0,
            sumx2 = 0.0,
            sumxy = 0.0,
            n = 0;
        var convert = Calc.Convert,
            arrayHelper = Calc._ArrayHelper;
        var length = arrayHelper.getLength(args[0]);
        if(length !== arrayHelper.getLength(args[1]))
            return Calc.Errors.NotAvailable;
        for(var i = 0; i < length; i++)
        {
            var valueY = arrayHelper.getValueByIndex(args[0],i);
            var valueX = arrayHelper.getValueByIndex(args[1],i);
            if(convert.isNumber(valueY) && convert.isNumber(valueX))
            {
                y = convert.toDouble(valueY);
                x = convert.toDouble(valueX);
                sumy += y;
                sumy2 += y * y;
                sumx += x;
                sumx2 += x * x;
                sumxy += x * y;
                n++
            }
            else if(convert.isError(valueY))
                return valueY;
            else if(convert.isError(valueX))
                return valueX
        }
        if(n * (n - 2) === 0)
            return Calc.Errors.DivideByZero;
        if(n * sumx2 - sumx * sumx === 0.0)
            return Calc.Errors.DivideByZero;
        return Math.sqrt((n * sumy2 - sumy * sumy - (n * sumxy - sumx * sumy) * (n * sumxy - sumx * sumy) / (n * sumx2 - sumx * sumx)) / (n * (n - 2)))
    }
    Functions.StatHelper = {
        normsdist: st_normsdist,
        __averageIncludeSubtotals: __averageIncludeSubtotals,
        __countIncludeSubtotals: __countIncludeSubtotals,
        __countaIncludeSubtotals: __countaIncludeSubtotals,
        __maxIncludeSubtotals: __maxIncludeSubtotals,
        __minIncludeSubtotals: __minIncludeSubtotals,
        __stdevIncludeSubtotals: __stdevIncludeSubtotals,
        __stdevpIncludeSubtotals: __stdevpIncludeSubtotals,
        __varrIncludeSubtotals: __varrIncludeSubtotals,
        __varpIncludeSubtotals: __varpIncludeSubtotals
    };
    def("MAX",st_max,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("MAXA",st_maxa,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("MIN",st_min,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("MINA",st_mina,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("LARGE",st_large,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsFirst,
        acceptsReference: acceptsFirst
    });
    def("SMALL",st_small,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsFirst,
        acceptsReference: acceptsFirst
    });
    def("AVERAGE",st_average,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("AVERAGEA",st_averagea,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("AVERAGEIF",st_averageif,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsThird,
        acceptsArray: acceptsFirstOrThird,
        acceptsReference: acceptsFirstOrThird
    });
    def("AVERAGEIFS",st_averageifs,{
        minArgs: 3,
        acceptsArray: acceptsFirstOrOdd,
        acceptsReference: acceptsFirstOrOdd
    });
    def("MEDIAN",st_median,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("MODE",st_mode,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("GEOMEAN",st_geomean,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("HARMEAN",st_harmean,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("TRIMMEAN",st_trimmean,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsFirst,
        acceptsReference: acceptsFirst
    });
    def("FREQUENCY",st_frequency,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("RANK",st_rank,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsThird,
        acceptsArray: acceptsSecond,
        acceptsReference: acceptsSecond
    });
    def("KURT",st_kurt,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("PERCENTILE",st_percentile,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsFirst,
        acceptsReference: acceptsFirst
    });
    def("PERCENTRANK",st_percentrank,{
        minArgs: 2,
        maxArgs: 3,
        acceptsMissingArgument: acceptsThird,
        acceptsArray: acceptsFirst,
        acceptsReference: acceptsFirst
    });
    def("QUARTILE",st_quartile,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsFirst,
        acceptsReference: acceptsFirst
    });
    def("COUNT",st_count,{
        minArgs: 1,
        acceptsError: acceptsAny,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("COUNTA",st_counta,{
        minArgs: 1,
        acceptsError: acceptsAny,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("COUNTBLANK",st_countblank,{
        minArgs: 1,
        maxArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("COUNTIF",st_countif,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsFirst,
        acceptsReference: acceptsFirst
    });
    def("COUNTIFS",st_countifs,{
        minArgs: 2,
        acceptsArray: acceptsEven,
        acceptsReference: acceptsEven
    });
    def("AVEDEV",st_avedev,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("STDEV",st_stdev,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("STDEVA",st_stdeva,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("STDEVP",st_stdevp,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("STDEVPA",st_stdevpa,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("VAR",st_varr,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("VARA",st_vara,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("VARP",st_varp,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("VARPA",st_varpa,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("COVAR",st_covar,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("DEVSQ",st_devsp,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("CONFIDENCE",st_confidence,{
        minArgs: 3,
        maxArgs: 3
    });
    def("FORECAST",st_forecast,{
        minArgs: 3,
        maxArgs: 3,
        acceptsArray: acceptAboveZero,
        acceptsReference: acceptAboveZero
    });
    def("INTERCEPT",st_intercept,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("LINEST",st_linest,{
        minArgs: 1,
        maxArgs: 4,
        acceptsReference: acceptsFirstOrOne,
        acceptsArray: acceptsFirstOrOne,
        acceptsMissingArgument: acceptsSecondOrThirdOrFourth
    });
    def("SLOPE",st_slope,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("TREND",st_trend,{
        minArgs: 1,
        maxArgs: 4,
        acceptsReference: acceptsFirstOrSecondOrThird,
        acceptsArray: acceptsFirstOrSecondOrThird,
        acceptsMissingArgument: acceptsSecondOrThirdOrFourth
    });
    def("GROWTH",st_growth,{
        minArgs: 1,
        maxArgs: 4,
        acceptsReference: acceptsNotFourth,
        acceptsArray: acceptsNotFourth,
        acceptsMissingArgument: acceptsSecondOrThirdOrFourth
    });
    def("LOGEST",st_logest,{
        minArgs: 1,
        maxArgs: 4,
        acceptsReference: acceptsFirstOrOne,
        acceptsArray: acceptsFirstOrOne,
        acceptsMissingArgument: acceptsSecondOrThirdOrFourth
    });
    def("STEYX",st_steyx,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("BETADIST",st_betadist,{
        minArgs: 3,
        maxArgs: 5,
        acceptsMissingArgument: acceptsFourthOrFifth
    });
    def("BETAINV",st_betainv,{
        minArgs: 3,
        maxArgs: 5,
        acceptsMissingArgument: acceptsFourthOrFifth
    });
    def("BINOMDIST",st_binomdist,{
        minArgs: 4,
        maxArgs: 4
    });
    def("NEGBINOMDIST",st_negbinomdist,{
        minArgs: 3,
        maxArgs: 3
    });
    def("CRITBINOM",st_critbinom,{
        minArgs: 3,
        maxArgs: 3
    });
    def("CHIDIST",st_chidist,{
        minArgs: 2,
        maxArgs: 2
    });
    def("CHIINV",st_chiinv,{
        minArgs: 2,
        maxArgs: 2
    });
    def("CHITEST",st_chitest,{
        minArgs: 2,
        maxArgs: 2,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("CORREL",st_correl,{
        minArgs: 2,
        maxArgs: 2,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("EXPONDIST",st_expondist,{
        minArgs: 3,
        maxArgs: 3
    });
    def("FDIST",st_fdist,{
        minArgs: 3,
        maxArgs: 3
    });
    def("FINV",st_finv,{
        minArgs: 3,
        maxArgs: 3
    });
    def("FISHER",st_fisher,{
        minArgs: 1,
        maxArgs: 1
    });
    def("FISHERINV",st_fisherinv,{
        minArgs: 1,
        maxArgs: 1
    });
    def("FTEST",st_ftest,{
        minArgs: 2,
        maxArgs: 2,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("GAMMADIST",st_gammadist,{
        minArgs: 4,
        maxArgs: 4
    });
    def("GAMMAINV",st_gammainv,{
        minArgs: 3,
        maxArgs: 3
    });
    def("GAMMALN",st_gammaln,{
        minArgs: 1,
        maxArgs: 1
    });
    def("HYPGEOMDIST",st_hypgeomdist,{
        minArgs: 4,
        maxArgs: 4
    });
    def("LOGNORMDIST",st_lognormdist,{
        minArgs: 3,
        maxArgs: 3
    });
    def("LOGINV",st_loginv,{
        minArgs: 3,
        maxArgs: 3
    });
    def("NORMDIST",st_normdist,{
        minArgs: 4,
        maxArgs: 4
    });
    def("NORMINV",st_norminv,{
        minArgs: 3,
        maxArgs: 3
    });
    def("NORMSDIST",st_normsdist,{
        minArgs: 1,
        maxArgs: 1
    });
    def("NORMSINV",st_normsinv,{
        minArgs: 1,
        maxArgs: 1
    });
    def("PEARSON",st_pearson,{
        minArgs: 2,
        maxArgs: 2,
        acceptsReference: acceptsAny,
        acceptsArray: acceptsAny
    });
    def("RSQ",st_rsq,{
        minArgs: 2,
        maxArgs: 2,
        acceptsReference: acceptsFirstOrOne,
        acceptsArray: acceptsFirstOrOne
    });
    def("POISSON",st_poisson,{
        minArgs: 3,
        maxArgs: 3
    });
    def("PROB",st_prob,{
        minArgs: 3,
        maxArgs: 4,
        acceptsMissingArgument: acceptsFourth,
        acceptsReference: acceptsFirstOrOne,
        acceptsArray: acceptsFirstOrOne
    });
    def("SKEW",st_skew,{
        minArgs: 1,
        acceptsArray: acceptsAny,
        acceptsReference: acceptsAny
    });
    def("STANDARDIZE",st_standardize,{
        minArgs: 3,
        maxArgs: 3
    });
    def("TDIST",st_tdist,{
        minArgs: 3,
        maxArgs: 3
    });
    def("TINV",st_tinv,{
        minArgs: 2,
        maxArgs: 2
    });
    def("TTEST",st_ttest,{
        minArgs: 4,
        maxArgs: 4,
        acceptsReference: acceptsFirstOrOne,
        acceptsArray: acceptsFirstOrOne
    });
    def("WEIBULL",st_weibull,{
        minArgs: 4,
        maxArgs: 4
    });
    def("ZTEST",st_ztest,{
        minArgs: 2,
        maxArgs: 3,
        acceptsReference: acceptsFirst,
        acceptsArray: acceptsFirst,
        acceptsMissingArgument: acceptsThird
    });
    def("PERMUT",st_permut,{
        minArgs: 2,
        maxArgs: 2
    })
})(window);
(function(window, $)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    var UI = GrapeCity.UI;
    UI.CriteriaType = {
        AnyValue: 0x0,
        WholeNumber: 0x1,
        DecimalValues: 0x2,
        List: 0x3,
        Date: 0x4,
        Time: 0x5,
        TextLength: 0x6,
        Custom: 0x7
    };
    UI.DataValidationResult = {
        ForceApply: 0,
        Discard: 1,
        Retry: 2
    };
    UI.ErrorStyle = {
        Stop: 0,
        Warning: 1,
        Information: 2
    };
    var CriteriaType = UI.CriteriaType;
    var DataValidationResult = UI.DataValidationResult;
    var ErrorStyle = UI.ErrorStyle;
    function convertToDouble(val)
    {
        if(val === undefined || val === null)
            return 0.0;
        else if(typeof val === "number")
            return val;
        else if(typeof val === "string")
        {
            var ret = parseFloat(val);
            if(!isNaN(ret))
                if(ret.toString() === val.toString())
                    return ret
        }
        return null
    }
    function convertToDateTime(val)
    {
        if(val instanceof Date)
            return val;
        else if(typeof val === "string")
            return new Date(val);
        return null
    }
    function AreaCondition(expected, formula)
    {
        this.expected = expected;
        this.formula = typeof formula === "string" ? $.trim(formula).replace("=","") : formula
    }
    AreaCondition.prototype = {
        ignoreBlank: false,
        _concatArray: function(destArray, array)
        {
            var temp,
                i;
            if(array.length === 1)
            {
                temp = array[0];
                if(temp instanceof Array)
                    for(i = 0; i < temp.length; i++)
                        destArray.push(temp[i]);
                else
                    destArray.push(temp)
            }
            else
                for(i = 0; i < array.length; i++)
                {
                    temp = array[i];
                    if(temp instanceof Array)
                    {
                        if(temp.length > 0)
                            destArray.push(temp[0])
                    }
                    else
                        destArray.push(temp)
                }
        },
        getValidList: function(evaluator, baseRow, baseColumn)
        {
            var validValuesTemp = [];
            if(this.formula && this.formula.length > 0)
            {
                var obj = this.getExpected(evaluator,baseRow,baseColumn);
                if(obj instanceof Array)
                    this._concatArray(validValuesTemp,obj);
                else
                    validValuesTemp.push(obj)
            }
            else if(this.expected && this.expected.length > 0)
            {
                var source = this.expected;
                var datas = source.split(",");
                if(datas)
                {
                    var formatter = new UI.GeneralFormatter;
                    for(var i = 0; i < datas.length; i++)
                    {
                        var data = datas[i];
                        if(data === undefined || data === null || data === "")
                            continue;
                        var dataTemp = $.trim(data);
                        if(dataTemp !== undefined && dataTemp !== null && dataTemp !== "")
                            try
                            {
                                validValuesTemp.push(formatter.Parse(dataTemp))
                            }
                            catch(ex)
                            {
                                validValuesTemp.push(dataTemp)
                            }
                    }
                }
            }
            return validValuesTemp
        },
        _equals: function(v1, v2)
        {
            if(v1 instanceof Date && v2 instanceof Date)
                return v1.valueOf() === v2.valueOf();
            else
                return v1 === v2
        },
        evaluate: function(evaluator, baseRow, baseColumn, actualObj)
        {
            if(actualObj === undefined || actualObj === null || actualObj === "")
                return this.ignoreBlank === true;
            var valids = this.getValidList(evaluator,baseRow,baseColumn);
            if(valids)
                for(var i = 0; i < valids.length; i++)
                {
                    var obj = valids[i];
                    if((obj === undefined || obj === null) && (actualObj === undefined || actualObj === null))
                        return true;
                    else if(obj !== undefined && obj !== null && this._equals(obj,actualObj))
                        return true
                }
            return false
        },
        getExpected: function(evaluator, baseRow, baseCol)
        {
            if(this.formula)
            {
                var rowCount,
                    colCount,
                    r,
                    c;
                var arrayValue = [];
                var calcService = evaluator.getCalcService();
                var expr = calcService.parse(this.formula,baseRow,baseCol);
                var v = calcService.evaluateParsedFormula(evaluator._getSheetSource(),expr,baseRow,baseCol,true);
                if(v instanceof GrapeCity.Calc.Reference)
                {
                    rowCount = v.getRowCount(0);
                    colCount = v.getColumnCount(0);
                    for(r = 0; r < rowCount; r++)
                    {
                        arrayValue[r] = [];
                        for(c = 0; c < colCount; c++)
                            arrayValue[r][c] = v.getValue(0,r,c)
                    }
                }
                else if(v instanceof GrapeCity.Calc.Array)
                {
                    rowCount = v.getRowCount();
                    colCount = v.getColumnCount();
                    for(r = 0; r < rowCount; r++)
                    {
                        arrayValue[r] = [];
                        for(c = 0; c < colCount; c++)
                            arrayValue[r][c] = v.getValue(r,c)
                    }
                }
                return arrayValue
            }
            else
                return this.expected
        },
        toJSON: function()
        {
            return{
                    expected: this.expected,
                    formula: this.formula,
                    conType: UI.ConditionType.AreaCondition,
                    ignoreBlank: this.ignoreBlank
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.expected !== null && settings.expected !== undefined)
                this.expected = settings.expected;
            if(settings.formula !== null && settings.formula !== undefined)
                this.formula = settings.formula;
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank
        }
    };
    AreaCondition.fromSource = function(expected)
    {
        return new AreaCondition(expected,null)
    };
    AreaCondition.fromFormula = function(formula)
    {
        return new AreaCondition("",formula)
    };
    UI.AreaCondition = AreaCondition;
    function DefaultDataValidator(condition)
    {
        this._init();
        this.condition = condition;
        if(this.condition)
            this.condition.ignoreBlank = this.ignoreBlank;
        this.type = CriteriaType.AnyValue
    }
    DefaultDataValidator.prototype = {
        condition: null,
        type: null,
        errorStyle: null,
        ignoreBlank: true,
        inCellDropdown: true,
        showInputMessage: true,
        showErrorMessage: true,
        inputTitle: "",
        errorTitle: "",
        inputMessage: "",
        errorMessage: "",
        comparisonOperator: null,
        IgnoreBlank: function(value)
        {
            if(arguments.length <= 0)
                return this.ignoreBlank;
            else
            {
                this.ignoreBlank = value;
                if(this.condition)
                    this.condition.ignoreBlank = value;
                return this
            }
        },
        _init: function()
        {
            this.errorStyle = ErrorStyle.Stop;
            this.ignoreBlank = true;
            this.inCellDropdown = true;
            this.showInputMessage = true;
            this.showErrorMessage = true;
            this.inputTitle = "";
            this.errorTitle = "";
            this.inputMessage = "";
            this.errorMessage = "";
            this.comparisonOperator = UI.ComparisonOperator.Between
        },
        value1: function()
        {
            var cond = this.condition && this.condition.item1 ? this.condition.item1 : this.condition;
            if(cond)
                if(cond.formula && cond.formula.length > 0)
                    return"=" + cond.formula.toString().toUpperCase();
                else
                    return cond.expected;
            return null
        },
        value2: function()
        {
            var cond = this.condition && this.condition.item2 ? this.condition.item2 : this.condition;
            if(cond)
                if(cond.formula && cond.formula.length > 0)
                    return"=" + cond.formula.toString().toUpperCase();
                else
                    return cond.expected;
            return null
        },
        isValid: function(evaluator, baseRow, baseColumn, actual)
        {
            if(this.condition)
            {
                this.IgnoreBlank(this.ignoreBlank);
                if(this.condition.ignoreBlank && (actual === undefined || actual === null || actual === ""))
                    return true;
                var val = actual,
                    v;
                if(actual !== undefined && actual !== null)
                    switch(this.type)
                    {
                        case CriteriaType.AnyValue:
                            return true;
                        case CriteriaType.DecimalValues:
                        case CriteriaType.WholeNumber:
                            v = convertToDouble(actual);
                            if(v !== undefined && v !== null)
                                val = v;
                            break;
                        case CriteriaType.Date:
                        case CriteriaType.Time:
                            v = convertToDateTime(actual);
                            if(v !== undefined && v !== null)
                                val = v;
                            break;
                        case CriteriaType.Custom:
                        case CriteriaType.List:
                        case CriteriaType.TextLength:
                            break
                    }
                return this.condition.evaluate(evaluator,baseRow,baseColumn,val,val)
            }
            return true
        },
        reset: function()
        {
            this._init();
            this.type = CriteriaType.AnyValue;
            this.condition = null
        },
        getValidList: function(evaluator, baseRow, baseColumn)
        {
            if(this.condition != null && this.type === CriteriaType.List && this.condition instanceof AreaCondition)
                return this.condition.getValidList(evaluator,baseRow,baseColumn);
            return null
        },
        toJSON: function()
        {
            return{
                    errorStyle: this.errorStyle,
                    ignoreBlank: this.ignoreBlank,
                    inCellDropdown: this.inCellDropdown,
                    showInputMessage: this.showInputMessage,
                    showErrorMessage: this.showErrorMessage,
                    inputTitle: this.inputTitle,
                    errorTitle: this.errorTitle,
                    inputMessage: this.inputMessage,
                    errorMessage: this.errorMessage,
                    comparisonOperator: this.comparisonOperator,
                    type: this.type,
                    condition: this.condition
                }
        },
        fromJSON: function(settings)
        {
            if(!settings)
                return;
            if(settings.condition !== null && settings.condition !== undefined)
            {
                var condition = null;
                var dict = this._getConditionTypes();
                var conditionClass = dict[settings.condition.conType];
                if(conditionClass)
                {
                    condition = new conditionClass;
                    condition.fromJSON(settings.condition)
                }
                this.condition = condition
            }
            if(settings.type !== null && settings.type !== undefined)
                this.type = settings.type;
            if(settings.comparisonOperator !== null && settings.comparisonOperator !== undefined)
                this.comparisonOperator = settings.comparisonOperator;
            if(settings.errorStyle !== null && settings.errorStyle !== undefined)
                this.errorStyle = settings.errorStyle;
            if(settings.ignoreBlank !== null && settings.ignoreBlank !== undefined)
                this.ignoreBlank = settings.ignoreBlank;
            if(settings.inCellDropdown !== null && settings.inCellDropdown !== undefined)
                this.inCellDropdown = settings.inCellDropdown;
            if(settings.showInputMessage !== null && settings.showInputMessage !== undefined)
                this.showInputMessage = settings.showInputMessage;
            if(settings.showErrorMessage !== null && settings.showErrorMessage !== undefined)
                this.showErrorMessage = settings.showErrorMessage;
            if(settings.inputTitle !== null && settings.inputTitle !== undefined)
                this.inputTitle = settings.inputTitle;
            if(settings.errorTitle !== null && settings.errorTitle !== undefined)
                this.errorTitle = settings.errorTitle;
            if(settings.inputMessage !== null && settings.inputMessage !== undefined)
                this.inputMessage = settings.inputMessage;
            if(settings.errorMessage !== null && settings.errorMessage !== undefined)
                this.errorMessage = settings.errorMessage
        },
        _getConditionTypes: function()
        {
            if(!this._dict)
            {
                var dict = {};
                dict[UI.ConditionType.RelationCondition] = UI.RelationCondition;
                dict[UI.ConditionType.NumberCondition] = UI.NumberCondition;
                dict[UI.ConditionType.TextCondition] = UI.TextCondition;
                dict[UI.ConditionType.ColorCondition] = UI.ColorCondition;
                dict[UI.ConditionType.FormulaCondition] = UI.FormulaCondition;
                dict[UI.ConditionType.DateCondition] = UI.DateCondition;
                dict[UI.ConditionType.DateExCondition] = UI.DateExCondition;
                dict[UI.ConditionType.TextLengthCondition] = UI.TextLengthCondition;
                dict[UI.ConditionType.Top10Condition] = UI.Top10Condition;
                dict[UI.ConditionType.UniqueCondition] = UI.UniqueCondition;
                dict[UI.ConditionType.AverageCondition] = UI.AverageCondition;
                dict[UI.ConditionType.CellValueCondition] = UI.CellValueCondition;
                dict[UI.ConditionType.AreaCondition] = UI.AreaCondition;
                this._dict = dict
            }
            return this._dict
        }
    };
    UI.DefaultDataValidator = DefaultDataValidator;
    DefaultDataValidator.isFormula = function(val)
    {
        return val && val[0] === "="
    };
    UI.DefaultDataValidator.createNumberValidator = function(typeOperator, v1, v2, isIntegerValue)
    {
        var formula1 = DefaultDataValidator.isFormula(v1) ? v1.replace("=","") : null;
        var expect1 = DefaultDataValidator.isFormula(v1) ? null : v1;
        var formula2 = DefaultDataValidator.isFormula(v2) ? v2.replace("=","") : null;
        var expect2 = DefaultDataValidator.isFormula(v2) ? null : v2;
        var conditionTemp = null,
            c1,
            c2;
        var comparisonOperator = UI.ComparisonOperator,
            generalCompareType = UI.GeneralCompareType,
            relationCompareType = UI.RelationCompareType,
            NumberCondition = UI.NumberCondition,
            RelationCondition = UI.RelationCondition;
        switch(typeOperator)
        {
            case comparisonOperator.Between:
                c1 = new NumberCondition(generalCompareType.GreaterThanOrEqualsTo,expect1,formula1);
                c1.integerValue = isIntegerValue;
                c2 = new NumberCondition(generalCompareType.LessThanOrEqualsTo,expect2,formula2);
                c2.integerValue = isIntegerValue;
                conditionTemp = new RelationCondition(relationCompareType.And,c1,c2);
                break;
            case comparisonOperator.NotBetween:
                c1 = new NumberCondition(generalCompareType.LessThan,expect1,formula1);
                c1.integerValue = isIntegerValue;
                c2 = new NumberCondition(generalCompareType.GreaterThan,expect2,formula2);
                c2.integerValue = isIntegerValue;
                conditionTemp = new RelationCondition(relationCompareType.Or,c1,c2);
                break;
            case comparisonOperator.EqualsTo:
                conditionTemp = new NumberCondition(generalCompareType.EqualsTo,expect1,formula1);
                conditionTemp.integerValue = isIntegerValue;
                break;
            case comparisonOperator.NotEqualsTo:
                conditionTemp = new NumberCondition(generalCompareType.NotEqualsTo,expect1,formula1);
                conditionTemp.integerValue = isIntegerValue;
                break;
            case comparisonOperator.GreaterThan:
                conditionTemp = new NumberCondition(generalCompareType.GreaterThan,expect1,formula1);
                conditionTemp.integerValue = isIntegerValue;
                break;
            case comparisonOperator.GreaterThanOrEqualsTo:
                conditionTemp = new NumberCondition(generalCompareType.GreaterThanOrEqualsTo,expect1,formula1);
                conditionTemp.integerValue = isIntegerValue;
                break;
            case comparisonOperator.LessThan:
                conditionTemp = new NumberCondition(generalCompareType.LessThan,expect1,formula1);
                conditionTemp.integerValue = isIntegerValue;
                break;
            case comparisonOperator.LessThanOrEqualsTo:
                conditionTemp = new NumberCondition(generalCompareType.LessThanOrEqualsTo,expect1,formula1);
                conditionTemp.integerValue = isIntegerValue;
                break
        }
        var t = new DefaultDataValidator(conditionTemp);
        t.type = isIntegerValue ? CriteriaType.WholeNumber : CriteriaType.DecimalValues;
        t.comparisonOperator = typeOperator;
        return t
    };
    UI.DefaultDataValidator.createDateValidator = function(typeOperator, v1, v2)
    {
        var formula1 = DefaultDataValidator.isFormula(v1) ? v1.replace("=","") : null;
        var expect1 = DefaultDataValidator.isFormula(v1) ? null : v1;
        var formula2 = DefaultDataValidator.isFormula(v2) ? v2.replace("=","") : null;
        var expect2 = DefaultDataValidator.isFormula(v2) ? null : v2;
        var conditionTemp = null,
            c1,
            c2;
        var comparisonOperator = UI.ComparisonOperator,
            dateCompareType = UI.DateCompareType,
            relationCompareType = UI.RelationCompareType,
            DateCondition = UI.DateCondition,
            RelationCondition = UI.RelationCondition;
        switch(typeOperator)
        {
            case comparisonOperator.Between:
                c1 = new DateCondition(dateCompareType.AfterEqualsTo,expect1,formula1);
                c2 = new DateCondition(dateCompareType.BeforeEqualsTo,expect2,formula2);
                conditionTemp = new RelationCondition(relationCompareType.And,c1,c2);
                break;
            case comparisonOperator.NotBetween:
                c1 = new DateCondition(dateCompareType.Before,expect1,formula1);
                c2 = new DateCondition(dateCompareType.After,expect2,formula2);
                conditionTemp = new RelationCondition(relationCompareType.Or,c1,c2);
                break;
            case comparisonOperator.EqualsTo:
                conditionTemp = new DateCondition(dateCompareType.EqualsTo,expect1,formula1);
                break;
            case comparisonOperator.NotEqualsTo:
                conditionTemp = new DateCondition(dateCompareType.NotEqualsTo,expect1,formula1);
                break;
            case comparisonOperator.GreaterThan:
                conditionTemp = new DateCondition(dateCompareType.After,expect1,formula1);
                break;
            case comparisonOperator.GreaterThanOrEqualsTo:
                conditionTemp = new DateCondition(dateCompareType.AfterEqualsTo,expect1,formula1);
                break;
            case comparisonOperator.LessThan:
                conditionTemp = new DateCondition(dateCompareType.Before,expect1,formula1);
                break;
            case comparisonOperator.LessThanOrEqualsTo:
                conditionTemp = new DateCondition(dateCompareType.BeforeEqualsTo,expect1,formula1);
                break
        }
        var t = new DefaultDataValidator(conditionTemp);
        t.type = CriteriaType.Date;
        t.comparisonOperator = typeOperator;
        return t
    };
    UI.DefaultDataValidator.createTextLengthValidator = function(typeOperator, v1, v2)
    {
        var formula1 = DefaultDataValidator.isFormula(v1) ? v1.replace("=","") : null;
        var expect1 = DefaultDataValidator.isFormula(v1) ? null : v1;
        var formula2 = DefaultDataValidator.isFormula(v2) ? v2.replace("=","") : null;
        var expect2 = DefaultDataValidator.isFormula(v2) ? null : v2;
        var conditionTemp = null,
            c1,
            c2;
        var comparisonOperator = UI.ComparisonOperator,
            generalCompareType = UI.GeneralCompareType,
            relationCompareType = UI.RelationCompareType,
            TextLengthCondition = UI.TextLengthCondition,
            RelationCondition = UI.RelationCondition;
        switch(typeOperator)
        {
            case comparisonOperator.Between:
                c1 = new TextLengthCondition(generalCompareType.GreaterThanOrEqualsTo,expect1,formula1);
                c2 = new TextLengthCondition(generalCompareType.LessThanOrEqualsTo,expect2,formula2);
                conditionTemp = new RelationCondition(relationCompareType.And,c1,c2);
                break;
            case comparisonOperator.NotBetween:
                c1 = new TextLengthCondition(generalCompareType.LessThan,expect1,formula1);
                c2 = new TextLengthCondition(generalCompareType.GreaterThan,expect2,formula2);
                conditionTemp = new RelationCondition(relationCompareType.Or,c1,c2);
                break;
            case comparisonOperator.EqualsTo:
                conditionTemp = new TextLengthCondition(generalCompareType.EqualsTo,expect1,formula1);
                break;
            case comparisonOperator.NotEqualsTo:
                conditionTemp = new TextLengthCondition(generalCompareType.NotEqualsTo,expect1,formula1);
                break;
            case comparisonOperator.GreaterThan:
                conditionTemp = new TextLengthCondition(generalCompareType.GreaterThan,expect1,formula1);
                break;
            case comparisonOperator.GreaterThanOrEqualsTo:
                conditionTemp = new TextLengthCondition(generalCompareType.GreaterThanOrEqualsTo,expect1,formula1);
                break;
            case comparisonOperator.LessThan:
                conditionTemp = new TextLengthCondition(generalCompareType.LessThan,expect1,formula1);
                break;
            case comparisonOperator.LessThanOrEqualsTo:
                conditionTemp = new TextLengthCondition(generalCompareType.LessThanOrEqualsTo,expect1,formula1);
                break
        }
        var t = new DefaultDataValidator(conditionTemp);
        t.type = CriteriaType.TextLength;
        t.comparisonOperator = typeOperator;
        return t
    };
    UI.DefaultDataValidator.createFormulaValidator = function(formula)
    {
        var s = formula;
        if(s && s.charAt(0) === "=")
            s = s.substr(1);
        var t = new DefaultDataValidator(new UI.FormulaCondition(UI.CustomValueType.Formula,s));
        t.type = CriteriaType.Custom;
        return t
    };
    UI.DefaultDataValidator.createFormulaListValidator = function(formula)
    {
        var t = new DefaultDataValidator(AreaCondition.fromFormula(formula));
        t.type = CriteriaType.List;
        return t
    };
    UI.DefaultDataValidator.createListValidator = function(source)
    {
        var t = new DefaultDataValidator(AreaCondition.fromSource(source));
        t.type = CriteriaType.List;
        return t
    }
})(window,jQuery);
(function(window, $, undefined)
{
    "use strict";;
    var GrapeCity = window.GrapeCity;
    if(typeof GrapeCity === "undefined")
        GrapeCity = window.GrapeCity = {};
    if(typeof GrapeCity.UI === "undefined")
        GrapeCity.UI = {};
    if(typeof GrapeCity.Calc === "undefined")
        GrapeCity.Calc = {};
    var spread = $.wijmo.wijspread,
        UI = GrapeCity.UI;
    spread.Spread = UI.GcSpread;
    spread.Key = UI.Key;
    spread.KeyMap = UI.KeyMap;
    spread.Direction = UI.Direction;
    spread.HorizontalAlign = UI.HorizontalAlign;
    spread.VerticalAlign = UI.VerticalAlign;
    spread.HorizontalPosition = UI.HorizontalPosition;
    spread.VerticalPosition = UI.VerticalPosition;
    spread.HeaderAutoText = UI.HeaderAutoText;
    spread.ClipboardPasteOptions = UI.ClipboardPasteOptions;
    spread.TextFileOpenFlags = UI.TextFileOpenFlags;
    spread.VisualState = UI.VisualState;
    spread.ReferenceStyle = UI.ReferenceStyle;
    spread.LineStyle = UI.LineStyle;
    spread.LineBorder = UI.LineBorder;
    spread.SpreadActions = UI.SpreadActions;
    spread.UndoRedo = UI.UndoRedo;
    spread.Sheet = UI.GcSheet;
    spread.Cell = UI.Cell;
    spread.Row = UI.Row;
    spread.Column = UI.Col;
    spread.Range = UI.Range;
    spread.Rect = UI.Rect;
    spread.SheetArea = UI.SheetArea;
    spread.StorageType = UI.StorageType;
    spread.Style = UI.Style;
    spread.CopyToOption = UI.CopyToOption;
    spread.Events = UI.Events;
    spread.DataContext = UI.DataContext;
    spread.FontFactory = UI.FontFactory;
    spread.SelectionPolicy = UI.SelectionPolicy;
    spread.SelectionUnit = UI.SelectionUnit;
    spread.EditorStatus = UI.EditorStatus;
    spread.NameInfo = UI.NameInfo;
    spread.ThemeColor = UI.ThemeColor;
    spread.ThemeColors = UI.ThemeColors;
    spread.SpreadTheme = UI.SpreadTheme;
    spread.SpreadThemes = UI.SpreadThemes;
    spread.CellsEnumerator = UI.CellsEnumerator;
    spread.SearchFlags = UI.SearchFlags;
    spread.SearchOrder = UI.SearchOrder;
    spread.SearchFoundFlags = UI.SearchFoundFlags;
    spread.SearchCondition = UI.SearchCondition;
    spread.SearchResult = UI.SearchResult;
    spread.AutoFillType = UI.AutoFillType;
    spread.FillDirection = UI.FillDirection;
    spread.FillSeries = UI.FillSeries;
    spread.FillType = UI.FillType;
    spread.FillDateUnit = UI.FillDateUnit;
    spread.DragFillDirection = UI.DragFillDirection;
    spread.FormatMode = UI.FormatMode;
    spread.NumberFormatType = UI.NumberFormatType;
    spread.FormatConverter = UI.FormatConverter;
    spread.GeneralFormatter = UI.GeneralFormatter;
    spread.AutoFormatter = UI.AutoFormatter;
    spread.RowFilterBase = UI.RowFilterBase;
    spread.HideRowFilter = UI.HideRowFilter;
    spread.RangeGroup = UI.RangeGroup;
    spread.RangeGroupItemInfo = UI.RangeGroupItemInfo;
    spread.GroupedItemIndexEnumerator = UI.GroupedItemIndexEnumerator;
    spread.GroupState = UI.GroupState;
    spread.RangeGroupDirection = UI.RangeGroupDirection;
    spread.RangeGroupInfo = UI.RangeGroupInfo;
    spread.EmptyValueStyle = UI.EmptyValueStyle;
    spread.SparklineAxisMinMax = UI.SparklineAxisMinMax;
    spread.SparklineType = UI.SparklineType;
    spread.DataOrientation = UI.DataOrientation;
    spread.SparklineSetting = UI.SparklineSetting;
    spread.SparklineGroup = UI.SparklineGroup;
    spread.Sparkline = UI.Sparkline;
    spread.CriteriaType = UI.CriteriaType;
    spread.DataValidationResult = UI.DataValidationResult;
    spread.ErrorStyle = UI.ErrorStyle;
    spread.DefaultDataValidator = UI.DefaultDataValidator;
    spread.RelationCompareType = UI.RelationCompareType;
    spread.ComparisonOperator = UI.ComparisonOperator;
    spread.GeneralCompareType = UI.GeneralCompareType;
    spread.TextComparisonOperator = UI.TextComparisonOperator;
    spread.TextCompareType = UI.TextCompareType;
    spread.ColorCompareType = UI.ColorCompareType;
    spread.CustomValueType = UI.CustomValueType;
    spread.DateCompareType = UI.DateCompareType;
    spread.Top10ConditionType = UI.Top10ConditionType;
    spread.DateOccurringType = UI.DateOccurringType;
    spread.QuarterType = UI.QuarterType;
    spread.SortState = UI.SortState;
    spread.AverageConditionType = UI.AverageConditionType;
    spread.ScaleValueType = UI.ScaleValueType;
    spread.ConditionalFormats = UI.ConditionalFormats;
    spread.ConditionRuleBase = UI.ConditionRuleBase;
    spread.CellValueRule = UI.CellValueRule;
    spread.RelationCondition = UI.RelationCondition;
    spread.NumberCondition = UI.NumberCondition;
    spread.SpecificTextRule = UI.SpecificTextRule;
    spread.TextCondition = UI.TextCondition;
    spread.ColorCondition = UI.ColorCondition;
    spread.FormulaRule = UI.FormulaRule;
    spread.FormulaCondition = UI.FormulaCondition;
    spread.DateCondition = UI.DateCondition;
    spread.DateOccurringRule = UI.DateOccurringRule;
    spread.DateExCondition = UI.DateExCondition;
    spread.TextLengthCondition = UI.TextLengthCondition;
    spread.Top10Rule = UI.Top10Rule;
    spread.Top10Condition = UI.Top10Condition;
    spread.UniqueRule = UI.UniqueRule;
    spread.DuplicateRule = UI.DuplicateRule;
    spread.UniqueCondition = UI.UniqueCondition;
    spread.AverageRule = UI.AverageRule;
    spread.AverageCondition = UI.AverageCondition;
    spread.ScaleValue = UI.ScaleValue;
    spread.TwoScaleRule = UI.TwoScaleRule;
    spread.ThreeScaleRule = UI.ThreeScaleRule;
    spread.BarDirection = UI.BarDirection;
    spread.DataBarAxisPosition = UI.DataBarAxisPosition;
    spread.DataBarRule = UI.DataBarRule;
    spread.IconSetType = UI.IconSetType;
    spread.IconValueType = UI.IconValueType;
    spread.IconSetRule = UI.IconSetRule;
    spread.IconCriterion = UI.IconCriterion;
    spread.Calc = GrapeCity.Calc;
    spread.CellBindingSource = UI.CellBindingSource;
    spread.BaseCellType = UI.BaseCellType;
    spread.TextCellType = UI.TextCellType;
    spread.ColumnHeaderCellType = UI.ColumnHeaderCellType;
    spread.RowHeaderCellType = UI.RowHeaderCellType;
    spread.CornerCellType = UI.CornerCellType;
    spread.CheckBoxCellType = UI.CheckBoxCellType;
    spread.CheckBoxTextAlign = UI.CheckBoxTextAlign;
    spread.ButtonCellType = UI.ButtonCellType;
    spread.EditorValueType = UI.EditorValueType;
    spread.ComboBoxCellType = UI.ComboBoxCellType;
    spread.HyperLinkCellType = UI.HyperLinkCellType;
    spread.CustomCellType = UI.CustomCellType
})(this,jQuery)

