<commit_before>/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
 * Licensed under the MIT License (LICENSE.txt).
 *
 * Version: 3.1.5-pre
 *
 * Requires: jQuery 1.2.2+
 */

(function (factory) {
    if ( typeof define === 'function' && define.amd ) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS style for Browserify
        module.exports = factory;
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

    var toFix  = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'];
    var toBind = 'onwheel' in document || document.documentMode >= 9 ? ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'];
    var slice  = Array.prototype.slice;
    var lowestDelta, lowestDeltaXY;

    if ( $.event.fixHooks ) {
        for ( var i = toFix.length; i; ) {
            $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
        }
    }

    $.event.special.mousewheel = {
        setup: function() {
            if ( this.addEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.addEventListener( toBind[--i], handler, false );
                }
            } else {
                this.onmousewheel = handler;
            }
        },

        teardown: function() {
            if ( this.removeEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.removeEventListener( toBind[--i], handler, false );
                }
            } else {
                this.onmousewheel = null;
            }
        }
    };

    $.fn.extend({
        mousewheel: function(fn) {
            return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
        },

        unmousewheel: function(fn) {
            return this.unbind('mousewheel', fn);
        }
    });

    function handler(event) {
        var orgEvent   = event || window.event,
            args       = slice.call(arguments, 1),
            delta      = 0,
            deltaX     = 0,
            deltaY     = 0,
            absDelta   = 0,
            absDeltaXY = 0,
            fn;
        event = $.event.fix(orgEvent);
        event.type = 'mousewheel';

        // Old school scrollwheel delta
        if ( 'detail'      in orgEvent ) { deltaY = orgEvent.detail * -1;      }
        if ( 'wheelDelta'  in orgEvent ) { deltaY = orgEvent.wheelDelta;       }
        if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY;      }
        if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }

        // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
        if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
            deltaY = 0;
            deltaX = delta * -1;
        }

        // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
        delta = deltaY === 0 ? deltaX : deltaY;

        // New school wheel delta (wheel event)
        if ( 'deltaY' in orgEvent ) {
          deltaY = orgEvent.deltaY * -1;
          delta  = deltaY;
        }
        if ( 'deltaX' in orgEvent ) {
          deltaX = orgEvent.deltaX;
          delta  = detlaX * -1;
        }

        // No change actually happened, no reason to go any further
        if ( deltaY === 0 && deltaX === 0 ) { return; }

        // Look for lowest delta to normalize the delta values
        absDelta = Math.abs(delta);
        if ( !lowestDelta || absDelta < lowestDelta ) { lowestDelta = absDelta; }
        absDeltaXY = Math.max(Math.abs(deltaY), Math.abs(deltaX));
        if ( !lowestDeltaXY || absDeltaXY < lowestDeltaXY ) { lowestDeltaXY = absDeltaXY; }

        // Get a whole value for the deltas
        fn     = delta > 0 ? 'floor' : 'ceil';
        delta  = Math[fn](delta  / lowestDelta);
        deltaX = Math[fn](deltaX / lowestDeltaXY);
        deltaY = Math[fn](deltaY / lowestDeltaXY);

        // Add event and delta to the front of the arguments
        args.unshift(event, delta, deltaX, deltaY);

        return ($.event.dispatch || $.event.handle).apply(this, args);
    }

}));
<commit_msg>Only look at the deltaY and deltaX for the lowestDelta and floor values that are greater than or equal to 1<commit_after>/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
 * Licensed under the MIT License (LICENSE.txt).
 *
 * Version: 3.1.5-pre
 *
 * Requires: jQuery 1.2.2+
 */

(function (factory) {
    if ( typeof define === 'function' && define.amd ) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS style for Browserify
        module.exports = factory;
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

    var toFix  = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
        toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
                    ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
        slice  = Array.prototype.slice,
        lowestDelta;

    if ( $.event.fixHooks ) {
        for ( var i = toFix.length; i; ) {
            $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
        }
    }

    $.event.special.mousewheel = {
        setup: function() {
            if ( this.addEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.addEventListener( toBind[--i], handler, false );
                }
            } else {
                this.onmousewheel = handler;
            }
        },

        teardown: function() {
            if ( this.removeEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.removeEventListener( toBind[--i], handler, false );
                }
            } else {
                this.onmousewheel = null;
            }
        }
    };

    $.fn.extend({
        mousewheel: function(fn) {
            return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
        },

        unmousewheel: function(fn) {
            return this.unbind('mousewheel', fn);
        }
    });

    function handler(event) {
        var orgEvent   = event || window.event,
            args       = slice.call(arguments, 1),
            delta      = 0,
            deltaX     = 0,
            deltaY     = 0,
            absDelta   = 0;
        event = $.event.fix(orgEvent);
        event.type = 'mousewheel';

        // Old school scrollwheel delta
        if ( 'detail'      in orgEvent ) { deltaY = orgEvent.detail * -1;      }
        if ( 'wheelDelta'  in orgEvent ) { deltaY = orgEvent.wheelDelta;       }
        if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY;      }
        if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }

        // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
        if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
            deltaY = 0;
            deltaX = delta * -1;
        }

        // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
        delta = deltaY === 0 ? deltaX : deltaY;

        // New school wheel delta (wheel event)
        if ( 'deltaY' in orgEvent ) {
          deltaY = orgEvent.deltaY * -1;
          delta  = deltaY;
        }
        if ( 'deltaX' in orgEvent ) {
          deltaX = orgEvent.deltaX;
          delta  = detlaX * -1;
        }

        // No change actually happened, no reason to go any further
        if ( deltaY === 0 && deltaX === 0 ) { return; }

        // Store lowest absolute delta to normalize the delta values
        absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );
        if ( !lowestDelta || absDelta < lowestDelta ) {
          lowestDelta = absDelta;
        }

        // Get a whole, normalized value for the deltas
        delta  = Math[ delta  >= 1 ? 'floor' : 'ceil' ](delta  / lowestDelta);
        deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
        deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);

        // Add event and delta to the front of the arguments
        args.unshift(event, delta, deltaX, deltaY);

        return ($.event.dispatch || $.event.handle).apply(this, args);
    }

}));
<|endoftext|>


<commit_before>Package.describe({
  name: "example-customization"
});

Package.onUse( function(api) {

  api.use([
    'fourseven:scss@4.5.0',

    'example-forum',
  ]);

  api.mainModule('server.js', 'server');
  api.mainModule('client.js', 'client');

  api.addFiles([
    'lib/modules.js'
  ], ['client', 'server']);

  api.addFiles([
    'lib/stylesheets/custom.scss'
  ], ['client']);

  api.addFiles([
    'lib/server/templates.js'
  ], ['server']);

  api.addAssets([
    'lib/server/emails/customNewPost.handlebars',
    'lib/server/emails/customEmail.handlebars'
  ], ['server']);

});
<commit_msg>fix example-customization package dependencies<commit_after>Package.describe({
  name: "example-customization"
});

Package.onUse( function(api) {

  api.use([
    'vulcan:core',
    'example-forum',

    'fourseven:scss@4.5.0',
  ]);

  api.mainModule('server.js', 'server');
  api.mainModule('client.js', 'client');

  api.addFiles([
    'lib/stylesheets/custom.scss'
  ], ['client']);

  api.addAssets([
    'lib/server/emails/customNewPost.handlebars',
    'lib/server/emails/customEmail.handlebars'
  ], ['server']);

});
<|endoftext|>


<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 = [];

    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,

            // 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) {
            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);

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

            P.$win.on('scroll', P.onScroll);
            if (this.options.responsive) {
                P.$win.on('resize', P.onWindowResize);
            }

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

        destroy: function () {
            var index = P.instances.indexOf(this);
            P.instances.splice(index, 1);
            return this;
        },

        // Update the position of the element.
        update: function () {
            var
                recalculateContainerSize = this.options.responsive,
                recalculateSize = this.options.responsive,
                // 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 = P.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);
            }

            if (recalculateContainerPosition || !containerPosition) {
                // Unlike sizes, jQUery calculates both vertical and horizontal
                // positions with one measurement, so no need to use `forAxes`.
                containerPosition = this._containerPosition = this.$container.offset();
            }

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

            if (css.left !== undefined || css.top !== undefined) {
                this.$el.css(css);
            }
        },

        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, overflow, pos;

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

            min = containerPosition + containerSize - windowSize;
            max = containerPosition;
            pct = (scrollPosition - min) / (max - min);
            overflow = elSize - (boundsMax - boundsMin);
            pos = boundsMin + (1 - pct) * -overflow;
            return pos;
        },

        // A utility that lets us avoid doing things we don't need to given the
        // axes of this instance.
        forAxes: function (horizontalFn, verticalFn) {
            if (this.options.horizontal) {
                horizontalFn.call(this);
            }
            if (this.options.vertical) {
                verticalFn.call(this);
            }
        },

        calculateElSize: function ($el) {
            var size = {};
            this.forAxes(
                function () { size.width = $el.outerWidth(true); },
                function () { size.height = $el.outerHeight(true); }
            );
            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 = {};
            this.forAxes(
                function () {
                    bounds.R = this.calculateBound(insets.R, containerSize.width);
                    bounds.L = this.calculateBound(insets.L, containerSize.width, true);
                },
                function () {
                    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;
        }
    };

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

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

    Parallaxin.onScroll = function () {
        P.getScrollPosition(true);
        $.each(P.instances, function (index, value) {
            value.update();
        });
    };

    Parallaxin.onWindowResize = function () {
        P.getWindowSize(true);
        $.each(P.instances, function (index, value) {
            if (value.options.responsive) {
                value.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>Math fix<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 = [];

    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,

            // 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) {
            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);

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

            P.$win.on('scroll', P.onScroll);
            if (this.options.responsive) {
                P.$win.on('resize', P.onWindowResize);
            }

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

        destroy: function () {
            var index = P.instances.indexOf(this);
            P.instances.splice(index, 1);
            return this;
        },

        // Update the position of the element.
        update: function () {
            var
                recalculateContainerSize = this.options.responsive,
                recalculateSize = this.options.responsive,
                // 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 = P.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);
            }

            if (recalculateContainerPosition || !containerPosition) {
                // Unlike sizes, jQUery calculates both vertical and horizontal
                // positions with one measurement, so no need to use `forAxes`.
                containerPosition = this._containerPosition = this.$container.offset();
            }

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

            if (css.left !== undefined || css.top !== undefined) {
                this.$el.css(css);
            }
        },

        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);
            return pos;
        },

        // A utility that lets us avoid doing things we don't need to given the
        // axes of this instance.
        forAxes: function (horizontalFn, verticalFn) {
            if (this.options.horizontal) {
                horizontalFn.call(this);
            }
            if (this.options.vertical) {
                verticalFn.call(this);
            }
        },

        calculateElSize: function ($el) {
            var size = {};
            this.forAxes(
                function () { size.width = $el.outerWidth(true); },
                function () { size.height = $el.outerHeight(true); }
            );
            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 = {};
            this.forAxes(
                function () {
                    bounds.R = this.calculateBound(insets.R, containerSize.width);
                    bounds.L = this.calculateBound(insets.L, containerSize.width, true);
                },
                function () {
                    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;
        }
    };

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

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

    Parallaxin.onScroll = function () {
        P.getScrollPosition(true);
        $.each(P.instances, function (index, value) {
            value.update();
        });
    };

    Parallaxin.onWindowResize = function () {
        P.getWindowSize(true);
        $.each(P.instances, function (index, value) {
            if (value.options.responsive) {
                value.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>/*
 * jQuery sortScroll V1.2.0
 * Sorting without moving !
 * The element being sorted will stay still while the rest of the page will scroll behind it.
 * https://github.com/jadus/jquery-sortScroll
 * lucas.menant@gmail.com
 * Licensed under the MIT license
 */
;
(function ($, window, document, undefined) {

    "use strict";
    var defaults = {
        animationDuration: 1000,// duration of the animation in ms
        cssEasing: "ease-in-out",// easing type for the animation
        keepStill: true// if false the page doesn't scroll to follow the element
    };

    function SortScroll(container, options) {

        this.container = $(container);
        this.settings = $.extend({}, defaults, options, this.container.data());
        this._sorting = false;
        this.elementClass = "sort-scroll-element";
        this.sortingClass = "sort-scroll-sorting";
        this.buttonUpClass = "sort-scroll-button-up";
        this.buttonDownClass = "sort-scroll-button-down";
        this.init();
    }

    $.extend(SortScroll.prototype, {
        init: function () {
            var self = this;

            //auto initialization
            self.container.on("click", "." + self.buttonUpClass + ", ." + self.buttonDownClass, function (event) {
                event.preventDefault();
                var button = $(this),
                    elementCollection = self.container.find("." + self.elementClass),
                    initialOrder = elementCollection.index(button.closest("." + self.elementClass)),
                    sortDirection;

                sortDirection = button.hasClass(self.buttonUpClass) ? -1 : 1;
                self.sortElement(initialOrder, sortDirection, self.settings.keepStill);
            })
        },
        sortElement: function (initialOrder, sortDirection, keepStill) {
            sortDirection = sortDirection === -1 ? sortDirection : 1;
            keepStill = !keepStill ? false : this.settings.keepStill;

            var self = this,
                elementCollection = self.container.find("." + self.elementClass),
                maxOrder = elementCollection.length - 1,
                destinationOrder = initialOrder + sortDirection;

            //don't move if element is at one end of the list
            if (destinationOrder < 0 || destinationOrder > maxOrder) {
                return false;
            }

            //if method is called again before animation end, we wait and we call it again on animation end
            if (self._sorting) {
                self.container.one("sortScroll.sortEnd", function (event, element, initialOrder, destinationOrder) {
                    self.sortElement(destinationOrder, sortDirection, keepStill);
                });
                return false;
            }

            elementCollection.each(function(){
                $(this).css("top", 0);
            });

            var element = elementCollection.eq(initialOrder),
                elementHeight = element.outerHeight(true),
                initialElementY = element.offset().top,
                otherElement = elementCollection.eq(destinationOrder),
                otherElementHeight = otherElement.outerHeight(true),
                initialOtherElementY = otherElement.offset().top,
                finalElementY = otherElement.offset().top,
                finalOtherElementY = initialOtherElementY + elementHeight,
                modifyElementRelative = 0,
                modifyOtherElementRelative = parseInt(otherElement.css("margin-top"),10);

            if (sortDirection > 0) {
                finalElementY = initialElementY + otherElementHeight;
                finalOtherElementY = initialElementY;
                modifyElementRelative = parseInt(element.css("margin-top"),10);
                modifyOtherElementRelative = 0;
            }

            var relativeElementY = finalElementY - initialElementY - modifyElementRelative,
                relativeOtherElementY = finalOtherElementY - initialOtherElementY - modifyOtherElementRelative,
                duration = self.settings.animationDuration,
                cssEasing = self.settings.cssEasing,
                body = $("body"),
                initialElementStyleAttr = element.attr("style") || '',
                initialOtherElementStyleAttr = otherElement.attr("style") || '',
                initialBodyStyleAttr = body.attr("style") || '',
                initialCssZIndex = element.css("z-index"),
                elementDfd = $.Deferred(),
                otherElementDfd = $.Deferred(),
                bodyDfd = $.Deferred(),
                transitionEndEvent = "transitionend webkitTransitionEnd msTransitionEnd oTransitionEnd",
                sortingZIndex;

            sortingZIndex = (initialCssZIndex === "auto") ? 2 : initialCssZIndex + 1;

            //animating
            self.container.trigger("sortScroll.sortStart", [element, initialOrder, destinationOrder]);
            self._sorting = true;
            element.add(otherElement).css({
                "position" : "relative",
                "transition" : duration+"ms "+cssEasing
            })
            element.addClass(self.sortingClass).css({
                "z-index": sortingZIndex,
                "top": relativeElementY+"px",
            });
            otherElement.css({
                "top": relativeOtherElementY+"px",
            });
            if(keepStill) {
                var initialScroll = $(window).scrollTop(),
                    finalScroll = initialScroll + relativeElementY,
                    maxScroll = Math.max(0, document.documentElement.scrollHeight - document.documentElement.clientHeight);

                finalScroll = Math.min(finalScroll, maxScroll);
                finalScroll = Math.max(finalScroll, 0);

                body.css({
                    "transition" : duration+"ms "+cssEasing,
                    "margin-top": ( initialScroll - finalScroll ) + "px"
                });
            }

            //after animation
            element.on(transitionEndEvent, function () {
                elementDfd.resolve();
            })
            otherElement.on(transitionEndEvent, function () {
                otherElementDfd.resolve();
            })
            body.on(transitionEndEvent, function () {
                bodyDfd.resolve();
            })
            $.when(elementDfd, otherElementDfd, bodyDfd).done(function () {
                //back to initial style
                element.attr("style",initialElementStyleAttr);
                otherElement.attr("style",initialOtherElementStyleAttr);
                body.attr("style",initialBodyStyleAttr);
                if(keepStill) {
                    $("html, body").scrollTop(finalScroll + ($(window).scrollTop() - initialScroll));
                }
                element.removeClass(self.sortingClass);

                //modifiyng dom
                if(sortDirection > 0){
                    otherElement.after(element);
                }else{
                    otherElement.before(element);
                }

                self._sorting = false;
                self.container.trigger("sortScroll.sortEnd", [element, initialOrder, destinationOrder]);
                return;
            });
        }
    });

    $.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);
                }
            }
        });
    };

    $(".sort-scroll-container").each(function () {
        $(this).sortScroll();
    });
})(jQuery, window, document);
<commit_msg>Adding fixed elements handling<commit_after>/*
 * jQuery sortScroll V1.3.0
 * Sorting without moving !
 * The element being sorted will stay still while the rest of the page will scroll behind it.
 * https://github.com/jadus/jquery-sortScroll
 * lucas.menant@gmail.com
 * Licensed under the MIT license
 */
;
(function ($, window, document, undefined) {

    "use strict";
    var defaults = {
        animationDuration: 1000,// duration of the animation in ms
        cssEasing: "ease-in-out",// easing type for the animation
        keepStill: true,// if false the page doesn't scroll to follow the element
        fixedElementsSelector: ""// a jQuery selector so that the plugin knows your fixed elements (like a fixed header)
    };

    function SortScroll(container, options) {

        this.container = $(container);
        this.settings = $.extend({}, defaults, options, this.container.data());
        this._sorting = false;
        this.elementClass = "sort-scroll-element";
        this.sortingClass = "sort-scroll-sorting";
        this.buttonUpClass = "sort-scroll-button-up";
        this.buttonDownClass = "sort-scroll-button-down";
        this.init();
    }

    $.extend(SortScroll.prototype, {
        init: function () {
            var self = this;

            //auto initialization
            self.container.on("click", "." + self.buttonUpClass + ", ." + self.buttonDownClass, function (event) {
                event.preventDefault();
                var button = $(this),
                    elementCollection = self.container.find("." + self.elementClass),
                    initialOrder = elementCollection.index(button.closest("." + self.elementClass)),
                    sortDirection;

                sortDirection = button.hasClass(self.buttonUpClass) ? -1 : 1;
                self.sortElement(initialOrder, sortDirection, self.settings.keepStill);
            })
        },
        sortElement: function (initialOrder, sortDirection, keepStill) {
            sortDirection = sortDirection === -1 ? sortDirection : 1;
            keepStill = !keepStill ? false : this.settings.keepStill;

            var self = this,
                elementCollection = self.container.find("." + self.elementClass),
                maxOrder = elementCollection.length - 1,
                destinationOrder = initialOrder + sortDirection;

            //don't move if element is at one end of the list
            if (destinationOrder < 0 || destinationOrder > maxOrder) {
                return false;
            }

            //if method is called again before animation end, we wait and we call it again on animation end
            if (self._sorting) {
                self.container.one("sortScroll.sortEnd", function (event, element, initialOrder, destinationOrder) {
                    self.sortElement(destinationOrder, sortDirection, keepStill);
                });
                return false;
            }

            elementCollection.each(function(){
                $(this).css("top", 0);
            });

            var element = elementCollection.eq(initialOrder),
                elementHeight = element.outerHeight(true),
                initialElementY = element.offset().top,
                otherElement = elementCollection.eq(destinationOrder),
                otherElementHeight = otherElement.outerHeight(true),
                initialOtherElementY = otherElement.offset().top,
                finalElementY = otherElement.offset().top,
                finalOtherElementY = initialOtherElementY + elementHeight,
                modifyElementRelative = 0,
                modifyOtherElementRelative = parseInt(otherElement.css("margin-top"),10);

            if (sortDirection > 0) {
                finalElementY = initialElementY + otherElementHeight;
                finalOtherElementY = initialElementY;
                modifyElementRelative = parseInt(element.css("margin-top"),10);
                modifyOtherElementRelative = 0;
            }

            var relativeElementY = finalElementY - initialElementY - modifyElementRelative,
                relativeOtherElementY = finalOtherElementY - initialOtherElementY - modifyOtherElementRelative,
                duration = self.settings.animationDuration,
                cssEasing = self.settings.cssEasing,
                body = $("body"),
                initialCssZIndex = element.css("z-index"),
                elementDfd = $.Deferred(),
                otherElementDfd = $.Deferred(),
                bodyDfd = $.Deferred(),
                transitionEndEvent = "transitionend webkitTransitionEnd msTransitionEnd oTransitionEnd",
                styleString = "style",
                sortingZIndex;

            sortingZIndex = (initialCssZIndex === "auto") ? 2 : initialCssZIndex + 1;

            //animating
            self.container.trigger("sortScroll.sortStart", [element, initialOrder, destinationOrder]);
            self._sorting = true;
            element.data(styleString,element.attr(styleString));
            otherElement.data(styleString,otherElement.attr(styleString));
            element.add(otherElement).css({
                "position" : "relative",
                "transition" : duration+"ms "+cssEasing
            })
            element.addClass(self.sortingClass).css({
                "z-index": sortingZIndex,
                "top": relativeElementY+"px",
            });
            otherElement.css({
                "top": relativeOtherElementY+"px",
            });
            if(keepStill) {
                var initialScroll = $(window).scrollTop(),
                    finalScroll = initialScroll + relativeElementY,
                    maxScroll = Math.max(0, document.documentElement.scrollHeight - document.documentElement.clientHeight),
                    fixedElements = $(self.settings.fixedElementsSelector).filter(function(){
                        return $(this).css("position") === "fixed";
                    });

                finalScroll = Math.min(finalScroll, maxScroll);
                finalScroll = Math.max(finalScroll, 0);

                body.data(styleString,body.attr(styleString)||"").css({
                    "transition" : duration+"ms "+cssEasing,
                    "margin-top": ( initialScroll - finalScroll ) + "px"
                });
                fixedElements.each(function(){
                    $(this).data(styleString,$(this).attr(styleString) || "")
                }).css({
                    "transition" : duration+"ms "+cssEasing,
                    "margin-top": -( initialScroll - finalScroll ) + "px"
                })
            }

            //after animation
            element.one(transitionEndEvent, function () {
                elementDfd.resolve();
            })
            otherElement.one(transitionEndEvent, function () {
                otherElementDfd.resolve();
            })
            body.one(transitionEndEvent, function () {
                bodyDfd.resolve();
            })
            $.when(elementDfd, otherElementDfd, bodyDfd).done(function () {
                //back to initial style
                element.attr(styleString,element.data(styleString));
                otherElement.attr(styleString,otherElement.data(styleString));
                body.attr(styleString,body.data(styleString));
                if(keepStill) {
                    $("html, body").scrollTop(finalScroll + ($(window).scrollTop() - initialScroll));
                    fixedElements.each(function(){
                        $(this).attr(styleString,$(this).data(styleString));
                    });
                }
                element.removeClass(self.sortingClass);

                //modifiyng dom
                if(sortDirection > 0){
                    otherElement.after(element);
                }else{
                    otherElement.before(element);
                }

                self._sorting = false;
                self.container.trigger("sortScroll.sortEnd", [element, initialOrder, destinationOrder]);
                return;
            });
        }
    });

    $.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);
                }
            }
        });
    };

    $(".sort-scroll-container").each(function () {
        $(this).sortScroll();
    });
})(jQuery, window, document);
<|endoftext|>


<commit_before>/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

var should = require("should");
var sinon = require("sinon");
var when = require("when");
var request = require('supertest');
var express = require("express");
var nock;
if (!process.version.match(/^v0\.[0-9]\./)) {
    // only set nock for node >= 0.10
    try {
        nock = require('nock');
    } catch (err) {
        // nevermind, will skip nock tests
        nock = null;
    }
}
var RED = require("../../red/red.js");
var redNodes = require("../../red/runtime/nodes");
var flows = require("../../red/runtime/nodes/flows");
var credentials = require("../../red/runtime/nodes/credentials");
var comms = require("../../red/api/comms.js");
var log = require("../../red/runtime/log.js");
var context = require("../../red/runtime/nodes/context.js");

var http = require('http');
var express = require('express');
var app = express();

var address = '127.0.0.1';
var listenPort = 0; // use ephemeral port
var port;
var url;
var logSpy;
var server;

function helperNode(n) {
    RED.nodes.createNode(this, n);
}

module.exports = {
    load: function(testNode, testFlows, testCredentials, cb) {
        var i;

        logSpy = sinon.spy(log,"log");
        logSpy.FATAL = log.FATAL;
        logSpy.ERROR = log.ERROR;
        logSpy.WARN = log.WARN;
        logSpy.INFO = log.INFO;
        logSpy.DEBUG = log.DEBUG;
        logSpy.TRACE = log.TRACE;
        logSpy.METRIC = log.METRIC;

        if (typeof testCredentials === 'function') {
            cb = testCredentials;
            testCredentials = {};
        }

        var storage = {
            getFlows: function() {
                return when.resolve({flows:testFlows,credentials:testCredentials});
            }
        };

        var settings = {
            available: function() { return false; }
        };

        var red = {};
        for (i in RED) {
            if (RED.hasOwnProperty(i) && !/^(init|start|stop)$/.test(i)) {
                var propDescriptor = Object.getOwnPropertyDescriptor(RED,i);
                Object.defineProperty(red,i,propDescriptor);
            }
        }

        red["_"] = function(messageId) {
            return messageId;
        };

        redNodes.init({settings:settings, storage:storage,log:log});
        RED.nodes.registerType("helper", helperNode);
        if (Array.isArray(testNode)) {
            for (i = 0; i < testNode.length; i++) {
                testNode[i](red);
            }
        } else {
            testNode(red);
        }
        flows.load().then(function() {
            flows.startFlows();
            should.deepEqual(testFlows, flows.getFlows().flows);
            cb();
        });
    },

    unload: function() {
        // TODO: any other state to remove between tests?
        redNodes.clearRegistry();
        logSpy.restore();
        context.clean({allNodes:[]});
        return flows.stopFlows();
    },

    getNode: function(id) {
        return flows.get(id);
    },

    credentials: credentials,

    clearFlows: function() {
        return flows.stopFlows();
    },

    request: function() {
        return request(RED.httpAdmin);
    },

    startServer: function(done) {
        server = http.createServer(function(req,res) { app(req,res); });
        RED.init(server, {
            SKIP_BUILD_CHECK: true,
            logging:{console:{level:'off'}}
        });
        server.listen(listenPort, address);
        server.on('listening', function() {
            port = server.address().port;
            url = 'http://' + address + ':' + port;
            comms.start();
            done();
        });
    },

    //TODO consider saving TCP handshake/server reinit on start/stop/start sequences
    stopServer: function(done) {
        if (server) {
            try {
                server.on('close', function() {
                    comms.stop();
                });
                server.close(done);
            } catch(e) {
                done();
            }
        }
    },

    url: function() { return url; },

    nock: nock,

    log: function() { return logSpy;}
};
<commit_msg>Add events to test helper<commit_after>/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

var should = require("should");
var sinon = require("sinon");
var when = require("when");
var request = require('supertest');
var express = require("express");
var nock;
if (!process.version.match(/^v0\.[0-9]\./)) {
    // only set nock for node >= 0.10
    try {
        nock = require('nock');
    } catch (err) {
        // nevermind, will skip nock tests
        nock = null;
    }
}
var RED = require("../../red/red.js");
var redNodes = require("../../red/runtime/nodes");
var flows = require("../../red/runtime/nodes/flows");
var credentials = require("../../red/runtime/nodes/credentials");
var comms = require("../../red/api/comms.js");
var log = require("../../red/runtime/log.js");
var context = require("../../red/runtime/nodes/context.js");
var events = require("../../red/runtime/events.js");

var http = require('http');
var express = require('express');
var app = express();

var address = '127.0.0.1';
var listenPort = 0; // use ephemeral port
var port;
var url;
var logSpy;
var server;

function helperNode(n) {
    RED.nodes.createNode(this, n);
}

module.exports = {
    load: function(testNode, testFlows, testCredentials, cb) {
        var i;

        logSpy = sinon.spy(log,"log");
        logSpy.FATAL = log.FATAL;
        logSpy.ERROR = log.ERROR;
        logSpy.WARN = log.WARN;
        logSpy.INFO = log.INFO;
        logSpy.DEBUG = log.DEBUG;
        logSpy.TRACE = log.TRACE;
        logSpy.METRIC = log.METRIC;

        if (typeof testCredentials === 'function') {
            cb = testCredentials;
            testCredentials = {};
        }

        var storage = {
            getFlows: function() {
                return when.resolve({flows:testFlows,credentials:testCredentials});
            }
        };

        var settings = {
            available: function() { return false; }
        };

        var red = {};
        for (i in RED) {
            if (RED.hasOwnProperty(i) && !/^(init|start|stop)$/.test(i)) {
                var propDescriptor = Object.getOwnPropertyDescriptor(RED,i);
                Object.defineProperty(red,i,propDescriptor);
            }
        }

        red["_"] = function(messageId) {
            return messageId;
        };

        redNodes.init({events:events,settings:settings, storage:storage,log:log,});
        RED.nodes.registerType("helper", helperNode);
        if (Array.isArray(testNode)) {
            for (i = 0; i < testNode.length; i++) {
                testNode[i](red);
            }
        } else {
            testNode(red);
        }
        flows.load().then(function() {
            flows.startFlows();
            should.deepEqual(testFlows, flows.getFlows().flows);
            cb();
        });
    },

    unload: function() {
        // TODO: any other state to remove between tests?
        redNodes.clearRegistry();
        logSpy.restore();
        context.clean({allNodes:[]});
        return flows.stopFlows();
    },

    getNode: function(id) {
        return flows.get(id);
    },

    credentials: credentials,

    clearFlows: function() {
        return flows.stopFlows();
    },

    request: function() {
        return request(RED.httpAdmin);
    },

    startServer: function(done) {
        server = http.createServer(function(req,res) { app(req,res); });
        RED.init(server, {
            SKIP_BUILD_CHECK: true,
            logging:{console:{level:'off'}}
        });
        server.listen(listenPort, address);
        server.on('listening', function() {
            port = server.address().port;
            url = 'http://' + address + ':' + port;
            comms.start();
            done();
        });
    },

    //TODO consider saving TCP handshake/server reinit on start/stop/start sequences
    stopServer: function(done) {
        if (server) {
            try {
                server.on('close', function() {
                    comms.stop();
                });
                server.close(done);
            } catch(e) {
                done();
            }
        }
    },

    url: function() { return url; },

    nock: nock,

    log: function() { return logSpy;}
};
<|endoftext|>


<commit_before>$(document).ready(function() {
  $("#submit").click(function(event) {
    event.preventDefault();
    var result = $("#result");
    var surgicalExtent = $("#surgical-extent").val();
    var surgicalLocation = $("#surgical-location").val();
    var surgicalPosition = $("#surgical-position").val();
    var procedureDuration = $("#procedure-duration").val();
    var bmi = $("#bmi").val();

    if (surgicalExtent === "major") {
      result.text("GA + Neuraxial Blockade");
    }
    else {
      if (surgicalLocation === "upper-extremities") {
        if (surgicalPosition === "prone"
          || surgicalPosition === "supine"
          || procedureDuration === "3-hours-or-longer"
          || bmi === "40-or-above") {
          result.text("GA + PNB");
        }
        else if (surgicalExtent === "moderate") {
          result.text("PNB and sedation or GA");
        }
        else if (surgicalExtent === "minor") {
          result.text("PNB and (sedation or GA or local)");
        }
      }
      else if (surgicalLocation === "lower-extremities") {
        if (surgicalPosition === "supine" || surgicalPosition === "lateral") {
          if (procedureDuration === "3-hours-or-longer" || bmi === "40-or-above") {
            result.text("GA + PNB");
          }
          else if (surgicalExtent === "moderate") {
            result.text("PNB and spinal or GA");
          }
          else if (surgicalExtent === "minor") {
            result.text("PNB and (spinal or local anesthesia or GA)");
          }
        }
        else if (surgicalPosition === "prone") {
          if (surgicalExtent === "minor") {
            result.text("GA, sedation, spinal, or local anesthesia");
          }
          else if (surgicalExtent === "moderate") {
            result.text("GA + PNB");
          }
        }
      }
      else if (surgicalLocation === "abdominal-or-spinal") {
        if (surgicalPosition === "prone") {
          if (surgicalExtent === "moderate") {
            result.text("GA");
          }
          else if (surgicalExtent === "minor") {
            result.text("GA or sedation");
          }
        }
        else if (surgicalPosition === "supine") {
          if (procedureDuration === "3-hours-or-longer" || bmi === "40-or-above") {
            result.text("GA");
          }
          else {
            if (surgicalExtent === "moderate") {
              result.text("Spinal + sedation or GA");
            }
            else if (surgicalExtent === "minor") {
              result.text("Spinal or GA");
            }
          }
        }
      }
    }
  });
});<commit_msg>Dynamically populates options for surgical position.<commit_after>$(document).ready(function() {
  $("#submit").click(function(event) {
    event.preventDefault();
    var result = $("#result");
    var surgicalExtent = $("#surgical-extent").val();
    var surgicalLocation = $("#surgical-location").val();
    var surgicalPosition = $("#surgical-position").val();
    var procedureDuration = $("#procedure-duration").val();
    var bmi = $("#bmi").val();

    if (surgicalExtent === "major") {
      result.text("GA + Neuraxial Blockade");
    }
    else {
      if (surgicalLocation === "upper-extremities") {
        if (surgicalPosition === "prone"
          || surgicalPosition === "supine"
          || procedureDuration === "3-hours-or-longer"
          || bmi === "40-or-above") {
          result.text("GA + PNB");
        }
        else if (surgicalExtent === "moderate") {
          result.text("PNB and sedation or GA");
        }
        else if (surgicalExtent === "minor") {
          result.text("PNB and (sedation or GA or local)");
        }
      }
      else if (surgicalLocation === "lower-extremities") {
        if (surgicalPosition === "supine" || surgicalPosition === "lateral") {
          if (procedureDuration === "3-hours-or-longer" || bmi === "40-or-above") {
            result.text("GA + PNB");
          }
          else if (surgicalExtent === "moderate") {
            result.text("PNB and spinal or GA");
          }
          else if (surgicalExtent === "minor") {
            result.text("PNB and (spinal or local anesthesia or GA)");
          }
        }
        else if (surgicalPosition === "prone") {
          if (surgicalExtent === "minor") {
            result.text("GA, sedation, spinal, or local anesthesia");
          }
          else if (surgicalExtent === "moderate") {
            result.text("GA + PNB");
          }
        }
      }
      else if (surgicalLocation === "abdominal-or-spinal") {
        if (surgicalPosition === "prone") {
          if (surgicalExtent === "moderate") {
            result.text("GA");
          }
          else if (surgicalExtent === "minor") {
            result.text("GA or sedation");
          }
        }
        else if (surgicalPosition === "supine") {
          if (procedureDuration === "3-hours-or-longer" || bmi === "40-or-above") {
            result.text("GA");
          }
          else {
            if (surgicalExtent === "moderate") {
              result.text("Spinal + sedation or GA");
            }
            else if (surgicalExtent === "minor") {
              result.text("Spinal or GA");
            }
          }
        }
      }
    }
  });

  $("#surgical-position option[value='lateral']").hide();

  $("#surgical-location").change(function() {
    $("#surgical-position option").hide();
    var surgicalLocation = $(this).val();
    $("#surgical-position option[value='supine']").show();
    $("#surgical-position option[value='prone']").show();
    if (surgicalLocation === "lower-extremities") {
      $("#surgical-position option[value='lateral']").show();
    }
    else if (surgicalLocation === "upper-extremities") {
      $("#surgical-position option[value='beach-chair']").show();
    }
  });
});<|endoftext|>


<commit_before>MWF.xDesktop.requireApp("process.Xform", "$Module", null, false);
/** @class SmartBI 统计图表组件。
 * @o2cn 统计图表组件
 * @example
 * //可以在脚本中获取该组件
 * //方法1：
 * var div = this.form.get("name"); //获取组件
 * //方法2
 * var div = this.target; //在组件事件脚本中获取
 * @extends MWF.xApplication.process.Xform.$Module
 * @o2category FormComponents
 * @o2range {Process|CMS|Portal}
 * @hideconstructor
 */
MWF.xApplication.process.Xform.SmartBI = MWF.APPSmartBI =  new Class({
    Extends: MWF.APP$Module,

    _loadUserInterface: function(){
        if (!this.json.smartbiresource || this.json.smartbiresource==="none") this.node.destroy();
        else{
            var _iframe = this.node.getElement("iframe");
            var url;
            if(_iframe){
                url = _iframe.get("src");
            }else{
                var value = this.json.smartbiresource;
                var SmartBIAction = o2.Actions.load("x_custom_smartbi_assemble_control");

                var addressUri = SmartBIAction.ResourceAction.address;

                if(addressUri){
                    SmartBIAction.ResourceAction.address(value,function(json){
                        if(json.data.value !==""){
                            url = json.data.value;
                            if(this.json.smartbidisplaytoolbar){
                                url = url +"&showtoolbar=true"
                            }else{
                                url = url +"&showtoolbar=false"
                            }

                            if(this.json.smartbidisplaylefttree){
                                url = url +"&showLeftTree=true"
                            }else{
                                url = url +"&showLeftTree=false"
                            }
                        }
                    }.bind(this),null,false)
                }else{
                    var address = SmartBIAction.ResourceAction.action.getAddress();
                    var uri = SmartBIAction.ResourceAction.action.actions.open.uri;
                    var url = uri.replace("{id}", encodeURIComponent(value));

                    if(this.json.smartbidisplaytoolbar){
                        url = url +"?showtoolbar=true"
                    }else{
                        url = url +"?showtoolbar=false"
                    }

                    if(this.json.smartbidisplaylefttree){
                        url = url +"&showLeftTree=true"
                    }else{
                        url = url +"&showLeftTree=false"
                    }

                    url = o2.filterUrl(address+url);
                }

            }

            this.iframe = new Element("iframe",{
                src:url,
                frameborder:"0",
                scrolling:"auto"
            }).inject(this.node,"after");

            var _properties = this.json.properties||{};
            this.node.destroy();
            this.node = this.iframe.setStyles({
                "width":"100%",
                "height":"100%",
                "min-height":"300px",
                "min-width":"300px"
            });

			this.node.set(_properties)

        }

	}
});<commit_msg>修改组件iframe资源获取路径<commit_after>MWF.xDesktop.requireApp("process.Xform", "$Module", null, false);
/** @class SmartBI 统计图表组件。
 * @o2cn 统计图表组件
 * @example
 * //可以在脚本中获取该组件
 * //方法1：
 * var div = this.form.get("name"); //获取组件
 * //方法2
 * var div = this.target; //在组件事件脚本中获取
 * @extends MWF.xApplication.process.Xform.$Module
 * @o2category FormComponents
 * @o2range {Process|CMS|Portal}
 * @hideconstructor
 */
MWF.xApplication.process.Xform.SmartBI = MWF.APPSmartBI =  new Class({
    Extends: MWF.APP$Module,

    _loadUserInterface: function(){
        if (!this.json.smartbiresource || this.json.smartbiresource==="none") this.node.destroy();
        else{

            var url;

            var value = this.json.smartbiresource;
            var SmartBIAction = o2.Actions.load("x_custom_smartbi_assemble_control");

            var addressUri = SmartBIAction.ResourceAction.address;

            if(addressUri){
                SmartBIAction.ResourceAction.address(value,function(json){
                    if(json.data.value !==""){
                        url = json.data.value;
                        if(this.json.smartbidisplaytoolbar){
                            url = url +"&showtoolbar=true"
                        }else{
                            url = url +"&showtoolbar=false"
                        }

                        if(this.json.smartbidisplaylefttree){
                            url = url +"&showLeftTree=true"
                        }else{
                            url = url +"&showLeftTree=false"
                        }
                    }
                }.bind(this),null,false)
            }else{
                var address = SmartBIAction.ResourceAction.action.getAddress();
                var uri = SmartBIAction.ResourceAction.action.actions.open.uri;
                var url = uri.replace("{id}", encodeURIComponent(value));

                if(this.json.smartbidisplaytoolbar){
                    url = url +"?showtoolbar=true"
                }else{
                    url = url +"?showtoolbar=false"
                }

                if(this.json.smartbidisplaylefttree){
                    url = url +"&showLeftTree=true"
                }else{
                    url = url +"&showLeftTree=false"
                }

                url = o2.filterUrl(address+url);
            }

            this.iframe = new Element("iframe",{
                src:url,
                frameborder:"0",
                scrolling:"auto"
            }).inject(this.node,"after");

            var _properties = this.json.properties||{};
            this.node.destroy();
            this.node = this.iframe.setStyles({
                "width":"100%",
                "height":"100%",
                "min-height":"300px",
                "min-width":"300px"
            });

			this.node.set(_properties)

        }

	}
});<|endoftext|>


<commit_before>module.exports = (withProtocol) => {

    let protocol = ``;
    if (withProtocol) {
        protocol = `^(http|https)://`;
    }
    const domain = '([a-z\\u00a1-\\uffff0-9\\-]+\\.)+';
    const tld = `[a-z\\u00a1-\\uffff\/]{2,}$`;
    const regex = `${protocol}${domain}${tld}`;

    return new RegExp(`${regex}`, 'i');
};<commit_msg>Accepting _ and tld is not at the end of the string.<commit_after>module.exports = (withProtocol) => {

    let protocol = ``;
    if (withProtocol) {
        protocol = `^(http|https)://`;
    }
    const domain = '([a-z\\u00a1-\\uffff0-9\\-\\_]+\\.){1,}';
    const tld = `[a-z\\u00a1-\\uffff\/]{2,}`;
    const regex = `${protocol}${domain}${tld}`;

    return new RegExp(`${regex}`, 'i');
};<|endoftext|>


<commit_before>require('./helper');

var selfText = fs.readFileSync(__filename, 'utf8'),
    etcText = fs.readFileSync('/etc/passwd', 'utf8');

expect('one');
expect('two');
Step(
  // Loads two files in parallel
  function loadStuff() {
    fulfill('one');
    fs.readFile(__filename, this.parallel());
    fs.readFile("/etc/passwd", this.parallel());
  },
  // Show the result when done
  function showStuff(err, code, users) {
    fulfill('two');
    if (err) throw err;
    assert.equal(selfText, code, "Code should come first");
    assert.equal(etcText, users, "Users should come second");
  }
);

// Test lock functionality with N sized groups
expect("test2: 1");
expect("test2: 1,2,3");
expect("test2: 2");
Step(
    function() {
        return 1;
    },
    function makeParallelCalls(err, num) {
        if(err) throw err;
        fulfill("test2: " + num);

        setTimeout((function(callback) { return function() { callback(null, 1); } })(this.parallel()), 100);
        this.parallel()(null, 2);
        setTimeout((function(callback) { return function() { callback(null, 3); } })(this.parallel()), 0);
    },
    function parallelResults(err, one, two, three) {
        if(err) throw err;
        fulfill("test2: " + [one, two, three]);
        return 2
    },
    function terminate(err, num) {
        if(err) throw err;
        fulfill("test2: " + num);
    }
)<commit_msg>Fixed type-o<commit_after>require('./helper');

var selfText = fs.readFileSync(__filename, 'utf8'),
    etcText = fs.readFileSync('/etc/passwd', 'utf8');

expect('one');
expect('two');
Step(
  // Loads two files in parallel
  function loadStuff() {
    fulfill('one');
    fs.readFile(__filename, this.parallel());
    fs.readFile("/etc/passwd", this.parallel());
  },
  // Show the result when done
  function showStuff(err, code, users) {
    fulfill('two');
    if (err) throw err;
    assert.equal(selfText, code, "Code should come first");
    assert.equal(etcText, users, "Users should come second");
  }
);

// Test lock functionality with N parallel calls
expect("test2: 1");
expect("test2: 1,2,3");
expect("test2: 2");
Step(
    function() {
        return 1;
    },
    function makeParallelCalls(err, num) {
        if(err) throw err;
        fulfill("test2: " + num);

        setTimeout((function(callback) { return function() { callback(null, 1); } })(this.parallel()), 100);
        this.parallel()(null, 2);
        setTimeout((function(callback) { return function() { callback(null, 3); } })(this.parallel()), 0);
    },
    function parallelResults(err, one, two, three) {
        if(err) throw err;
        fulfill("test2: " + [one, two, three]);
        return 2
    },
    function terminate(err, num) {
        if(err) throw err;
        fulfill("test2: " + num);
    }
)<|endoftext|>


<commit_before>var _ = require('underscore');
var Backbone = require('../lib/backbonej');
var THREE = require('three');
var Asset = require('./asset');
var Image = require('./image');
var getArray = require('../lib/get');

"use strict";

var basicMaterial = new THREE.MeshPhongMaterial();
basicMaterial.transparent = true;

var Mesh = Backbone.Model.extend({

    defaults : function () {
        return {
            alpha : 1,
            up : new THREE.Vector3(0, 1, 0),
            front : new THREE.Vector3(0, 0, 1)
        }
    },

    urlRoot: "meshes",

    initialize : function() {
        _.bindAll(this, 'changeAlpha', 'loadThumbnail', 'dispose',
                  'wireframeToggle');
        this.listenTo(this, "change:alpha", this.changeAlpha);
    },

    url: function () {
        return this.get('server').map(this.urlRoot + '/' + this.id);
    },

    up: function () {
        return this.get('up');
    },

    front: function () {
        return this.get('front');
    },

    alpha: function () {
        return this.get('alpha');
    },

    hasTexture: function() {
        return this.has('texture');
    },

    hasThumbnail: function() {
        return this.has('thumbnail');
    },

    isTextureOn: function () {
        return this.hasTexture() && this.get('textureOn');
    },

    isWireframeOn: function () {
        return this.get('t_mesh').material.wireframe;
    },

    textureOn: function() {
        if (this.isTextureOn() || !this.hasTexture()) {
            return;  // texture already off or no texture
        }
        var wf = this.isWireframeOn();
        this.get('t_mesh').material = this.get('texture');
        if (wf) {
            this.wireframeOn();
        } else {
            this.wireframeOff();
        }
        this.changeAlpha();
        this.set('textureOn', true);
    },

    textureOff: function() {
        if (!this.isTextureOn()) {
            return;  // texture already on
        }
        var wf = this.isWireframeOn();
        this.get('t_mesh').material = basicMaterial;
        if (wf) {
            this.wireframeOn();
        } else {
            this.wireframeOff();
        }
        this.changeAlpha();
        this.set('textureOn', false);
    },

    textureToggle: function () {
        if (this.isTextureOn()) {
            this.textureOff();
        } else {
            this.textureOn();
        }
    },

    wireframeOn: function() {
        if (this.isWireframeOn()) {
            return;
        }
        this.get('t_mesh').material.wireframe = true;
        this.set('wireframeOn', true);
        this.changeAlpha();
    },

    wireframeOff: function() {
        if (!this.isWireframeOn()) {
            return;
        }
        this.get('t_mesh').material.wireframe = false;
        this.set('wireframeOn', false);
        this.changeAlpha();
    },

    wireframeToggle: function () {
        if (this.isWireframeOn()) {
            this.wireframeOff();
        } else {
            this.wireframeOn();
        }
    },

    toJSON: function () {
        var trilist = _.map(this.get('t_mesh').geometry.faces, function (face) {
            return [face.a, face.b, face.c];
        });

        var points = _.map(this.get('t_mesh').geometry.vertices, function (v) {
            return [v.x, v.y, v.z];
        });

        return {
            points: points,
            trilist: trilist
        };
    },

    changeAlpha: function () {
        this.get('t_mesh').material.opacity = this.get('alpha');
    },

    parseBuffer: function () {
        var promise = getArray(this.url());
        var that = this;
        promise.then(function (buffer) {
            var lenMeta = 4;
            var bytes = 4;
            var meta = new Uint32Array(buffer, 0, lenMeta);
            var nTris = meta[0];
            var isTextured = Boolean(meta[1]);
            var hasNormals = Boolean(meta[2]);
            var hasBinning = Boolean(meta[2]);  // used for efficient lookup
            var stride = nTris * 3;

            // Points
            var pointsOffset = lenMeta * bytes;
            var points = new Float32Array(buffer, pointsOffset, stride * 3);
            var normalOffset = pointsOffset + stride * 3 * bytes;

            // Normals (optional)
            var normals = null;  // initialize for no normals
            var tcoordsOffset = normalOffset;  // no normals -> tcoords next
            if (hasNormals) {
                // correct if has normals
                normals = new Float32Array(buffer, normalOffset, stride * 3);
                // need to advance the pointer on tcoords offset
                tcoordsOffset = normalOffset + stride * 3 * bytes;
            }

            // Tcoords (optional)
            var tcoords = null;  // initialize for no tcoords
            var binningOffset = tcoordsOffset;  // no tcoords -> binning next
            if (isTextured) {
                tcoords = new Float32Array(buffer, tcoordsOffset, stride * 2);
                binningOffset = tcoordsOffset + stride * 2 * bytes;
            }

            // Binning (optional)
            if (hasBinning) {
                console.log('ready to read from binning file at ' + binningOffset)
            }

            return that._newBufferMesh(points, normals, tcoords);
        }).catch(function () {
            console.log('something went wrong');
        });
        return promise;
    },

    _newBufferMesh: function(points, normals, tcoords) {
        console.log('in new buffer mesh');
        window.points = points;
        var geometry = new THREE.BufferGeometry();
        geometry.addAttribute('position', new THREE.BufferAttribute(points, 3));
        if (normals) {
            geometry.addAttribute('normal', new THREE.BufferAttribute(normals, 3));
        } else {
            geometry.computeVertexNormals();
        }
        var material;
        var result;
        var that = this;
        if (tcoords) {
            geometry.addAttribute('uv', new THREE.BufferAttribute(tcoords, 2));
            // this mesh has a texture - grab it
            var textureURL = this.get('server').map('textures/' + this.id);
            material = new THREE.MeshPhongMaterial(
                {
                    map: THREE.ImageUtils.loadTexture(textureURL,
                        new THREE.UVMapping(),
                        function() {that.trigger("change:texture");}
                    )
                }
            );
            material.transparent = true;
            result = {
                t_mesh: new THREE.Mesh(geometry, material),
                texture: material,
                textureOn: true
            };
        } else {
            // default to basic Phong lighting
            result = {
                t_mesh: new THREE.Mesh(geometry, basicMaterial)
            };
        }
        geometry.computeBoundingSphere();
        result.t_mesh.name = this.id;
        return this.set(result);
    },

    loadThumbnail: function () {
        var that = this;
        var image = new Image.Image({
            id: this.id,
            server: this.get('server'),
            thumbnailDelegate: this.collection
        });
        // load the thumbnail for this mesh
        image.fetch({
            success: function () {
                console.log('grabbed thumbnail preview!');
                that.set('thumbnail', image);
            },
            error: function () {
                // couldn't load a thumbnail - must be an untextured mesh!
                // trigger the thumbnail loaded so our progress bar finishes
                that.collection.trigger('thumbnailLoaded');
            }
        });
    },

    // reset this mesh back to how it was at fetch time.
    dispose : function () {
        var texture;
        this.get('t_mesh').geometry.dispose();
        if (this.hasTexture()) {
            texture = this.get('texture');
            texture.map.dispose();
            texture.dispose();
            this.unset('texture');
        }
        this.unset('t_mesh');
        this._previousAttributes = {};
    }
});

var MeshList = Backbone.Collection.extend({
    model: Mesh
});

// Holds a list of available meshes, and a MeshList. The MeshList
// is populated immediately, although meshes aren't fetched until demanded.
// Also has a mesh parameter - the currently active mesh.
var MeshSource = Asset.AssetSource.extend({

    parse: function (response) {
        var that = this;
        var mesh;
        var meshes = _.map(response, function (assetId) {
            mesh = new Mesh({
                id: assetId,
                server: that.get('server')
            });
            return mesh;
        });
        var meshList = new MeshList(meshes.slice(0, 100));
        return {
            assets: meshList
        };
    },

    _changeAssets: function () {
        this.assets().each(function(mesh) {
            // immediately load the preview texture for the mesh
            // TODO this may need to be less agressive on large datasets
            mesh.loadThumbnail();
        })
    },

    setAsset: function (newMesh) {
        var that = this;
        var oldAsset = this.get('asset');
        // kill any current fetches
        _.each(this.pending, function (xhr) {
            xhr.abort();
        }, this);
        this.set('assetIsLoading', true);
        // set the asset immediately (triggering change in UI, landmark fetch)
        that.set('asset', newMesh);
        if (newMesh.hasThumbnail()) {
            console.log('setting mesh to thumbnail');
            that.set('mesh', newMesh.get('thumbnail').get('mesh'));
        }
        // fetch the new asset and update the mesh when the fetch is complete
        var promise = newMesh.parseBuffer();
        this.pending[newMesh.id] = promise.xhr();
        promise.then(function () {
                console.log('grabbed new mesh');
                // once the mesh is downloaded, advance the mesh.
                that.set('mesh', newMesh);
                // now everyone has moved onto the new mesh, clean up the old
                // one.
                if (oldAsset) {
                    oldAsset.dispose();
                    oldAsset = null;
                }
                delete that.pending[newMesh.id];
                that.set('assetIsLoading', false);
        });
    }
});

exports.Mesh = Mesh;
exports.MeshList = MeshList;
exports.MeshSource = MeshSource;
<commit_msg>texturing now unaffected by lighting<commit_after>var _ = require('underscore');
var Backbone = require('../lib/backbonej');
var THREE = require('three');
var Asset = require('./asset');
var Image = require('./image');
var getArray = require('../lib/get');

"use strict";

var basicMaterial = new THREE.MeshPhongMaterial();
basicMaterial.transparent = true;

var Mesh = Backbone.Model.extend({

    defaults : function () {
        return {
            alpha : 1,
            up : new THREE.Vector3(0, 1, 0),
            front : new THREE.Vector3(0, 0, 1)
        }
    },

    urlRoot: "meshes",

    initialize : function() {
        _.bindAll(this, 'changeAlpha', 'loadThumbnail', 'dispose',
                  'wireframeToggle');
        this.listenTo(this, "change:alpha", this.changeAlpha);
    },

    url: function () {
        return this.get('server').map(this.urlRoot + '/' + this.id);
    },

    up: function () {
        return this.get('up');
    },

    front: function () {
        return this.get('front');
    },

    alpha: function () {
        return this.get('alpha');
    },

    hasTexture: function() {
        return this.has('texture');
    },

    hasThumbnail: function() {
        return this.has('thumbnail');
    },

    isTextureOn: function () {
        return this.hasTexture() && this.get('textureOn');
    },

    isWireframeOn: function () {
        return this.get('t_mesh').material.wireframe;
    },

    textureOn: function() {
        if (this.isTextureOn() || !this.hasTexture()) {
            return;  // texture already off or no texture
        }
        var wf = this.isWireframeOn();
        this.get('t_mesh').material = this.get('texture');
        if (wf) {
            this.wireframeOn();
        } else {
            this.wireframeOff();
        }
        this.changeAlpha();
        this.set('textureOn', true);
    },

    textureOff: function() {
        if (!this.isTextureOn()) {
            return;  // texture already on
        }
        var wf = this.isWireframeOn();
        this.get('t_mesh').material = basicMaterial;
        if (wf) {
            this.wireframeOn();
        } else {
            this.wireframeOff();
        }
        this.changeAlpha();
        this.set('textureOn', false);
    },

    textureToggle: function () {
        if (this.isTextureOn()) {
            this.textureOff();
        } else {
            this.textureOn();
        }
    },

    wireframeOn: function() {
        if (this.isWireframeOn()) {
            return;
        }
        this.get('t_mesh').material.wireframe = true;
        this.set('wireframeOn', true);
        this.changeAlpha();
    },

    wireframeOff: function() {
        if (!this.isWireframeOn()) {
            return;
        }
        this.get('t_mesh').material.wireframe = false;
        this.set('wireframeOn', false);
        this.changeAlpha();
    },

    wireframeToggle: function () {
        if (this.isWireframeOn()) {
            this.wireframeOff();
        } else {
            this.wireframeOn();
        }
    },

    toJSON: function () {
        var trilist = _.map(this.get('t_mesh').geometry.faces, function (face) {
            return [face.a, face.b, face.c];
        });

        var points = _.map(this.get('t_mesh').geometry.vertices, function (v) {
            return [v.x, v.y, v.z];
        });

        return {
            points: points,
            trilist: trilist
        };
    },

    changeAlpha: function () {
        this.get('t_mesh').material.opacity = this.get('alpha');
    },

    parseBuffer: function () {
        var promise = getArray(this.url());
        var that = this;
        promise.then(function (buffer) {
            var lenMeta = 4;
            var bytes = 4;
            var meta = new Uint32Array(buffer, 0, lenMeta);
            var nTris = meta[0];
            var isTextured = Boolean(meta[1]);
            var hasNormals = Boolean(meta[2]);
            var hasBinning = Boolean(meta[2]);  // used for efficient lookup
            var stride = nTris * 3;

            // Points
            var pointsOffset = lenMeta * bytes;
            var points = new Float32Array(buffer, pointsOffset, stride * 3);
            var normalOffset = pointsOffset + stride * 3 * bytes;

            // Normals (optional)
            var normals = null;  // initialize for no normals
            var tcoordsOffset = normalOffset;  // no normals -> tcoords next
            if (hasNormals) {
                // correct if has normals
                normals = new Float32Array(buffer, normalOffset, stride * 3);
                // need to advance the pointer on tcoords offset
                tcoordsOffset = normalOffset + stride * 3 * bytes;
            }

            // Tcoords (optional)
            var tcoords = null;  // initialize for no tcoords
            var binningOffset = tcoordsOffset;  // no tcoords -> binning next
            if (isTextured) {
                tcoords = new Float32Array(buffer, tcoordsOffset, stride * 2);
                binningOffset = tcoordsOffset + stride * 2 * bytes;
            }

            // Binning (optional)
            if (hasBinning) {
                console.log('ready to read from binning file at ' + binningOffset)
            }

            return that._newBufferMesh(points, normals, tcoords);
        }).catch(function () {
            console.log('something went wrong');
        });
        return promise;
    },

    _newBufferMesh: function(points, normals, tcoords) {
        console.log('in new buffer mesh');
        window.points = points;
        var geometry = new THREE.BufferGeometry();
        geometry.addAttribute('position', new THREE.BufferAttribute(points, 3));
        if (normals) {
            geometry.addAttribute('normal', new THREE.BufferAttribute(normals, 3));
        } else {
            geometry.computeVertexNormals();
        }
        var material;
        var result;
        var that = this;
        if (tcoords) {
            geometry.addAttribute('uv', new THREE.BufferAttribute(tcoords, 2));
            // this mesh has a texture - grab it
            var textureURL = this.get('server').map('textures/' + this.id);
            material = new THREE.MeshBasicMaterial(
                {
                    map: THREE.ImageUtils.loadTexture(textureURL,
                        new THREE.UVMapping(),
                        function() {that.trigger("change:texture");}
                    )
                }
            );
            material.transparent = true;
            result = {
                t_mesh: new THREE.Mesh(geometry, material),
                texture: material,
                textureOn: true
            };
        } else {
            // default to basic Phong lighting
            result = {
                t_mesh: new THREE.Mesh(geometry, basicMaterial)
            };
        }
        geometry.computeBoundingSphere();
        result.t_mesh.name = this.id;
        return this.set(result);
    },

    loadThumbnail: function () {
        var that = this;
        var image = new Image.Image({
            id: this.id,
            server: this.get('server'),
            thumbnailDelegate: this.collection
        });
        // load the thumbnail for this mesh
        image.fetch({
            success: function () {
                console.log('grabbed thumbnail preview!');
                that.set('thumbnail', image);
            },
            error: function () {
                // couldn't load a thumbnail - must be an untextured mesh!
                // trigger the thumbnail loaded so our progress bar finishes
                that.collection.trigger('thumbnailLoaded');
            }
        });
    },

    // reset this mesh back to how it was at fetch time.
    dispose : function () {
        var texture;
        this.get('t_mesh').geometry.dispose();
        if (this.hasTexture()) {
            texture = this.get('texture');
            texture.map.dispose();
            texture.dispose();
            this.unset('texture');
        }
        this.unset('t_mesh');
        this._previousAttributes = {};
    }
});

var MeshList = Backbone.Collection.extend({
    model: Mesh
});

// Holds a list of available meshes, and a MeshList. The MeshList
// is populated immediately, although meshes aren't fetched until demanded.
// Also has a mesh parameter - the currently active mesh.
var MeshSource = Asset.AssetSource.extend({

    parse: function (response) {
        var that = this;
        var mesh;
        var meshes = _.map(response, function (assetId) {
            mesh = new Mesh({
                id: assetId,
                server: that.get('server')
            });
            return mesh;
        });
        var meshList = new MeshList(meshes.slice(0, 100));
        return {
            assets: meshList
        };
    },

    _changeAssets: function () {
        this.assets().each(function(mesh) {
            // immediately load the preview texture for the mesh
            // TODO this may need to be less agressive on large datasets
            mesh.loadThumbnail();
        })
    },

    setAsset: function (newMesh) {
        var that = this;
        var oldAsset = this.get('asset');
        // kill any current fetches
        _.each(this.pending, function (xhr) {
            xhr.abort();
        }, this);
        this.set('assetIsLoading', true);
        // set the asset immediately (triggering change in UI, landmark fetch)
        that.set('asset', newMesh);
        if (newMesh.hasThumbnail()) {
            console.log('setting mesh to thumbnail');
            that.set('mesh', newMesh.get('thumbnail').get('mesh'));
        }
        // fetch the new asset and update the mesh when the fetch is complete
        var promise = newMesh.parseBuffer();
        this.pending[newMesh.id] = promise.xhr();
        promise.then(function () {
                console.log('grabbed new mesh');
                // once the mesh is downloaded, advance the mesh.
                that.set('mesh', newMesh);
                // now everyone has moved onto the new mesh, clean up the old
                // one.
                if (oldAsset) {
                    oldAsset.dispose();
                    oldAsset = null;
                }
                delete that.pending[newMesh.id];
                that.set('assetIsLoading', false);
        });
    }
});

exports.Mesh = Mesh;
exports.MeshList = MeshList;
exports.MeshSource = MeshSource;
<|endoftext|>