All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.butterfaces-js.butterfaces-expandable.jquery.js Maven / Gradle / Ivy

The newest version!
/**
 * jQuery-Plugin "Expanded TextAreas" for expandable text areas. It is used for the JSF-Component "b:textarea".
 * Works with at least jQuery 1.7.
 *
 * How to use:
 * jQuery("#someTextAreaSelector").butterExpandable();
 */
(function ($) {
    // extend jQuery --------------------------------------------------------------------
    $.fn.butterExpandable = function () {
        return this.each(function () {
            if ($(this).find("textarea").length > 0) {
                new TextareaExpandable(this);
            } else {
                new DivExpandable(this);
            }
        });
    };

    // define objects --------------------------------------------------------------------

    var _AbstractExpandable = Class.extend({
        init: function (rootElement) {
            this.EXPAND_HEIGHT = 250; //in px
            this.EXPAND_WIDTH = 500; //in px
            this.ANIMATION_DURATION = 200; //in ms
            this.REPOSITION_INTERVAL = 500; //in ms
            this.EASING = "swing";
            this.KEYCODE_ESCAPE = 27;

            this.$rootElement = $(rootElement);

            this.$ghostElement = null;
            this.$originalElement;
            this.initialHeight;
            this.initialWidth;
            this.initialOffset;
            this.positionTriggerInterval;

            this.initialize();
        },

        initialize: function () {
            // should intentionally be overridden
        },

        isExpansionEventIgnored: function (event) {
            // should intentionally be overridden
            return false;
        },

        onGhostElementCreated: function () {
            // should intentionally be overridden
        },

        onGhostElementCollapsed: function (isCancelled) {
            // should intentionally be overridden
        },

        transferValueToGhostElement: function () {
            // should intentionally be overridden
        },

        expandElement: function (event) {
            if (this.isExpansionEventIgnored(event)) {
                return;
            }

            // console.log("expanding element");

            this.initialHeight = this.$originalElement.outerHeight();
            this.initialWidth = this.$originalElement.outerWidth();
            this.initialOffset = this.$originalElement.offset();

            //create a ghost element that be animated on gets the focus
            var self = this;
            this.$ghostElement = this.createGhostElement();
            this.transferValueToGhostElement();
            this.$ghostElement.css("width", this.initialWidth)
                    .css("height", this.initialHeight)
                    .css("position", "absolute")
                    .css("top", this.initialOffset.top)
                    .css("left", this.initialOffset.left)
                    .css("z-index", 2000)
                    .css("box-shadow", "5px 5px 5px 0 #999")
                    .addClass("butter-component-expandable-ghost")
                    .appendTo($("body"))
                    .animate({
                        height: self.EXPAND_HEIGHT,
                        width: self.initialWidth > self.EXPAND_WIDTH ? self.initialWidth : self.EXPAND_WIDTH
                    }, self.ANIMATION_DURATION, self.EASING, function () {
                        $(document)
                                .on("click.expandable", function (event) {
                                    self._handleMouseClick(event);
                                })
                                .on("keydown.expandable", function (event) {
                                    self._handleEscapeKey(event);
                                });

                        $(window).on("resize.expandable", function (event) {
                            self._repositionGhostElement(event);
                        });

                        //keep track of the orginal element's position
                        self.positionTriggerInterval =
                                window.setInterval(self._repositionGhostElement, self.REPOSITION_INTERVAL);
                    });

            //make original invisible
            this.$originalElement
                    .css("visibility", "hidden")
                    .siblings()
                    .css("visibility", "hidden");

            this.onGhostElementCreated();
        },

        /**
         * @returns {*|HTMLElement}
         */
        createGhostElement: function () {
            // should intentionally be overridden
            return null;
        },

        /**
         * Collapses the ghost element and sets the value if not isCancelled
         * @param isCancelled
         */
        collapseElement: function (cancelled) {
            // console.log("collapsing element");

            // 'cancelled' can be an event object
            var isCancelled = typeof cancelled === "boolean" && cancelled;

            $(document)
                    .off("click.expandable")
                    .off("keydown.expandable");

            //make original visible again
            this.$originalElement
                    .css("visibility", "visible")
                    .siblings()
                    .css("visibility", "visible");

            var self = this;
            this.$ghostElement.animate({
                height: self.initialHeight,
                width: self.initialWidth
            }, self.ANIMATION_DURATION, self.EASING, function () {
                //on animation complete

                self.onGhostElementCollapsed(isCancelled);

                //delete the ghost element
                self.$ghostElement.remove();
                self.$ghostElement = null;

                //delete position trigger timeout and resize listener
                window.clearInterval(self.positionTriggerInterval);
                $(window).off("resize.expandable");
            });
        },

        _handleMouseClick: function (event) {
            // collapse ghost element if user clicks beside it
            if (!$(event.target).is(".butter-component-expandable-ghost")) {
                this.collapseElement(false);
            }
        },

        _handleEscapeKey: function (event) {
            if (event.which === this.KEYCODE_ESCAPE) {
                this.collapseElement(true);
            }
        },

        _repositionGhostElement: function () {
            //keep track of window resizing and reposition the ghost element
            if (this.$ghostElement !== undefined && this.$ghostElement != null) {
                this.initialOffset = this.$originalElement.offset();
                this.$ghostElement
                        .css("top", this.initialOffset.top)
                        .css("left", this.initialOffset.left);
            }
        }
    });

    var DivExpandable = _AbstractExpandable.extend({
        initialize: function () {
            this.$originalElement = this.$rootElement.find(".butter-component-value-readonly");
            this._rearrangeOriginalElementStructure();
        },

        _rearrangeOriginalElementStructure: function () {
            var self = this;
            var _label = this.$rootElement.find(".butter-component-label");

            this.$originalElement
                    .addClass("butter-component-expandable-original")
                    .click(function (event) {
                        self.expandElement(event);
                    })
                    .detach();

            var _container = $("
") .addClass("butter-component-expandable-readonly-container") .insertAfter(_label); var _icon = $("").addClass("glyphicon glyphicon-resize-full"); this.$originalElement.appendTo(_container); $("
") .addClass("butter-component-expandable-readonly-icon") .append(_icon) .appendTo(_container); }, createGhostElement: function () { return $("
"); }, transferValueToGhostElement: function () { $("
") .html(this.$originalElement.html()) .addClass("butter-component-expandable-ghost-readonlyContent") .appendTo(this.$ghostElement); } }); var TextareaExpandable = _AbstractExpandable.extend({ initialize: function () { this.blockFocusEventOnOriginal = false; this.blockBlurEventOnOriginal = false; this.$originalElement = this.$rootElement.find("textarea"); this.$originalElement.addClass("butter-component-expandable-original"); var self = this; this.$originalElement.focus(function (event) { self.expandElement(event); }); this.$originalElement.blur(function (event) { self._handleBlurEvent(event); }); this._addInputGroupAddon(); }, _addInputGroupAddon: function () { this.$originalElement .addClass("form-control") .parent() .addClass("input-group"); $("") .insertAfter(this.$originalElement); }, _handleBlurEvent: function (event) { if (this.blockBlurEventOnOriginal) { // prevent blur event bubbling, so it will not be triggered in jsf event.preventDefault(); } }, createGhostElement: function () { return $("