<commit_before>/*jslint browser: true, nomen: true, todo: true */
(function ($, window, document) {
    'use strict';

    var P, Parallaxin, px, pct;

    px = function (n) { return {value: n, px: true}; };
    pct = function (n) { return {value: n, pct: true}; };

    P = Parallaxin = function () {};
    Parallaxin.instances = [];

    P.PositionMethod = {
        ELEMENT_POSITION: function ($el, left, top) {
          $el.css({left: left, top: top});
        },
        CSS_TRANSFORM: function ($el, left, top) {
            var value = '';
            if (left != null) {
                value += 'translateX(' + left + 'px)';
            }
            if (top != null) {
                value += ' translateY(' + top + 'px)';
            }
            if (value) {
                $el.css('transform', value);
            }
        }
    };

    Parallaxin.prototype = {
        optionAttributePrefix: 'data-parallaxin-',

        defaultOptions: {
            // The container element. Defaults to the parent of the selected element.
            container: null,

            // Should the element move vertically?
            vertical: true,

            // Should the element move horizontally?
            horizontal: false,

            // Does the container change sizes?
            responsive: false,

            // The element whose scrolling should change the position.
            scrollingElement: 'window',

            // How is the position set? This can be either a function or a
            // string that correspondes to one of the built-in functions.
            positionMethod: P.PositionMethod.CSS_TRANSFORM,

            // Should the element use fixed positioning? The default value
            // depends on whether the element is styles as "position: fixed"
            // at the time the plugin is initialized, so normally you shouldn't
            // need to set this option; just set "position: fixed" in your
            // stylesheet.
            //
            // Using fixed positioning means the element is positioned relative
            // to the document instead of its parent and can result in a much
            // smoother parallax effect.
            fixed: null,

            // Should the element be hidden when its container leaves the
            // viewport? This helps to eliminate z-index issues when using fixed
            // positioning and multiple parallaxin elements.
            hideOnExit: true,

            // Contrain the range of motion within the container. Can be a
            // string or array, specified in the order "top", "right", "bottom",
            // "left." If less than four values are provided, they will be
            // expanded like with CSS. For example, the following are all
            // equivalent: `0`, `'0'`, `'0 0'`, `'0 0 0 0'` `[0]`, `[0, 0]`,
            // `[0, 0, 0, 0]`. Percentage values are also allowed.
            insets: 0
        },

        initialize: function ($el, options) {
            var isFixed;

            this.$el = $el;
            this.options = $.extend({}, this.defaultOptions, this.getHtmlOptions(), options);
            if (this.options.container) {
                this.$container = $(this.options.container);
            } else {
                this.$container = this.$el.parent();
            }
            this._insets = this.parseInsets(this.options.insets);

            isFixed = $el.css('position') === 'fixed';
            if (this.options.fixed === null || this.options.fixed === undefined) {
                this.options.fixed = isFixed;
            }
            if (this.options.fixed && !isFixed) {
                this.$el.css('position', 'fixed');
            }

            if (typeof this.options.positionMethod === 'string') {
                switch (this.options.positionMethod) {
                case 'elementPosition':
                    this.options.positionMethod = P.PositionMethod.ELEMENT_POSITION;
                    break;
                case 'cssTransform':
                    this.options.positionMethod = P.PositionMethod.CSS_TRANSFORM;
                    break;
                default:
                    $.error('Invalid positionMethod value: ' + this.options.positionMethod)
                }
            }

            if (!P.$win) {
                P.$win = $(window);
            }

            // Add the listener to the scrolling element.
            this.$scrollingEl = this.options.scrollingElement ? $(this.options.scrollingElement) : P.$win;
            this._onScroll = $.proxy(this.onScroll, this);
            this.$scrollingEl.on('scroll', this._onScroll);

            if (!P._hasResizeHandler && this.options.responsive) {
                P._hasResizeHandler = true;
                P.$win.on('resize', P.onWindowResize);
            }

            P.instances.push(this);
            this.update(false);
            return this;
        },

        destroy: function () {
            var index = P.instances.indexOf(this);
            P.instances.splice(index, 1);
            this.$scrollingEl.off('scroll', this._onScroll);
            return this;
        },

        // Update the position of the element.
        update: function (r) {
            var self = this;
            if (requestAnimationFrame) {
                if (!this._afScheduled) {
                    this._afScheduled = true;
                    requestAnimationFrame(function () {
                        self.updateNow(r);
                        self._afScheduled = false;
                    });
                }
            } else {
                this.updateNow(r);
            }
        },

        updateNow: function (r) {
            var
                a, b, c,
                shouldHide,
                recalculate = r !== false,
                recalculateContainerSize = recalculate,
                recalculateSize = recalculate,
                // TODO: Performance could be improved if there could be an option for setting this to false and a way to do it manually. (Gotta figure out a good name for the option.)
                recalculateContainerPosition = true,
                bounds = this._bounds,
                size = this._size,
                containerSize = this._containerSize,
                containerPosition = this._containerPosition,
                scrollPosition = this.getScrollPosition(),
                windowSize = P.getWindowSize(),
                css = {};

            if (recalculateContainerSize || !containerSize) {
                containerSize = this._containerSize = this.calculateElSize(this.$container);
                bounds = this._bounds = this.calculateBounds(this._insets, containerSize);
            }

            if (recalculateSize || !size) {
                size = this._size = this.calculateElSize(this.$el, this._isHidden);
            }

            if (recalculateContainerPosition || !containerPosition) {
                // Unlike sizes, jQuery calculates both vertical and horizontal
                // positions with one measurement, so we can't do them
                // separately.

                // Determine the position of the element relative to the top of
                // the scrolling element's contents. We need to make sure we
                // account for both the normal case (scrolling the document) and
                // for scrolling of arbitrary (overflown) elements.
                a = this.$container.offset();
                b = {left: this.$scrollingEl.scrollLeft(), top: this.$scrollingEl.scrollTop()};
                c = {left: P.$win.scrollLeft(), top: P.$win.scrollTop()};
                containerPosition = this._containerPosition = {
                  left: a.left + b.left - c.left,
                  top: a.top + b.top - c.top
                }
            }

            if (this.options.horizontal) {
                css.left = this.updatePosition(bounds.L, bounds.R,
                    containerPosition.left, containerSize.width, size.width,
                    scrollPosition.left, windowSize.width);
            }
            if (this.options.vertical) {
                css.top = this.updatePosition(bounds.T, bounds.B,
                    containerPosition.top, containerSize.height, size.height,
                    scrollPosition.top, windowSize.height);
            }

            if (this.options.hideOnExit) {
                shouldHide = (!this.options.horizontal || css.left === undefined) &&
                    (!this.options.vertical || css.top === undefined);
                if (shouldHide !== !!this._isHidden) {
                    this.$el[shouldHide ? 'hide' : 'show']();
                    this._isHidden = shouldHide;
                }
            }
            if (css.left !== undefined || css.top !== undefined) {
                this.options.positionMethod(this.$el, css.left, css.top);
            }
        },

        updatePosition: function (boundsMin, boundsMax, containerPosition, containerSize, elSize, scrollPosition, windowSize) {
            // When the container is at the top of the viewport (min scrollTop),
            // $el should be aligned to its top. When the container is at the
            // bottom of the viewport (max scrollTop), $el should be aligned to
            // the bottom.
            var min, max, pct, targetMin, targetMax, pos;

            if (containerPosition + containerSize < scrollPosition) {
                // Container is above viewport.
                return undefined;
            }
            if (containerPosition > scrollPosition + windowSize) {
                // Container is below viewport.
                return undefined;
            }

            if (elSize <= windowSize) {
                max = containerPosition;
                min = containerPosition + containerSize - windowSize;
                targetMin = boundsMax - elSize;
                targetMax = boundsMin;
            } else {
                min = containerPosition - windowSize;
                max = containerPosition + containerSize;
                targetMin = boundsMin - elSize;
                targetMax = boundsMax;
            }

            pct = (scrollPosition - min) / (max - min);
            pos = targetMin + pct * (targetMax - targetMin);

            if (this.options.fixed) {
                // Make the positioin relative to the document instead of the
                // container.
                pos += containerPosition - scrollPosition;
            }

            return pos;
        },

        calculateElSize: function ($el, isHidden) {
            var size = {};
            if (isHidden) {
                $el.show();
            }
            if (this.options.horizontal) {
                size.width = $el.outerWidth();
            }
            if (this.options.vertical) {
                size.height = $el.outerHeight();
            }
            if (isHidden) {
                $el.hide();
            }
            return size;
        },

        calculateBound: function (inset, size, fromOpposite) {
            if (inset.pct) {
                return this.calculateBound(px(inset.value * size), size, fromOpposite);
            }
            if (fromOpposite) {
                return size - inset.value;
            }
            return inset.value;
        },

        // Convert parsed insets into actual pixel positions based on the size
        // of the container.
        calculateBounds: function (insets, containerSize) {
            var bounds = {};
            if (this.options.horizontal) {
                bounds.R = this.calculateBound(insets.R, containerSize.width);
                bounds.L = this.calculateBound(insets.L, containerSize.width, true);
            }
            if (this.options.vertical) {
                bounds.T = this.calculateBound(insets.T, containerSize.height);
                bounds.B = this.calculateBound(insets.B, containerSize.height, true);
            }
            return bounds;
        },

        parseInset: function (val) {
            switch ($.type(val)) {
            case 'number':
                return px(val);
            case 'string':
                if (val.indexOf('%') > -1) {
                    return pct(parseFloat(val) / 100);
                }
                return px(parseInt(val, 10));
            }
        },

        // Parse inset options into numbers and units corresponding to each
        // side.
        parseInsets: function (val) {
            var parsed, a, v, h, t, r, b, l;

            switch ($.type(val)) {
            case 'number':
                return {T: px(val), R: px(val), B: px(val), L: px(val)};
            case 'array':
                switch (val.length) {
                case 1:
                    a = this.parseInset(val[0]);
                    return {T: a, R: a, B: a, L: a};
                case 2:
                    v = this.parseInset(val[0]);
                    h = this.parseInset(val[1]);
                    return {T: v, R: h, B: v, L: h};
                case 4:
                    t = this.parseInset(val[0]);
                    r = this.parseInset(val[1]);
                    b = this.parseInset(val[2]);
                    l = this.parseInset(val[4]);
                    return {T: t, R: r, B: b, L: l};
                }
                break;
            case 'string':
                return this.parseInsets(val.replace(/^\s+|\s+$/, '').split(/\s+/));
            }
        },

        getHtmlOptions: function () {
            var self = this,
                opts = {};
            $.each(this.$el[0].attributes, function (i, attr) {
                if (attr.name.indexOf(self.optionAttributePrefix) !== -1) {
                    opts[attr.name.substr(self.optionAttributePrefix.length)] = attr.value;
                }
            });
            return opts;
        },

        onScroll: function () {
            // TODO: We really want to not get the position multiple times for the same element.
            var
                oldPos = this._scrollPosition,
                pos = this.getScrollPosition(true),
                topChanged = !oldPos || oldPos.top !== pos.top,
                leftChanged = !oldPos || oldPos.top !== pos.top;

            if ((topChanged && this.options.vertical) || (leftChanged && this.options.horizontal)) {
                this.update(false);
            }
        },

        getScrollPosition: function (recalculate) {
            if (recalculate || !this._scrollPosition) {
                this._scrollPosition = {
                    top: this.$scrollingEl.scrollTop(),
                    left: this.$scrollingEl.scrollLeft()
                };
            }
            return this._scrollPosition;
        }
    };

    Parallaxin.getWindowSize = function (recalculate) {
        if (recalculate || !P._windowSize) {
            P._windowSize = {
                width: P.$win.width(),
                height: P.$win.height()
            };
        }
        return P._windowSize;
    };

    Parallaxin.onWindowResize = function () {
        var i, instance;
        P.getWindowSize(true);
        for (i = P.instances.length - 1; i >= 0; i -= 1) {
            instance = P.instances[i];
            if (instance.options.responsive) {
                instance.update();
            }
        }
    };

    $.extend($.fn, {
        parallaxin: function (optionsOrMethod) {
            var returnValue = this,
                args = arguments;
            this.each(function (i, el) {
                var $el = $(el),
                    plugin = $el.data('parallaxin'),
                    method = typeof optionsOrMethod === 'string' ? optionsOrMethod : null,
                    options = method === null ? optionsOrMethod || {} : {};

                if (!plugin) {
                    if (method) {
                        $.error('You can\'t call the parallaxin method "' + method
                                + '" without first initializing the plugin by calling '
                                + 'parallaxin() on the jQuery object.');
                    } else {
                        plugin = new Parallaxin().initialize($el, options);
                        $el.data('parallaxin', plugin);
                    }
                } else if (method) {
                    if (typeof plugin[method] !== 'function') {
                        $.error('Method "' + method + '" does not exist on jQuery.parallaxin');
                    } else {
                        // NOTE: If you call a method that returns a value, you will only get the result from the last item in the collection.
                        returnValue = plugin[method].apply(plugin, Array.prototype.slice.call(args, 1));
                    }
                }
            });
            return returnValue;
        }
    });
}(this.jQuery, window, document));
<commit_msg>Rename CSS_TRANSFORM to CSS_TRANSLATE<commit_after>/*jslint browser: true, nomen: true, todo: true */
(function ($, window, document) {
    'use strict';

    var P, Parallaxin, px, pct;

    px = function (n) { return {value: n, px: true}; };
    pct = function (n) { return {value: n, pct: true}; };

    P = Parallaxin = function () {};
    Parallaxin.instances = [];

    P.PositionMethod = {
        ELEMENT_POSITION: function ($el, left, top) {
          $el.css({left: left, top: top});
        },
        CSS_TRANSLATE: function ($el, left, top) {
            var value = '';
            if (left != null) {
                value += 'translateX(' + left + 'px)';
            }
            if (top != null) {
                value += ' translateY(' + top + 'px)';
            }
            if (value) {
                $el.css('transform', value);
            }
        }
    };

    Parallaxin.prototype = {
        optionAttributePrefix: 'data-parallaxin-',

        defaultOptions: {
            // The container element. Defaults to the parent of the selected element.
            container: null,

            // Should the element move vertically?
            vertical: true,

            // Should the element move horizontally?
            horizontal: false,

            // Does the container change sizes?
            responsive: false,

            // The element whose scrolling should change the position.
            scrollingElement: 'window',

            // How is the position set? This can be either a function or a
            // string that correspondes to one of the built-in functions.
            positionMethod: P.PositionMethod.CSS_TRANSLATE,

            // Should the element use fixed positioning? The default value
            // depends on whether the element is styles as "position: fixed"
            // at the time the plugin is initialized, so normally you shouldn't
            // need to set this option; just set "position: fixed" in your
            // stylesheet.
            //
            // Using fixed positioning means the element is positioned relative
            // to the document instead of its parent and can result in a much
            // smoother parallax effect.
            fixed: null,

            // Should the element be hidden when its container leaves the
            // viewport? This helps to eliminate z-index issues when using fixed
            // positioning and multiple parallaxin elements.
            hideOnExit: true,

            // Contrain the range of motion within the container. Can be a
            // string or array, specified in the order "top", "right", "bottom",
            // "left." If less than four values are provided, they will be
            // expanded like with CSS. For example, the following are all
            // equivalent: `0`, `'0'`, `'0 0'`, `'0 0 0 0'` `[0]`, `[0, 0]`,
            // `[0, 0, 0, 0]`. Percentage values are also allowed.
            insets: 0
        },

        initialize: function ($el, options) {
            var isFixed;

            this.$el = $el;
            this.options = $.extend({}, this.defaultOptions, this.getHtmlOptions(), options);
            if (this.options.container) {
                this.$container = $(this.options.container);
            } else {
                this.$container = this.$el.parent();
            }
            this._insets = this.parseInsets(this.options.insets);

            isFixed = $el.css('position') === 'fixed';
            if (this.options.fixed === null || this.options.fixed === undefined) {
                this.options.fixed = isFixed;
            }
            if (this.options.fixed && !isFixed) {
                this.$el.css('position', 'fixed');
            }

            if (typeof this.options.positionMethod === 'string') {
                switch (this.options.positionMethod) {
                case 'elementPosition':
                    this.options.positionMethod = P.PositionMethod.ELEMENT_POSITION;
                    break;
                case 'cssTranslate':
                    this.options.positionMethod = P.PositionMethod.CSS_TRANSLATE;
                    break;
                default:
                    $.error('Invalid positionMethod value: ' + this.options.positionMethod)
                }
            }

            if (!P.$win) {
                P.$win = $(window);
            }

            // Add the listener to the scrolling element.
            this.$scrollingEl = this.options.scrollingElement ? $(this.options.scrollingElement) : P.$win;
            this._onScroll = $.proxy(this.onScroll, this);
            this.$scrollingEl.on('scroll', this._onScroll);

            if (!P._hasResizeHandler && this.options.responsive) {
                P._hasResizeHandler = true;
                P.$win.on('resize', P.onWindowResize);
            }

            P.instances.push(this);
            this.update(false);
            return this;
        },

        destroy: function () {
            var index = P.instances.indexOf(this);
            P.instances.splice(index, 1);
            this.$scrollingEl.off('scroll', this._onScroll);
            return this;
        },

        // Update the position of the element.
        update: function (r) {
            var self = this;
            if (requestAnimationFrame) {
                if (!this._afScheduled) {
                    this._afScheduled = true;
                    requestAnimationFrame(function () {
                        self.updateNow(r);
                        self._afScheduled = false;
                    });
                }
            } else {
                this.updateNow(r);
            }
        },

        updateNow: function (r) {
            var
                a, b, c,
                shouldHide,
                recalculate = r !== false,
                recalculateContainerSize = recalculate,
                recalculateSize = recalculate,
                // TODO: Performance could be improved if there could be an option for setting this to false and a way to do it manually. (Gotta figure out a good name for the option.)
                recalculateContainerPosition = true,
                bounds = this._bounds,
                size = this._size,
                containerSize = this._containerSize,
                containerPosition = this._containerPosition,
                scrollPosition = this.getScrollPosition(),
                windowSize = P.getWindowSize(),
                css = {};

            if (recalculateContainerSize || !containerSize) {
                containerSize = this._containerSize = this.calculateElSize(this.$container);
                bounds = this._bounds = this.calculateBounds(this._insets, containerSize);
            }

            if (recalculateSize || !size) {
                size = this._size = this.calculateElSize(this.$el, this._isHidden);
            }

            if (recalculateContainerPosition || !containerPosition) {
                // Unlike sizes, jQuery calculates both vertical and horizontal
                // positions with one measurement, so we can't do them
                // separately.

                // Determine the position of the element relative to the top of
                // the scrolling element's contents. We need to make sure we
                // account for both the normal case (scrolling the document) and
                // for scrolling of arbitrary (overflown) elements.
                a = this.$container.offset();
                b = {left: this.$scrollingEl.scrollLeft(), top: this.$scrollingEl.scrollTop()};
                c = {left: P.$win.scrollLeft(), top: P.$win.scrollTop()};
                containerPosition = this._containerPosition = {
                  left: a.left + b.left - c.left,
                  top: a.top + b.top - c.top
                }
            }

            if (this.options.horizontal) {
                css.left = this.updatePosition(bounds.L, bounds.R,
                    containerPosition.left, containerSize.width, size.width,
                    scrollPosition.left, windowSize.width);
            }
            if (this.options.vertical) {
                css.top = this.updatePosition(bounds.T, bounds.B,
                    containerPosition.top, containerSize.height, size.height,
                    scrollPosition.top, windowSize.height);
            }

            if (this.options.hideOnExit) {
                shouldHide = (!this.options.horizontal || css.left === undefined) &&
                    (!this.options.vertical || css.top === undefined);
                if (shouldHide !== !!this._isHidden) {
                    this.$el[shouldHide ? 'hide' : 'show']();
                    this._isHidden = shouldHide;
                }
            }
            if (css.left !== undefined || css.top !== undefined) {
                this.options.positionMethod(this.$el, css.left, css.top);
            }
        },

        updatePosition: function (boundsMin, boundsMax, containerPosition, containerSize, elSize, scrollPosition, windowSize) {
            // When the container is at the top of the viewport (min scrollTop),
            // $el should be aligned to its top. When the container is at the
            // bottom of the viewport (max scrollTop), $el should be aligned to
            // the bottom.
            var min, max, pct, targetMin, targetMax, pos;

            if (containerPosition + containerSize < scrollPosition) {
                // Container is above viewport.
                return undefined;
            }
            if (containerPosition > scrollPosition + windowSize) {
                // Container is below viewport.
                return undefined;
            }

            if (elSize <= windowSize) {
                max = containerPosition;
                min = containerPosition + containerSize - windowSize;
                targetMin = boundsMax - elSize;
                targetMax = boundsMin;
            } else {
                min = containerPosition - windowSize;
                max = containerPosition + containerSize;
                targetMin = boundsMin - elSize;
                targetMax = boundsMax;
            }

            pct = (scrollPosition - min) / (max - min);
            pos = targetMin + pct * (targetMax - targetMin);

            if (this.options.fixed) {
                // Make the positioin relative to the document instead of the
                // container.
                pos += containerPosition - scrollPosition;
            }

            return pos;
        },

        calculateElSize: function ($el, isHidden) {
            var size = {};
            if (isHidden) {
                $el.show();
            }
            if (this.options.horizontal) {
                size.width = $el.outerWidth();
            }
            if (this.options.vertical) {
                size.height = $el.outerHeight();
            }
            if (isHidden) {
                $el.hide();
            }
            return size;
        },

        calculateBound: function (inset, size, fromOpposite) {
            if (inset.pct) {
                return this.calculateBound(px(inset.value * size), size, fromOpposite);
            }
            if (fromOpposite) {
                return size - inset.value;
            }
            return inset.value;
        },

        // Convert parsed insets into actual pixel positions based on the size
        // of the container.
        calculateBounds: function (insets, containerSize) {
            var bounds = {};
            if (this.options.horizontal) {
                bounds.R = this.calculateBound(insets.R, containerSize.width);
                bounds.L = this.calculateBound(insets.L, containerSize.width, true);
            }
            if (this.options.vertical) {
                bounds.T = this.calculateBound(insets.T, containerSize.height);
                bounds.B = this.calculateBound(insets.B, containerSize.height, true);
            }
            return bounds;
        },

        parseInset: function (val) {
            switch ($.type(val)) {
            case 'number':
                return px(val);
            case 'string':
                if (val.indexOf('%') > -1) {
                    return pct(parseFloat(val) / 100);
                }
                return px(parseInt(val, 10));
            }
        },

        // Parse inset options into numbers and units corresponding to each
        // side.
        parseInsets: function (val) {
            var parsed, a, v, h, t, r, b, l;

            switch ($.type(val)) {
            case 'number':
                return {T: px(val), R: px(val), B: px(val), L: px(val)};
            case 'array':
                switch (val.length) {
                case 1:
                    a = this.parseInset(val[0]);
                    return {T: a, R: a, B: a, L: a};
                case 2:
                    v = this.parseInset(val[0]);
                    h = this.parseInset(val[1]);
                    return {T: v, R: h, B: v, L: h};
                case 4:
                    t = this.parseInset(val[0]);
                    r = this.parseInset(val[1]);
                    b = this.parseInset(val[2]);
                    l = this.parseInset(val[4]);
                    return {T: t, R: r, B: b, L: l};
                }
                break;
            case 'string':
                return this.parseInsets(val.replace(/^\s+|\s+$/, '').split(/\s+/));
            }
        },

        getHtmlOptions: function () {
            var self = this,
                opts = {};
            $.each(this.$el[0].attributes, function (i, attr) {
                if (attr.name.indexOf(self.optionAttributePrefix) !== -1) {
                    opts[attr.name.substr(self.optionAttributePrefix.length)] = attr.value;
                }
            });
            return opts;
        },

        onScroll: function () {
            // TODO: We really want to not get the position multiple times for the same element.
            var
                oldPos = this._scrollPosition,
                pos = this.getScrollPosition(true),
                topChanged = !oldPos || oldPos.top !== pos.top,
                leftChanged = !oldPos || oldPos.top !== pos.top;

            if ((topChanged && this.options.vertical) || (leftChanged && this.options.horizontal)) {
                this.update(false);
            }
        },

        getScrollPosition: function (recalculate) {
            if (recalculate || !this._scrollPosition) {
                this._scrollPosition = {
                    top: this.$scrollingEl.scrollTop(),
                    left: this.$scrollingEl.scrollLeft()
                };
            }
            return this._scrollPosition;
        }
    };

    Parallaxin.getWindowSize = function (recalculate) {
        if (recalculate || !P._windowSize) {
            P._windowSize = {
                width: P.$win.width(),
                height: P.$win.height()
            };
        }
        return P._windowSize;
    };

    Parallaxin.onWindowResize = function () {
        var i, instance;
        P.getWindowSize(true);
        for (i = P.instances.length - 1; i >= 0; i -= 1) {
            instance = P.instances[i];
            if (instance.options.responsive) {
                instance.update();
            }
        }
    };

    $.extend($.fn, {
        parallaxin: function (optionsOrMethod) {
            var returnValue = this,
                args = arguments;
            this.each(function (i, el) {
                var $el = $(el),
                    plugin = $el.data('parallaxin'),
                    method = typeof optionsOrMethod === 'string' ? optionsOrMethod : null,
                    options = method === null ? optionsOrMethod || {} : {};

                if (!plugin) {
                    if (method) {
                        $.error('You can\'t call the parallaxin method "' + method
                                + '" without first initializing the plugin by calling '
                                + 'parallaxin() on the jQuery object.');
                    } else {
                        plugin = new Parallaxin().initialize($el, options);
                        $el.data('parallaxin', plugin);
                    }
                } else if (method) {
                    if (typeof plugin[method] !== 'function') {
                        $.error('Method "' + method + '" does not exist on jQuery.parallaxin');
                    } else {
                        // NOTE: If you call a method that returns a value, you will only get the result from the last item in the collection.
                        returnValue = plugin[method].apply(plugin, Array.prototype.slice.call(args, 1));
                    }
                }
            });
            return returnValue;
        }
    });
}(this.jQuery, window, document));
<|endoftext|>


<commit_before>var request = require('request');
var iconv = require('iconv-lite');
var cheerio = require('cheerio');
//传一个分类列表的URL，得到分类对象数组
/**
 * 1.向url发起http请求得到响应体
 * 2.分析响应体页面，得到对象数组
 */
exports.category = function(url,callback){
    //向服务器发起请求，不指定编码，得到响应的buffer
    request({url,encoding:null},function(err,response,body){
      //如果没有错误，并且响应的状态码是200的话
      if(!err && response.statusCode == 200){
          //把响应的buffer转成字符串
          body = iconv.decode(body,'gbk');
          //把字符串转成$对象
          var $ = cheerio.load(body);
          var items = [];
          //用筛选符把对应的元素提取成一个集合，然后对这个集合进行迭代
          $('.hd .title a').each(function(){
              //惊悚电影
              var $this = $(this);//把原生的DOM对象转成jquery对象
              var href = $this.attr('href');//取出href属性
              var result = href.match(/buzz\?b=(\d+)/);//匹配正则提取id
              var item = {
                  id:parseInt(result[1]),//返回值的第二个元素就是第一个分组，也就是想找的ID
                  name:$this.text(),//名称
                  url:`http://top.baidu.com/buzz?b=${id}&c=1&fr=topcategory_c1`//此URL地址用来进行下一步请求
              }
              items.push(item);//向数组中添加此元素
          });
          callback(null,items);//把数组传递给回调函数
      }else{
          callback(err);
      }
    })
}

//传入一个分类下的电影列表，得到电影对象数组
exports.movie = function(url,callback){

}

exports.category('http://top.baidu.com/category?c=1&fr=topindex',function(err,items){
        console.log(items);
});<commit_msg>socket.io<commit_after>var request = require('request');
var iconv = require('iconv-lite');
var cheerio = require('cheerio');
//传一个分类列表的URL，得到分类对象数组
/**
 * 1.向url发起http请求得到响应体
 * 2.分析响应体页面，得到对象数组
 */
exports.category = function(url,callback){
    //向服务器发起请求，不指定编码，得到响应的buffer
    request({url,encoding:null},function(err,response,body){
      //如果没有错误，并且响应的状态码是200的话
      if(!err && response.statusCode == 200){
          //把响应的buffer转成字符串
          body = iconv.decode(body,'gbk');
          //把字符串转成$对象
          var $ = cheerio.load(body);
          var items = [];
          //用筛选符把对应的元素提取成一个集合，然后对这个集合进行迭代
          $('.hd .title a').each(function(){
              //惊悚电影
              var $this = $(this);//把原生的DOM对象转成jquery对象
              var href = $this.attr('href');//取出href属性
              var result = href.match(/buzz\?b=(\d+)/);//匹配正则提取id
              var id = parseInt(result[1]);
              var item = {
                  id:id,//返回值的第二个元素就是第一个分组，也就是想找的ID
                  name:$this.text(),//名称
                  url:`http://top.baidu.com/buzz?b=${id}&c=1&fr=topcategory_c1`//此URL地址用来进行下一步请求
              }
              items.push(item);//向数组中添加此元素
          });
          callback(null,items);//把数组传递给回调函数
      }else{
          callback(err);
      }
    })
}

//传入一个分类下的电影列表，得到电影对象数组
exports.movie = function(url,callback){

}

exports.category('http://top.baidu.com/category?c=1&fr=topindex',function(err,items){
        console.log(items);
});<|endoftext|>


<commit_before>/*!
 * PopConfirm 0.4.4
 * http://ifnot.github.io/PopConfirm/
 *
 * Use jQuery & Bootstrap
 * http://jquery.com/
 * http://getbootstrap.com/
 *
 * Copyright 2014 Anael Favre and other contributors
 * Released under the MIT license
 * https://raw.github.com/AnaelFavre/PopConfirm/master/LICENCE
 *
 * Thanks to contributors :
 * Thomas Hanson https://github.com/diresquirrel
 * Mohamed Aymen https://github.com/kernel64
 * Muhammad Ubaid Raza https://github.com/mubaidr
 */

(function ($) {
  'use strict';
  /*global jQuery, $*/
  /*jslint nomen: true, evil: true*/
  $.fn.extend({
    popConfirm: function (options) {
      var defaults = {
          title: 'Confirmation',
          content: 'Are you really sure ?',
          placement: 'right',
          container: 'body',
          yesBtn: 'Yes',
          noBtn: 'No'
        },
        last = null;
      options = $.extend(defaults, options);
      return this.each(function () {
        var self = $(this),
          arrayActions = [],
          arrayDelegatedActions = [],
          eventToConfirm,
          optName,
          optValue,
          i,
          elmType,
          code,
          form;

        // Load data-* attriutes
        for (optName in options) {
          if (options.hasOwnProperty(optName)) {
            optValue = $(this).attr('data-confirm-' + optName);
            if (optValue) {
              options[optName] = optValue;
            }
          }
        }

        // If there are jquery click events
        if (jQuery._data(this, "events") && jQuery._data(this, "events").click) {

          // Save all click handlers
          for (i = 0; i < jQuery._data(this, "events").click.length; i = i + 1) {
            arrayActions.push(jQuery._data(this, "events").click[i].handler);
          }

          // unbind it to prevent it firing
          $(self).unbind("click");
        }

        // If there are jquery delegated click events
        if (self.data('remote') && jQuery._data(document, "events") && jQuery._data(document, "events").click) {

          // Save all delegated click handlers that apply
          for (i = 0; i < jQuery._data(document, "events").click.length; i = i + 1) {
            elmType = self[0].tagName.toLowerCase();
            if (jQuery._data(document, "events").click[i].selector && jQuery._data(document, "events").click[i].selector.indexOf(elmType + "[data-remote]") !== -1) {
              arrayDelegatedActions.push(jQuery._data(document, "events").click[i].handler);
            }
          }
        }

        // If there are hard onclick attribute
        if (self.attr('onclick')) {
          // Extracting the onclick code to evaluate and bring it into a closure
          code = self.attr('onclick');
          arrayActions.push(function () {
            eval(code);
          });
          $(self).prop("onclick", null);
        }

        // If there are href link defined
        if (!self.data('remote') && self.attr('href')) {
          // Assume there is a href attribute to redirect to
          arrayActions.push(function () {
            window.location.href = self.attr('href');
          });
        }

        // If the button is a submit one
        if (self.attr('type') && self.attr('type') === 'submit') {
          // Get the form related to this button then store submiting in closure
          form = $(this).parents('form:first');
          arrayActions.push(function () {
            // Add the button name / value if specified
            if(typeof self.attr('name') !== "undefined") {
              $('<input type="hidden">').attr('name', self.attr('name')).attr('value', self.attr('value')).appendTo(form);
            }
            form.submit();
          });
        }

        self.popover({
          trigger: 'manual',
          title: options.title,
          html: true,
          placement: options.placement,
          container: options.container,
          //Avoid using multiline strings, no support in older browsers.
          content: options.content + '
<button type="button" class="btn btn-small confirm-dialog-btn-abort">' + options.noBtn + '</button> <button type="button" class="btn btn-small btn-danger confirm-dialog-btn-confirm">' + options.yesBtn + '</button>
'
        }).click(function (e) {
          if (last && last !== self) {
            last.popover('hide').removeClass('popconfirm-active');
          }
          last = self;
        });

        $(document).on('click', function () {
          if (last) {
            last.popover('hide').removeClass('popconfirm-active');
          }
        });

        self.bind('click', function (e) {
          eventToConfirm = e;

          e.preventDefault();
          e.stopPropagation();

          $('.popconfirm-active').not(self).popover('hide').removeClass('popconfirm-active');
          self.popover('show').addClass('popconfirm-active');

          $(document).find('.popover .confirm-dialog-btn-confirm').one('click', function (e) {
            for (i = 0; i < arrayActions.length; i = i + 1) {
              arrayActions[i].apply(self);
            }

            for (i = 0; i < arrayDelegatedActions.length; i = i + 1) {
              arrayDelegatedActions[i].apply(self, [eventToConfirm.originalEvent]);
            }

            self.popover('hide').removeClass('popconfirm-active');
          });
          $(document).find('.popover .confirm-dialog-btn-abord').bind('click', function (e) {
            self.popover('hide').removeClass('popconfirm-active');
          });
        });
      });
    }
  });
}(jQuery));
<commit_msg>version bump<commit_after>/*!
 * PopConfirm 0.4.5
 * http://ifnot.github.io/PopConfirm/
 *
 * Use jQuery & Bootstrap
 * http://jquery.com/
 * http://getbootstrap.com/
 *
 * Copyright 2014 Anael Favre and other contributors
 * Released under the MIT license
 * https://raw.github.com/AnaelFavre/PopConfirm/master/LICENCE
 *
 * Thanks to contributors :
 * Thomas Hanson https://github.com/diresquirrel
 * Mohamed Aymen https://github.com/kernel64
 * Muhammad Ubaid Raza https://github.com/mubaidr
 */

(function ($) {
  'use strict';
  /*global jQuery, $*/
  /*jslint nomen: true, evil: true*/
  $.fn.extend({
    popConfirm: function (options) {
      var defaults = {
          title: 'Confirmation',
          content: 'Are you really sure ?',
          placement: 'right',
          container: 'body',
          yesBtn: 'Yes',
          noBtn: 'No'
        },
        last = null;
      options = $.extend(defaults, options);
      return this.each(function () {
        var self = $(this),
          arrayActions = [],
          arrayDelegatedActions = [],
          eventToConfirm,
          optName,
          optValue,
          i,
          elmType,
          code,
          form;

        // Load data-* attriutes
        for (optName in options) {
          if (options.hasOwnProperty(optName)) {
            optValue = $(this).attr('data-confirm-' + optName);
            if (optValue) {
              options[optName] = optValue;
            }
          }
        }

        // If there are jquery click events
        if (jQuery._data(this, "events") && jQuery._data(this, "events").click) {

          // Save all click handlers
          for (i = 0; i < jQuery._data(this, "events").click.length; i = i + 1) {
            arrayActions.push(jQuery._data(this, "events").click[i].handler);
          }

          // unbind it to prevent it firing
          $(self).unbind("click");
        }

        // If there are jquery delegated click events
        if (self.data('remote') && jQuery._data(document, "events") && jQuery._data(document, "events").click) {

          // Save all delegated click handlers that apply
          for (i = 0; i < jQuery._data(document, "events").click.length; i = i + 1) {
            elmType = self[0].tagName.toLowerCase();
            if (jQuery._data(document, "events").click[i].selector && jQuery._data(document, "events").click[i].selector.indexOf(elmType + "[data-remote]") !== -1) {
              arrayDelegatedActions.push(jQuery._data(document, "events").click[i].handler);
            }
          }
        }

        // If there are hard onclick attribute
        if (self.attr('onclick')) {
          // Extracting the onclick code to evaluate and bring it into a closure
          code = self.attr('onclick');
          arrayActions.push(function () {
            eval(code);
          });
          $(self).prop("onclick", null);
        }

        // If there are href link defined
        if (!self.data('remote') && self.attr('href')) {
          // Assume there is a href attribute to redirect to
          arrayActions.push(function () {
            window.location.href = self.attr('href');
          });
        }

        // If the button is a submit one
        if (self.attr('type') && self.attr('type') === 'submit') {
          // Get the form related to this button then store submiting in closure
          form = $(this).parents('form:first');
          arrayActions.push(function () {
            // Add the button name / value if specified
            if(typeof self.attr('name') !== "undefined") {
              $('<input type="hidden">').attr('name', self.attr('name')).attr('value', self.attr('value')).appendTo(form);
            }
            form.submit();
          });
        }

        self.popover({
          trigger: 'manual',
          title: options.title,
          html: true,
          placement: options.placement,
          container: options.container,
          //Avoid using multiline strings, no support in older browsers.
          content: options.content + '
<button type="button" class="btn btn-small confirm-dialog-btn-abort">' + options.noBtn + '</button> <button type="button" class="btn btn-small btn-danger confirm-dialog-btn-confirm">' + options.yesBtn + '</button>
'
        }).click(function (e) {
          if (last && last !== self) {
            last.popover('hide').removeClass('popconfirm-active');
          }
          last = self;
        });

        $(document).on('click', function () {
          if (last) {
            last.popover('hide').removeClass('popconfirm-active');
          }
        });

        self.bind('click', function (e) {
          eventToConfirm = e;

          e.preventDefault();
          e.stopPropagation();

          $('.popconfirm-active').not(self).popover('hide').removeClass('popconfirm-active');
          self.popover('show').addClass('popconfirm-active');

          $(document).find('.popover .confirm-dialog-btn-confirm').one('click', function (e) {
            for (i = 0; i < arrayActions.length; i = i + 1) {
              arrayActions[i].apply(self);
            }

            for (i = 0; i < arrayDelegatedActions.length; i = i + 1) {
              arrayDelegatedActions[i].apply(self, [eventToConfirm.originalEvent]);
            }

            self.popover('hide').removeClass('popconfirm-active');
          });
          $(document).find('.popover .confirm-dialog-btn-abord').bind('click', function (e) {
            self.popover('hide').removeClass('popconfirm-active');
          });
        });
      });
    }
  });
}(jQuery));
<|endoftext|>


<commit_before>;
(function ($, window, document, undefined)
{

    "use strict";
    var defaults = {
        buttonClass: "sort-scroll-button",
        sortableClass: "sort-scroll-element",
        movingClass: "sort-scroll-moving",
        animationDuration: 1000,
        easing: "swing"
    };

    function SortScroll(container, options)
    {
        this.settings = $.extend({}, defaults, options);
        this.container = $(container);
        this._moving = false;
        this.init();
    }

    $.extend(SortScroll.prototype, {
        init: function ()
        {
            var self = this;
            self.container.on("click", "." + self.settings.buttonClass, function (event)
            {
                var button = $(this),
                    elementCollection = self.container.find('.' + self.settings.sortableClass),
                    initialOrder = elementCollection.index(button.closest("." + self.settings.sortableClass)),
                    orderModify = button.data("direction");
                event.preventDefault();
                self.moveElement(initialOrder, orderModify);
            })
        },
        moveElement: function (initialOrder, orderModify)
        {
            var self = this,
                elementCollection = self.container.find('.' + self.settings.sortableClass),
                maxOrder = elementCollection.length - 1,
                destinationOrder = initialOrder + orderModify;

            if (destinationOrder < 0 || destinationOrder > maxOrder)
            {
                return false;
            }

            var element = elementCollection.eq(initialOrder);

            if (self._moving)
            {
                self.container.one("sortScroll.sortEnd", function (event, element, initialOrder, destinationOrder)
                {
                    self.moveElement(destinationOrder, orderModify);
                });
                return false;
            }

            self.container.trigger("sortScroll.sortStart", [element, initialOrder, destinationOrder]);
            self._moving = true;

            var marginTop = parseInt(element.css('margin-top'), 10),
                height = element.outerHeight(true),
                destinationElement,
                removeDestination = false;

            if (destinationOrder === maxOrder)
            {
                destinationElement = $("<div/>").addClass(self.settings.sortableClass);
                elementCollection.last().after(destinationElement);
                removeDestination = true;
            }
            else
            {
                var destinationElementOrder = destinationOrder;
                if (destinationElementOrder > initialOrder)
                {
                    destinationElementOrder = destinationElementOrder + 1;
                }
                destinationElement = elementCollection.eq(destinationElementOrder);
            }

            if (removeDestination)
            {
                destinationElement.hide();
            }

            var finalY = destinationElement.offset().top,
                maxScroll = Math.max(0, document.documentElement.scrollHeight - document.documentElement.clientHeight),
                initialScroll = $(window).scrollTop(),
                initialY = element.offset().top;

            if (finalY > initialY)
            {
                finalY -= element.outerHeight(true);
            }

            var relativeY = finalY - initialY,
                finalScroll = initialScroll + relativeY,
                move = 0;

            if (finalScroll > maxScroll)
            {
                move = finalScroll - maxScroll;
                finalScroll = maxScroll;
            }

            if (finalScroll < 0)
            {
                move = finalScroll;
                finalScroll = 0;
            }

            var initialGhost = $('<div/>').css({
                    height: height,
                    visibility: "hidden"
                }).insertAfter(element),
                finalGhost = $('<div/>').css({
                    height: 0
                }).insertBefore(destinationElement),
                initialCssPosition = element.css("position"),
                width = element.css("width"),
                fixedY = initialY - initialScroll - marginTop,
                duration = self.settings.animationDuration,
                easing = self.settings.easing;

            element.css("position", "fixed");
            element.css("width", width);
            element.css("top", fixedY + "px");
            element.addClass(self.settings.movingClass);

            initialGhost.animate({
                height: 0
            }, duration, "swing");
            finalGhost.animate({
                height: height + "px"
            }, duration, easing);
            $("html, body").animate({scrollTop: finalScroll + "px"}, duration, easing);
            if (move != 0)
            {
                element.animate({top: fixedY + move}, duration, easing)
            }

            elementCollection.add($("html, body")).promise().done(function ()
            {
                initialGhost.remove();
                finalGhost.remove();
                element.removeClass(self.settings.movingClass);
                element.css("position", initialCssPosition);
                element.css("top", 0);
                destinationElement.before(element);
                if (removeDestination)
                {
                    destinationElement.remove();
                }
                self._moving = false;
                self.container.trigger("sortScroll.sortEnd", [element, initialOrder, destinationOrder]);
            });

        }
    });

    $.fn.sortScroll = function (opt)
    {
        var args = Array.prototype.slice.call(arguments, 1);
        return this.each(function ()
        {
            var item = $(this), instance = item.data('sortScroll');
            if (!instance)
            {
                item.data('sortScroll', new SortScroll(this, opt));
            } else
            {
                if (typeof opt === 'string')
                {
                    instance[opt].apply(instance, args);
                }
            }
        });
    }

})(jQuery, window, document);

$(".sort-scroll").each(function ()
{
    $(this).sortScroll();
});<commit_msg>Changed buttons behavior<commit_after>;
(function ($, window, document, undefined)
{

    "use strict";
    var defaults = {
        buttonClassUp: "sort-scroll-button-up",
        buttonClassDown: "sort-scroll-button-down",
        sortableClass: "sort-scroll-element",
        movingClass: "sort-scroll-moving",
        animationDuration: 1000,
        easing: "swing"
    };

    function SortScroll(container, options)
    {
        this.settings = $.extend({}, defaults, options);
        this.container = $(container);
        this._moving = false;
        this.init();
    }

    $.extend(SortScroll.prototype, {
        init: function ()
        {
            var self = this;
            console.log("." + self.settings.buttonClassUp + ", ." + self.settings.buttonClassDown);
            self.container.on("click", "." + self.settings.buttonClassUp + ", ." + self.settings.buttonClassDown, function (event)
            {
                var button = $(this),
                    elementCollection = self.container.find('.' + self.settings.sortableClass),
                    initialOrder = elementCollection.index(button.closest("." + self.settings.sortableClass)),
                    orderModify;
                (button.hasClass(self.settings.buttonClassUp)) ? orderModify = -1 : 1;
                event.preventDefault();
                self.moveElement(initialOrder, orderModify);
            })
        },
        moveElement: function (initialOrder, orderModify)
        {
            var self = this,
                elementCollection = self.container.find('.' + self.settings.sortableClass),
                maxOrder = elementCollection.length - 1,
                destinationOrder = initialOrder + orderModify;

            if (destinationOrder < 0 || destinationOrder > maxOrder)
            {
                return false;
            }

            var element = elementCollection.eq(initialOrder);

            if (self._moving)
            {
                self.container.one("sortScroll.sortEnd", function (event, element, initialOrder, destinationOrder)
                {
                    self.moveElement(destinationOrder, orderModify);
                });
                return false;
            }

            self.container.trigger("sortScroll.sortStart", [element, initialOrder, destinationOrder]);
            self._moving = true;

            var marginTop = parseInt(element.css('margin-top'), 10),
                height = element.outerHeight(true),
                destinationElement,
                removeDestination = false;

            if (destinationOrder === maxOrder)
            {
                destinationElement = $("<div/>").addClass(self.settings.sortableClass);
                elementCollection.last().after(destinationElement);
                removeDestination = true;
            }
            else
            {
                var destinationElementOrder = destinationOrder;
                if (destinationElementOrder > initialOrder)
                {
                    destinationElementOrder = destinationElementOrder + 1;
                }
                destinationElement = elementCollection.eq(destinationElementOrder);
            }

            if (removeDestination)
            {
                destinationElement.hide();
            }

            var finalY = destinationElement.offset().top,
                maxScroll = Math.max(0, document.documentElement.scrollHeight - document.documentElement.clientHeight),
                initialScroll = $(window).scrollTop(),
                initialY = element.offset().top;

            if (finalY > initialY)
            {
                finalY -= element.outerHeight(true);
            }

            var relativeY = finalY - initialY,
                finalScroll = initialScroll + relativeY,
                move = 0;

            if (finalScroll > maxScroll)
            {
                move = finalScroll - maxScroll;
                finalScroll = maxScroll;
            }

            if (finalScroll < 0)
            {
                move = finalScroll;
                finalScroll = 0;
            }

            var initialGhost = $('<div/>').css({
                    height: height,
                    visibility: "hidden"
                }).insertAfter(element),
                finalGhost = $('<div/>').css({
                    height: 0
                }).insertBefore(destinationElement),
                initialCssPosition = element.css("position"),
                width = element.css("width"),
                fixedY = initialY - initialScroll - marginTop,
                duration = self.settings.animationDuration,
                easing = self.settings.easing;

            element.css("position", "fixed");
            element.css("width", width);
            element.css("top", fixedY + "px");
            element.addClass(self.settings.movingClass);

            initialGhost.animate({
                height: 0
            }, duration, easing);
            finalGhost.animate({
                height: height + "px"
            }, duration, easing);
            $("html, body").animate({scrollTop: finalScroll + "px"}, duration, easing);
            if (move != 0)
            {
                element.animate({top: fixedY + move}, duration, easing)
            }

            elementCollection.add($("html, body")).promise().done(function ()
            {
                initialGhost.remove();
                finalGhost.remove();
                element.removeClass(self.settings.movingClass);
                element.css("position", initialCssPosition);
                element.css("top", 0);
                destinationElement.before(element);
                if (removeDestination)
                {
                    destinationElement.remove();
                }
                self._moving = false;
                self.container.trigger("sortScroll.sortEnd", [element, initialOrder, destinationOrder]);
            });

        }
    });

    $.fn.sortScroll = function (opt)
    {
        var args = Array.prototype.slice.call(arguments, 1);
        return this.each(function ()
        {
            var item = $(this), instance = item.data('sortScroll');
            if (!instance)
            {
                item.data('sortScroll', new SortScroll(this, opt));
            } else
            {
                if (typeof opt === 'string')
                {
                    instance[opt].apply(instance, args);
                }
            }
        });
    }

})(jQuery, window, document);

$(".sort-scroll").each(function ()
{
    $(this).sortScroll();
});<|endoftext|>


<commit_before>/**
 * jQuery plugin for adding, removing and making changes to CSS rules
 *
 * @author Vimal Aravindashan
 * @version 0.3.6
 * @licensed MIT license
 */
(function ($) {
	var	_ahref = $(document.createElement('a')), /**< <a> tag used for evaluating hrefs */
		_styles = _ahref.prop('style'), /**< Collection of styles available on the host */
		_sheet = function(s) {
			return s.sheet || s.styleSheet;
		}($('<style type="text/css">*{}</style>').appendTo('head')[0]), /**< StyleSheet for adding new rules*/
		_rules = ('cssRules' in _sheet) ? 'cssRules' : 'rules', /**< Attribute name for rules collection in a stylesheet */
		vendorPrefixes = ["Webkit", "O", "Moz", "ms"]; /**< Case sensitive list of vendor specific prefixes */

	/**
	 * @function filterStyleSheet
	 * Filter a stylesheet based on accessibility and, ID or location
	 * @param {String} filter Filter to be applied. id or href of the style element can be used as filters.
	 * @param {CSSStyleSheet} styleSheet StyleSheet to be filtered
	 * @returns {Boolean} true if styleSheet matches the filter, false otherwise
	 */
	function filterStyleSheet(filter, styleSheet) {
		try {
			if(styleSheet[_rules]) {
				filter = filter || '';
				var node = $(styleSheet.ownerNode || styleSheet.owningElement);
				return (filter === '') || (filter === '*') ||
					('#'+(node.prop('id') || '') == filter) ||
					((node.prop('href') || '') == _ahref.prop('href', filter).prop('href'));
			} else {
				return false;
			}
		} catch(e) {
			return false;
		}
	}

	/**
	 * @function parseSelector
	 * Splits a jQuery.stylesheet compatible selector into stylesheet filter and selector text
	 * @param {String} selector Selector text to be parsed
	 * @returns {Object} object with two properties 'styleSheet' and 'selectorText'
	 */
	function parseSelector(selector) {
		var styleSheet = (/.*?{/.exec(selector) || ['{'])[0],
			selectorText = /{.*}/g.exec(selector); //TODO: replace selector with dict object
		if(selectorText === null) {
			var parts = selector.split('{');
			selectorText = '{'+parts[parts.length==1 ? 0 : 1].split('}')[0]+'}';
		} else {
			selectorText = selectorText[0];
		}
		return {
			styleSheet: $.trim(styleSheet.substr(0, styleSheet.length-1)),
			selectorText: normalizeSelector(selectorText.substr(1, selectorText.length-2))
		};
	}

	/**
	 * @function normalizeSelector
	 * Normalizes selectorText to work cross-browser
	 * @param {String} selectorText selector string to normalize
	 * @returns {String} normalized selector string
	 */
	function normalizeSelector(selectorText) {
		var selector = [], last, len;
		last = _sheet[_rules].length;
		insertRule.call(_sheet, selectorText, ';'); //NOTE: IE doesn't seem to mind ';' as non-empty
		len = _sheet[_rules].length;
		for(var i=len-1; i>=last; i--) {
			selector.push(_sheet[_rules][i].selectorText);
			deleteRule.call(_sheet, i);
		}
		return selector.reverse().join(', ');
	}

	/**
	 * @function matchSelector
	 * Matches given selector to selectorText of cssRule
	 * @param {CSSStyleRule} cssRule to match with
	 * @param {String} selectorText selector string to compare
	 * @param {Boolean} matchGroups when true, selector is matched in grouped style rules
	 * @returns true if selectorText of cssRule matches given selector, false otherwise
	 */
	function matchSelector(cssRule, selectorText, matchGroups) {
		if($.type(cssRule.selectorText) !== 'string') {
			return false;
		}

		if(cssRule.selectorText === selectorText) {
			return true;
		} else if (matchGroups === true) {
			return $($.map(cssRule.selectorText.split(','), $.trim)).filter(function(i) {
				return this.toString() === selectorText;
			}).length > 0;
		} else {
			return false;
		}
	}

	/**
	 * @function vendorPropName
	 * Vendor prefixed style property name.
	 * Based on similar function in jQuery library.
	 * @param {String} name camelCased CSS property name
	 * @returns {String} Vendor specific tag prefixed style name
	 * if found in styles, else passed name as-is
	 * @see vendorPrefixes
	 * @see _styles
	 */
	function vendorPropName(name) {
		var titleName = name[0].toUpperCase() + name.slice(1),
			styleName, i = vendorPrefixes.length;
		while( --i ) {
			styleName = vendorPrefixes[i] + titleName;
			if(styleName in _styles) {
				return styleName;
			}
		}
		return name;
	}

	/**
	 * @function normalizeRule
	 * Normalizes the CSSStyleRule object to work better across browsers
	 * @param {CSSStyleRule} rule CSSStyleRule object to be normalized
	 * @param {StyleSheet} styleSheet parent stylesheet of the rule
	 * @returns {CSSStyleRule} normalized CSSStyleRule
	 */
	function normalizeRule(rule, styleSheet) {
		//NOTE: this is experimental, however, it does have it's benefits
		//      for use with $.animate(), be sure to include jquery.stylesheet-animate.js as well
		//TODO: move some of the defaults used here to user options
		rule.ownerDocument = rule.ownerDocument || document; //XXX: Hack for jQuery.isHidden()
		rule.nodeType = rule.nodeType || 1; //XXX: Hack for jQuery's defaultPrefilter()
		rule.nodeName = rule.nodeName || 'DIV'; //XXX: Hack for jQuery's acceptData()
		rule.parentNode = rule.parentNode || styleSheet.ownerNode || styleSheet.owningElement; //XXX: Hack for jQuery.contains()
		rule.parentStyleSheet = rule.parentStyleSheet || styleSheet; //XXX: Fix for IE7
		return rule;
	}
	/*
	 * Checking for 'instanceof CSSStyleRule' fails in IE7 but not in IE8, however, the call to normalizeRule() fails in both.
	 * So, we will define our custom CSSStyleRule class on all browsers where normalizeRule() fails.
	 */
	try {
		normalizeRule(_sheet[_rules][0], _sheet);
		$.support.nativeCSSStyleRule = true;
	} catch(e) {
		$.support.nativeCSSStyleRule = false;
		CSSStyleRule = function(rule) {
			$.extend(this, rule);
			this.rule = rule; //XXX: deleteRule() requires the original object
			this.currentStyle = rule.style; //XXX: Hack for jQuery's curCSS()/getStyles() for IE7
		};
	}

	/**
	 * @function insertRule
	 * Cross-browser function for inserting rules
	 * @param {String} selector selectorText for the rule
	 * @param {String} css CSS property-value pair string
	 * @param {Number} index Index position to insert the string;
	 * defaults to end of rules collection
	 */
	function insertRule(selector, css, index) {
		if(!selector || !css) {
			return -1; //NOTE: IE does not like addRule(selector,'',index)
		}
		var self = this,
			_insfn = self.insertRule ? function (selector, css, index) { this.insertRule(selector+'{'+css+'}', index); } : self.addRule;
		index = index || this[_rules].length;
		try {
			return _insfn.call(self, selector, css, index);
		} catch(e) {
			$.each(selector.split(','), function(i, sel) {
				_insfn.call(self, $.trim(sel), css);
			});
			return -1;
		}
	}

	/**
	 * @function deleteRule
	 * Cross-browser function for deleting rules
	 * @param {Number|CSSStyleRule} Index of rule to be deleted, or
	 * reference to rule to be deleted from rules collection
	 */
	function deleteRule(rule) {
		//NOTE: If we are using our custom CSSStyleRule, then CSSStyleRule.rule is the real style rule object
		rule = (rule && rule.rule) ? rule.rule : rule;
		if(!rule) {
			return;
		}
		var self = this,
			_delfn = self.deleteRule || self.removeRule;
		if(!_delfn) { //NOTE: IE7 has issues with rule.parentStyleSheet, so we need to search for the parent stylesheet
			$(document.styleSheets).each(function (i, styleSheet) {
				if($(styleSheet[_rules]).filter(function() {return this === rule;}).length == 1) {
					self = styleSheet;
					_delfn = self.deleteRule || self.removeRule;
					return false;
				}
			});
		}
		if($.type(rule) == 'number') {
			_delfn.call(self, rule);
		} else {
			$.each(self[_rules], function (i, _rule) {
				if(rule === _rule) {
					_delfn.call(self, i);
					return false;
				}
			});
		}
	}

	/**
	 * jQuery.stylesheet
	 *
	 * Constructor/Factory method for initializing a jQuery.stylesheet object.
	 * Includes a short-cut to apply style changes immediately.
	 * @param {String} selector CSS rule selector text with optional stylesheet filter
	 * @param {String|Array|Object} name Name of style property to get/set.
	 * Also accepts array of property names and object of name/value pairs.
	 * @param {String} value If defined, then value of the style property
	 * is updated with it. Unused when name is an object map.
	 * @returns {jQuery.stylesheet|String|Object} A new jQuery.stylesheet object
	 * if name/value is not passed, or value of property or object of name/value pairs
	 */
	$.stylesheet = function (selector, name, value) {
		if(!(this instanceof $.stylesheet)) {
			return new $.stylesheet(selector, name, value);
		}

		this.init(selector);
		return this.css(name, value);
	};

	$.extend($.stylesheet, {
		/**
		 * @function jQuery.stylesheet.cssRules
		 * @param {String} selector CSS rule selector text with optional stylesheet filter
		 * @returns {Array} Array of CSSStyleRule objects that match the selector text
		 * and pass the stylesheet filter
		 */
		cssRules: function (selector) {
			var rules = [],
				filters = parseSelector(selector);
			//NOTE: The stylesheet filter will be treated as case-sensitive
			//      The selectorText filter's case depends on the browser
			$(document.styleSheets).each(function (i, styleSheet) {
				if(filterStyleSheet(filters.styleSheet, styleSheet)) {
					$.merge(rules, $(styleSheet[_rules]).filter(function() {
						return matchSelector(this, filters.selectorText, filters.styleSheet === '*');
					}).map(function() {
						return normalizeRule($.support.nativeCSSStyleRule ? this : new CSSStyleRule(this), styleSheet);
					}));
				}
			});
			return rules.reverse();
		},

		/**
		 * @function jQuery.stylesheet.camelCase
		 * jQuery.camelCase is undocumented and could be removed at any point
		 * @param {String} str Hypenated string to be camelCased
		 * @returns {String} camelCased string
		 */
		camelCase: $.camelCase || function( str ) {
			return str.replace(/-([\da-z])/g, function(a){return a.toUpperCase().replace('-','');});
		},

		/**
		 * Normalized CSS property names
		 * jQuery.cssProps is undocumented and could be removed at any point
		 */
		cssProps: $.cssProps || {},

		/**
		 * @function jQuery.styesheet.cssStyleName
		 * @param {String} name Hypenated CSS property name
		 * @returns {String} camelCased or vendor specific name if found in host styles
		 */
		cssStyleName: function (name) {
			if(name) {
				var camelcasedName = $.camelCase(name);
				if(camelcasedName in _styles) {
					return camelcasedName;
				} else if (($.cssProps[name] || ($.cssProps[name] = vendorPropName(camelcasedName))) in _styles) {
					return $.cssProps[name];
				}
			}
		}
	});

	$.stylesheet.fn = $.stylesheet.prototype = {
		/**
		 * @function jQuery.stylesheet.fn.init
		 * Initializes a jQuery.stylesheet object.
		 * Selects a list of applicable CSS rules for given selector.
		 * @see jQuery.stylesheet.cssRules
		 * @param {String|Array|Object} selector CSS rule selector text(s)
		 * with optional stylesheet filter(s)
		 */
		init: function (selector) {
			var rules = []; /**< Array of CSSStyleRule objects matching the selector initialized with */

			switch($.type(selector)) {
			case 'string':
				rules = $.stylesheet.cssRules(selector);
				break;
			case 'array':
				$.each(selector, function (idx, val) {
					if($.type(val) === 'string') {
						$.merge(rules, $.stylesheet.cssRules(val));
					} else if(val instanceof CSSStyleRule) {
						rules.push(val);
					}
				});
				break;
			case 'object':
				if(selector instanceof CSSStyleRule) {
					rules.push(val);
				}
				break;
			}

			$.extend(this, {
				/**
				 * @function jQuery.stylesheet.rules
				 * @returns {Array} Copy of array of CSSStyleRule objects used
				 * by this instance of jQuery.stylesheet
				 */
				rules: function() {
					return rules.slice();
				},

				/**
				 * @function jQuery.stylesheet.css()
				 * @param {String|Array|Object} name Name of style property to get/set.
				 * Also accepts array of property names and object of name/value pairs.
				 * @param {String} value If defined, then value of the style property
				 * is updated with it. Unused when name is an object map.
				 * @returns {jQuery.stylesheet|String|Object} A new jQuery.stylesheet object
				 * if name/value is not passed, or value of property or object of name/value pairs
				 */
				css: function (name, value) {
					var self = this, styles = undefined;

					switch($.type(name)) {
					case 'null':
						$.each(rules, function (idx, rule) {
							deleteRule.call(rule.parentStyleSheet, rule);
						});
						//NOTE: Safari seems to replace the rules collection object on insert/delete
						//      Refresh our private collection to reflect the changes
						rules = $.stylesheet.cssRules(selector);
						return self;
					case 'string':
						var stylename = $.stylesheet.cssStyleName(name);
						if(stylename) {
							if(rules.length === 0 && value !== undefined) {
								var filters = parseSelector(selector),
									sheet = $(document.styleSheets).filter(function () {
										return filterStyleSheet(filters.styleSheet, this);
									});
								sheet = (sheet && sheet.length == 1) ? sheet[0] : _sheet;
								insertRule.call(sheet, filters.selectorText, name+':'+value+';');
								//NOTE: See above note on Safari
								//      Also, IE has different behaviour for grouped selectors
								rules = $.stylesheet.cssRules(selector);
								styles = self;
							} else {
								$.each(rules, function (i, rule) {
									if(rule.style[stylename] !== '') {
										if(value !== undefined) {
											rule.style[stylename] = value;
											styles = self;
										} else {
											styles = rule.style[stylename];
										}
										return false;
									}
								});
								if(styles === undefined && value !== undefined) {
									rules[0].style[stylename] = value;
									styles = self;
								}
							}
						}
						break;
					case 'array':
						styles = {};
						$.each(name, function (idx, key) {
							styles[key] = self.css(key, value);
						});
						if(value !== undefined) {
							styles = self;
						}
						break;
					case 'object':
						$.each(name, function (key, val) {
							self.css(key, val);
						});
						return self;
					default: /*undefined*/
						return self;
					}

					return styles;
				}
			});
		}
	};
})(jQuery);
<commit_msg>Delete jquery.stylesheet.js<commit_after><|endoftext|>


<commit_before>/**
 * @file jquery.tablechart.js
 *
 * A jQuery plugin that scrapes HTML tables and generates charts with jqPlot
 *
 * Created by David Eads (davideads__at__gmail_com), 2010 for the US
 * Department of Energy E-Center Project (see the project page at
 * https://ecenter.fnal.gov and the Google code project at
 * http://code.google.com/p/ecenter)
 *
 * Released under the Fermitools license (modified BSD). See LICENSE.txt for
 * more information.
 *
 * Requires jqPlot (http://www.jqplot.com)
 * Requires the jQuery UUID plugin (http://plugins.jquery.com/project/uuid)
 *
 * Usage and behavior notes:
 *
 * Invoke with $(selector).tablechart({options}), or simply
 * $(selector).tablechart()
 *
 * If the matched element returned by the selector is a table, a chart will
 * be generated from the table. If the matched element returned by the selector
 * contains one or more tables, all table data will be added to the chart.
 *
 * Using the default callbacks, data to be plotted should be wrapped in a proper
 *
 tag, and each row should provide a  tag that includes the x
 * value for the row. Each column will be added as a new chart series.
 *
 *

 *

 *

 *      x-axis label
 *      Series 1 label
 *      Series 2 label
 *

 *

 *

 *

 *       X value
 *       Series 1 (y) value
 *       Series 2 (y) value
 *

 *     ...
 *

 *

 *
 * If a
 is provided and the headerSeriesLabels option is true, the
 *
 row will be used to generate series labels unless specified in
 * plotOptions.
 *
 * A note about parsing X and Y values: The tablechart plugin provides two
 * trivial parsing callbacks for your parsing pleasure: $.tablechart.parseFloat
 * and $.tablechart.parseText.
 *
 * By default, all table data is assumed to be expressed as floats. You may
 * need to use the parseText helper function to account for dates, or define
 * your own parsing callback to strip out non-numeric characters or apply
 * special number formatting.
 *
 * All configurable callbacks are call()'d with a $.tablechart instance
 * provided as the calling context.
 *
 * Configuration options (no options are required):
 *
 *  hideTables: Hide source tables (default: false)
 *  height: Height of chart container (default: null)
 *  width: Width of chart container (default: null)
 *  chartName: Optional chart name. Override the chart name to create multiple
 *    charts from the same source tables (default: 'default')
 *  headerSeriesLabels: Use headers in thead section to name series
 *    (default: true)
 *  parseX: Callback to parse X values (default: $.tablechart.parseFloat)
 *  parseY: Callback to parse Y values (default: $.tablechart.parseFloat)
 *  scrapeSingle: Callback for scraping a single table
 *    (default: $.tablechart.scrapeSingle)
 *  scrapeMultiple: Callback for scraping multiple tables
 *    (default: $.tablechart.scrapeMultiple)
 *  processSeries: Callback for series post-processing (default: null)
 *  attachMethod: Callback for attaching chart
 *    (default: function(container) { $(this.el).before(container); })
 *  plotOptions: jqPlot options. See jqPlot options documentation for details:
 *    http://www.jqplot.com/docs/files/jqPlotOptions-txt.html
 *
 */

(function($) {

/**
 * Table chart plugin
 */
$.fn.tablechart = function(options) {
  var options = $.extend(true, {}, $.fn.tablechart.defaults, options);
  this.each(function(i) {
    var charts = $(this).data('tablechart') || {};
    if (charts[options.chartName] == undefined) {
      charts[options.chartName] = new $.tablechart(this, options);
      $(this).data('tablechart', charts);
    }
    charts[options.chartName].draw();
  });
  return this;
};

/**
 * Tablechart object constructor
 */
$.tablechart = function(el, options) {
  // Properties
  this.options = options;
  this.el = el;

  // Create container
  this.chartId = $.uuid('chart-');
  this.chartContainer = $('
').attr('id', this.chartId);

  if (options.height) { this.chartContainer.height(options.height); }
  if (options.width)  { this.chartContainer.width(options.width); }

  // Attach container
  options.attachMethod.call(this, this.chartContainer);
}

/**
 * Tablechart draw method
 */
$.tablechart.prototype.draw = function() {
  var tables;

  // Is matched element a table?
  if (!$.nodeName(this.el, 'table')) {
    tables = $('table', this.el);
    series = this.options.scrapeMultiple.call(this, tables);
  } else {
    tables = $(this.el);
    series = this.options.scrapeSingle.call(this, tables);
  }

  // Hide tables
  if (this.options.hideTables) {
    tables.hide();
  }

  // Apply any additional series processing
  if (this.options.processSeries) {
    series = this.options.processSeries.call(this, series);
  }

  // Draw chart
  if (series.length > 0) {
    this.chart = $.jqplot(this.chartId, series, this.options.plotOptions);
  }
}

/**
 * Utility function: Scrape single table for values
 */
$.tablechart.scrapeSingle = function(table) {
  var series = [];
  var options = this.options;
  var tablechart = this;

  if (options.headerSeriesLabels) {
    $(table).find('thead th:gt(0)').each(function(i) {
      options.plotOptions.series[i] = $.extend(
        {label: $(this).text()},
        options.plotOptions.series[i]
      );
    });
  }

  $(table).find('tbody tr').each(function() {
    var x = 0, y = 0;
    $(this).find('th').each(function() {
      x = options.parseX.call(tablechart, this);
    });
    $(this).find('td').each(function(i) {
      if (!series[i]) {
        series[i] = [];
      }
      y = options.parseY.call(tablechart, this);
      series[i].push([x, y]);
    });
  });

  return series;
}

/**
 * Utility function: Scrape multiple tables for values
 */
$.tablechart.scrapeMultiple = function(tables) {
  var series = [];
  var options = this.options;
  var tablechart = this;

  // Flip on magical "internal" option if scraping multiple
  if (options.headerSeriesLabels) {
    options.headerSeriesLabels = false;
    options.multitablesHeaderSeriesLabels = true;
  }

  var series_idx = 0;
  $(tables).each(function() {
    // Generate series labels (requires "global" counter)
    if (tablechart.options.multitablesHeaderSeriesLabels) {
      $(this).find('thead th:gt(0)').each(function() {
        options.plotOptions.series[series_idx] = $.extend(
          {label: $(this).text()},
          options.plotOptions.series[series_idx]
        );
        series_idx += 1;
      });
    }
    // Now simply scrape each matched table
    series = series.concat($.tablechart.scrapeSingle.call(tablechart, this));
  });

  return series;
}

/**
 * Utility function: Parse text
 */
$.tablechart.parseText = function(el) {
  return $(el).text();
}

/**
 * Utility function: Parse text to floating point
 */
$.tablechart.parseFloat = function(el) {
  return parseFloat($(el).text());
}

/**
 * Defaults
 */
$.fn.tablechart.defaults = {
  hideTables: false,
  height: null,
  width: null,
  chartName: 'default',
  headerSeriesLabels: true,
  parseX: $.tablechart.parseFloat,
  parseY: $.tablechart.parseFloat,
  scrapeSingle: $.tablechart.scrapeSingle,
  scrapeMultiple: $.tablechart.scrapeMultiple,
  processSeries: null,
  attachMethod: function(container) { $(this.el).before(container); },
  plotOptions: {series: []}
};

})(jQuery);
<commit_msg>Fixed replotting bug by clearing chart container before drawing.<commit_after>/**
 * @file jquery.tablechart.js
 *
 * A jQuery plugin that scrapes HTML tables and generates charts with jqPlot
 *
 * Created by David Eads (davideads__at__gmail_com), 2010 for the US
 * Department of Energy E-Center Project (see the project page at
 * https://ecenter.fnal.gov and the Google code project at
 * http://code.google.com/p/ecenter)
 *
 * Released under the Fermitools license (modified BSD). See LICENSE.txt for
 * more information.
 *
 * Requires jqPlot (http://www.jqplot.com)
 * Requires the jQuery UUID plugin (http://plugins.jquery.com/project/uuid)
 *
 * Usage and behavior notes:
 *
 * Invoke with $(selector).tablechart({options}), or simply
 * $(selector).tablechart()
 *
 * If the matched element returned by the selector is a table, a chart will
 * be generated from the table. If the matched element returned by the selector
 * contains one or more tables, all table data will be added to the chart.
 *
 * Using the default callbacks, data to be plotted should be wrapped in a proper
 *
 tag, and each row should provide a  tag that includes the x
 * value for the row. Each column will be added as a new chart series.
 *
 *

 *

 *

 *      x-axis label
 *      Series 1 label
 *      Series 2 label
 *

 *

 *

 *

 *       X value
 *       Series 1 (y) value
 *       Series 2 (y) value
 *

 *     ...
 *

 *

 *
 * If a
 is provided and the headerSeriesLabels option is true, the
 *
 row will be used to generate series labels unless specified in
 * plotOptions.
 *
 * A note about parsing X and Y values: The tablechart plugin provides two
 * trivial parsing callbacks for your parsing pleasure: $.tablechart.parseFloat
 * and $.tablechart.parseText.
 *
 * By default, all table data is assumed to be expressed as floats. You may
 * need to use the parseText helper function to account for dates, or define
 * your own parsing callback to strip out non-numeric characters or apply
 * special number formatting.
 *
 * All configurable callbacks are call()'d with a $.tablechart instance
 * provided as the calling context.
 *
 * Configuration options (no options are required):
 *
 *  hideTables: Hide source tables (default: false)
 *  height: Height of chart container (default: null)
 *  width: Width of chart container (default: null)
 *  chartName: Optional chart name. Override the chart name to create multiple
 *    charts from the same source tables (default: 'default')
 *  headerSeriesLabels: Use headers in thead section to name series
 *    (default: true)
 *  parseX: Callback to parse X values (default: $.tablechart.parseFloat)
 *  parseY: Callback to parse Y values (default: $.tablechart.parseFloat)
 *  scrapeSingle: Callback for scraping a single table
 *    (default: $.tablechart.scrapeSingle)
 *  scrapeMultiple: Callback for scraping multiple tables
 *    (default: $.tablechart.scrapeMultiple)
 *  processSeries: Callback for series post-processing (default: null)
 *  attachMethod: Callback for attaching chart
 *    (default: function(container) { $(this.el).before(container); })
 *  plotOptions: jqPlot options. See jqPlot options documentation for details:
 *    http://www.jqplot.com/docs/files/jqPlotOptions-txt.html
 *
 */

(function($) {

/**
 * Table chart plugin
 */
$.fn.tablechart = function(options) {
  var options = $.extend(true, {}, $.fn.tablechart.defaults, options);
  this.each(function(i) {
    var charts = $(this).data('tablechart') || {};
    if (charts[options.chartName] == undefined) {
      charts[options.chartName] = new $.tablechart(this, options);
      $(this).data('tablechart', charts);
    }
    charts[options.chartName].draw();
  });
  return this;
};

/**
 * Tablechart object constructor
 */
$.tablechart = function(el, options) {
  // Properties
  this.options = options;
  this.el = el;

  // Create container
  this.chartId = $.uuid('chart-');
  this.chartContainer = $('
').attr('id', this.chartId);

  if (options.height) { this.chartContainer.height(options.height); }
  if (options.width)  { this.chartContainer.width(options.width); }

  // Attach container
  options.attachMethod.call(this, this.chartContainer);
}

/**
 * Tablechart draw method
 */
$.tablechart.prototype.draw = function() {
  var tables;

  // Is matched element a table?
  if (!$.nodeName(this.el, 'table')) {
    tables = $('table', this.el);
    series = this.options.scrapeMultiple.call(this, tables);
  } else {
    tables = $(this.el);
    series = this.options.scrapeSingle.call(this, tables);
  }

  // Hide tables
  if (this.options.hideTables) {
    tables.hide();
  }

  // Apply any additional series processing
  if (this.options.processSeries) {
    series = this.options.processSeries.call(this, series);
  }

  // Draw chart

  // @TODO Because I don't understand replotting in jqPlot (and/or it is buggy),
  // we simply clear the container and redraw.  This is possibly not ideal, but
  // it works reliably.
  $('#' + this.chartId).html('');
  if (series.length > 0) {
    this.chart = $.jqplot(this.chartId, series, this.options.plotOptions);
  }
}

/**
 * Utility function: Scrape single table for values
 */
$.tablechart.scrapeSingle = function(table) {
  var series = [];
  var options = this.options;
  var tablechart = this;

  if (options.headerSeriesLabels) {
    $(table).find('thead th:gt(0)').each(function(i) {
      options.plotOptions.series[i] = $.extend(
        {label: $(this).text()},
        options.plotOptions.series[i]
      );
    });
  }

  $(table).find('tbody tr').each(function() {
    var x = 0, y = 0;
    $(this).find('th').each(function() {
      x = options.parseX.call(tablechart, this);
    });
    $(this).find('td').each(function(i) {
      if (!series[i]) {
        series[i] = [];
      }
      y = options.parseY.call(tablechart, this);
      series[i].push([x, y]);
    });
  });

  return series;
}

/**
 * Utility function: Scrape multiple tables for values
 */
$.tablechart.scrapeMultiple = function(tables) {
  var series = [];
  var options = this.options;
  var tablechart = this;

  // Flip on magical "internal" option if scraping multiple
  if (options.headerSeriesLabels) {
    options.headerSeriesLabels = false;
    options.multitablesHeaderSeriesLabels = true;
  }

  var series_idx = 0;
  $(tables).each(function() {
    // Generate series labels (requires "global" counter)
    if (tablechart.options.multitablesHeaderSeriesLabels) {
      $(this).find('thead th:gt(0)').each(function() {
        options.plotOptions.series[series_idx] = $.extend(
          {label: $(this).text()},
          options.plotOptions.series[series_idx]
        );
        series_idx += 1;
      });
    }
    // Now simply scrape each matched table
    series = series.concat($.tablechart.scrapeSingle.call(tablechart, this));
  });

  return series;
}

/**
 * Utility function: Parse text
 */
$.tablechart.parseText = function(el) {
  return $(el).text();
}

/**
 * Utility function: Parse text to floating point
 */
$.tablechart.parseFloat = function(el) {
  return parseFloat($(el).text());
}

/**
 * Defaults
 */
$.fn.tablechart.defaults = {
  hideTables: false,
  height: null,
  width: null,
  chartName: 'default',
  headerSeriesLabels: true,
  parseX: $.tablechart.parseFloat,
  parseY: $.tablechart.parseFloat,
  scrapeSingle: $.tablechart.scrapeSingle,
  scrapeMultiple: $.tablechart.scrapeMultiple,
  processSeries: null,
  attachMethod: function(container) { $(this.el).before(container); },
  plotOptions: {series: []}
};

})(jQuery);
<|endoftext|>


<commit_before>$(document).ready(function(){
    ObtenerDatos();
});
function ObtenerDatos(){

    var rows = "";
    for(var i=1;i < 11;i++){
     rows+= "
 Descripcion del contenido: "+i+"	 09/09/2016	Abierto	<button type='button' class='btn btn-sm btn-danger'>Editar</button><button type='button' class='btn btn-sm btn-success'>Ver</button>
";
    }
    $("#DatosJson").html(rows);
}

<commit_msg>Ariel - Vista pintada<commit_after>$(document).ready(function() {
  $('#example').DataTable( {
        "ajax": "API/comparativos"
    } );
} );

<|endoftext|>