<commit_before>(function (root,$) {
    root.SCS = {
        version: '1.0.0'
    };
    //default settings
    SCS.defaults = {
        navArr: [//province，city，district
            { title: "省", id: "scs_items_prov" },
            { title: "市", id: "scs_items_city" },
            { title: "区", id: "scs_items_dist" }
        ],
        skinCss: '',//customize skin by adding an extra class
        onOk: function (selectedKey, selectedValue) { },//trigger when click on ok, the selectedDey and selectedValue can be used directly
        onCancel: function () { }//trigger when click on cancel
    };
    SCS.init = function (options) {
        var options = $.extend({}, SCS.defaults, options);
        var html = '',
            navArr = options.navArr;
        html += '

';
        navArr.forEach(function (nav) {
            html += '

' + nav.title + '

';
        });
        html += '

确定

取消

';
        $("#cascade_scroll_selects").remove();//make sure only one SCS is running
        $("body").append(html);
        var This = $("#cascade_scroll_selects");
        This.find(".scs_dialog").slideDown(300);
        //bind click events
        This.find(".scs_cover").click(function () {
            This.find(".scs_dialog").slideUp(200, function () {
                This.remove();
            });
        });
        This.find(".scs_btn_ok").click(function () {
            var selectedKey = '',
                selectedValue = '';
            $("#cascade_scroll_selects").find(".scs_selected").each(function (n) {
                var $obj = $(this);
                if (n != 0) {
                    selectedKey += ("-" + $obj.attr("data-val"));
                    selectedValue += (" " + $obj.html());
                } else {
                    selectedKey = $obj.attr("data-val");
                    selectedValue = $obj.html();
                }
            });
            options.onOk(selectedKey, selectedValue);
            This.find(".scs_dialog").slideUp(200, function () {
                This.remove();
            });
        });
        This.find(".scs_btn_cancel").click(function () {
            options.onCancel();
            This.find(".scs_dialog").slideUp(200, function () {
                This.remove();
            });
        });
    }
    SCS.destroy = function (callback) {//destroy SCS
        if (typeof callback === 'function')
            callback();
        var This = $("#cascade_scroll_selects");
        This.find(".scs_dialog").slideUp(200, function () {
            This.remove();
        });
    }
    //a scroller class
    SCS.scrollCascadeSelect = function (options) {
        var defaults = {
            el: undefined,
            dataArr: [],
            startIndex: 0,
            onInit: function (selectedItem, selectedIndex) { },//exposing the selectedItem and selectIndex parameters for outter using
            onChange: function (selectedItem, selectedIndex) { }//exposing the selectedItem and selectIndex parameters for outter using
        };
        var options = $.extend({}, defaults, options);
        var This = $(options.el),
            el = This.get(0),
            startPos = { x: 0, y: 0 },//a relative start position
            STARTPOS = { x: 0, y: 0 },//a precise start position
            currentPos = { x: 0, y: 0 },
            dataArr = options.dataArr,
            startIndex = options.startIndex,
            _self = this;//use '_self' to store 'this' in case of some misunderstanding.
        //exposing the selected item and index for outter use
        _self.selectedIndex = startIndex;
        _self.getSelectedItem = function () {
            return dataArr[this.selectedIndex];
        };
        //bind all the touch event
        el.addEventListener('touchstart', function (event) {
            startPos = { x: event.changedTouches[0].pageX, y: event.changedTouches[0].pageY };
            STARTPOS = { x: event.changedTouches[0].pageX, y: event.changedTouches[0].pageY };
        }, false);
        el.addEventListener('touchmove', function (event) {
            event.preventDefault();//prevent default scrolling event when touch moving
            currentPos = { x: event.changedTouches[0].pageX, y: event.changedTouches[0].pageY };
            var movedDistance = currentPos.y - startPos.y,
                currentTranslatedY = parseInt(This.css("webkitTransform").split(",").pop().replace(" ", "").replace(")", "")),
                newTranslatedY = movedDistance + currentTranslatedY;
            if (newTranslatedY > 80) {
                newTranslatedY = 80;
            } else if (newTranslatedY < (This.attr("data-height") - 120) * (-1)) {
                newTranslatedY = (This.attr("data-height") - 120) * (-1);
            }
            This.css('-webkit-transform', 'translate3d(0, ' + newTranslatedY + 'px, 0)');
            This.css('transform', 'translate3d(0, ' + newTranslatedY + 'px, 0)');
            startPos = { x: currentPos.x, y: currentPos.y };
        }, false);
        el.addEventListener('touchend', function (event) {
            currentPos = { x: event.changedTouches[0].pageX, y: event.changedTouches[0].pageY };
            if (Math.abs(currentPos.y - STARTPOS.y) > 5) {//exclude the 'click' event
                var currentTranslatedY = parseInt(This.css("webkitTransform").split(",").pop().replace(" ", "").replace(")", "")),
                    residue = currentTranslatedY % 40;
                if (Math.abs(residue) >= 20) {
                    if (residue < 0)
                        currentTranslatedY += ((40 + residue) * -1);
                    else {
                        currentTranslatedY += (40 - residue);
                    }
                } else {
                    currentTranslatedY -= residue;
                }
                This.css('-webkit-transform', 'translate3d(0, ' + currentTranslatedY + 'px, 0)');
                This.css('transform', 'translate3d(0, ' + currentTranslatedY + 'px, 0)');
                _self.selectedIndex = Math.abs((currentTranslatedY - 80) / (-40));
                This.find(".scs_item").removeClass("scs_selected").eq(_self.selectedIndex).addClass("scs_selected");
                startPos = { x: 0, y: 0 },
                currentPos = { x: 0, y: 0 };
                options.onChange(_self.getSelectedItem(), _self.selectedIndex);//trigger onChange event
            }
        }, false);
        //bind a click event
        This.on("click", ".scs_item", function (e) {
            var That = $(this),
            itemPositionY = That.position().top,
            currentTranslatedY = 80 - itemPositionY;
            This.css('-webkit-transform', 'translate3d(0, ' + currentTranslatedY + 'px, 0)');
            This.css('transform', 'translate3d(0, ' + currentTranslatedY + 'px, 0)');
            This.find(".scs_item").removeClass("scs_selected");
            That.addClass("scs_selected");
            _self.selectedIndex = Math.abs((currentTranslatedY - 80) / (-40));
            options.onChange(_self.getSelectedItem(), _self.selectedIndex);//trigger onChange event
        });
        //a public out-exposing function, render / re-render the scroller, based on the parameters 'newDataArr' and 'newStartIndex'
        _self.render = function (newDataArr, newStartIndex, callback) {
            var html = "",
                sIndex = newStartIndex || 0,
                currentTranslatedY = 80 - sIndex * 40;
            if (newDataArr != undefined)
                newDataArr.forEach(function (obj, index) {
                    if (sIndex != index)
                        html += '
' + obj.val + '
';
                    else {
                        html += '
' + obj.val + '
';
                    }
                });
            else {
                This.html(html);
                return;
            }
            This.html(html);
            This.attr("data-height", newDataArr.length * 40);
            This.css('-webkit-transform', 'translate3d(0, ' + currentTranslatedY + 'px, 0)');
            This.css('transform', 'translate3d(0, ' + currentTranslatedY + 'px, 0)');
            _self.selectedIndex = sIndex;
            dataArr = newDataArr;
            if (typeof callback === 'function')
                callback();

        };
        //private funtion，initialization
        var init = function () {
            _self.render(dataArr, startIndex);
            options.onInit(_self.getSelectedItem(), _self.selectedIndex);//trigger the onInit callback function
        };
        init();//bootstrap a scroller
    }
})(this,jQuery);
<commit_msg>added mouse-dragging effect<commit_after>(function (root,$) {
    root.SCS = {
        version: '1.0.0'
    };
    //default settings
    SCS.defaults = {
        navArr: [//province，city，district
            { title: "省", id: "scs_items_prov" },
            { title: "市", id: "scs_items_city" },
            { title: "区", id: "scs_items_dist" }
        ],
        skinCss: '',//customize skin by adding an extra class
        onOk: function (selectedKey, selectedValue) { },//trigger when click on ok, the selectedDey and selectedValue can be used directly
        onCancel: function () { }//trigger when click on cancel
    };
    SCS.init = function (options) {
        var options = $.extend({}, SCS.defaults, options);
        var html = '',
            navArr = options.navArr;
        html += '

';
        navArr.forEach(function (nav) {
            html += '

' + nav.title + '

';
        });
        html += '

确定

取消

';
        $("#cascade_scroll_selects").remove();//make sure only one SCS is running
        $("body").append(html);
        var This = $("#cascade_scroll_selects");
        This.find(".scs_dialog").slideDown(300);
        //bind click events
        This.find(".scs_cover").click(function () {
            This.find(".scs_dialog").slideUp(200, function () {
                This.remove();
            });
        });
        This.find(".scs_btn_ok").click(function () {
            var selectedKey = '',
                selectedValue = '';
            $("#cascade_scroll_selects").find(".scs_selected").each(function (n) {
                var $obj = $(this);
                if (n != 0) {
                    selectedKey += ("-" + $obj.attr("data-val"));
                    selectedValue += (" " + $obj.html());
                } else {
                    selectedKey = $obj.attr("data-val");
                    selectedValue = $obj.html();
                }
            });
            options.onOk(selectedKey, selectedValue);
            This.find(".scs_dialog").slideUp(200, function () {
                This.remove();
            });
        });
        This.find(".scs_btn_cancel").click(function () {
            options.onCancel();
            This.find(".scs_dialog").slideUp(200, function () {
                This.remove();
            });
        });
    }
    SCS.destroy = function (callback) {//destroy SCS
        if (typeof callback === 'function')
            callback();
        var This = $("#cascade_scroll_selects");
        This.find(".scs_dialog").slideUp(200, function () {
            This.remove();
        });
    }
    //a scroller class
    SCS.scrollCascadeSelect = function (options) {
        var defaults = {
            el: undefined,
            addrArr: [],
            startIndex: 0,
            onInit: function (selectedItem, selectedIndex) { },//exposing the selectedItem and selectIndex parameters for outter using
            onChange: function (selectedItem, selectedIndex) { }//exposing the selectedItem and selectIndex parameters for outter using
        };
        var options = $.extend({}, defaults, options);
        var This = $(options.el),
            el = This.get(0),
            addrArr = options.addrArr,
            startIndex = options.startIndex,
            _self = this;//use '_self' to store 'this' in case of some misunderstanding.
        var isDown = false,
            havedClicked=false,
            startPosY = 0,
            currentPosY = 0,
            startTranslatedY = 0,
            currentTranslatedY = startTranslatedY,
            startTime = 0,
            endTime = 0,
            lastTime = (new Date()).getTime(),
            lastPosY = 0,
            lastV = 0,
            supportedTouch = true;
        try { ontouchstart; } catch (e) { supportedTouch = false; }
        //exposing the selected item and index for outter use
        _self.selectedIndex = startIndex;
        _self.getSelectedItem = function () {
            return addrArr[this.selectedIndex];
        };
        if (supportedTouch) {
            //bind all the touch event
            el.addEventListener('touchstart', function (e) {
                startPosY = e.changedTouches[0].pageY;
                startTime = (new Date()).getTime();
                startTranslatedY = parseInt(This.css("webkitTransform").split(",").pop().replace(" ", "").replace(")", ""));
                lastV = 0;
            }, false);
            el.addEventListener('touchmove', function (e) {
                event.preventDefault();//prevent default scrolling event when touch moving
                lastV = (e.changedTouches[0].pageY - lastPosY) / ((new Date()).getTime() - lastTime);
                currentPosY = e.changedTouches[0].pageY;
                currentTranslatedY = startTranslatedY + currentPosY - startPosY;
                This.css({
                    '-webkit-transition': '-webkit-transform ' + 0 + 's ease-out',
                    '-webkit-transform': 'translate3d(0, ' + currentTranslatedY + 'px, 0)'
                });
                lastPosY = currentPosY;
                lastTime = (new Date()).getTime();
            }, false);
            el.addEventListener('touchend', function (e) {
                endTime = (new Date()).getTime();
                if (Math.abs(currentPosY - startPosY) > 5 && endTime - startTime > 100) {
                    var v = lastV,
              s = v > 0 ? 0.5 * v * v / 0.001 : -0.5 * v * v / 0.001,
              t = Math.abs(v) / 0.001;
                    currentTranslatedY = parseInt(This.css("webkitTransform").split(",").pop().replace(" ", "").replace(")", ""));
                    currentTranslatedY += s;
                    var residue = currentTranslatedY % 40;
                    if (Math.abs(residue) >= 20) {
                        if (residue < 0)
                            currentTranslatedY += ((40 + residue) * -1);
                        else {
                            currentTranslatedY += (40 - residue);
                        }
                    } else {
                        currentTranslatedY -= residue;
                    }
                    if (currentTranslatedY > 80) {
                        currentTranslatedY = 80;
                    } else if (currentTranslatedY < (This.attr("data-height") - 120) * (-1)) {
                        currentTranslatedY = (This.attr("data-height") - 120) * (-1);
                    }
                    This.css({
                        '-webkit-transition': '-webkit-transform ' + t / 1000 + 's ease-out',
                        '-webkit-transform': 'translate3d(0, ' + currentTranslatedY + 'px, 0)'
                    });
                    _self.selectedIndex = Math.abs((currentTranslatedY - 80) / (-40));
                    This.find(".scs_item").removeClass("scs_selected").eq(_self.selectedIndex).addClass("scs_selected");
                    options.onChange(_self.getSelectedItem(), _self.selectedIndex);//trigger onChange event
                } else {
                    havedClicked = true;
                }
                startPosY = 0;
                currentPosY = 0;
                startTranslatedY = 0;
                currentTranslatedY = 0;
                startTime = endTime = 0;
                lastPosY = lastV = 0;
            }, false);
        } else {
            //bind all the mouse event
            el.onmousedown = function (e) {
                isDown = true;
                startPosY = e.pageY;
                startTime = (new Date()).getTime();
                startTranslatedY = parseInt(This.css("webkitTransform").split(",").pop().replace(" ", "").replace(")", ""));
            }
            el.onmousemove = function (e) {
                if (isDown) {
                    lastV = (e.pageY - lastPosY) / ((new Date()).getTime() - lastTime);
                    currentPosY = e.pageY;
                    currentTranslatedY = startTranslatedY + currentPosY - startPosY;
                    This.css({
                        '-webkit-transition': '-webkit-transform ' + 0 + 's ease-out',
                        '-webkit-transform': 'translate3d(0, ' + currentTranslatedY + 'px, 0)'
                    });
                    lastPosY = currentPosY;
                    lastTime = (new Date()).getTime();
                }
            }
            el.onmouseup = function (e) {
                endTime = (new Date()).getTime();
                if (Math.abs(currentPosY - startPosY) > 5 && endTime - startTime > 100) {
                    var v = lastV,
              s = v > 0 ? 0.5 * v * v / 0.001 : -0.5 * v * v / 0.001,
              t = Math.abs(v) / 0.001;
                    currentTranslatedY = parseInt(This.css("webkitTransform").split(",").pop().replace(" ", "").replace(")", ""));
                    currentTranslatedY += s;

                    var residue = currentTranslatedY % 40;
                    if (Math.abs(residue) >= 20) {
                        if (residue < 0)
                            currentTranslatedY += ((40 + residue) * -1);
                        else {
                            currentTranslatedY += (40 - residue);
                        }
                    } else {
                        currentTranslatedY -= residue;
                    }
                    if (currentTranslatedY > 80) {
                        currentTranslatedY = 80;
                    } else if (currentTranslatedY < (This.attr("data-height") - 120) * (-1)) {
                        currentTranslatedY = (This.attr("data-height") - 120) * (-1);
                    }
                    This.css({
                        '-webkit-transition': '-webkit-transform ' + t / 1000 + 's ease-out',
                        '-webkit-transform': 'translate3d(0, ' + currentTranslatedY + 'px, 0)'
                    });
                    _self.selectedIndex = Math.abs((currentTranslatedY - 80) / (-40));
                    This.find(".scs_item").removeClass("scs_selected").eq(_self.selectedIndex).addClass("scs_selected");
                    options.onChange(_self.getSelectedItem(), _self.selectedIndex);//trigger onChange event
                } else {
                    havedClicked = true;
                }
                isDown = false;
                startPosY = 0;
                currentPosY = 0;
                startTranslatedY = 0;
                currentTranslatedY = 0;
                startTime = endTime = 0;
                lastPosY = lastV = 0;
            };
            el.onmouseleave = function (e) {
                if (isDown) {
                    endTime = (new Date()).getTime();
                    if (Math.abs(currentPosY - startPosY) > 5 && endTime - startTime > 100) {//exclude the 'click' event
                        var v = lastV,
                  s = v > 0 ? 0.5 * v * v / 0.001 : -0.5 * v * v / 0.001,
                  t = Math.abs(v) / 0.001;
                        currentTranslatedY = parseInt(This.css("webkitTransform").split(",").pop().replace(" ", "").replace(")", ""));
                        currentTranslatedY += s;
                            residue = currentTranslatedY % 40;
                        if (Math.abs(residue) >= 20) {
                            if (residue < 0)
                                currentTranslatedY += ((40 + residue) * -1);
                            else {
                                currentTranslatedY += (40 - residue);
                            }
                        } else {
                            currentTranslatedY -= residue;
                        }
                        if (currentTranslatedY > 80) {
                            currentTranslatedY = 80;
                        } else if (currentTranslatedY < (This.attr("data-height") - 120) * (-1)) {
                            currentTranslatedY = (This.attr("data-height") - 120) * (-1);
                        }
                        This.css({
                            '-webkit-transition': '-webkit-transform ' + t / 1000 + 's ease-out',
                            '-webkit-transform': 'translate3d(0, ' + currentTranslatedY + 'px, 0)'
                        });
                        _self.selectedIndex = Math.abs((currentTranslatedY - 80) / (-40));
                        This.find(".scs_item").removeClass("scs_selected").eq(_self.selectedIndex).addClass("scs_selected");
                        options.onChange(_self.getSelectedItem(), _self.selectedIndex);//trigger onChange event
                    }
                    isDown = false;
                    startPosY = 0;
                    currentPosY = 0;
                    startTranslatedY = 0;
                    currentTranslatedY = 0;
                    startTime = endTime = 0;
                    lastPosY = lastV = 0;
                }
            }
        }
        //bind a click event
        This.on("click", ".scs_item", function (e) {
            if (havedClicked) {
                var That = $(this),
                itemPositionY = That.position().top,
                currentTranslatedY = 80 - itemPositionY;
                This.css({
                    '-webkit-transition': '-webkit-transform ' + 0 + 's ease-out',
                    '-webkit-transform': 'translate3d(0, ' + currentTranslatedY + 'px, 0)'
                });
                This.find(".scs_item").removeClass("scs_selected");
                That.addClass("scs_selected");
                _self.selectedIndex = Math.abs((currentTranslatedY - 80) / (-40));
                options.onChange(_self.getSelectedItem(), _self.selectedIndex);//trigger onChange event
                havedClick = false;
            }
        });
        //a public out-exposing function, render / re-render the scroller, based on the parameters 'newAddrArr' and 'newStartIndex'
        _self.render = function (newAddrArr, newStartIndex, callback) {
            var html = "",
                sIndex = newStartIndex || 0,
                currentTranslatedY = 80 - sIndex * 40;
            if (newAddrArr != undefined)
                newAddrArr.forEach(function (obj, index) {
                    if (sIndex != index)
                        html += '
' + obj.val + '
';
                    else {
                        html += '
' + obj.val + '
';
                    }
                });
            else {
                This.html(html);
                return;
            }
            This.html(html);
            This.attr("data-height", newAddrArr.length * 40);
            This.css('-webkit-transform', 'translate3d(0, ' + currentTranslatedY + 'px, 0)');
            This.css('transform', 'translate3d(0, ' + currentTranslatedY + 'px, 0)');
            _self.selectedIndex = sIndex;
            addrArr = newAddrArr;
            if (typeof callback === 'function')
                callback();

        };
        //private funtion，initialization
        var init = function () {
            _self.render(addrArr, startIndex);
            options.onInit(_self.getSelectedItem(), _self.selectedIndex);//trigger the onInit callback function
        };
        init();//bootstrap a scroller
    }
})(this,jQuery);
<|endoftext|>


<commit_before>/**
* @hack 替换pixi的DisplayObject，优化 updateTransform
*/

// 显示改变的各种状态
var DisplayChangeStatus = qc.DisplayChangeStatus = {
    OFFSET : 0x01,
    SCALE : 0x02,
    ROTATION : 0x04,
    SIZE : 0x08,
    TINT : 0x10,
    ALPHA : 0x20,
    SHOW : 0x40,
    HIDE : 0x80,
    TEXTURE : 0x100,
    ORDER : 0x200
};

DisplayChangeStatus.VISIBLE_MASK = DisplayChangeStatus.SHOW | DisplayChangeStatus.HIDE;
DisplayChangeStatus.INHERIT_MASK = DisplayChangeStatus.SIZE | DisplayChangeStatus.ORDER | DisplayChangeStatus.OFFSET | DisplayChangeStatus.SCALE | DisplayChangeStatus.ROTATION | DisplayChangeStatus.VISIBLE_MASK;
DisplayChangeStatus.RECALC_MASK = DisplayChangeStatus.ROTATION | DisplayChangeStatus.VISIBLE_MASK | DisplayChangeStatus.SIZE | DisplayChangeStatus.SCALE;
DisplayChangeStatus.REOFFSET_MASK = DisplayChangeStatus.OFFSET;

/**
 * 销毁时需要注册脏矩形区域
 */
var oldDestroy = PIXI.DisplayObject.prototype.destroy;
PIXI.DisplayObject.prototype.destroy = function() {
    this.maybeOutWorld();
    oldDestory.call(this);
};

/**
 * 设置绘制代理，主要用于CacheAsBitmap等处理
 */
PIXI.DisplayObject.prototype.setDisplayProxy = function(proxy, skipChildren) {
    this._displayProxy = proxy;
    this._skipChildren = skipChildren;
};

/**
 * 得到绘制代理对象
 */
PIXI.DisplayObject.prototype.getDisplayProxy = function() {
    return this._displayProxy;
};

/**
 * 是否跳过子节点绘制
 */
PIXI.DisplayObject.prototype.isSkipChildrenRender = function() {
    return this._skipChildren;
};

/**
 * 可能被移出世界，并不在进入世界显示时
 */
PIXI.DisplayObject.prototype.maybeOutWorld = function() {
    if (this.game)
        this.game._qc && this.game._qc.dirtyRectangle.redirectDirty(this._currWorldBounds);
    else if (this.stage) {
        this.stage.game._qc && this.stage.game._qc.dirtyRectangle.redirectDirty(this._currWorldBounds);
    }
};

/**
 * 显示状态改变
 */
PIXI.DisplayObject.prototype.displayChanged = function(type) {
    if (type === 0 || type == null) {
        return;
    }
    var self = this;
    if (self._displayChangeStatus == null) {
        self._displayChangeStatus = DisplayChangeStatus.SHOW;
    }
    self._displayChangeStatus |= type;
    if (type & DisplayChangeStatus.HIDE) {
        // 隐藏时，刷新下所有子节点的位置等信息。
        this.displayObjectUpdateTransform();
        for (var i = 0, j = this.children.length; i < j; ++i) {
            this.children[i].updateTransform();
        }
    }
    else if (type & DisplayChangeStatus.SHOW) {
        self._displayChangeStatus &= ~DisplayChangeStatus.HIDE;
    }

    var parent = self;
    while ((parent = parent.parent) && !parent._subDisplayChanged) {
        parent._subDisplayChanged = true;
    }
};

/**
 * 快速计算显示对象的边界信息
 */
PIXI.DisplayObject.prototype._fastCalcBounds = function(lastBounds, dirtyStatus) {
    var self = this,
        width = self.getSelfWidth && self.getSelfWidth(),
        height = self.getSelfHeight && self.getSelfHeight();

    // 隐藏，完全透明，宽或高为0时，当前区域为空
    if ((dirtyStatus & DisplayChangeStatus.HIDE) ||
        !self.worldAlpha ||
        !self.worldVisible ||
        !width ||
        !height)
        return null;
    var wt = self.worldTransform,
        a = wt.a,
        b = wt.b,
        c = wt.c,
        d = wt.d,
        tx = wt.tx,
        ty = wt.ty;

    // 没有历史记录或者需要重新计算，则完全重算
    if (!lastBounds || (dirtyStatus & DisplayChangeStatus.RECALC_MASK)) {
        var anchorX = self.anchor ? self.anchor.x : 0,
            anchorY = self.anchor ? self.anchor.y : 0;
        var sx = width * -anchorX,
            sy = height * -anchorY,
            ex = sx + width,
            ey = sy + height;
        var temp;
        if (width < 0) {
            temp = sx;
            sx = ex;
            ex = temp;
        }
        if (height < 0) {
            temp = sy;
            sy = ey;
            ey = temp;
        }
        var minX, maxX, minY, maxY;

        if (b === 0 && c === 0) {
            if (a < 0) {
                minX = a * ex + tx;
                maxX = a * sx + tx;
            }
            else {
                minX = a * sx + tx;
                maxX = a * ex + tx;
            }

            if (d < 0) {
                minY = d * ey + ty;
                maxY = d * sy + ty;
            }
            else {
                minY = d * sy + ty;
                maxY = d * ey + ty;
            }
        }
        else {
            var aminx, amaxx, bminx, bmaxx, cminy, cmaxy, dminy, dmaxy;
            if (a >= 0) {
                aminx = a * sx;
                amaxx = a * ex;
            }
            else {
                aminx = a * ex;
                amaxx = a * sx;
            }
            if (b >= 0) {
                bminx = b * sx;
                bmaxx = b * ex;
            }
            else {
                bminx = b * ex;
                bmaxx = b * sx;
            }
            if (c >= 0) {
                cminy = c * sy;
                cmaxy = c * ey;
            }
            else {
                cminy = c * ey;
                cmaxy = c * sy;
            }
            if (d >= 0) {
                dminy = d * sy;
                dmaxy = d * ey;
            }
            else {
                dminy = d * ey;
                dmaxy = d * sy;
            }

            minX = aminx + cminy + tx;
            maxX = amaxx + cmaxy + tx;
            minY = bminx + dminy + ty;
            maxY = bmaxx + dmaxy + ty;
        }
        return {
            x : minX,
            y : minY,
            width : maxX - minX,
            height : maxY - minY,
            tx : tx,
            ty : ty,
            area : (maxX - minX) * (maxY - minY)
        };
    }
    else if (dirtyStatus & DisplayChangeStatus.REOFFSET_MASK) {
        var offX = tx - lastBounds.tx,
            offY = ty - lastBounds.ty;
        return {
            x : lastBounds.x + tx - lastBounds.tx,
            y : lastBounds.y + ty - lastBounds.ty,
            width : lastBounds.width,
            height : lastBounds.height,
            tx : tx,
            ty : ty,
            area : lastBounds.area
        };
    }
    else {
        // tint变化或者alpha变化，范围矩形不变
        return {
            x : lastBounds.x,
            y : lastBounds.y,
            tx : lastBounds.tx,
            ty : lastBounds.ty,
            width : lastBounds.width,
            height : lastBounds.height,
            area : lastBounds.area
        };
    }
};

/**
 * 更新节点在世界中的范围
 * 在调用前，必须确保调用过updateTransform，以保证能获取到正确的worldTransform
 * 返回是否可以跳过当前节点子节点的计算
 */
PIXI.DisplayObject.prototype._updateBounds = function(force) {
    var self = this,
        wt = self.worldTransform,
        parent = self.parent;

    var parentStatus = parent ? parent._displayChangeStatus : 0;

    if (self._displayChangeStatus == null || force) {
        self._displayChangeStatus = DisplayChangeStatus.SHOW;
    }
    var dirtyStatus = self._displayChangeStatus;

    // 自身属性的变化，在变化时进行设置，这里主要是继承父节点的变化
    // 父节点的偏移，缩放，旋转和visible发生变化时，自己需要继承
    dirtyStatus = dirtyStatus | (parentStatus & DisplayChangeStatus.INHERIT_MASK);
    if (!self.worldVisible && !(dirtyStatus & DisplayChangeStatus.HIDE)){
        // 不可见且不是HIDE事件发生的帧则忽略后续计算
        return true;
    }

    if (dirtyStatus === 0)
        return false;

    // 将当前范围信息记入历史
    self._lastWorldBounds || (self._lastWorldBounds = []);
    var isFrameSamples = self._qc && self._qc.animationType === qc.Sprite.FRAME_SAMPLES;
    var record = self._currWorldBounds;
    self._currWorldBounds && self._lastWorldBounds.push(self._currWorldBounds);
    self._displayChangeStatus = dirtyStatus;
    if (isFrameSamples) {
        var bounds = self._qc.getSampledAnimationBounds();
        var p1 = self.worldTransform.apply(new qc.Point(bounds.x, bounds.y));
        var p2 = self.worldTransform.apply(new qc.Point(bounds.x + bounds.width, bounds.y));
        var p3 = self.worldTransform.apply(new qc.Point(bounds.x + bounds.width, bounds.y + bounds.height));
        var p4 = self.worldTransform.apply(new qc.Point(bounds.x, bounds.y + bounds.height));

        // 取极值
        var left = Math.min(p1.x, p2.x, p3.x, p4.x);
        var right = Math.max(p1.x, p2.x, p3.x, p4.x);
        var top = Math.min(p1.y, p2.y, p3.y, p4.y);
        var bottom = Math.max(p1.y, p2.y, p3.y, p4.y);
        self._currWorldBounds = {
            x: left,
            y: top,
            width: right - left,
            height: bottom - top,
            area: (right - left) * (bottom - top)
        };
    }
    else {
        self._currWorldBounds = self._fastCalcBounds(self._lastWorldBounds[self._lastWorldBounds.length - 1], dirtyStatus);
    }
    self._dirtyBounds = record !== self._currWorldBounds;
    return false;
};

PIXI.DisplayObject.prototype._resetDisplayChangeStatus = function() {
    var self = this;
    self._displayChangeStatus = 0;
};

/*
* Updates the object transform for rendering
*
* @method updateTransform
* @private
*/
PIXI.DisplayObject.prototype.updateTransform = function() {
    var parent = this.parent;
    if (!parent) {
        return;
    }

    // TODO: 考虑将此代码移除出去，没有必要每帧都来调度
    // multiply the alphas..
    this.worldAlpha = this.alpha * this.parent.worldAlpha;
    if (this._lastWorldAlpha !== this.worldAlpha) {
        this._lastWorldAlpha = this.worldAlpha;
        this.displayChanged(DisplayChangeStatus.ALPHA);
    }

    if (!this._isNotNeedCalcTransform || parent._isSubNeedCalcTransform) {
        this.game && this.game._calcTransformCount++;
        var pt = this.parent.worldTransform;
        var wt = this.worldTransform;

        // temporary matrix variables
        var a, b, c, d, tx, ty;
        wt.inUpdate = true;
        // so if rotation is between 0 then we can simplify the multiplication process..
        if (this.rotation % PIXI.PI_2) {
            // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes
            if (this.rotation !== this.rotationCache) {
                this.rotationCache = this.rotation;
                this._sr = Math.sin(this.rotation);
                this._cr = Math.cos(this.rotation);
            }

            // get the matrix values of the displayobject based on its transform properties..
            a  =  this._cr * this.scale.x;
            b  =  this._sr * this.scale.x;
            c  = -this._sr * this.scale.y;
            d  =  this._cr * this.scale.y;
            tx =  this.position.x;
            ty =  this.position.y;

            // check for pivot.. not often used so geared towards that fact!
            if (this.pivot.x || this.pivot.y) {
                tx -= this.pivot.x * a + this.pivot.y * c;
                ty -= this.pivot.x * b + this.pivot.y * d;
            }

            // concat the parent matrix with the objects transform.
            wt.a  = a  * pt.a + b  * pt.c;
            wt.b  = a  * pt.b + b  * pt.d;
            wt.c  = c  * pt.a + d  * pt.c;
            wt.d  = c  * pt.b + d  * pt.d;
            wt.tx = tx * pt.a + ty * pt.c + pt.tx;
            wt.ty = tx * pt.b + ty * pt.d + pt.ty;
        }
        else {
            // lets do the fast version as we know there is no rotation..
            a  = this.scale.x;
            d  = this.scale.y;

            tx = this.position.x - this.pivot.x * a;
            ty = this.position.y - this.pivot.y * d;

            wt.a  = a  * pt.a;
            wt.b  = a  * pt.b;
            wt.c  = d  * pt.c;
            wt.d  = d  * pt.d;
            wt.tx = tx * pt.a + ty * pt.c + pt.tx;
            wt.ty = tx * pt.b + ty * pt.d + pt.ty;
        }
        this._isNotNeedCalcTransform = true;
        this._isSubNeedCalcTransform = true;

        if (this.worldTransformChangedCallback) {
            this.worldTransformChangedCallback.call(this.worldTransformChangedContext);
        }

        //  Custom callback?
        if (this.transformCallback) {
            wt.inUpdate = false;
            this.transformCallback.call(this.transformCallbackContext, wt, pt);
        }

        if (this._onTransformChanged) {
            this._onTransformChanged.dispatch(wt, pt);
        }
    }
    else {
        this._isSubNeedCalcTransform = false;
        var wt = this.worldTransform;
        var pt = parent.worldTransform;

        //  Custom callback?
        if (this.transformCallback) {
            wt.inUpdate = false;
            this.transformCallback.call(this.transformCallbackContext, wt, pt);
        }
    }
};

/**
 * 替换updateTransform
 */
PIXI.DisplayObject.prototype.displayObjectUpdateTransform = PIXI.DisplayObject.prototype.updateTransform;

/**
 * 获取当前的worldTransform
 * @returns {PIXI.DisplayObject.worldTransform|*}
 */
PIXI.DisplayObject.prototype.getWorldTransform = function() {
    var isDirty = false;
    var parent = this;
    var lastDirtyNode = null;
    while (parent) {
        if (!parent._isNotNeedCalcTransform) {
            isDirty = true;
            lastDirtyNode = parent;
        }
        parent = parent.parent;
    }
    if (isDirty) {
        lastDirtyNode.updateTransform();
    }
    return this.worldTransform;
};
<commit_msg>Fixed: Destructor error caused misspelled word.<commit_after>/**
* @hack 替换pixi的DisplayObject，优化 updateTransform
*/

// 显示改变的各种状态
var DisplayChangeStatus = qc.DisplayChangeStatus = {
    OFFSET : 0x01,
    SCALE : 0x02,
    ROTATION : 0x04,
    SIZE : 0x08,
    TINT : 0x10,
    ALPHA : 0x20,
    SHOW : 0x40,
    HIDE : 0x80,
    TEXTURE : 0x100,
    ORDER : 0x200
};

DisplayChangeStatus.VISIBLE_MASK = DisplayChangeStatus.SHOW | DisplayChangeStatus.HIDE;
DisplayChangeStatus.INHERIT_MASK = DisplayChangeStatus.SIZE | DisplayChangeStatus.ORDER | DisplayChangeStatus.OFFSET | DisplayChangeStatus.SCALE | DisplayChangeStatus.ROTATION | DisplayChangeStatus.VISIBLE_MASK;
DisplayChangeStatus.RECALC_MASK = DisplayChangeStatus.ROTATION | DisplayChangeStatus.VISIBLE_MASK | DisplayChangeStatus.SIZE | DisplayChangeStatus.SCALE;
DisplayChangeStatus.REOFFSET_MASK = DisplayChangeStatus.OFFSET;

/**
 * 销毁时需要注册脏矩形区域
 */
var oldDestroy = PIXI.DisplayObject.prototype.destroy;
PIXI.DisplayObject.prototype.destroy = function() {
    this.maybeOutWorld();
    oldDestroy.call(this);
};

/**
 * 设置绘制代理，主要用于CacheAsBitmap等处理
 */
PIXI.DisplayObject.prototype.setDisplayProxy = function(proxy, skipChildren) {
    this._displayProxy = proxy;
    this._skipChildren = skipChildren;
};

/**
 * 得到绘制代理对象
 */
PIXI.DisplayObject.prototype.getDisplayProxy = function() {
    return this._displayProxy;
};

/**
 * 是否跳过子节点绘制
 */
PIXI.DisplayObject.prototype.isSkipChildrenRender = function() {
    return this._skipChildren;
};

/**
 * 可能被移出世界，并不在进入世界显示时
 */
PIXI.DisplayObject.prototype.maybeOutWorld = function() {
    if (this.game)
        this.game._qc && this.game._qc.dirtyRectangle.redirectDirty(this._currWorldBounds);
    else if (this.stage) {
        this.stage.game._qc && this.stage.game._qc.dirtyRectangle.redirectDirty(this._currWorldBounds);
    }
};

/**
 * 显示状态改变
 */
PIXI.DisplayObject.prototype.displayChanged = function(type) {
    if (type === 0 || type == null) {
        return;
    }
    var self = this;
    if (self._displayChangeStatus == null) {
        self._displayChangeStatus = DisplayChangeStatus.SHOW;
    }
    self._displayChangeStatus |= type;
    if (type & DisplayChangeStatus.HIDE) {
        // 隐藏时，刷新下所有子节点的位置等信息。
        this.displayObjectUpdateTransform();
        for (var i = 0, j = this.children.length; i < j; ++i) {
            this.children[i].updateTransform();
        }
    }
    else if (type & DisplayChangeStatus.SHOW) {
        self._displayChangeStatus &= ~DisplayChangeStatus.HIDE;
    }

    var parent = self;
    while ((parent = parent.parent) && !parent._subDisplayChanged) {
        parent._subDisplayChanged = true;
    }
};

/**
 * 快速计算显示对象的边界信息
 */
PIXI.DisplayObject.prototype._fastCalcBounds = function(lastBounds, dirtyStatus) {
    var self = this,
        width = self.getSelfWidth && self.getSelfWidth(),
        height = self.getSelfHeight && self.getSelfHeight();

    // 隐藏，完全透明，宽或高为0时，当前区域为空
    if ((dirtyStatus & DisplayChangeStatus.HIDE) ||
        !self.worldAlpha ||
        !self.worldVisible ||
        !width ||
        !height)
        return null;
    var wt = self.worldTransform,
        a = wt.a,
        b = wt.b,
        c = wt.c,
        d = wt.d,
        tx = wt.tx,
        ty = wt.ty;

    // 没有历史记录或者需要重新计算，则完全重算
    if (!lastBounds || (dirtyStatus & DisplayChangeStatus.RECALC_MASK)) {
        var anchorX = self.anchor ? self.anchor.x : 0,
            anchorY = self.anchor ? self.anchor.y : 0;
        var sx = width * -anchorX,
            sy = height * -anchorY,
            ex = sx + width,
            ey = sy + height;
        var temp;
        if (width < 0) {
            temp = sx;
            sx = ex;
            ex = temp;
        }
        if (height < 0) {
            temp = sy;
            sy = ey;
            ey = temp;
        }
        var minX, maxX, minY, maxY;

        if (b === 0 && c === 0) {
            if (a < 0) {
                minX = a * ex + tx;
                maxX = a * sx + tx;
            }
            else {
                minX = a * sx + tx;
                maxX = a * ex + tx;
            }

            if (d < 0) {
                minY = d * ey + ty;
                maxY = d * sy + ty;
            }
            else {
                minY = d * sy + ty;
                maxY = d * ey + ty;
            }
        }
        else {
            var aminx, amaxx, bminx, bmaxx, cminy, cmaxy, dminy, dmaxy;
            if (a >= 0) {
                aminx = a * sx;
                amaxx = a * ex;
            }
            else {
                aminx = a * ex;
                amaxx = a * sx;
            }
            if (b >= 0) {
                bminx = b * sx;
                bmaxx = b * ex;
            }
            else {
                bminx = b * ex;
                bmaxx = b * sx;
            }
            if (c >= 0) {
                cminy = c * sy;
                cmaxy = c * ey;
            }
            else {
                cminy = c * ey;
                cmaxy = c * sy;
            }
            if (d >= 0) {
                dminy = d * sy;
                dmaxy = d * ey;
            }
            else {
                dminy = d * ey;
                dmaxy = d * sy;
            }

            minX = aminx + cminy + tx;
            maxX = amaxx + cmaxy + tx;
            minY = bminx + dminy + ty;
            maxY = bmaxx + dmaxy + ty;
        }
        return {
            x : minX,
            y : minY,
            width : maxX - minX,
            height : maxY - minY,
            tx : tx,
            ty : ty,
            area : (maxX - minX) * (maxY - minY)
        };
    }
    else if (dirtyStatus & DisplayChangeStatus.REOFFSET_MASK) {
        var offX = tx - lastBounds.tx,
            offY = ty - lastBounds.ty;
        return {
            x : lastBounds.x + tx - lastBounds.tx,
            y : lastBounds.y + ty - lastBounds.ty,
            width : lastBounds.width,
            height : lastBounds.height,
            tx : tx,
            ty : ty,
            area : lastBounds.area
        };
    }
    else {
        // tint变化或者alpha变化，范围矩形不变
        return {
            x : lastBounds.x,
            y : lastBounds.y,
            tx : lastBounds.tx,
            ty : lastBounds.ty,
            width : lastBounds.width,
            height : lastBounds.height,
            area : lastBounds.area
        };
    }
};

/**
 * 更新节点在世界中的范围
 * 在调用前，必须确保调用过updateTransform，以保证能获取到正确的worldTransform
 * 返回是否可以跳过当前节点子节点的计算
 */
PIXI.DisplayObject.prototype._updateBounds = function(force) {
    var self = this,
        wt = self.worldTransform,
        parent = self.parent;

    var parentStatus = parent ? parent._displayChangeStatus : 0;

    if (self._displayChangeStatus == null || force) {
        self._displayChangeStatus = DisplayChangeStatus.SHOW;
    }
    var dirtyStatus = self._displayChangeStatus;

    // 自身属性的变化，在变化时进行设置，这里主要是继承父节点的变化
    // 父节点的偏移，缩放，旋转和visible发生变化时，自己需要继承
    dirtyStatus = dirtyStatus | (parentStatus & DisplayChangeStatus.INHERIT_MASK);
    if (!self.worldVisible && !(dirtyStatus & DisplayChangeStatus.HIDE)){
        // 不可见且不是HIDE事件发生的帧则忽略后续计算
        return true;
    }

    if (dirtyStatus === 0)
        return false;

    // 将当前范围信息记入历史
    self._lastWorldBounds || (self._lastWorldBounds = []);
    var isFrameSamples = self._qc && self._qc.animationType === qc.Sprite.FRAME_SAMPLES;
    var record = self._currWorldBounds;
    self._currWorldBounds && self._lastWorldBounds.push(self._currWorldBounds);
    self._displayChangeStatus = dirtyStatus;
    if (isFrameSamples) {
        var bounds = self._qc.getSampledAnimationBounds();
        var p1 = self.worldTransform.apply(new qc.Point(bounds.x, bounds.y));
        var p2 = self.worldTransform.apply(new qc.Point(bounds.x + bounds.width, bounds.y));
        var p3 = self.worldTransform.apply(new qc.Point(bounds.x + bounds.width, bounds.y + bounds.height));
        var p4 = self.worldTransform.apply(new qc.Point(bounds.x, bounds.y + bounds.height));

        // 取极值
        var left = Math.min(p1.x, p2.x, p3.x, p4.x);
        var right = Math.max(p1.x, p2.x, p3.x, p4.x);
        var top = Math.min(p1.y, p2.y, p3.y, p4.y);
        var bottom = Math.max(p1.y, p2.y, p3.y, p4.y);
        self._currWorldBounds = {
            x: left,
            y: top,
            width: right - left,
            height: bottom - top,
            area: (right - left) * (bottom - top)
        };
    }
    else {
        self._currWorldBounds = self._fastCalcBounds(self._lastWorldBounds[self._lastWorldBounds.length - 1], dirtyStatus);
    }
    self._dirtyBounds = record !== self._currWorldBounds;
    return false;
};

PIXI.DisplayObject.prototype._resetDisplayChangeStatus = function() {
    var self = this;
    self._displayChangeStatus = 0;
};

/*
* Updates the object transform for rendering
*
* @method updateTransform
* @private
*/
PIXI.DisplayObject.prototype.updateTransform = function() {
    var parent = this.parent;
    if (!parent) {
        return;
    }

    // TODO: 考虑将此代码移除出去，没有必要每帧都来调度
    // multiply the alphas..
    this.worldAlpha = this.alpha * this.parent.worldAlpha;
    if (this._lastWorldAlpha !== this.worldAlpha) {
        this._lastWorldAlpha = this.worldAlpha;
        this.displayChanged(DisplayChangeStatus.ALPHA);
    }

    if (!this._isNotNeedCalcTransform || parent._isSubNeedCalcTransform) {
        this.game && this.game._calcTransformCount++;
        var pt = this.parent.worldTransform;
        var wt = this.worldTransform;

        // temporary matrix variables
        var a, b, c, d, tx, ty;
        wt.inUpdate = true;
        // so if rotation is between 0 then we can simplify the multiplication process..
        if (this.rotation % PIXI.PI_2) {
            // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes
            if (this.rotation !== this.rotationCache) {
                this.rotationCache = this.rotation;
                this._sr = Math.sin(this.rotation);
                this._cr = Math.cos(this.rotation);
            }

            // get the matrix values of the displayobject based on its transform properties..
            a  =  this._cr * this.scale.x;
            b  =  this._sr * this.scale.x;
            c  = -this._sr * this.scale.y;
            d  =  this._cr * this.scale.y;
            tx =  this.position.x;
            ty =  this.position.y;

            // check for pivot.. not often used so geared towards that fact!
            if (this.pivot.x || this.pivot.y) {
                tx -= this.pivot.x * a + this.pivot.y * c;
                ty -= this.pivot.x * b + this.pivot.y * d;
            }

            // concat the parent matrix with the objects transform.
            wt.a  = a  * pt.a + b  * pt.c;
            wt.b  = a  * pt.b + b  * pt.d;
            wt.c  = c  * pt.a + d  * pt.c;
            wt.d  = c  * pt.b + d  * pt.d;
            wt.tx = tx * pt.a + ty * pt.c + pt.tx;
            wt.ty = tx * pt.b + ty * pt.d + pt.ty;
        }
        else {
            // lets do the fast version as we know there is no rotation..
            a  = this.scale.x;
            d  = this.scale.y;

            tx = this.position.x - this.pivot.x * a;
            ty = this.position.y - this.pivot.y * d;

            wt.a  = a  * pt.a;
            wt.b  = a  * pt.b;
            wt.c  = d  * pt.c;
            wt.d  = d  * pt.d;
            wt.tx = tx * pt.a + ty * pt.c + pt.tx;
            wt.ty = tx * pt.b + ty * pt.d + pt.ty;
        }
        this._isNotNeedCalcTransform = true;
        this._isSubNeedCalcTransform = true;

        if (this.worldTransformChangedCallback) {
            this.worldTransformChangedCallback.call(this.worldTransformChangedContext);
        }

        //  Custom callback?
        if (this.transformCallback) {
            wt.inUpdate = false;
            this.transformCallback.call(this.transformCallbackContext, wt, pt);
        }

        if (this._onTransformChanged) {
            this._onTransformChanged.dispatch(wt, pt);
        }
    }
    else {
        this._isSubNeedCalcTransform = false;
        var wt = this.worldTransform;
        var pt = parent.worldTransform;

        //  Custom callback?
        if (this.transformCallback) {
            wt.inUpdate = false;
            this.transformCallback.call(this.transformCallbackContext, wt, pt);
        }
    }
};

/**
 * 替换updateTransform
 */
PIXI.DisplayObject.prototype.displayObjectUpdateTransform = PIXI.DisplayObject.prototype.updateTransform;

/**
 * 获取当前的worldTransform
 * @returns {PIXI.DisplayObject.worldTransform|*}
 */
PIXI.DisplayObject.prototype.getWorldTransform = function() {
    var isDirty = false;
    var parent = this;
    var lastDirtyNode = null;
    while (parent) {
        if (!parent._isNotNeedCalcTransform) {
            isDirty = true;
            lastDirtyNode = parent;
        }
        parent = parent.parent;
    }
    if (isDirty) {
        lastDirtyNode.updateTransform();
    }
    return this.worldTransform;
};
<|endoftext|>


<commit_before>import { Vector3 } from '../math/Vector3';
import { Object3D } from '../core/Object3D';
import { LineSegments } from '../objects/LineSegments';
import { LineBasicMaterial } from '../materials/LineBasicMaterial';
import { Float32BufferAttribute } from '../core/BufferAttribute';
import { BufferGeometry } from '../core/BufferGeometry';

/**
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 * @author WestLangley / http://github.com/WestLangley
*/

function SpotLightHelper( light ) {

	Object3D.call( this );

	this.light = light;
	this.light.updateMatrixWorld();

	this.matrix = light.matrixWorld;
	this.matrixAutoUpdate = false;

	var geometry = new BufferGeometry();

	var positions = [
		0, 0, 0,   0,   0,   1,
		0, 0, 0,   1,   0,   1,
		0, 0, 0, - 1,   0,   1,
		0, 0, 0,   0,   1,   1,
		0, 0, 0,   0, - 1,   1
	];

	for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) {

		var p1 = ( i / l ) * Math.PI * 2;
		var p2 = ( j / l ) * Math.PI * 2;

		positions.push(
			Math.cos( p1 ), Math.sin( p1 ), 1,
			Math.cos( p2 ), Math.sin( p2 ), 1
		);

	}

	geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );

	var material = new LineBasicMaterial( { fog: false } );

	this.cone = new LineSegments( geometry, material );
	this.add( this.cone );

	this.update();

}

SpotLightHelper.prototype = Object.create( Object3D.prototype );
SpotLightHelper.prototype.constructor = SpotLightHelper;

SpotLightHelper.prototype.dispose = function () {

	this.cone.geometry.dispose();
	this.cone.material.dispose();

};

SpotLightHelper.prototype.update = function () {

	var vector = new Vector3();
	var vector2 = new Vector3();

	return function update() {

		var coneLength = this.light.distance ? this.light.distance : 1000;
		var coneWidth = coneLength * Math.tan( this.light.angle );

		this.cone.scale.set( coneWidth, coneWidth, coneLength );

		vector.setFromMatrixPosition( this.light.matrixWorld );
		vector2.setFromMatrixPosition( this.light.target.matrixWorld );

		this.cone.lookAt( vector2.sub( vector ) );

		this.cone.material.color.copy( this.light.color );

	};

}();

export { SpotLightHelper };
<commit_msg>SpotLightHelper: Make sure matrixWorld is updated.<commit_after>import { Vector3 } from '../math/Vector3';
import { Object3D } from '../core/Object3D';
import { LineSegments } from '../objects/LineSegments';
import { LineBasicMaterial } from '../materials/LineBasicMaterial';
import { Float32BufferAttribute } from '../core/BufferAttribute';
import { BufferGeometry } from '../core/BufferGeometry';

/**
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 * @author WestLangley / http://github.com/WestLangley
*/

function SpotLightHelper( light ) {

	Object3D.call( this );

	this.light = light;
	this.light.updateMatrixWorld();

	this.matrix = light.matrixWorld;
	this.matrixAutoUpdate = false;

	var geometry = new BufferGeometry();

	var positions = [
		0, 0, 0,   0,   0,   1,
		0, 0, 0,   1,   0,   1,
		0, 0, 0, - 1,   0,   1,
		0, 0, 0,   0,   1,   1,
		0, 0, 0,   0, - 1,   1
	];

	for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) {

		var p1 = ( i / l ) * Math.PI * 2;
		var p2 = ( j / l ) * Math.PI * 2;

		positions.push(
			Math.cos( p1 ), Math.sin( p1 ), 1,
			Math.cos( p2 ), Math.sin( p2 ), 1
		);

	}

	geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );

	var material = new LineBasicMaterial( { fog: false } );

	this.cone = new LineSegments( geometry, material );
	this.add( this.cone );

	this.update();

}

SpotLightHelper.prototype = Object.create( Object3D.prototype );
SpotLightHelper.prototype.constructor = SpotLightHelper;

SpotLightHelper.prototype.dispose = function () {

	this.cone.geometry.dispose();
	this.cone.material.dispose();

};

SpotLightHelper.prototype.update = function () {

	var vector = new Vector3();
	var vector2 = new Vector3();

	return function update() {

		this.light.updateMatrixWorld();

		var coneLength = this.light.distance ? this.light.distance : 1000;
		var coneWidth = coneLength * Math.tan( this.light.angle );

		this.cone.scale.set( coneWidth, coneWidth, coneLength );

		vector.setFromMatrixPosition( this.light.matrixWorld );
		vector2.setFromMatrixPosition( this.light.target.matrixWorld );

		this.cone.lookAt( vector2.sub( vector ) );

		this.cone.material.color.copy( this.light.color );

	};

}();

export { SpotLightHelper };
<|endoftext|>


<commit_before>var getData = require('./data');

module.exports = function (callback) {
    getData(function (err, data) {
        if (err) {
            callback(null, getPage({ body: 'Error Fetching Data. My bad.' }))
        }
        else {
            callback(null, getPage({ body: getDataBody(data.items[0] || {}) }))
        }
    })
}
var style =
    `
        body {
            background-color: #222f35;
            color: #4bd000;
            font-family: sans-serif
        }

        .data-container {
            display: flex;
            flex-wrap: wrap;
        }

        .pressure-container, .humidity-container,  .temp-container, .time-container, .lux-container {
            margin-bottom: 20px;
        }

        .temp-container {
            flex: 0 1 100%;
            font-size: 250px;
            text-align: center;
            padding: 20px 0px 20px 0px;
        }

        .pressure-container, .humidity-container, .lux-container {
            flex: 0 1 100%;
            font-size: 50px;
            text-align: center;
        }

        .time-container {
            flex: 0 1 100%;
            font-size: 30px;
            text-align: center;
        }
    `
var getPage = function (config) {
    return `

            ${config.body}

    `
}

var getDataBody = function (data) {
    var temp = (data.temp || {}).ambient,
        pressure = (data.barometer || {}).pressure,
        humidity = (data.humidity || {}).humidity,
        lux = (data.luxmeter || {}).lux,
        time = new Date(data.time),
        timeOptions = {
            timeZone: 'America/Chicago'
        };
    return `

                ${temp}°

                ${pressure} mBar

                ${humidity}%

                ${lux} lux

                ${time.toLocaleString('en-US', timeOptions).replace(',', '')}

    `
}<commit_msg>Add no data message<commit_after>var getData = require('./data');

module.exports = function (callback) {
    getData(function (err, data) {
        if (err) {
            callback(null, getPage({ body: 'Error Fetching Data. My bad.' }))
        }
        else {
            callback(null, getPage({ body: getDataBody(data.items[0] || null) }))
        }
    })
}
var style =
    `
        body {
            background-color: #222f35;
            color: #4bd000;
            font-family: sans-serif
        }

        .data-container {
            display: flex;
            flex-wrap: wrap;
        }

        .pressure-container, .humidity-container,  .temp-container, .time-container, .lux-container {
            margin-bottom: 20px;
        }

        .temp-container {
            flex: 0 1 100%;
            font-size: 250px;
            text-align: center;
            padding: 20px 0px 20px 0px;
        }

        .pressure-container, .humidity-container, .lux-container {
            flex: 0 1 100%;
            font-size: 50px;
            text-align: center;
        }

        .time-container {
            flex: 0 1 100%;
            font-size: 30px;
            text-align: center;
        }

        .no-data{
            text-align: center;
            margin-top: 50px;
            font-size: 100px;
        }
    `
var getPage = function (config) {
    return `

            ${config.body}

    `
}

var getDataBody = function (data) {
    if (!data) {
        return `<div class="no-data">No Data!</div>`
    }
    var temp = (data.temp || {}).ambient,
        pressure = (data.barometer || {}).pressure,
        humidity = (data.humidity || {}).humidity,
        lux = (data.luxmeter || {}).lux,
        time = new Date(data.time),
        timeOptions = {
            timeZone: 'America/Chicago'
        };
    return `

                ${temp || ''}°

                ${pressure} mBar

                ${humidity}%

                ${lux} lux

                ${time.toLocaleString('en-US', timeOptions).replace(',', '')}

    `
}<|endoftext|>


<commit_before>(function ($) {

	// default player values
	mejs.MepDefaults = {
		// url to poster (to fix iOS 3.x)
		poster: '',
		// default if the <video width> is not specified
		defaultVideoWidth: 480,
		// default if the <video height> is not specified
		defaultVideoHeight: 270,
		// if set, overrides <video width>
		videoWidth: -1,
		// if set, overrides <video height>
		videoHeight: -1,
		// width of audio player
		audioWidth: 400,
		// height of audio player
		audioHeight: 30,
		// initial volume when the player starts (overrided by user cookie)
		startVolume: 0.8,
		// useful for <audio> player loops
		loop: false,
		// resize to media dimensions
		enableAutosize: true,
		// features to show
		features: ['playpause','current','progress','duration','tracks','volume','fullscreen']
	};

	mejs.mepIndex = 0;

	// wraps a MediaElement object in player controls
	mejs.MediaElementPlayer = function($node, o) {
		// enforce object, even without "new" (via John Resig)
		if ( !(this instanceof mejs.MediaElementPlayer) ) {
			return new mejs.MediaElementPlayer($node, o);
		}

		var
			t = this,
			mf = mejs.MediaFeatures;

		// create options
		t.options = $.extend({},mejs.MepDefaults,o);
		t.$media = t.$node = $($node);

		// check for existing player
		if ($node[0].player) {
			return $node[0].player;
		} else {
			// attach player to DOM node for reference
			$node[0].player = t;
		}

		t.isVideo = (t.$media[0].tagName.toLowerCase() === 'video');

		/* FUTURE WORK = create player without existing <video> or <audio> node

		// if not a video or audio tag, then we'll dynamically create it
		if (tagName == 'video' || tagName == 'audio') {
			t.$media = $($node);
		} else if (o.tagName !== '' && o.src !== '') {
			// create a new node
			if (o.mode == 'auto' || o.mode == 'native') {

				$media = $(o.tagName);
				if (typeof o.src == 'string') {
					$media.attr('src',o.src);
				} else if (typeof o.src == 'object') {
					// create source nodes
					for (var x in o.src) {
						$media.append($('<source src="' + o.src[x].src + '" type="' + o.src[x].type + '" />'));
					}
				}
				if (o.type != '') {
					$media.attr('type',o.type);
				}
				if (o.poster != '') {
					$media.attr('poster',o.poster);
				}
				if (o.videoWidth > 0) {
					$media.attr('width',o.videoWidth);
				}
				if (o.videoHeight > 0) {
					$media.attr('height',o.videoHeight);
				}

				$node.clear();
				$node.append($media);
				t.$media = $media;
			} else if (o.mode == 'shim') {
				$media = $();
				// doesn't want a media node
				// let MediaElement object handle this
			}
		} else {
			// fail?
			return;
		}
		*/

		if (mf.isiPad || mf.isiPhone) {
			// add controls and stop
			t.$media.attr('controls', 'controls');

			// fix iOS 3 bug
			t.$media.removeAttr('poster');

			// override Apple's autoplay override for iPads
			if (mf.isiPad && t.$media[0].getAttribute('autoplay') !== null) {
				t.$media[0].load();
				t.$media[0].play();
			}

			// don't do the rest
			return;
		} else if (mf.isAndroid) {

			if (t.isVideo) {
				// Android fails when there are multiple types
				// <video>
				// <source src="file.mp4" type="video/mp4" />
				// <source src="file.webm" type="video/webm" />
				// </video>
				if (t.$media.find('source').length > 0) {
					// find an mp4 and make it the root element source
					t.$media[0].src = t.$media.find('source[src$="mp4"]').attr('src');
				}

				// attach a click event to the video and hope Android can play it
				t.$media.click(function() {
					t.$media[0].play();
				});

				return;
			} else {
				// audio?
				// 2.1 = no support
				// 2.2 = Flash support
				// 2.3 = Native HTML5
			}

		} else {

			// remove native controls and use MEJS
			t.$media.removeAttr('controls');
		}

		t.init();

		return t;
	};

	// actual player
	mejs.MediaElementPlayer.prototype = {
		init: function() {

			var
				t = this,
				meOptions = $.extend(true, {}, t.options, {
					success: function(media, domNode) { t.meReady(media, domNode); },
					error: function(e) { t.handleError(e);}
				});

			// unique ID
			t.id = 'mep_' + mejs.mepIndex++;

			// build container
			t.container =
				$('
'+
					'
'+
						'

'+
						'

'+
						'

'+
						'

'+
					'
' +
				'
')
				.addClass(t.$media[0].className)
				.insertBefore(t.$media);

			// move the <video/video> tag into the right spot
			t.container.find('.mejs-mediaelement').append(t.$media);

			// find parts
			t.controls = t.container.find('.mejs-controls');
			t.layers = t.container.find('.mejs-layers');

			// determine the size
			if (t.isVideo) {
				// priority = videoWidth (forced), width attribute, defaultVideoWidth
				t.width = (t.options.videoWidth > 0) ? t.options.videoWidth : (t.$media[0].getAttribute('width') !== null) ? t.$media.attr('width') : t.options.defaultVideoWidth;
				t.height = (t.options.videoHeight > 0) ? t.options.videoHeight : (t.$media[0].getAttribute('height') !== null) ? t.$media.attr('height') : t.options.defaultVideoHeight;
			} else {
				t.width = t.options.audioWidth;
				t.height = t.options.audioHeight;
			}

			// set the size, while we wait for the plugins to load below
			t.setPlayerSize(t.width, t.height);

			// create MediaElementShim
			meOptions.pluginWidth = t.height;
			meOptions.pluginHeight = t.width;
			mejs.MediaElement(t.$media[0], meOptions);
		},

		// Sets up all controls and events
		meReady: function(media, domNode) {

			var t = this,
				f,
				feature;

			// make sure it can't create itself again if a plugin reloads
			if (this.created)
				return;
			else
				this.created = true;

			t.media = media;
			t.domNode = domNode;

			// two built in features
			t.buildposter(t, t.controls, t.layers, t.media);
			t.buildoverlays(t, t.controls, t.layers, t.media);

			// grab for use by feautres
			t.findTracks();

			// add user-defined features/controls
			for (f in t.options.features) {
				feature = t.options.features[f];
				if (t['build' + feature]) {
					try {
						t['build' + feature](t, t.controls, t.layers, t.media);
					} catch (e) {
						// TODO: report control error
					}
				}
			}

			// reset all layers and controls
			t.setPlayerSize(t.width, t.height);
			t.setControlsSize();

			// controls fade
			if (t.isVideo) {
				// show/hide controls
				t.container
					.bind('mouseenter', function () {
						t.controls.css('visibility','visible');
						t.controls.stop(true, true).fadeIn(200);
					})
					.bind('mouseleave', function () {
						if (!t.media.paused) {
							t.controls.stop(true, true).fadeOut(200, function() {
								$(this).css('visibility','hidden');
								$(this).css('display','block');
							});
						}
					});

				// resizer
				if (t.options.enableAutosize) {
					t.media.addEventListener('loadedmetadata', function(e) {
						// if the <video height> was not set and the options.videoHeight was not set
						// then resize to the real dimensions
						if (t.options.videoHeight <= 0 && t.$media[0].getAttribute('height') === null && !isNaN(e.target.videoHeight)) {
							t.setPlayerSize(e.target.videoWidth, e.target.videoHeight);
							t.setControlsSize();
							t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight);
						}
					}, false);
				}
			}

			// ended for all
			t.media.addEventListener('ended', function (e) {
				t.media.setCurrentTime(0);
				t.media.pause();

				if (t.options.loop) {
					t.media.play();
				} else {
					t.controls.css('visibility','visible');
				}
			}, true);

			// webkit has trouble doing this without a delay
			setTimeout(function () {
				t.setControlsSize();
				t.setPlayerSize(t.width, t.height);
			}, 50);

			if (t.options.success) {
				t.options.success(t.media, t.domNode);
			}
		},

		handleError: function(e) {
			// Tell user that the file cannot be played
			if (this.options.error) {
				this.options.error(e);
			}
		},

		setPlayerSize: function(width,height) {
			var t = this;

			// ie9 appears to need this (jQuery bug?)
			t.width = parseInt(width, 10);
			t.height = parseInt(height, 10);

			t.container
				.width(t.width)
				.height(t.height);

			t.layers.children('div.mejs-layer')
				.width(t.width)
				.height(t.height);
		},

		setControlsSize: function() {
			var t = this,
				usedWidth = 0,
				railWidth = 0,
				rail = t.controls.find('.mejs-time-rail'),
				total = t.controls.find('.mejs-time-total'),
				others = rail.siblings();

			// find the size of all the other controls besides the rail
			others.each(function() {
				if ($(this).css('position') != 'absolute') {
					usedWidth += $(this).outerWidth(true);
				}
			});
			// fit the rail into the remaining space
			railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.outerWidth(false));

			rail.width(railWidth);
			total.width(railWidth - (total.outerWidth(true) - total.width()));
		},

		buildposter: function(player, controls, layers, media) {
			var poster =
				$('
'+
					''+
				'
')
					.appendTo(layers),
				posterUrl = player.$media.attr('poster'),
				posterImg = poster.find('img').width(player.width).height(poster.height);

			// prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)
			if (player.options.poster != '') {
				posterImg.attr('src',player.options.poster);
			// second, try the real poster
			} else if (posterUrl !== '' && posterUrl != null) {
				posterImg.attr('src',posterUrl);
			} else {
				poster.hide();
			}

			media.addEventListener('play',function() {
				poster.hide();
			}, false);
		},

		buildoverlays: function(player, controls, layers, media) {
			if (!player.isVideo)
				return;

			var
			loading =
				$('
'+
					'

'+
				'
')
				.hide() // start out hidden
				.appendTo(layers),
			error =
				$('
'+
					'

'+
				'
')
				.hide() // start out hidden
				.appendTo(layers),

			// this needs to come last so it's on top
			bigPlay =
				$('
'+
					'

'+
				'
')
				.appendTo(layers)
				.click(function() {
					if (media.paused) {
						media.play();
					} else {
						media.pause();
					}
				});

			// show/hide big play button
			media.addEventListener('play',function() {
				bigPlay.hide();
			}, false);
			media.addEventListener('pause',function() {
				bigPlay.show();
			}, false);

			// show/hide loading
			media.addEventListener('loadstart',function() {
				loading.show();
			}, false);
			media.addEventListener('canplay',function() {
				loading.hide();
			}, false);

			// error handling
			media.addEventListener('error',function() {
				loading.hide();
				error.show();
				error.find('mejs-overlay-error').html("Error loading this resource");
			}, false);
		},

		findTracks: function() {
			var t = this,
				tracktags = t.$media.find('track');

			// store for use by plugins
			t.tracks = [];
			tracktags.each(function() {
				t.tracks.push({
					srclang: $(this).attr('srclang').toLowerCase(),
					src: $(this).attr('src'),
					kind: $(this).attr('kind'),
					entries: [],
					isLoaded: false
				});
			});
		},
		changeSkin: function(className) {
			this.container[0].className = 'mejs-container ' + className;
			this.setPlayerSize();
			this.setControlsSize();
		},
		play: function() {
			this.media.play();
		},
		pause: function() {
			this.media.pause();
		},
		load: function() {
			this.media.load();
		},
		setMuted: function(muted) {
			this.media.setMuted(muted);
		},
		setCurrentTime: function(time) {
			this.media.setCurrentTime(time);
		},
		getCurrentTime: function() {
			return this.media.currentTime;
		},
		setVolume: function(volume) {
			this.media.setVolume(volume);
		},
		getVolume: function() {
			return this.media.volume;
		},
		setSrc: function(src) {
			this.media.setSrc(src);
		}
	};

	// turn into jQuery plugin
	jQuery.fn.mediaelementplayer = function (options) {
		return this.each(function () {
			new mejs.MediaElementPlayer($(this), options);
		});
	};

	// push out to window
	window.MediaElementPlayer = mejs.MediaElementPlayer;

})(jQuery);
<commit_msg>- poster fix<commit_after>(function ($) {

	// default player values
	mejs.MepDefaults = {
		// url to poster (to fix iOS 3.x)
		poster: '',
		// default if the <video width> is not specified
		defaultVideoWidth: 480,
		// default if the <video height> is not specified
		defaultVideoHeight: 270,
		// if set, overrides <video width>
		videoWidth: -1,
		// if set, overrides <video height>
		videoHeight: -1,
		// width of audio player
		audioWidth: 400,
		// height of audio player
		audioHeight: 30,
		// initial volume when the player starts (overrided by user cookie)
		startVolume: 0.8,
		// useful for <audio> player loops
		loop: false,
		// resize to media dimensions
		enableAutosize: true,
		// features to show
		features: ['playpause','current','progress','duration','tracks','volume','fullscreen']
	};

	mejs.mepIndex = 0;

	// wraps a MediaElement object in player controls
	mejs.MediaElementPlayer = function($node, o) {
		// enforce object, even without "new" (via John Resig)
		if ( !(this instanceof mejs.MediaElementPlayer) ) {
			return new mejs.MediaElementPlayer($node, o);
		}

		var
			t = this,
			mf = mejs.MediaFeatures;

		// create options
		t.options = $.extend({},mejs.MepDefaults,o);
		t.$media = t.$node = $($node);

		// check for existing player
		if ($node[0].player) {
			return $node[0].player;
		} else {
			// attach player to DOM node for reference
			$node[0].player = t;
		}

		t.isVideo = (t.$media[0].tagName.toLowerCase() === 'video');

		/* FUTURE WORK = create player without existing <video> or <audio> node

		// if not a video or audio tag, then we'll dynamically create it
		if (tagName == 'video' || tagName == 'audio') {
			t.$media = $($node);
		} else if (o.tagName !== '' && o.src !== '') {
			// create a new node
			if (o.mode == 'auto' || o.mode == 'native') {

				$media = $(o.tagName);
				if (typeof o.src == 'string') {
					$media.attr('src',o.src);
				} else if (typeof o.src == 'object') {
					// create source nodes
					for (var x in o.src) {
						$media.append($('<source src="' + o.src[x].src + '" type="' + o.src[x].type + '" />'));
					}
				}
				if (o.type != '') {
					$media.attr('type',o.type);
				}
				if (o.poster != '') {
					$media.attr('poster',o.poster);
				}
				if (o.videoWidth > 0) {
					$media.attr('width',o.videoWidth);
				}
				if (o.videoHeight > 0) {
					$media.attr('height',o.videoHeight);
				}

				$node.clear();
				$node.append($media);
				t.$media = $media;
			} else if (o.mode == 'shim') {
				$media = $();
				// doesn't want a media node
				// let MediaElement object handle this
			}
		} else {
			// fail?
			return;
		}
		*/

		if (mf.isiPad || mf.isiPhone) {
			// add controls and stop
			t.$media.attr('controls', 'controls');

			// fix iOS 3 bug
			t.$media.removeAttr('poster');

			// override Apple's autoplay override for iPads
			if (mf.isiPad && t.$media[0].getAttribute('autoplay') !== null) {
				t.$media[0].load();
				t.$media[0].play();
			}

			// don't do the rest
			return;
		} else if (mf.isAndroid) {

			if (t.isVideo) {
				// Android fails when there are multiple types
				// <video>
				// <source src="file.mp4" type="video/mp4" />
				// <source src="file.webm" type="video/webm" />
				// </video>
				if (t.$media.find('source').length > 0) {
					// find an mp4 and make it the root element source
					t.$media[0].src = t.$media.find('source[src$="mp4"]').attr('src');
				}

				// attach a click event to the video and hope Android can play it
				t.$media.click(function() {
					t.$media[0].play();
				});

				return;
			} else {
				// audio?
				// 2.1 = no support
				// 2.2 = Flash support
				// 2.3 = Native HTML5
			}

		} else {

			// remove native controls and use MEJS
			t.$media.removeAttr('controls');
		}

		t.init();

		return t;
	};

	// actual player
	mejs.MediaElementPlayer.prototype = {
		init: function() {

			var
				t = this,
				meOptions = $.extend(true, {}, t.options, {
					success: function(media, domNode) { t.meReady(media, domNode); },
					error: function(e) { t.handleError(e);}
				});

			// unique ID
			t.id = 'mep_' + mejs.mepIndex++;

			// build container
			t.container =
				$('
'+
					'
'+
						'

'+
						'

'+
						'

'+
						'

'+
					'
' +
				'
')
				.addClass(t.$media[0].className)
				.insertBefore(t.$media);

			// move the <video/video> tag into the right spot
			t.container.find('.mejs-mediaelement').append(t.$media);

			// find parts
			t.controls = t.container.find('.mejs-controls');
			t.layers = t.container.find('.mejs-layers');

			// determine the size
			if (t.isVideo) {
				// priority = videoWidth (forced), width attribute, defaultVideoWidth
				t.width = (t.options.videoWidth > 0) ? t.options.videoWidth : (t.$media[0].getAttribute('width') !== null) ? t.$media.attr('width') : t.options.defaultVideoWidth;
				t.height = (t.options.videoHeight > 0) ? t.options.videoHeight : (t.$media[0].getAttribute('height') !== null) ? t.$media.attr('height') : t.options.defaultVideoHeight;
			} else {
				t.width = t.options.audioWidth;
				t.height = t.options.audioHeight;
			}

			// set the size, while we wait for the plugins to load below
			t.setPlayerSize(t.width, t.height);

			// create MediaElementShim
			meOptions.pluginWidth = t.height;
			meOptions.pluginHeight = t.width;
			mejs.MediaElement(t.$media[0], meOptions);
		},

		// Sets up all controls and events
		meReady: function(media, domNode) {

			var t = this,
				f,
				feature;

			// make sure it can't create itself again if a plugin reloads
			if (this.created)
				return;
			else
				this.created = true;

			t.media = media;
			t.domNode = domNode;

			// two built in features
			t.buildposter(t, t.controls, t.layers, t.media);
			t.buildoverlays(t, t.controls, t.layers, t.media);

			// grab for use by feautres
			t.findTracks();

			// add user-defined features/controls
			for (f in t.options.features) {
				feature = t.options.features[f];
				if (t['build' + feature]) {
					try {
						t['build' + feature](t, t.controls, t.layers, t.media);
					} catch (e) {
						// TODO: report control error
					}
				}
			}

			// reset all layers and controls
			t.setPlayerSize(t.width, t.height);
			t.setControlsSize();

			// controls fade
			if (t.isVideo) {
				// show/hide controls
				t.container
					.bind('mouseenter', function () {
						t.controls.css('visibility','visible');
						t.controls.stop(true, true).fadeIn(200);
					})
					.bind('mouseleave', function () {
						if (!t.media.paused) {
							t.controls.stop(true, true).fadeOut(200, function() {
								$(this).css('visibility','hidden');
								$(this).css('display','block');
							});
						}
					});

				// resizer
				if (t.options.enableAutosize) {
					t.media.addEventListener('loadedmetadata', function(e) {
						// if the <video height> was not set and the options.videoHeight was not set
						// then resize to the real dimensions
						if (t.options.videoHeight <= 0 && t.$media[0].getAttribute('height') === null && !isNaN(e.target.videoHeight)) {
							t.setPlayerSize(e.target.videoWidth, e.target.videoHeight);
							t.setControlsSize();
							t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight);
						}
					}, false);
				}
			}

			// ended for all
			t.media.addEventListener('ended', function (e) {
				t.media.setCurrentTime(0);
				t.media.pause();

				if (t.options.loop) {
					t.media.play();
				} else {
					t.controls.css('visibility','visible');
				}
			}, true);

			// webkit has trouble doing this without a delay
			setTimeout(function () {
				t.setControlsSize();
				t.setPlayerSize(t.width, t.height);
			}, 50);

			if (t.options.success) {
				t.options.success(t.media, t.domNode);
			}
		},

		handleError: function(e) {
			// Tell user that the file cannot be played
			if (this.options.error) {
				this.options.error(e);
			}
		},

		setPlayerSize: function(width,height) {
			var t = this;

			// ie9 appears to need this (jQuery bug?)
			t.width = parseInt(width, 10);
			t.height = parseInt(height, 10);

			t.container
				.width(t.width)
				.height(t.height);

			t.layers.children('div.mejs-layer')
				.width(t.width)
				.height(t.height);
		},

		setControlsSize: function() {
			var t = this,
				usedWidth = 0,
				railWidth = 0,
				rail = t.controls.find('.mejs-time-rail'),
				total = t.controls.find('.mejs-time-total'),
				others = rail.siblings();

			// find the size of all the other controls besides the rail
			others.each(function() {
				if ($(this).css('position') != 'absolute') {
					usedWidth += $(this).outerWidth(true);
				}
			});
			// fit the rail into the remaining space
			railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.outerWidth(false));

			rail.width(railWidth);
			total.width(railWidth - (total.outerWidth(true) - total.width()));
		},

		buildposter: function(player, controls, layers, media) {
			var poster =
				$('
'+
					''+
				'
')
					.appendTo(layers),
				posterUrl = player.$media.attr('poster'),
				posterImg = poster.find('img').width(player.width).height(poster.height);

			// prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)
			if (player.options.poster != '') {
				posterImg.attr('src',player.options.poster);
			// second, try the real poster
			} else if (posterUrl !== '' && posterUrl != null) {
				posterImg.attr('src',posterUrl);
			} else {
				poster.remove();
			}

			media.addEventListener('play',function() {
				poster.hide();
			}, false);
		},

		buildoverlays: function(player, controls, layers, media) {
			if (!player.isVideo)
				return;

			var
			loading =
				$('
'+
					'

'+
				'
')
				.hide() // start out hidden
				.appendTo(layers),
			error =
				$('
'+
					'

'+
				'
')
				.hide() // start out hidden
				.appendTo(layers),

			// this needs to come last so it's on top
			bigPlay =
				$('
'+
					'

'+
				'
')
				.appendTo(layers)
				.click(function() {
					if (media.paused) {
						media.play();
					} else {
						media.pause();
					}
				});

			// show/hide big play button
			media.addEventListener('play',function() {
				bigPlay.hide();
			}, false);
			media.addEventListener('pause',function() {
				bigPlay.show();
			}, false);

			// show/hide loading
			media.addEventListener('loadstart',function() {
				loading.show();
			}, false);
			media.addEventListener('canplay',function() {
				loading.hide();
			}, false);

			// error handling
			media.addEventListener('error',function() {
				loading.hide();
				error.show();
				error.find('mejs-overlay-error').html("Error loading this resource");
			}, false);
		},

		findTracks: function() {
			var t = this,
				tracktags = t.$media.find('track');

			// store for use by plugins
			t.tracks = [];
			tracktags.each(function() {
				t.tracks.push({
					srclang: $(this).attr('srclang').toLowerCase(),
					src: $(this).attr('src'),
					kind: $(this).attr('kind'),
					entries: [],
					isLoaded: false
				});
			});
		},
		changeSkin: function(className) {
			this.container[0].className = 'mejs-container ' + className;
			this.setPlayerSize();
			this.setControlsSize();
		},
		play: function() {
			this.media.play();
		},
		pause: function() {
			this.media.pause();
		},
		load: function() {
			this.media.load();
		},
		setMuted: function(muted) {
			this.media.setMuted(muted);
		},
		setCurrentTime: function(time) {
			this.media.setCurrentTime(time);
		},
		getCurrentTime: function() {
			return this.media.currentTime;
		},
		setVolume: function(volume) {
			this.media.setVolume(volume);
		},
		getVolume: function() {
			return this.media.volume;
		},
		setSrc: function(src) {
			this.media.setSrc(src);
		}
	};

	// turn into jQuery plugin
	jQuery.fn.mediaelementplayer = function (options) {
		return this.each(function () {
			new mejs.MediaElementPlayer($(this), options);
		});
	};

	// push out to window
	window.MediaElementPlayer = mejs.MediaElementPlayer;

})(jQuery);
<|endoftext|>