﻿// polyfills
if (!String.prototype.endsWith) {
	String.prototype.endsWith = function (searchString, position) {
		var subjectString = this.toString();
		if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
			position = subjectString.length;
		}
		position -= searchString.length;
		var lastIndex = subjectString.lastIndexOf(searchString, position);
		return lastIndex !== -1 && lastIndex === position;
	};
}
if (!String.prototype.startsWith) {
	String.prototype.startsWith = function (searchString, position) {
		position = position || 0;
		return this.substr(position, searchString.length) === searchString;
	};
}

/* bug with removing a record then refreshing the gridview */
Ext.define('Ext.view.Table', {
    override: 'Ext.view.Table',
    saveFocusState: function () {
        var me = this,
            store = me.dataSource,
            actionableMode = me.actionableMode,
            navModel = me.getNavigationModel(),
            focusPosition = actionableMode ? me.actionPosition : navModel.getPosition(true),
            refocusRow, refocusCol;
        if (focusPosition) {
            // Separate this from the instance that the nav model is using.
            try {
                focusPosition = focusPosition.clone();
            } catch (ex) { focusPosition = null; }
            // Exit actionable mode.
            // We must inform any Actionables that they must relinquish control.
            // Tabbability must be reset.
            if (actionableMode) {
                me.ownerGrid.setActionableMode(false);
            }
            // Blur the focused descendant, but do not trigger focusLeave.
            me.el.dom.focus();
            // Exiting actionable mode navigates to the owning cell, so in either focus mode we must
            // clear the navigation position
            navModel.setPosition();

            if (!focusPosition) {
                return Ext.emptyFn;
            }

            // The following function will attempt to refocus back in the same mode to the same cell
            // as it was at before based upon the previous record (if it's still inthe store), or the row index.
            return function () {
                // If we still have data, attempt to refocus in the same mode.
                if (store.getCount()) {
                    // Adjust expectations of where we are able to refocus according to what kind of destruction
                    // might have been wrought on this view's DOM during focus save.
                    refocusRow = Math.min(focusPosition.rowIdx, me.all.getCount() - 1);
                    refocusCol = Math.min(focusPosition.colIdx, me.getVisibleColumnManager().getColumns().length - 1);
                    focusPosition = new Ext.grid.CellContext(me).setPosition(store.contains(focusPosition.record) ? focusPosition.record : refocusRow, refocusCol);
                    if (actionableMode) {
                        me.ownerGrid.setActionableMode(true, focusPosition);
                    } else {
                        me.cellFocused = true;
                        // Pass "preventNavigation" as true so that that does not cause selection.
                        navModel.setPosition(focusPosition, null, null, null, true);
                    }
                } else // No rows - focus associated column header
                {
                    focusPosition.column.focus();
                }
            };
        }
        return Ext.emptyFn;
    }
});

/**
 * workaround for bug in ExtJs 6.2.0.
 * Resolved in current yet unreleased version
 */
Ext.define('Mb.override.dom.Element', {
    override: 'Ext.dom.Element'
},
function () {
    var additiveEvents = this.prototype.additiveEvents,
        eventMap = this.prototype.eventMap;
    if (Ext.supports.TouchEvents && Ext.firefoxVersion >= 52 && Ext.os.is.Desktop) {
        eventMap['touchstart'] = 'mousedown';
        eventMap['touchmove'] = 'mousemove';
        eventMap['touchend'] = 'mouseup';
        eventMap['touchcancel'] = 'mouseup';
        eventMap['click'] = 'click';
        eventMap['dblclick'] = 'dblclick';
        additiveEvents['mousedown'] = 'mousedown';
        additiveEvents['mousemove'] = 'mousemove';
        additiveEvents['mouseup'] = 'mouseup';
        additiveEvents['touchstart'] = 'touchstart';
        additiveEvents['touchmove'] = 'touchmove';
        additiveEvents['touchend'] = 'touchend';
        additiveEvents['touchcancel'] = 'touchcancel';

        additiveEvents['pointerdown'] = 'mousedown';
        additiveEvents['pointermove'] = 'mousemove';
        additiveEvents['pointerup'] = 'mouseup';
        additiveEvents['pointercancel'] = 'mouseup';
    }
})

//
Ext.define('Ext.window.Window', {
	override: 'Ext.window.Window',
	show: function (cmp) {
		var that = this;
		if (!this.initialHeight && this.height) {
			this.initialHeight = this.height;
		}
		if (!this.initialWidth && this.width) {
			this.initialWidth = this.width;
		}
		if (!this.initialHeight) {
			this.initialHeight = this.getHeight();
		}
		if (!this.initialWidth) {
			this.initialWidth = this.getWidth();
		}
		if (!this.docListen) {
			this.docListen = Ext.getBody().on({
				destroyable: true,
				'resize': function (body) {
                    // Try catch is necessary due to random errors that occur after a window is resized
                    //    Those random errors result in the window not being centered.
                    try {
					    var bodyHeight = body.getHeight(),
						    bodyWidth = body.getWidth(),
						    windowHeight = that.getHeight(),
						    windowWidth = that.getWidth(),
						    reduceWidth = false,
						    reduceHeight = false;

					    if (windowHeight >= bodyHeight) {
						    that.setHeight(bodyHeight - 80/*buffer top and bottom*/);
						    reduceHeight = true;
					    } else if (windowHeight < (bodyHeight - 120)) {
						    that.setHeight(that.initialHeight);
					    }
					    if (windowWidth >= bodyWidth) {
						    that.setWidth(bodyWidth - 80/*buffer left and right*/);
						    reduceWidth = true;
					    } else if (windowWidth < (bodyWidth - 120)) {
						    that.setWidth(that.initialWidth);
					    }
                    }
                    catch(error) {
                        
                    }
                    // Put this outside of the try catch to ensure the window is always centered
					that.center();
				}
			});
		}
		this.callParent();
	},
	beforeclose: function () {
		if (this.docListen) {
			Ext.destroy(this.docListen);
			this.docListen = null;
		}
		this.callParent();
	},
	beforedestroy: function () {
		if (this.docListen) {
			Ext.destroy(this.docListen);
			this.docListen = null;
		}
		this.callParent();
	}
});

Ext.define('Ext.form.TextField', {
	override: 'Ext.form.TextField',
	validator: function (text) {
		if (this.allowBlank == false && Ext.util.Format.trim(text).length == 0)
			return false;
		else
			return true;
	}
});

Ext.define('Ext.form.TextArea', {
	override: 'Ext.form.TextArea',
	validator: function (text) {
		if (this.allowBlank == false && Ext.util.Format.trim(text).length == 0)
			return false;
		else
			return true;
	}
});

Ext.define('Ext.window.MessageBox', {
	override: 'Ext.window.MessageBox',
	confirm: function (cfg, message, fn, scope) {
	    if (Ext.isString(cfg)) {
	        cfg = {
	            title: cfg,
	            icon: this.QUESTION,
	            message: message,
	            buttons: this.YESNO,
	            callback: fn,
	            scope: scope,
	        };
	    }
	    var y = this.show(cfg);
	    y.addBodyCls('x-ie-overflow-reset');
	    return y;
	},
	alert: function (title, message, fn, scope) {
	    if (Ext.isString(title)) {
	        title = {
	            title: title,
	            message: message,
	            buttons: this.OK,
	            fn: fn,
	            scope: scope,
	            minWidth: this.minWidth
	        };
	    }
	    var y = this.show(title);
	    y.addBodyCls('x-ie-overflow-reset');
	    return y;
	},
});

Ext.define('MyApp.data.AbstractStore', {
    override: 'Ext.data.AbstractStore',
    /**
     * Groups data inside the store.
     * @param {String/Object} grouper Either a string name of one of the fields in this Store's
     * configured {@link Ext.data.Model Model}, or an object, or a {@link Ext.util.Grouper grouper} configuration object.
     * @param {String} [direction] The overall direction to group the data by. Defaults to the value of {@link #groupDir}.
     */
    group: function (grouper, direction) {
        var me = this,
            sorters = me.getSorters(false),
            change = grouper || (sorters && sorters.length);
        if (grouper && typeof grouper === 'string') {
            grouper = {
                property: grouper,
                direction: direction || me.getGroupDir()
            };
        }
        me.settingGroups = true;
        me.getData().setGrouper(grouper);
        delete me.settingGroups;


        if (change) {
            //Override - check autoLoad is enabled or store is loading/loaded
            var load = me.autoLoad || me.loading || me.isLoaded();
            if (me.getRemoteSort() && load) {
                me.load({
                    scope: me,
                    callback: me.fireGroupChange
                });
            } else {
                me.fireEvent('datachanged', me);
                me.fireEvent('refresh', me);
                me.fireGroupChange();
            }
        } else // groupchange event must fire when group is cleared.
        // The Grouping feature forces a view refresh when changed to a null grouper
        {
            me.fireGroupChange();
        }
    }
});


/** 
 * @class Ext.ux.FitToParent
 * @extends Object
 * <p>Plugin for {@link Ext.BoxComponent BoxComponent} and descendants that adjusts the size of the component to fit inside a parent element</p>
 * <p>The following example will adjust the size of the panel to fit inside the element with id="some-el":<pre><code>
var panel = new Ext.Panel({
    title: 'Test',
    renderTo: 'some-el',
    plugins: ['fittoparent']
});</code></pre></p>
 * <p>It is also possible to specify additional parameters:<pre><code>
var panel = new Ext.Panel({
    title: 'Test',
    renderTo: 'other-el',
    autoHeight: true,
    plugins: [
        new Ext.ux.FitToParent({
            parent: 'parent-el',
            fitHeight: false,
            offsets: [10, 0]
        })
    ]
});</code></pre></p>
 * <p>The element the component is rendered to needs to have <tt>style="overflow:hidden"</tt>, otherwise the component will only grow to fit the parent element, but it will never shrink.</p>
 * <p>Note: This plugin should not be used when the parent element is the document body. In this case you should use a {@link Ext.Viewport Viewport} container.</p>
 */
Ext.define('Ext.plugin.FitToParent', {
    extend: 'Ext.AbstractPlugin',
    alias: 'plugin.fittoparent',

    /**
     * @cfg {HTMLElement/Ext.Element/String} parent The element to fit the component size to (defaults to the element the component is rendered to).
     */
    /**
     * @cfg {Boolean} fitWidth If the plugin should fit the width of the component to the parent element (default <tt>true</tt>).
     */
    fitWidth: true,
    /**
     * @cfg {Boolean} fitHeight If the plugin should fit the height of the component to the parent element (default <tt>true</tt>).
     */
    fitHeight: true,
    /**
     * @cfg {Boolean} offsets Decreases the final size with [width, height] (default <tt>[0, 0]</tt>).
     */
    offsets: [0, 0],
    /**
     * @constructor
     * @param {HTMLElement/Ext.Element/String/Object} config The parent element or configuration options.
     * @ptype fittoparent
     */
    constructor: function (config) {
        config = config || {};
        if (config.tagName || config.dom || Ext.isString(config)) {
            config = { parent: config };
        }


        this.callParent([config])
    },
    init: function (c) {
        this.component = c;
        c.on('render', function (c) {
            this.parent = Ext.get(this.parent || c.el.dom.parentNode);
            if (c.doLayout) {
                c.monitorResize = true;
                c.doLayout = c.doLayout.createInterceptor(this.fitSize, this);
            } else {
                this.fitSize();
                Ext.on('resize', this.fitSize, this)
                //Ext.EventManager.onWindowResize(this.fitSize, this);
            }
        }, this, { single: true });
    },
    fitSize: function () {
        var pos = this.component.getPosition(true),
            size = this.parent.getViewSize(),
            oSize = {width: null, height: null};

        if (this.parent.dom) {
            oSize.width = this.parent.dom.offsetWidth;
            oSize.height = this.parent.dom.offsetHeight;
        }
        if (oSize.width > size.width && oSize.height > size.height) {
            size.width = oSize.width;
            size.height = oSize.height;
        }

        this.component.setSize(
            this.fitWidth ? size.width - pos[0] - this.offsets[0] : undefined,
            this.fitHeight ? size.height - pos[1] - this.offsets[1] : undefined);
    }
});

//http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/
(function () {
    var attachEvent = document.attachEvent;
    var isIE = navigator.userAgent.match(/Trident/);
    //console.log(isIE);
    var requestFrame = (function () {
        var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
            function (fn) { return window.setTimeout(fn, 20); };
        return function (fn) { return raf(fn); };
    })();

    var cancelFrame = (function () {
        var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame ||
            window.clearTimeout;
        return function (id) { return cancel(id); };
    })();

    function resizeListener(e) {
        var win = e.target || e.srcElement;
        if (win.__resizeRAF__) cancelFrame(win.__resizeRAF__);
        win.__resizeRAF__ = requestFrame(function () {
            var trigger = win.__resizeTrigger__;
            trigger.__resizeListeners__.forEach(function (fn) {
                fn.call(trigger, e);
            });
        });
    }

    function objectLoad(e) {
        this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__;
        this.contentDocument.defaultView.addEventListener('resize', resizeListener);
    }

    Ext.Function.addResizeListener = function (element, fn) {
        if (!element.__resizeListeners__) {
            element.__resizeListeners__ = [];
            if (attachEvent) {
                element.__resizeTrigger__ = element;
                element.attachEvent('onresize', resizeListener);
            }
            else {
                if (getComputedStyle(element).position == 'static') element.style.position = 'relative';
                var obj = element.__resizeTrigger__ = document.createElement('object');
                obj.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;');
                obj.__resizeElement__ = element;
                obj.onload = objectLoad;
                obj.type = 'text/html';
                if (isIE) element.appendChild(obj);
                obj.data = 'about:blank';
                if (!isIE) element.appendChild(obj);
            }
        }
        element.__resizeListeners__.push(fn);
    };

    Ext.Function.removeResizeListener = function (element, fn) {
        element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
        if (!element.__resizeListeners__.length) {
            if (attachEvent) element.detachEvent('onresize', resizeListener);
            else {
                element.__resizeTrigger__.contentDocument.defaultView.removeEventListener('resize', resizeListener);
                element.__resizeTrigger__ = !element.removeChild(element.__resizeTrigger__);
            }
        }
    }
})();