/*

*  webui popover plugin  - v1.2.17
*  A lightWeight popover plugin with jquery ,enchance the  popover plugin of bootstrap with some awesome new features. It works well with bootstrap ,but bootstrap is not necessary!
*  https://github.com/sandywalker/webui-popover
*
*  Made by Sandy Duan
*  Under MIT License
*/

(function(window, document, undefined) {

'use strict';
(function(factory) {
    if (typeof define === 'function' && define.amd) {
        // Register as an anonymous AMD module.
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS
        module.exports = factory(require('jquery'));
    } else {
        // Browser globals
        factory(window.jQuery);
    }
}(function($) {
    // Create the defaults once
    var pluginName = 'webuiPopover';
    var pluginClass = 'webui-popover';
    var pluginType = 'webui.popover';
    var defaults = {
        placement: 'auto',
        container: null,
        width: 'auto',
        height: 'auto',
        trigger: 'click', //hover,click,sticky,manual
        style: '',
        selector: false, // jQuery selector, if a selector is provided, popover objects will be delegated to the specified. 
        delay: {
            show: null,
            hide: 300
        },
        async: {
            type: 'GET',
            before: null, //function(that, xhr, settings){}
            success: null, //function(that, xhr){}
            error: null //function(that, xhr, data){}
        },
        cache: true,
        multi: false,
        arrow: true,
        title: '',
        content: '',
        closeable: false,
        padding: true,
        url: '',
        type: 'html',
        direction: '', // ltr,rtl
        animation: null,
        template: '<div class="webui-popover">' +
            '<div class="webui-arrow"></div>' +
            '<div class="webui-popover-inner">' +
            '<a href="#" class="close"></a>' +
            '<h3 class="webui-popover-title"></h3>' +
            '<div class="webui-popover-content"><i class="icon-refresh"></i> <p>&nbsp;</p></div>' +
            '</div>' +
            '</div>',
        backdrop: false,
        dismissible: true,
        onShow: null,
        onHide: null,
        abortXHR: true,
        autoHide: false,
        offsetTop: 0,
        offsetLeft: 0,
        iframeOptions: {
            frameborder: '0',
            allowtransparency: 'true',
            id: '',
            name: '',
            scrolling: '',
            onload: '',
            height: '',
            width: ''
        },
        hideEmpty: false
    };

    var rtlClass = pluginClass + '-rtl';
    var _srcElements = [];
    var backdrop = $('<div class="webui-popover-backdrop"></div>');
    var _globalIdSeed = 0;
    var _isBodyEventHandled = false;
    var _offsetOut = -2000; // the value offset  out of the screen
    var $document = $(document);

    var toNumber = function(numeric, fallback) {
        return isNaN(numeric) ? (fallback || 0) : Number(numeric);
    };

    var getPopFromElement = function($element) {
        return $element.data('plugin_' + pluginName);
    };

    var hideAllPop = function() {
        var pop = null;
        for (var i = 0; i < _srcElements.length; i++) {
            pop = getPopFromElement(_srcElements[i]);
            if (pop) {
                pop.hide(true);
            }
        }
        $document.trigger('hiddenAll.' + pluginType);
    };

    var hideOtherPops = function(currentPop) {
        var pop = null;
        for (var i = 0; i < _srcElements.length; i++) {
            pop = getPopFromElement(_srcElements[i]);
            if (pop && pop.id !== currentPop.id) {
                pop.hide(true);
            }
        }
        $document.trigger('hiddenAll.' + pluginType);
    };

    var isMobile = ('ontouchstart' in document.documentElement) && (/Mobi/.test(navigator.userAgent));

    var pointerEventToXY = function(e) {
        var out = {
            x: 0,
            y: 0
        };
        if (e.type === 'touchstart' || e.type === 'touchmove' || e.type === 'touchend' || e.type === 'touchcancel') {
            var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
            out.x = touch.pageX;
            out.y = touch.pageY;
        } else if (e.type === 'mousedown' || e.type === 'mouseup' || e.type === 'click') {
            out.x = e.pageX;
            out.y = e.pageY;
        }
        return out;
    };

    // The actual plugin constructor
    function WebuiPopover(element, options) {
        this.$element = $(element);
        if (options) {
            if ($.type(options.delay) === 'string' || $.type(options.delay) === 'number') {
                options.delay = {
                    show: options.delay,
                    hide: options.delay
                }; // bc break fix
            }
        }
        this.options = $.extend({}, defaults, options);
        this._defaults = defaults;
        this._name = pluginName;
        this._targetclick = false;
        this.init();
        _srcElements.push(this.$element);
        return this;

    }

    WebuiPopover.prototype = {
        //init webui popover
        init: function() {
            if (this.$element[0] instanceof document.constructor && !this.options.selector) {
                throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!');
            }

            if (this.getTrigger() !== 'manual') {
                //init the event handlers
                if (isMobile) {
                    this.$element.off('touchend', this.options.selector).on('touchend', this.options.selector, $.proxy(this.toggle, this));
                } else if (this.getTrigger() === 'click') {
                    this.$element.off('click', this.options.selector).on('click', this.options.selector, $.proxy(this.toggle, this));
                } else if (this.getTrigger() === 'hover') {
                    this.$element
                        .off('mouseenter mouseleave click', this.options.selector)
                        .on('mouseenter', this.options.selector, $.proxy(this.mouseenterHandler, this))
                        .on('mouseleave', this.options.selector, $.proxy(this.mouseleaveHandler, this));
                }
            }
            this._poped = false;
            this._inited = true;
            this._opened = false;
            this._idSeed = _globalIdSeed;
            this.id = pluginName + this._idSeed;
            // normalize container
            this.options.container = $(this.options.container || document.body).first();

            if (this.options.backdrop) {
                backdrop.appendTo(this.options.container).hide();
            }
            _globalIdSeed++;
            if (this.getTrigger() === 'sticky') {
                this.show();
            }

            if (this.options.selector) {
                this._options = $.extend({}, this.options, {
                    selector: ''
                });
            }

        },
        /* api methods and actions */
        destroy: function() {
            var index = -1;

            for (var i = 0; i < _srcElements.length; i++) {
                if (_srcElements[i] === this.$element) {
                    index = i;
                    break;
                }
            }

            _srcElements.splice(index, 1);

            this.hide();
            this.$element.data('plugin_' + pluginName, null);
            if (this.getTrigger() === 'click') {
                this.$element.off('click');
            } else if (this.getTrigger() === 'hover') {
                this.$element.off('mouseenter mouseleave');
            }
            if (this.$target) {
                this.$target.remove();
            }
        },
        getDelegateOptions: function() {
            var options = {};

            this._options && $.each(this._options, function(key, value) {
                if (defaults[key] !== value) {
                    options[key] = value;
                }
            });
            return options;
        },
        /*
            param: force    boolean value, if value is true then force hide the popover
            param: event    dom event,
        */
        hide: function(force, event) {

            if (!force && this.getTrigger() === 'sticky') {
                return;
            }
            if (!this._opened) {
                return;
            }
            if (event) {
                event.preventDefault();
                event.stopPropagation();
            }

            if (this.xhr && this.options.abortXHR === true) {
                this.xhr.abort();
                this.xhr = null;
            }

            var e = $.Event('hide.' + pluginType);
            this.$element.trigger(e, [this.$target]);
            if (this.$target) {
                this.$target.removeClass('in').addClass(this.getHideAnimation());
                var that = this;
                setTimeout(function() {
                    that.$target.hide();
                    if (!that.getCache()) {
                        that.$target.remove();
                        //that.getTriggerElement.removeAttr('data-target');
                    }
                }, that.getHideDelay());
            }
            if (this.options.backdrop) {
                backdrop.hide();
            }
            this._opened = false;
            this.$element.trigger('hidden.' + pluginType, [this.$target]);

            if (this.options.onHide) {
                this.options.onHide(this.$target);
            }

        },
        resetAutoHide: function() {
            var that = this;
            var autoHide = that.getAutoHide();
            if (autoHide) {
                if (that.autoHideHandler) {
                    clearTimeout(that.autoHideHandler);
                }
                that.autoHideHandler = setTimeout(function() {
                    that.hide();
                }, autoHide);
            }
        },
        delegate: function(eventTarget) {
            var self = $(eventTarget).data('plugin_' + pluginName);
            if (!self) {
                self = new WebuiPopover(eventTarget, this.getDelegateOptions());
                $(eventTarget).data('plugin_' + pluginName, self);
            }
            return self;
        },
        toggle: function(e) {
            var self = this;
            if (e) {
                e.preventDefault();
                e.stopPropagation();
                if (this.options.selector) {
                    self = this.delegate(e.currentTarget);
                }
            }
            self[self.getTarget().hasClass('in') ? 'hide' : 'show']();
        },
        hideAll: function() {
            hideAllPop();
        },
        hideOthers: function() {
            hideOtherPops(this);
        },
        /*core method ,show popover */
        show: function() {
            if (this._opened) {
                return;
            }
            //removeAllTargets();
            var
                $target = this.getTarget().removeClass().addClass(pluginClass).addClass(this._customTargetClass);
            if (!this.options.multi) {
                this.hideOthers();
            }

            // use cache by default, if not cache setted  , reInit the contents
            if (!this.getCache() || !this._poped || this.content === '') {
                this.content = '';
                this.setTitle(this.getTitle());
                if (!this.options.closeable) {
                    $target.find('.close').off('click').remove();
                }
                if (!this.isAsync()) {
                    this.setContent(this.getContent());
                } else {
                    this.setContentASync(this.options.content);
                }

                if (this.canEmptyHide() && this.content === '') {
                    return;
                }
                $target.show();
            }

            this.displayContent();

            if (this.options.onShow) {
                this.options.onShow($target);
            }

            this.bindBodyEvents();
            if (this.options.backdrop) {
                backdrop.show();
            }
            this._opened = true;
            this.resetAutoHide();
        },
        displayContent: function() {
            var
            //element postion
                elementPos = this.getElementPosition(),
                //target postion
                $target = this.getTarget().removeClass().addClass(pluginClass).addClass(this._customTargetClass),
                //target content
                $targetContent = this.getContentElement(),
                //target Width
                targetWidth = $target[0].offsetWidth,
                //target Height
                targetHeight = $target[0].offsetHeight,
                //placement
                placement = 'bottom',
                e = $.Event('show.' + pluginType);

            if (this.canEmptyHide()) {

                var content = $targetContent.children().html();
                if (content !== null && content.trim().length === 0) {
                    return;
                }
            }

            //if (this.hasContent()){
            this.$element.trigger(e, [$target]);
            //}
            // support width as data attribute
            var optWidth = this.$element.data('width') || this.options.width;
            if (optWidth === '') {
                optWidth = this._defaults.width;
            }

            if (optWidth !== 'auto') {
                $target.width(optWidth);
            }

            // support height as data attribute
            var optHeight = this.$element.data('height') || this.options.height;
            if (optHeight === '') {
                optHeight = this._defaults.height;
            }

            if (optHeight !== 'auto') {
                $targetContent.height(optHeight);
            }

            if (this.options.style) {
                this.$target.addClass(pluginClass + '-' + this.options.style);
            }

            //check rtl
            if (this.options.direction === 'rtl' && !$targetContent.hasClass(rtlClass)) {
                $targetContent.addClass(rtlClass);
            }

            //init the popover and insert into the document body
            if (!this.options.arrow) {
                $target.find('.webui-arrow').remove();
            }
            $target.detach().css({
                top: _offsetOut,
                left: _offsetOut,
                display: 'block'
            });

            if (this.getAnimation()) {
                $target.addClass(this.getAnimation());
            }
            $target.appendTo(this.options.container);

            placement = this.getPlacement(elementPos);

            //This line is just for compatible with knockout custom binding
            this.$element.trigger('added.' + pluginType);

            this.initTargetEvents();

            if (!this.options.padding) {
                if (this.options.height !== 'auto') {
                    $targetContent.css('height', $targetContent.outerHeight());
                }
                this.$target.addClass('webui-no-padding');
            }

            // add maxHeight and maxWidth support by limodou@gmail.com 2016/10/1
            if (this.options.maxHeight) {
                $targetContent.css('maxHeight', this.options.maxHeight);
            }

            if (this.options.maxWidth) {
                $targetContent.css('maxWidth', this.options.maxWidth);
            }
            // end

            targetWidth = $target[0].offsetWidth;
            targetHeight = $target[0].offsetHeight;

            var postionInfo = this.getTargetPositin(elementPos, placement, targetWidth, targetHeight);

            this.$target.css(postionInfo.position).addClass(placement).addClass('in');

            if (this.options.type === 'iframe') {
                var $iframe = $target.find('iframe');
                var iframeWidth = $target.width();
                var iframeHeight = $iframe.parent().height();

                if (this.options.iframeOptions.width !== '' && this.options.iframeOptions.width !== 'auto') {
                    iframeWidth = this.options.iframeOptions.width;
                }

                if (this.options.iframeOptions.height !== '' && this.options.iframeOptions.height !== 'auto') {
                    iframeHeight = this.options.iframeOptions.height;
                }

                $iframe.width(iframeWidth).height(iframeHeight);
            }

            if (!this.options.arrow) {
                this.$target.css({
                    'margin': 0
                });
            }
            if (this.options.arrow) {
                var $arrow = this.$target.find('.webui-arrow');
                $arrow.removeAttr('style');

                //prevent arrow change by content size
                if (placement === 'left' || placement === 'right') {
                    $arrow.css({
                        top: this.$target.height() / 2
                    });
                } else if (placement === 'top' || placement === 'bottom') {
                    $arrow.css({
                        left: this.$target.width() / 2
                    });
                }

                if (postionInfo.arrowOffset) {
                    //hide the arrow if offset is negative
                    if (postionInfo.arrowOffset.left === -1 || postionInfo.arrowOffset.top === -1) {
                        $arrow.hide();
                    } else {
                        $arrow.css(postionInfo.arrowOffset);
                    }
                }

            }
            this._poped = true;
            this.$element.trigger('shown.' + pluginType, [this.$target]);
        },

        isTargetLoaded: function() {
            return this.getTarget().find('i.glyphicon-refresh').length === 0;
        },

        /*getter setters */
        getTriggerElement: function() {
            return this.$element;
        },
        getTarget: function() {
            if (!this.$target) {
                var id = pluginName + this._idSeed;
                this.$target = $(this.options.template)
                    .attr('id', id);
                this._customTargetClass = this.$target.attr('class') !== pluginClass ? this.$target.attr('class') : null;
                this.getTriggerElement().attr('data-target', id);
            }
            if (!this.$target.data('trigger-element')) {
                this.$target.data('trigger-element', this.getTriggerElement());
            }
            return this.$target;
        },
        removeTarget: function() {
            this.$target.remove();
            this.$target = null;
            this.$contentElement = null;
        },
        getTitleElement: function() {
            return this.getTarget().find('.' + pluginClass + '-title');
        },
        getContentElement: function() {
            if (!this.$contentElement) {
                this.$contentElement = this.getTarget().find('.' + pluginClass + '-content');
            }
            return this.$contentElement;
        },
        getTitle: function() {
            return this.$element.attr('data-title') || this.options.title || this.$element.attr('title');
        },
        getUrl: function() {
            return this.$element.attr('data-url') || this.options.url;
        },
        getAutoHide: function() {
            return this.$element.attr('data-auto-hide') || this.options.autoHide;
        },
        getOffsetTop: function() {
            return toNumber(this.$element.attr('data-offset-top')) || this.options.offsetTop;
        },
        getOffsetLeft: function() {
            return toNumber(this.$element.attr('data-offset-left')) || this.options.offsetLeft;
        },
        getCache: function() {
            var dataAttr = this.$element.attr('data-cache');
            if (typeof(dataAttr) !== 'undefined') {
                switch (dataAttr.toLowerCase()) {
                    case 'true':
                    case 'yes':
                    case '1':
                        return true;
                    case 'false':
                    case 'no':
                    case '0':
                        return false;
                }
            }
            return this.options.cache;
        },
        getTrigger: function() {
            return this.$element.attr('data-trigger') || this.options.trigger;
        },
        getDelayShow: function() {
            var dataAttr = this.$element.attr('data-delay-show');
            if (typeof(dataAttr) !== 'undefined') {
                return dataAttr;
            }
            return this.options.delay.show === 0 ? 0 : this.options.delay.show || 100;
        },
        getHideDelay: function() {
            var dataAttr = this.$element.attr('data-delay-hide');
            if (typeof(dataAttr) !== 'undefined') {
                return dataAttr;
            }
            return this.options.delay.hide === 0 ? 0 : this.options.delay.hide || 100;
        },
        getAnimation: function() {
            var dataAttr = this.$element.attr('data-animation');
            return dataAttr || this.options.animation;
        },
        getHideAnimation: function() {
            var ani = this.getAnimation();
            return ani ? ani + '-out' : 'out';
        },
        setTitle: function(title) {
            var $titleEl = this.getTitleElement();
            if (title) {
                //check rtl
                if (this.options.direction === 'rtl' && !$titleEl.hasClass(rtlClass)) {
                    $titleEl.addClass(rtlClass);
                }
                $titleEl.html(title);
            } else {
                $titleEl.remove();
            }
        },
        hasContent: function() {
            return this.getContent();
        },
        canEmptyHide: function() {
            return this.options.hideEmpty && this.options.type === 'html';
        },
        getIframe: function() {
            var $iframe = $('<iframe></iframe>').attr('src', this.getUrl());
            var self = this;
            $.each(this._defaults.iframeOptions, function(opt) {
                if (typeof self.options.iframeOptions[opt] !== 'undefined') {
                    $iframe.attr(opt, self.options.iframeOptions[opt]);
                }
            });

            return $iframe;
        },
        getContent: function() {
            if (this.getUrl()) {
                switch (this.options.type) {
                    case 'iframe':
                        this.content = this.getIframe();
                        break;
                    case 'html':
                        try {
                            this.content = $(this.getUrl());
                            if (!this.content.is(':visible')) {
                                this.content.show();
                            }
                        } catch (error) {
                            throw new Error('Unable to get popover content. Invalid selector specified.');
                        }
                        break;
                }
            } else if (!this.content) {
                var content = '';
                if ($.isFunction(this.options.content)) {
                    content = this.options.content.apply(this.$element[0], [this]);
                } else {
                    content = this.options.content;
                }
                this.content = this.$element.attr('data-content') || content;
                if (!this.content) {
                    var $next = this.$element.next();

                    if ($next && $next.hasClass(pluginClass + '-content')) {
                        this.content = $next;
                    }
                }
            }
            return this.content;
        },
        setContent: function(content) {
            var $target = this.getTarget();
            var $ct = this.getContentElement();
            if (typeof content === 'string') {
                $ct.html(content);
            } else if (content instanceof $) {
                $ct.html('');
                //Don't want to clone too many times.
                if (!this.options.cache) {
                    content.clone(true, true).removeClass(pluginClass + '-content').appendTo($ct);
                } else {
                    content.removeClass(pluginClass + '-content').appendTo($ct);
                }
            }
            this.$target = $target;
        },
        isAsync: function() {
            return this.options.type === 'async';
        },
        setContentASync: function(content) {
            var that = this;
            if (this.xhr) {
                return;
            }
            this.xhr = $.ajax({
                url: this.getUrl(),
                type: this.options.async.type,
                cache: this.getCache(),
                beforeSend: function(xhr, settings) {
                    if (that.options.async.before) {
                        that.options.async.before(that, xhr, settings);
                    }
                },
                success: function(data) {
                    that.bindBodyEvents();
                    if (content && $.isFunction(content)) {
                        that.content = content.apply(that.$element[0], [data]);
                    } else {
                        that.content = data;
                    }
                    that.setContent(that.content);
                    var $targetContent = that.getContentElement();
                    $targetContent.removeAttr('style');
                    that.displayContent();
                    if (that.options.async.success) {
                        that.options.async.success(that, data);
                    }
                },
                complete: function() {
                    that.xhr = null;
                },
                error: function(xhr, data) {
                    if (that.options.async.error) {
                        that.options.async.error(that, xhr, data);
                    }
                }
            });
        },

        bindBodyEvents: function() {
            if (_isBodyEventHandled) {
                return;
            }
            if (this.options.dismissible && this.getTrigger() === 'click') {
                if (isMobile) {
                    $document.off('touchstart.webui-popover').on('touchstart.webui-popover', $.proxy(this.bodyTouchStartHandler, this));
                } else {
                    $document.off('keyup.webui-popover').on('keyup.webui-popover', $.proxy(this.escapeHandler, this));
                    $document.off('click.webui-popover').on('click.webui-popover', $.proxy(this.bodyClickHandler, this));
                }
            } else if (this.getTrigger() === 'hover') {
                $document.off('touchend.webui-popover')
                    .on('touchend.webui-popover', $.proxy(this.bodyClickHandler, this));
            }
        },

        /* event handlers */
        mouseenterHandler: function(e) {
            var self = this;

            if (e && this.options.selector) {
                self = this.delegate(e.currentTarget);
            }

            if (self._timeout) {
                clearTimeout(self._timeout);
            }
            self._enterTimeout = setTimeout(function() {
                if (!self.getTarget().is(':visible')) {
                    self.show();
                }
            }, this.getDelayShow());
        },
        mouseleaveHandler: function() {
            var self = this;
            clearTimeout(self._enterTimeout);
            //key point, set the _timeout  then use clearTimeout when mouse leave
            self._timeout = setTimeout(function() {
                self.hide();
            }, this.getHideDelay());
        },
        escapeHandler: function(e) {
            if (e.keyCode === 27) {
                this.hideAll();
            }
        },
        bodyTouchStartHandler: function(e) {
            var self = this;
            var $eventEl = $(e.currentTarget);
            $eventEl.on('touchend', function(e) {
                self.bodyClickHandler(e);
                $eventEl.off('touchend');
            });
            $eventEl.on('touchmove', function() {
                $eventEl.off('touchend');
            });
        },
        bodyClickHandler: function(e) {
            _isBodyEventHandled = true;
            var canHide = true;
            for (var i = 0; i < _srcElements.length; i++) {
                var pop = getPopFromElement(_srcElements[i]);
                if (pop && pop._opened) {
                    var offset = pop.getTarget().offset();
                    var popX1 = offset.left;
                    var popY1 = offset.top;
                    var popX2 = offset.left + pop.getTarget().width();
                    var popY2 = offset.top + pop.getTarget().height();
                    var pt = pointerEventToXY(e);
                    var inPop = pt.x >= popX1 && pt.x <= popX2 && pt.y >= popY1 && pt.y <= popY2;
                    if (inPop) {
                        canHide = false;
                        break;
                    }
                }
            }
            if (canHide) {
                hideAllPop();
            }
        },

        /*
        targetClickHandler: function() {
            this._targetclick = true;
        },
        */

        //reset and init the target events;
        initTargetEvents: function() {
            if (this.getTrigger() === 'hover') {
                this.$target
                    .off('mouseenter mouseleave')
                    .on('mouseenter', $.proxy(this.mouseenterHandler, this))
                    .on('mouseleave', $.proxy(this.mouseleaveHandler, this));
            }
            this.$target.find('.close').off('click').on('click', $.proxy(this.hide, this, true));
            //this.$target.off('click.webui-popover').on('click.webui-popover', $.proxy(this.targetClickHandler, this));
        },
        /* utils methods */
        //caculate placement of the popover
        getPlacement: function(pos) {
            var
                placement,
                container = this.options.container,
                clientWidth = container.innerWidth(),
                clientHeight = container.innerHeight(),
                scrollTop = container.scrollTop(),
                scrollLeft = container.scrollLeft(),
                pageX = Math.max(0, pos.left - scrollLeft),
                pageY = Math.max(0, pos.top - scrollTop);
            //arrowSize = 20;

            //if placement equals auto,caculate the placement by element information;
            if (typeof(this.options.placement) === 'function') {
                placement = this.options.placement.call(this, this.getTarget()[0], this.$element[0]);
            } else {
                placement = this.$element.data('placement') || this.options.placement;
            }

            var isH = placement === 'horizontal';
            var isV = placement === 'vertical';
            var detect = placement === 'auto' || isH || isV;

            if (detect) {
                if (pageX < clientWidth / 3) {
                    if (pageY < clientHeight / 3) {
                        placement = isH ? 'right-bottom' : 'bottom-right';
                    } else if (pageY < clientHeight * 2 / 3) {
                        if (isV) {
                            placement = pageY <= clientHeight / 2 ? 'bottom-right' : 'top-right';
                        } else {
                            placement = 'right';
                        }
                    } else {
                        placement = isH ? 'right-top' : 'top-right';
                    }
                    //placement= pageY>targetHeight+arrowSize?'top-right':'bottom-right';
                } else if (pageX < clientWidth * 2 / 3) {
                    if (pageY < clientHeight / 3) {
                        if (isH) {
                            placement = pageX <= clientWidth / 2 ? 'right-bottom' : 'left-bottom';
                        } else {
                            placement = 'bottom';
                        }
                    } else if (pageY < clientHeight * 2 / 3) {
                        if (isH) {
                            placement = pageX <= clientWidth / 2 ? 'right' : 'left';
                        } else {
                            placement = pageY <= clientHeight / 2 ? 'bottom' : 'top';
                        }
                    } else {
                        if (isH) {
                            placement = pageX <= clientWidth / 2 ? 'right-top' : 'left-top';
                        } else {
                            placement = 'top';
                        }
                    }
                } else {
                    //placement = pageY>targetHeight+arrowSize?'top-left':'bottom-left';
                    if (pageY < clientHeight / 3) {
                        placement = isH ? 'left-bottom' : 'bottom-left';
                    } else if (pageY < clientHeight * 2 / 3) {
                        if (isV) {
                            placement = pageY <= clientHeight / 2 ? 'bottom-left' : 'top-left';
                        } else {
                            placement = 'left';
                        }
                    } else {
                        placement = isH ? 'left-top' : 'top-left';
                    }
                }
            } else if (placement === 'auto-top') {
                if (pageX < clientWidth / 3) {
                    placement = 'top-right';
                } else if (pageX < clientWidth * 2 / 3) {
                    placement = 'top';
                } else {
                    placement = 'top-left';
                }
            } else if (placement === 'auto-bottom') {
                if (pageX < clientWidth / 3) {
                    placement = 'bottom-right';
                } else if (pageX < clientWidth * 2 / 3) {
                    placement = 'bottom';
                } else {
                    placement = 'bottom-left';
                }
            } else if (placement === 'auto-left') {
                if (pageY < clientHeight / 3) {
                    placement = 'left-top';
                } else if (pageY < clientHeight * 2 / 3) {
                    placement = 'left';
                } else {
                    placement = 'left-bottom';
                }
            } else if (placement === 'auto-right') {
                if (pageY < clientHeight / 3) {
                    placement = 'right-bottom';
                } else if (pageY < clientHeight * 2 / 3) {
                    placement = 'right';
                } else {
                    placement = 'right-top';
                }
            }
            return placement;
        },
        getElementPosition: function() {
            // If the container is the body or normal conatiner, just use $element.offset()
            var elRect = this.$element[0].getBoundingClientRect();
            var container = this.options.container;
            var cssPos = container.css('position');

            if (container.is(document.body) || cssPos === 'static') {
                return $.extend({}, this.$element.offset(), {
                    width: this.$element[0].offsetWidth || elRect.width,
                    height: this.$element[0].offsetHeight || elRect.height
                });
                // Else fixed container need recalculate the  position
            } else if (cssPos === 'fixed') {
                var containerRect = container[0].getBoundingClientRect();
                return {
                    top: elRect.top - containerRect.top + container.scrollTop(),
                    left: elRect.left - containerRect.left + container.scrollLeft(),
                    width: elRect.width,
                    height: elRect.height
                };
            } else if (cssPos === 'relative') {
                return {
                    top: this.$element.offset().top - container.offset().top,
                    left: this.$element.offset().left - container.offset().left,
                    width: this.$element[0].offsetWidth || elRect.width,
                    height: this.$element[0].offsetHeight || elRect.height
                };
            }
        },

        getTargetPositin: function(elementPos, placement, targetWidth, targetHeight) {
            var pos = elementPos,
                container = this.options.container,
                //clientWidth = container.innerWidth(),
                //clientHeight = container.innerHeight(),
                elementW = this.$element.outerWidth(),
                elementH = this.$element.outerHeight(),
                scrollTop = document.documentElement.scrollTop + container.scrollTop(),
                scrollLeft = document.documentElement.scrollLeft + container.scrollLeft(),
                position = {},
                arrowOffset = null,
                arrowSize = this.options.arrow ? 20 : 0,
                padding = 10,
                fixedW = elementW < arrowSize + padding ? arrowSize : 0,
                fixedH = elementH < arrowSize + padding ? arrowSize : 0,
                refix = 0,
                pageH = document.documentElement.clientHeight + scrollTop,
                pageW = document.documentElement.clientWidth + scrollLeft;

            var validLeft = pos.left + pos.width / 2 - fixedW > 0;
            var validRight = pos.left + pos.width / 2 + fixedW < pageW;
            var validTop = pos.top + pos.height / 2 - fixedH > 0;
            var validBottom = pos.top + pos.height / 2 + fixedH < pageH;

            switch (placement) {
                case 'bottom':
                    position = {
                        top: pos.top + pos.height,
                        left: pos.left + pos.width / 2 - targetWidth / 2
                    };
                    break;
                case 'top':
                    position = {
                        top: pos.top - targetHeight,
                        left: pos.left + pos.width / 2 - targetWidth / 2
                    };
                    break;
                case 'left':
                    position = {
                        top: pos.top + pos.height / 2 - targetHeight / 2,
                        left: pos.left - targetWidth
                    };
                    break;
                case 'right':
                    position = {
                        top: pos.top + pos.height / 2 - targetHeight / 2,
                        left: pos.left + pos.width
                    };
                    break;
                case 'top-right':
                    position = {
                        top: pos.top - targetHeight,
                        left: validLeft ? pos.left - fixedW : padding
                    };
                    arrowOffset = {
                        left: validLeft ? Math.min(elementW, targetWidth) / 2 + fixedW : _offsetOut
                    };
                    break;
                case 'top-left':
                    refix = validRight ? fixedW : -padding;
                    position = {
                        top: pos.top - targetHeight,
                        left: pos.left - targetWidth + pos.width + refix
                    };
                    arrowOffset = {
                        left: validRight ? targetWidth - Math.min(elementW, targetWidth) / 2 - fixedW : _offsetOut
                    };
                    break;
                case 'bottom-right':
                    position = {
                        top: pos.top + pos.height,
                        left: validLeft ? pos.left - fixedW : padding
                    };
                    arrowOffset = {
                        left: validLeft ? Math.min(elementW, targetWidth) / 2 + fixedW : _offsetOut
                    };
                    break;
                case 'bottom-left':
                    refix = validRight ? fixedW : -padding;
                    position = {
                        top: pos.top + pos.height,
                        left: pos.left - targetWidth + pos.width + refix
                    };
                    arrowOffset = {
                        left: validRight ? targetWidth - Math.min(elementW, targetWidth) / 2 - fixedW : _offsetOut
                    };
                    break;
                case 'right-top':
                    refix = validBottom ? fixedH : -padding;
                    position = {
                        top: pos.top - targetHeight + pos.height + refix,
                        left: pos.left + pos.width
                    };
                    arrowOffset = {
                        top: validBottom ? targetHeight - Math.min(elementH, targetHeight) / 2 - fixedH : _offsetOut
                    };
                    break;
                case 'right-bottom':
                    position = {
                        top: validTop ? pos.top - fixedH : padding,
                        left: pos.left + pos.width
                    };
                    arrowOffset = {
                        top: validTop ? Math.min(elementH, targetHeight) / 2 + fixedH : _offsetOut
                    };
                    break;
                case 'left-top':
                    refix = validBottom ? fixedH : -padding;
                    position = {
                        top: pos.top - targetHeight + pos.height + refix,
                        left: pos.left - targetWidth
                    };
                    arrowOffset = {
                        top: validBottom ? targetHeight - Math.min(elementH, targetHeight) / 2 - fixedH : _offsetOut
                    };
                    break;
                case 'left-bottom':
                    position = {
                        top: validTop ? pos.top - fixedH : padding,
                        left: pos.left - targetWidth
                    };
                    arrowOffset = {
                        top: validTop ? Math.min(elementH, targetHeight) / 2 + fixedH : _offsetOut
                    };
                    break;

            }
            position.top += this.getOffsetTop();
            position.left += this.getOffsetLeft();

            return {
                position: position,
                arrowOffset: arrowOffset
            };
        }
    };
    $.fn[pluginName] = function(options, noInit) {
        var results = [];
        var $result = this.each(function() {

            var webuiPopover = $.data(this, 'plugin_' + pluginName);
            if (!webuiPopover) {
                if (!options) {
                    webuiPopover = new WebuiPopover(this, null);
                } else if (typeof options === 'string') {
                    if (options !== 'destroy') {
                        if (!noInit) {
                            webuiPopover = new WebuiPopover(this, null);
                            results.push(webuiPopover[options]());
                        }
                    }
                } else if (typeof options === 'object') {
                    webuiPopover = new WebuiPopover(this, options);
                }
                $.data(this, 'plugin_' + pluginName, webuiPopover);
            } else {
                if (options === 'destroy') {
                    webuiPopover.destroy();
                } else if (typeof options === 'string') {
                    results.push(webuiPopover[options]());
                }
            }
        });
        return (results.length) ? results : $result;
    };

    //Global object exposes to window.
    var webuiPopovers = (function() {
        var _hideAll = function() {
            hideAllPop();
        };
        var _create = function(selector, options) {
            options = options || {};
            $(selector).webuiPopover(options);
        };
        var _isCreated = function(selector) {
            var created = true;
            $(selector).each(function(i, item) {
                created = created && $(item).data('plugin_' + pluginName) !== undefined;
            });
            return created;
        };
        var _show = function(selector, options) {
            if (options) {
                $(selector).webuiPopover(options).webuiPopover('show');
            } else {
                $(selector).webuiPopover('show');
            }
        };
        var _hide = function(selector) {
            $(selector).webuiPopover('hide');
        };

        var _setDefaultOptions = function(options) {
            defaults = $.extend({}, defaults, options);
        };

        var _updateContent = function(selector, content) {
            var pop = $(selector).data('plugin_' + pluginName);
            if (pop) {
                var cache = pop.getCache();
                pop.options.cache = false;
                pop.options.content = content;
                if (pop._opened) {
                    pop._opened = false;
                    pop.show();
                } else {
                    if (pop.isAsync()) {
                        pop.setContentASync(content);
                    } else {
                        pop.setContent(content);
                    }
                }
                pop.options.cache = cache;
            }
        };

        var _updateContentAsync = function(selector, url) {
            var pop = $(selector).data('plugin_' + pluginName);
            if (pop) {
                var cache = pop.getCache();
                var type = pop.options.type;
                pop.options.cache = false;
                pop.options.url = url;

                if (pop._opened) {
                    pop._opened = false;
                    pop.show();
                } else {
                    pop.options.type = 'async';
                    pop.setContentASync(pop.content);
                }
                pop.options.cache = cache;
                pop.options.type = type;
            }
        };

        return {
            show: _show,
            hide: _hide,
            create: _create,
            isCreated: _isCreated,
            hideAll: _hideAll,
            updateContent: _updateContent,
            updateContentAsync: _updateContentAsync,
            setDefaultOptions: _setDefaultOptions
        };
    })();
    window.WebuiPopovers = webuiPopovers;
}));

})(window, document);