!(function () {
    var scale = 1, transformY = 0, selected = true;
    var html = document.getElementsByTagName('html')[0];
    var wrapper = document.getElementById("wrapper");
    var fixedContainer = document.createElement('div');
    fixedContainer.style.position = "absolute";
    fixedContainer.style.top = "0";
    fixedContainer.style.left = "0";
    fixedContainer.style.width = "100%";

    wrapper.appendChild(fixedContainer);

    var jQuery = $ || window.jQuery;
    var injectOffset = jQuery.fn.offset;
    var injectEventFix = jQuery.event.fix;
    var injectMouseInBounds = jQuery.fn.__isMouseInBounds__;
    var injectToolTipShow = null;
    var injectBubblesShow = null;
    var adjustHeight = BI.Combo.prototype.adjustHeight;
    var originalRender =  BI.Widget._renderEngine;
    var injectSearch = BI.Searcher.prototype._assertPopupView;

    var injectCreate = BI.createWidget;

    // 修正事件偏移
    function correctEvent(e) {
        if (e && !e.corrected) {
            e.pageX = e.pageX / scale;
            e.pageY = (e.pageY - transformY) / scale;
            e.clientX = e.clientX / scale;
            e.clientY = (e.clientY - transformY) / scale;
            e.corrected = true;
        }
        return e;
    }

    function checkInPopupView(el) {
        var t = el.parentElement;
        var flag = false;
        do {
            if (t.className && t.className.indexOf("bi-popup-view") !== -1) {
                flag = true;
                break;
            }
            t = t.parentElement
        } while (t);

        return flag;
    }

    // 进行缩放
    function transformScale() {
        var bounds = getScaleBounds();
        scale = bounds.scale;

        wrapper.style.width = bounds.width + "px";
        wrapper.style.height = bounds.height + "px";
        wrapper.style.transform = "scale(" + bounds.scale+ ")";
        wrapper.style.transformOrigin = "top left";
        wrapper.style["-ms-transform"] = "scale(" + bounds.scale+ ")";
        wrapper.style["-ms-transform-origin"] = "top left";
        fixedContainer.style.height = (document.body.clientHeight / scale) + "px";
        wrapper.style.overflowY = "hidden";
        document.body.style.overflowX = "hidden";
        document.body.style.overflowY = "auto";
        html.style.backgroundColor = "#ffffff";

        if (scale < 1) {
            document.body.style.width = bounds.width + "px";
            document.body.style.height = bounds.height + "px";
        }

        window.scale = window.devicePixelRatio * scale;
    }

    // 取消缩放
    function removeScale() {
        wrapper.style.width = "100%";
        wrapper.style.height = "100%";
        wrapper.style.transform = "";
        wrapper.style.transformOrigin = "";
        wrapper.style["-ms-transform"] = "";
        wrapper.style["-ms-transform-origin"] = "";
        wrapper.style.overflowY = "hidden";
        wrapper.style.top = "";
        fixedContainer.style.height = "0px";
        document.body.style.overflowX = "hidden";
        document.body.style.overflowY = "hidden";
        html.style.backgroundColor = "#ffffff";

        document.body.style.width = "100%";
        document.body.style.height = "100%";

        scale = 1;
        window.scale = window.devicePixelRatio / scale;
    }

    // 获取缩放倍数,原模板宽高
    function getScaleBounds() {
        var widgets = BI.designConfigure.widgets,
            layoutRatio = BI.designConfigure.layoutRatio,
            freeLayoutRatio = BI.designConfigure.freeLayoutRatio,
            freeWidgets = BI.designConfigure.freeWidgetIds;

        if (Object.keys(widgets).length === 0) {
            return {
                scale: 1,
                width: html.clientWidth,
                height: html.clientHeight
            }
        }
        var left = null, right = null, top = null, bottom = null,
            freeLeft = null, freeRight = null, freeTop = null, freeBottom = null;
        BI.each(widgets, function (wId, widget) {
            var bounds = widget.bounds || {};
            if (BI.contains(freeWidgets, wId)) {
                freeLeft = BI.isNull(freeLeft) ? bounds.left : Math.min(freeLeft, bounds.left);
                freeRight = Math.max(freeRight, bounds.left + bounds.width);
                freeTop = BI.isNull(freeTop) ? bounds.top : Math.min(freeTop, bounds.top);
                freeBottom = Math.max(freeBottom, bounds.top + bounds.height);
            } else {
                left = BI.isNull(left) ? bounds.left : Math.min(left, bounds.left);
                right = Math.max(right, bounds.left + bounds.width);
                top = BI.isNull(top) ? bounds.top : Math.min(top, bounds.top);
                bottom = Math.max(bottom, bounds.top + bounds.height);
            }
        });

        var templateWidth = (Math.round((right / (layoutRatio.x || 1)) || (freeRight / (freeLayoutRatio.x || 1)))) + 60;
        var templateHeight = (Math.round((bottom / (layoutRatio.y || 1)) || (freeBottom / (freeLayoutRatio.y || 1)))) + 30;
        var scaleRatio = parseFloat((html.clientWidth / templateWidth).toFixed(1));
        var bHeight = html.clientHeight / scaleRatio;

        return {
            scale: scaleRatio,
            width: html.clientWidth / scaleRatio,
            height: Math.max(bHeight, scaleRatio === 1 ?  html.clientHeight : templateHeight)
        };
    }

    // 准备环境,主要是纠正事件偏移
    function prepareEnv() {
        // IE的fixed元素不受transform:scale影响
        injectToolTipShow = injectToolTipShow || BI.Tooltips.show;
        injectBubblesShow = injectBubblesShow || BI.Bubbles.show;
        jQuery.fn.__isMouseInBounds__ = function (e) {
            var offset2Body = this.get(0).getBoundingClientRect ? this.get(0).getBoundingClientRect() : this.offset();
            var width = offset2Body.width || this.outerWidth();
            var height = offset2Body.height || this.outerHeight();
            var pageX = e.clientX * scale,
                pageY = e.clientY * scale + transformY;
            return !(pageX < Math.floor(offset2Body.left) || pageX > offset2Body.left + width
                || pageY < Math.floor(offset2Body.top) || pageY > offset2Body.top + height);
        };
        jQuery.fn.offset = function( options ) {
            function getWindow( elem ) {
                return jQuery.isWindow( elem ) ?
                    elem :
                    elem.nodeType === 9 ?
                        elem.defaultView || elem.parentWindow :
                        false;
            }

            if ( arguments.length ) {
                return options === undefined ?
                    this :
                    this.each(function( i ) {
                        jQuery.offset.setOffset( this, options, i );
                    });
            }


            var docElem, win,
                box = { top: 0, left: 0 },
                elem = this[ 0 ],
                doc = elem && elem.ownerDocument;

            if ( !doc ) {
                return;
            }

            docElem = doc.documentElement;

            // Make sure it's not a disconnected DOM node
            if ( !jQuery.contains( docElem, elem ) ) {
                return box;
            }

            var el = elem,
                offsetLeft = 0,
                offsetTop  = 0;

            do{
                offsetLeft += el.offsetLeft;
                offsetTop  += el.offsetTop;
                el = el.offsetParent;
            } while( el );

            var elm = elem;
            if (!checkInPopupView(elm)) {
                do{
                    if (elm === document.body) {
                        break;
                    }
                    offsetLeft -= elm.scrollLeft || 0;
                    offsetTop  -= elm.scrollTop || 0;
                    elm = elm.offsetParent;
                } while( elm );
            }
            win = getWindow( doc );
            return {
                top: offsetTop  + ( win.pageYOffset || docElem.scrollTop )  - ( docElem.clientTop  || 0 ),
                left: offsetLeft + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
            };
        };

        jQuery.event.fix = function ( e ) {
            return correctEvent(injectEventFix.call(this, e));
        };


        // Popovers
        BI.Popovers = new BI.PopoverController({render: fixedContainer});
        if (BI.isIE()) {
            BI.Combo.prototype.adjustHeight = function (e) {
                adjustHeight.call(this, e);
                if (this.popupView) {
                    if (!checkInPopupView(this.popupView.element[0])) {
                        this.popupView.element[0].style.left = (parseInt(this.popupView.element[0].style.left) * scale - document.body.scrollLeft) + "px";
                        this.popupView.element[0].style.top = (parseInt(this.popupView.element[0].style.top) * scale + transformY - document.body.scrollTop) + "px";
                    } else {
                        var cBounds = this.popupView.element[0].getBoundingClientRect();
                        var pBounds = this.popupView.element[0].parentElement.getBoundingClientRect();
                        if (parseInt(cBounds.top) >= parseInt(pBounds.top) && parseInt(cBounds.left) >= parseInt(pBounds.left)) {
                            this.popupView.element[0].style.top = (parseInt(pBounds.bottom) + transformY - document.body.scrollTop) + "px";
                            this.popupView.element[0].style.left = (parseInt(pBounds.left) + transformY - document.body.scrollTop) + "px";
                        }

                        if (parseInt(cBounds.top) >= parseInt(pBounds.top) && parseInt(cBounds.left) < parseInt(pBounds.left)) {
                            this.popupView.element[0].style.top = (parseInt(pBounds.bottom) + transformY - document.body.scrollTop) + "px";
                            this.popupView.element[0].style.left = (parseInt(pBounds.left) + transformY - document.body.scrollTop) + "px";
                        }
                    }
                    this.popupView.element[0].style.transform = "scale(" + scale+ ")";
                    this.popupView.element[0].style.transformOrigin = "top left";
                }
            };
            BI.Tooltips.show = function (e, name, text, level, context, opt) {
                injectToolTipShow.call(this, e, name, text, level, context, opt);
                var tooltip = this.get(name);
                tooltip.element[0].style.left = parseInt(tooltip.element[0].style.left) * scale - document.body.scrollLeft + "px";
                tooltip.element[0].style.top = parseInt(tooltip.element[0].style.top) * scale + transformY - document.body.scrollTop + "px";
                tooltip.element[0].style.transform = "scale(" + scale+ ")";
                tooltip.element[0].style.transformOrigin = "top left";
            }
            BI.Bubbles.show = function (name, text, context, opt) {
                injectBubblesShow.call(this, name, text, context, opt);
                var tooltip = this.get(name);
                tooltip.element[0].style.left = parseInt(tooltip.element[0].style.left) * scale - document.body.scrollLeft + "px";
                tooltip.element[0].style.top = parseInt(tooltip.element[0].style.top) * scale + transformY - document.body.scrollTop + "px";
                tooltip.element[0].style.transform = "scale(" + scale+ ")";
                tooltip.element[0].style.transformOrigin = "top left";
            }

            var tempAdd = BI.Popovers.add;
            BI.Popovers.add = function (name, popover, options, context) {
                tempAdd.call(this, name, popover, options, context);
                if (this.floatContainer[name].options.cls && this.floatContainer[name].options.cls.includes("bi-popup-view")) {
                    this.floatContainer[name].element[0].style.setProperty("position", "absolute", "important");
                }
            }

            // Searcher
            BI.Searcher.prototype._assertPopupView = function () {
                injectSearch.call(this);
                var self = this;
                if (this.popupView) {
                    BI.nextTick(function () {
                        self.popupView.element[0].parentElement.style.transform = "scale(" + scale + ")";
                        self.popupView.element[0].parentElement.style.transformOrigin = "left top";
                    })
                }
            }
        }

        BI.createWidget = function () {
            if (arguments[0] && arguments[0].element === "body") {
                arguments[0].element = fixedContainer;
            }
            return injectCreate.apply(this, arguments);
        };

        BI.Widget.registerRenderEngine({
            createElement: function (widget) {
                if (widget === "body") {
                    return BI.$(wrapper);
                }
                return originalRender.createElement(widget);
            },
            createFragment: function () {
                return originalRender.createFragment();
            }
        });

        window.addEventListener('resize', updateScale);
        window.matchMedia && window.matchMedia('screen and (min-resolution: 2dppx)').
        addListener(selectHandler);
    }

    function restoreEnv() {
        jQuery.fn.__isMouseInBounds__ = injectMouseInBounds;
        jQuery.fn.offset = injectOffset;
        jQuery.event.fix = injectEventFix;
        if (BI.isIE()) {
            BI.Combo.prototype.adjustHeight = adjustHeight;
            BI.Tooltips.show = injectToolTipShow;
            BI.Bubbles.show = injectBubblesShow;
        }
        BI.createWidget = injectCreate;

        // Popovers
        BI.Popovers = new BI.PopoverController();
        BI.Widget.registerRenderEngine(originalRender);

        BI.Searcher.prototype._assertPopupView = injectSearch;

        document.body.onmousedown = null;
        window.removeEventListener('resize', updateScale);
        window.matchMedia && window.matchMedia('screen and (min-resolution: 2dppx)').
        removeListener(selectHandler);
    }

    var selectHandler = BI.debounce(function () {
        selected && prepareEnv();
        selected ? transformScale() : removeScale();
        !selected && restoreEnv();
    }, 30);

    function updateScale () {
        if (html.getBoundingClientRect().width !== wrapper.getBoundingClientRect().width) {
            selectHandler();
        }
    }

    var injectCaptureMouseMoves = BI.MouseMoveTracker.prototype.captureMouseMoves;
    var injectOnMouseMove = BI.MouseMoveTracker.prototype._onMouseMove;
    // 用jQEvent代替MouseEvent
    function injectMouseMoveTracker() {
        BI.MouseMoveTracker.prototype.captureMouseMoves = function (e) {
            var jQEvent = e.originalEvent ? e : jQuery.event.fix(e);
            injectCaptureMouseMoves.call(this, correctEvent(jQEvent));
        };
        BI.MouseMoveTracker.prototype._onMouseMove = function (e) {
            var jQEvent = e.originalEvent ? e : jQuery.event.fix(e);
            injectOnMouseMove.call(this, correctEvent(jQEvent));
        }
    }
    injectMouseMoveTracker();

    selectHandler();
    BI.config("bi.constant.dashboard.toolbar.left.items", function (config) {
        return BI.concat(config, [{
            type: "bi.multi_select_item",
            text: BI.i18nText("BI-Plugin-Scale_Adaptive"),
            selected: true,
            listeners: [{
                eventName: BI.MultiSelectItem.EVENT_CHANGE,
                action: function () {
                    selected = this.isSelected();
                    selectHandler();
                }
            }]
        }])
    });
}());