/*
 *  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
 */

layui.define(['jquery', 'element'], function(exports) {
    var $=layui.$;
            // 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: '',
                opacity:null,
                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',
                        opacity:this.options.opacity || 1
                    });

                    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;
        exports("popover",WebuiPopovers);
})