<commit_before>/*
 * Creates a comment block to the screenshot comments area.
 *
 * @param {int}    x          The X area of the block.
 * @param {int}    y          The Y area of the block.
 * @param {int}    width      The block's width.
 * @param {int}    height     The block's height.
 * @param {jQuery} container  The container for the comment block.
 * @param {array}  comments   The list of comments in this block.
 *
 * @return {object} The comment block.
 */
function CommentBlock(x, y, width, height, container, comments) {
    var self = this;

    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.hasDraft = false;
    this.comments = [];
    this.canDelete = false;
    this.draftComment = null;

    this.el = $('
').appendTo(container);
    this.tooltip = $.tooltip(this.el, {
        side: "lrbt"
    }).addClass("comments");
    this.flag = $('
').appendTo(this.el);

    /*
     * Find out if there's any draft comments, and filter them out of the
     * stored list of comments.
     */
    if (comments && comments.length > 0) {
        for (var i in comments) {
            var comment = comments[i];

            if (comment.localdraft) {
                this._createDraftComment(comment.comment_id, comment.text);
            } else {
                this.comments.push(comment);
            }
        }
    } else {
        this._createDraftComment();
    }

    this.el
        .move(this.x, this.y, "absolute")
        .width(this.width)
        .height(this.height);

    this.updateCount();
    this.updateTooltip();

    return this;
}

jQuery.extend(CommentBlock.prototype, {
    /*
     * Updates the tooltip contents.
     */
    updateTooltip: function() {
        function addEntry(text) {
            var item = $("
").appendTo(list);
            item.text(text.truncate());
            return item;
        }

        this.tooltip.empty();
        var list = $("
").appendTo(this.tooltip);

        if (this.draftComment != null) {
            addEntry(this.draftComment.text).addClass("draft");
        }

        $(this.comments).each(function(i) {
            addEntry(this.text);
        });
    },

    /*
     * Updates the displayed number of comments in the comment block.
     *
     * If there's a draft comment, it will be added to the count. Otherwise,
     * this depends solely on the number of published comments.
     */
    updateCount: function() {
        var count = this.comments.length;

        if (this.draftComment != null) {
            count++;
        }

        this.count = count;
        this.flag.html(count);
    },

    /*
     * Notifies the user of some update. This notification appears in the
     * comment area.
     *
     * @param {string} text  The notification text.
     */
    notify: function(text, cb) {
        var offset = this.el.offset();

        var bubble = $("
")
            .addClass("bubble")
            .appendTo(this.el)
            .text(text);

        bubble
            .css("opacity", 0)
            .move(Math.round((this.el.width()  - bubble.width())  / 2),
                  Math.round((this.el.height() - bubble.height()) / 2))
            .animate({
                top: "-=10px",
                opacity: 0.8
            }, 350, "swing")
            .delay(1200)
            .animate({
                top: "+=10px",
                opacity: 0
            }, 350, "swing", function() {
                bubble.remove();

                if ($.isFunction(cb)) {
                    cb();
                }
            });
    },

    _createDraftComment: function(id, text) {
        if (this.draftComment != null) {
            return;
        }

        var self = this;
        var el = this.el;
        var comment = gReviewRequest.createReview().createScreenshotComment(
            id, gScreenshotId, this.x, this.y, this.width, this.height);

        if (text) {
            comment.text = text;
        }

        $.event.add(comment, "textChanged", function() {
            self.updateTooltip();
        });

        $.event.add(comment, "deleted", function() {
            el.queue(function() {
                self.notify("Comment Deleted", function() {
                    el.dequeue();
                });
            });
        });

        $.event.add(comment, "destroyed", function() {
            /* Discard the comment block if empty. */
            if (self.comments.length == 0) {
                el.fadeOut(350, function() { el.remove(); })
            } else {
                el.removeClass("draft");
                self.flag.removeClass("flag-draft");
                self.updateCount();
                self.updateTooltip();
            }
        });

        $.event.add(comment, "saved", function() {
            self.updateCount();
            self.updateTooltip();
            self.notify("Comment Saved");
            showReviewBanner();
        });

        this.draftComment = comment;
        el.addClass("draft");
        this.flag.addClass("flag-draft");
    }
});

/*
 * Creates a box for creating and seeing all comments on a screenshot.
 *
 * @param {object} regions  The regions containing comments.
 *
 * @return {jQuery} This jQuery.
 */
jQuery.fn.screenshotCommentBox = function(regions) {
    var self = this;

    /* State */
    var activeCommentBlock = null;

    /* Page elements */
    var image = $("img", this);

    var selectionArea =
        $('
')
        .prependTo(this);

    var activeSelection =
        $('
')
        .prependTo(selectionArea)
        .hide();

    gCommentDlg.bind("close", function() { activeCommentBlock = null; });

    /*
     * Register events on the selection area for handling new comment
     * creation.
     */
    $([image[0], selectionArea[0]])
        .mousedown(function(evt) {
            if (evt.which == 1 && !activeCommentBlock &&
                !$(evt.target).hasClass("selection-flag")) {
                var offset = selectionArea.offset();
                activeSelection.beginX =
                    evt.pageX - Math.floor(offset.left) - 1;
                activeSelection.beginY =
                    evt.pageY - Math.floor(offset.top) - 1;

                activeSelection
                    .move(activeSelection.beginX, activeSelection.beginY)
                    .width(1)
                    .height(1)
                    .show();

                if (activeSelection.is(":hidden")) {
                    gCommentDlg.hide();
                }

                return false;
            }
        })
        .mouseup(function(evt) {
            if (!activeCommentBlock && activeSelection.is(":visible")) {
                evt.stopPropagation();

                var width  = activeSelection.width();
                var height = activeSelection.height();
                var offset = activeSelection.position();

                activeSelection.hide();

                /*
                 * If we don't pass an arbitrary minimum size threshold,
                 * don't do anything.  This helps avoid making people mad
                 * if they accidentally click on the image.
                 */
                if (width > 5 && height > 5) {
                    if (!activeCommentBlock) {
                        showCommentDlg(addCommentBlock(offset.left,
                                                       offset.top,
                                                       width, height));
                    } else {
                        // TODO: Reposition the old block. */
                    }
                }
            }
        })
        .mousemove(function(evt) {
            if (!activeCommentBlock && activeSelection.is(":visible")) {
                var offset = selectionArea.offset();
                var x = evt.pageX - Math.floor(offset.left) - 1;
                var y = evt.pageY - Math.floor(offset.top) - 1;

                activeSelection
                    .css(activeSelection.beginX <= x
                         ? {
                               left:  activeSelection.beginX,
                               width: x - activeSelection.beginX
                           }
                         : {
                               left:  x,
                               width: activeSelection.beginX - x
                           })
                    .css(activeSelection.beginY <= y
                         ? {
                               top:    activeSelection.beginY,
                               height: y - activeSelection.beginY
                           }
                         : {
                               top:    y,
                               height: activeSelection.beginY - y
                           });

                return false;
            }
        })
        .proxyTouchEvents();

    /*
     * Register a hover event to hide the comments when the mouse is not
     * over the comment area.
     */
    this.hover(
        function() {
            selectionArea.show();
        },
        function() {
            if (activeSelection.is(":hidden") &&
                gCommentDlg.is(":hidden")) {
                selectionArea.hide();
            }
        }
    );

    /*
     * Register a resize event to reposition the selection area on page
     * resize, so that comments are in the right locations.
     */
    $(window)
        .resize(function() {
            var offset = image.position();

            /*
             * The margin: 0 auto means that position.left() will return
             * the left-most part of the entire block, rather than the actual
             * position of the image on Chrome. Every other browser returns 0
             * for this margin, as we'd expect. So, just play it safe and
             * offset by the margin-left. (Bug #1050)
             */
            offset.left += image.getExtents("m", "l");

            if ($.browser.msie && $.browser.version == 6) {
                offset.left -= self.getExtents("mp", "l");
            }

            selectionArea
                .width(image.width())
                .height(image.height())
                .css("left", offset.left);
        })
        .triggerHandler("resize");

    /* Add all existing comment regions to the page. */
    for (region in regions) {
        var comments = regions[region];
        addCommentBlock(comments[0].x, comments[0].y,
                        comments[0].w, comments[0].h,
                        comments);
    }

    /*
     * Adds a new comment block to the selection area. This may contain
     * existing comments or may be a newly created comment block.
     *
     * @param {int}   x         The X area of the block.
     * @param {int}   y         The Y area of the block.
     * @param {int}   width     The block's width.
     * @param {int}   height    The block's height.
     * @param {array} comments  The list of comments in this block.
     *
     * @return {CommentBlock} The new comment block.
     */
    function addCommentBlock(x, y, width, height, comments) {
        var commentBlock = new CommentBlock(x, y, width, height,
                                            selectionArea, comments)
        commentBlock.el.click(function() {
            showCommentDlg(commentBlock);
        });

        return commentBlock;
    }

    /*
     * Shows the comment details dialog for a comment block.
     *
     * @param {CommentBlock} commentBlock  The comment block to show.
     */
    function showCommentDlg(commentBlock) {
        gCommentDlg
            .one("close", function() {
                commentBlock._createDraftComment();
                activeCommentBlock = commentBlock;

                gCommentDlg
                    .setDraftComment(commentBlock.draftComment)
                    .setCommentsList(commentBlock.comments,
                                     "screenshot_comment")
                    .positionToSide(commentBlock.flag, {
                        side: 'b',
                        fitOnScreen: true
                    });
                gCommentDlg.open();
            })
            .close();
    }

    return this;
}

// vim: set et ts=4:
<commit_msg>Fixed an issue that screenshot comment didn't on the right location in Chrome<commit_after>/*
 * Creates a comment block to the screenshot comments area.
 *
 * @param {int}    x          The X area of the block.
 * @param {int}    y          The Y area of the block.
 * @param {int}    width      The block's width.
 * @param {int}    height     The block's height.
 * @param {jQuery} container  The container for the comment block.
 * @param {array}  comments   The list of comments in this block.
 *
 * @return {object} The comment block.
 */
function CommentBlock(x, y, width, height, container, comments) {
    var self = this;

    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.hasDraft = false;
    this.comments = [];
    this.canDelete = false;
    this.draftComment = null;

    this.el = $('
').appendTo(container);
    this.tooltip = $.tooltip(this.el, {
        side: "lrbt"
    }).addClass("comments");
    this.flag = $('
').appendTo(this.el);

    /*
     * Find out if there's any draft comments, and filter them out of the
     * stored list of comments.
     */
    if (comments && comments.length > 0) {
        for (var i in comments) {
            var comment = comments[i];

            if (comment.localdraft) {
                this._createDraftComment(comment.comment_id, comment.text);
            } else {
                this.comments.push(comment);
            }
        }
    } else {
        this._createDraftComment();
    }

    this.el
        .move(this.x, this.y, "absolute")
        .width(this.width)
        .height(this.height);

    this.updateCount();
    this.updateTooltip();

    return this;
}

jQuery.extend(CommentBlock.prototype, {
    /*
     * Updates the tooltip contents.
     */
    updateTooltip: function() {
        function addEntry(text) {
            var item = $("
").appendTo(list);
            item.text(text.truncate());
            return item;
        }

        this.tooltip.empty();
        var list = $("
").appendTo(this.tooltip);

        if (this.draftComment != null) {
            addEntry(this.draftComment.text).addClass("draft");
        }

        $(this.comments).each(function(i) {
            addEntry(this.text);
        });
    },

    /*
     * Updates the displayed number of comments in the comment block.
     *
     * If there's a draft comment, it will be added to the count. Otherwise,
     * this depends solely on the number of published comments.
     */
    updateCount: function() {
        var count = this.comments.length;

        if (this.draftComment != null) {
            count++;
        }

        this.count = count;
        this.flag.html(count);
    },

    /*
     * Notifies the user of some update. This notification appears in the
     * comment area.
     *
     * @param {string} text  The notification text.
     */
    notify: function(text, cb) {
        var offset = this.el.offset();

        var bubble = $("
")
            .addClass("bubble")
            .appendTo(this.el)
            .text(text);

        bubble
            .css("opacity", 0)
            .move(Math.round((this.el.width()  - bubble.width())  / 2),
                  Math.round((this.el.height() - bubble.height()) / 2))
            .animate({
                top: "-=10px",
                opacity: 0.8
            }, 350, "swing")
            .delay(1200)
            .animate({
                top: "+=10px",
                opacity: 0
            }, 350, "swing", function() {
                bubble.remove();

                if ($.isFunction(cb)) {
                    cb();
                }
            });
    },

    _createDraftComment: function(id, text) {
        if (this.draftComment != null) {
            return;
        }

        var self = this;
        var el = this.el;
        var comment = gReviewRequest.createReview().createScreenshotComment(
            id, gScreenshotId, this.x, this.y, this.width, this.height);

        if (text) {
            comment.text = text;
        }

        $.event.add(comment, "textChanged", function() {
            self.updateTooltip();
        });

        $.event.add(comment, "deleted", function() {
            el.queue(function() {
                self.notify("Comment Deleted", function() {
                    el.dequeue();
                });
            });
        });

        $.event.add(comment, "destroyed", function() {
            /* Discard the comment block if empty. */
            if (self.comments.length == 0) {
                el.fadeOut(350, function() { el.remove(); })
            } else {
                el.removeClass("draft");
                self.flag.removeClass("flag-draft");
                self.updateCount();
                self.updateTooltip();
            }
        });

        $.event.add(comment, "saved", function() {
            self.updateCount();
            self.updateTooltip();
            self.notify("Comment Saved");
            showReviewBanner();
        });

        this.draftComment = comment;
        el.addClass("draft");
        this.flag.addClass("flag-draft");
    }
});

/*
 * Creates a box for creating and seeing all comments on a screenshot.
 *
 * @param {object} regions  The regions containing comments.
 *
 * @return {jQuery} This jQuery.
 */
jQuery.fn.screenshotCommentBox = function(regions) {
    var self = this;

    /* State */
    var activeCommentBlock = null;

    /* Page elements */
    var image = $("img", this);

    var selectionArea =
        $('
')
        .prependTo(this);

    var activeSelection =
        $('
')
        .prependTo(selectionArea)
        .hide();

    gCommentDlg.bind("close", function() { activeCommentBlock = null; });

    /*
     * Register events on the selection area for handling new comment
     * creation.
     */
    $([image[0], selectionArea[0]])
        .mousedown(function(evt) {
            if (evt.which == 1 && !activeCommentBlock &&
                !$(evt.target).hasClass("selection-flag")) {
                var offset = selectionArea.offset();
                activeSelection.beginX =
                    evt.pageX - Math.floor(offset.left) - 1;
                activeSelection.beginY =
                    evt.pageY - Math.floor(offset.top) - 1;

                activeSelection
                    .move(activeSelection.beginX, activeSelection.beginY)
                    .width(1)
                    .height(1)
                    .show();

                if (activeSelection.is(":hidden")) {
                    gCommentDlg.hide();
                }

                return false;
            }
        })
        .mouseup(function(evt) {
            if (!activeCommentBlock && activeSelection.is(":visible")) {
                evt.stopPropagation();

                var width  = activeSelection.width();
                var height = activeSelection.height();
                var offset = activeSelection.position();

                activeSelection.hide();

                /*
                 * If we don't pass an arbitrary minimum size threshold,
                 * don't do anything.  This helps avoid making people mad
                 * if they accidentally click on the image.
                 */
                if (width > 5 && height > 5) {
                    if (!activeCommentBlock) {
                        showCommentDlg(addCommentBlock(offset.left,
                                                       offset.top,
                                                       width, height));
                    } else {
                        // TODO: Reposition the old block. */
                    }
                }
            }
        })
        .mousemove(function(evt) {
            if (!activeCommentBlock && activeSelection.is(":visible")) {
                var offset = selectionArea.offset();
                var x = evt.pageX - Math.floor(offset.left) - 1;
                var y = evt.pageY - Math.floor(offset.top) - 1;

                activeSelection
                    .css(activeSelection.beginX <= x
                         ? {
                               left:  activeSelection.beginX,
                               width: x - activeSelection.beginX
                           }
                         : {
                               left:  x,
                               width: activeSelection.beginX - x
                           })
                    .css(activeSelection.beginY <= y
                         ? {
                               top:    activeSelection.beginY,
                               height: y - activeSelection.beginY
                           }
                         : {
                               top:    y,
                               height: activeSelection.beginY - y
                           });

                return false;
            }
        })
        .proxyTouchEvents();

    /*
     * Register a hover event to hide the comments when the mouse is not
     * over the comment area.
     */
    this.hover(
        function() {
            selectionArea.show();
        },
        function() {
            if (activeSelection.is(":hidden") &&
                gCommentDlg.is(":hidden")) {
                selectionArea.hide();
            }
        }
    );

    /*
     * Reposition the selection area on page resize or loaded, so that
     * comments are in the right locations.
     */
    $(window)
        .resize(adjustPos)
        .load(adjustPos);

    /* Add all existing comment regions to the page. */
    for (region in regions) {
        var comments = regions[region];
        addCommentBlock(comments[0].x, comments[0].y,
                        comments[0].w, comments[0].h,
                        comments);
    }

    /*
     * Adds a new comment block to the selection area. This may contain
     * existing comments or may be a newly created comment block.
     *
     * @param {int}   x         The X area of the block.
     * @param {int}   y         The Y area of the block.
     * @param {int}   width     The block's width.
     * @param {int}   height    The block's height.
     * @param {array} comments  The list of comments in this block.
     *
     * @return {CommentBlock} The new comment block.
     */
    function addCommentBlock(x, y, width, height, comments) {
        var commentBlock = new CommentBlock(x, y, width, height,
                                            selectionArea, comments)
        commentBlock.el.click(function() {
            showCommentDlg(commentBlock);
        });

        return commentBlock;
    }

    /*
     * Shows the comment details dialog for a comment block.
     *
     * @param {CommentBlock} commentBlock  The comment block to show.
     */
    function showCommentDlg(commentBlock) {
        gCommentDlg
            .one("close", function() {
                commentBlock._createDraftComment();
                activeCommentBlock = commentBlock;

                gCommentDlg
                    .setDraftComment(commentBlock.draftComment)
                    .setCommentsList(commentBlock.comments,
                                     "screenshot_comment")
                    .positionToSide(commentBlock.flag, {
                        side: 'b',
                        fitOnScreen: true
                    });
                gCommentDlg.open();
            })
            .close();
    }

    /*
     * Reposition the selection area to the right locations.
     */
    function adjustPos() {
        var offset = image.position();

        /*
         * The margin: 0 auto means that position.left() will return
         * the left-most part of the entire block, rather than the actual
         * position of the image on Chrome. Every other browser returns 0
         * for this margin, as we'd expect. So, just play it safe and
         * offset by the margin-left. (Bug #1050)
         */
        offset.left += image.getExtents("m", "l");

        if ($.browser.msie && $.browser.version == 6) {
            offset.left -= self.getExtents("mp", "l");
        }

        selectionArea
            .width(image.width())
            .height(image.height())
            .css("left", offset.left);
    }

    return this;
}

// vim: set et ts=4:
<|endoftext|>


<commit_before>//
/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD 3-clause license that
 * can be found in the LICENSE.txt file in the project root.
 */
//

// The following images show the relation of states and
// {@link ATNState//transitions} for various grammar constructs.
//
//

//
//
Solid edges marked with an &//0949; indicate a required
// {@link EpsilonTransition}.

//
//
Dashed edges indicate locations where any transition derived from
// {@link Transition} might appear.

//
//
Dashed nodes are place holders for either a sequence of linked
// {@link BasicState} states or the inclusion of a block representing a nested
// construct in one of the forms below.

//
//
Nodes showing multiple outgoing alternatives with a {@code ...} support
// any number of alternatives (one or more). Nodes without the {@code ...} only
// support the exact number of alternatives shown in the diagram.

//
//

//
//
Basic Blocks

//
//
Rule

//
// <embed src="images/Rule.svg" type="image/svg+xml"/>
//
//
Block of 1 or more alternatives

//
// <embed src="images/Block.svg" type="image/svg+xml"/>
//
//
Greedy Loops

//
//
Greedy Closure: {@code (...)*}

//
// <embed src="images/ClosureGreedy.svg" type="image/svg+xml"/>
//
//
Greedy Positive Closure: {@code (...)+}

//
// <embed src="images/PositiveClosureGreedy.svg" type="image/svg+xml"/>
//
//
Greedy Optional: {@code (...)?}

//
// <embed src="images/OptionalGreedy.svg" type="image/svg+xml"/>
//
//
Non-Greedy Loops

//
//
Non-Greedy Closure: {@code (...)*?}

//
// <embed src="images/ClosureNonGreedy.svg" type="image/svg+xml"/>
//
//
Non-Greedy Positive Closure: {@code (...)+?}

//
// <embed src="images/PositiveClosureNonGreedy.svg" type="image/svg+xml"/>
//
//
Non-Greedy Optional: {@code (...)??}

//
// <embed src="images/OptionalNonGreedy.svg" type="image/svg+xml"/>
//

var INITIAL_NUM_TRANSITIONS = 4;

function ATNState() {
    // Which ATN are we in?
    this.atn = null;
    this.stateNumber = ATNState.INVALID_STATE_NUMBER;
    this.stateType = null;
    this.ruleIndex = 0; // at runtime, we don't have Rule objects
    this.epsilonOnlyTransitions = false;
    // Track the transitions emanating from this ATN state.
    this.transitions = [];
    // Used to cache lookahead during parsing, not used during construction
    this.nextTokenWithinRule = null;
    return this;
}

// constants for serialization
ATNState.INVALID_TYPE = 0;
ATNState.BASIC = 1;
ATNState.RULE_START = 2;
ATNState.BLOCK_START = 3;
ATNState.PLUS_BLOCK_START = 4;
ATNState.STAR_BLOCK_START = 5;
ATNState.TOKEN_START = 6;
ATNState.RULE_STOP = 7;
ATNState.BLOCK_END = 8;
ATNState.STAR_LOOP_BACK = 9;
ATNState.STAR_LOOP_ENTRY = 10;
ATNState.PLUS_LOOP_BACK = 11;
ATNState.LOOP_END = 12;

ATNState.serializationNames = [
            "INVALID",
            "BASIC",
            "RULE_START",
            "BLOCK_START",
            "PLUS_BLOCK_START",
            "STAR_BLOCK_START",
            "TOKEN_START",
            "RULE_STOP",
            "BLOCK_END",
            "STAR_LOOP_BACK",
            "STAR_LOOP_ENTRY",
            "PLUS_LOOP_BACK",
            "LOOP_END" ];

ATNState.INVALID_STATE_NUMBER = -1;

ATNState.prototype.toString = function() {
	return this.stateNumber;
};

ATNState.prototype.equals = function(other) {
    if (other instanceof ATNState) {
        return this.stateNumber===other.stateNumber;
    } else {
        return false;
    }
};

ATNState.prototype.isNonGreedyExitState = function() {
    return false;
};

ATNState.prototype.addTransition = function(trans, index) {
	if(index===undefined) {
		index = -1;
	}
    if (this.transitions.length===0) {
        this.epsilonOnlyTransitions = trans.isEpsilon;
    } else if(this.epsilonOnlyTransitions !== trans.isEpsilon) {
        this.epsilonOnlyTransitions = false;
    }
    if (index===-1) {
        this.transitions.push(trans);
    } else {
        this.transitions.splice(index, 1, trans);
    }
};

function BasicState() {
	ATNState.call(this);
    this.stateType = ATNState.BASIC;
    return this;
}

BasicState.prototype = Object.create(ATNState.prototype);
BasicState.prototype.constructor = BasicState;

function DecisionState() {
	ATNState.call(this);
    this.decision = -1;
    this.nonGreedy = false;
    return this;
}

DecisionState.prototype = Object.create(ATNState.prototype);
DecisionState.prototype.constructor = DecisionState;

//  The start of a regular {@code (...)} block.
function BlockStartState() {
	DecisionState.call(this);
	this.endState = null;
	return this;
}

BlockStartState.prototype = Object.create(DecisionState.prototype);
BlockStartState.prototype.constructor = BlockStartState;

function BasicBlockStartState() {
	BlockStartState.call(this);
	this.stateType = ATNState.BLOCK_START;
	return this;
}

BasicBlockStartState.prototype = Object.create(BlockStartState.prototype);
BasicBlockStartState.prototype.constructor = BasicBlockStartState;

// Terminal node of a simple {@code (a|b|c)} block.
function BlockEndState() {
	ATNState.call(this);
	this.stateType = ATNState.BLOCK_END;
    this.startState = null;
    return this;
}

BlockEndState.prototype = Object.create(ATNState.prototype);
BlockEndState.prototype.constructor = BlockEndState;

// The last node in the ATN for a rule, unless that rule is the start symbol.
//  In that case, there is one transition to EOF. Later, we might encode
//  references to all calls to this rule to compute FOLLOW sets for
//  error handling.
//
function RuleStopState() {
	ATNState.call(this);
    this.stateType = ATNState.RULE_STOP;
    return this;
}

RuleStopState.prototype = Object.create(ATNState.prototype);
RuleStopState.prototype.constructor = RuleStopState;

function RuleStartState() {
	ATNState.call(this);
	this.stateType = ATNState.RULE_START;
	this.stopState = null;
	this.isPrecedenceRule = false;
	return this;
}

RuleStartState.prototype = Object.create(ATNState.prototype);
RuleStartState.prototype.constructor = RuleStartState;

// Decision state for {@code A+} and {@code (A|B)+}.  It has two transitions:
//  one to the loop back to start of the block and one to exit.
//
function PlusLoopbackState() {
	DecisionState.call(this);
	this.stateType = ATNState.PLUS_LOOP_BACK;
	return this;
}

PlusLoopbackState.prototype = Object.create(DecisionState.prototype);
PlusLoopbackState.prototype.constructor = PlusLoopbackState;

// Start of {@code (A|B|...)+} loop. Technically a decision state, but
//  we don't use for code generation; somebody might need it, so I'm defining
//  it for completeness. In reality, the {@link PlusLoopbackState} node is the
//  real decision-making note for {@code A+}.
//
function PlusBlockStartState() {
	BlockStartState.call(this);
	this.stateType = ATNState.PLUS_BLOCK_START;
    this.loopBackState = null;
    return this;
}

PlusBlockStartState.prototype = Object.create(BlockStartState.prototype);
PlusBlockStartState.prototype.constructor = PlusBlockStartState;

// The block that begins a closure loop.
function StarBlockStartState() {
	BlockStartState.call(this);
	this.stateType = ATNState.STAR_BLOCK_START;
	return this;
}

StarBlockStartState.prototype = Object.create(BlockStartState.prototype);
StarBlockStartState.prototype.constructor = StarBlockStartState;

function StarLoopbackState() {
	ATNState.call(this);
	this.stateType = ATNState.STAR_LOOP_BACK;
	return this;
}

StarLoopbackState.prototype = Object.create(ATNState.prototype);
StarLoopbackState.prototype.constructor = StarLoopbackState;

function StarLoopEntryState() {
	DecisionState.call(this);
	this.stateType = ATNState.STAR_LOOP_ENTRY;
    this.loopBackState = null;
    // Indicates whether this state can benefit from a precedence DFA during SLL decision making.
    this.isPrecedenceDecision = null;
    return this;
}

StarLoopEntryState.prototype = Object.create(DecisionState.prototype);
StarLoopEntryState.prototype.constructor = StarLoopEntryState;

// Mark the end of a * or + loop.
function LoopEndState() {
	ATNState.call(this);
	this.stateType = ATNState.LOOP_END;
	this.loopBackState = null;
	return this;
}

LoopEndState.prototype = Object.create(ATNState.prototype);
LoopEndState.prototype.constructor = LoopEndState;

// The Tokens rule start state linking to each lexer rule start state */
function TokensStartState() {
	DecisionState.call(this);
	this.stateType = ATNState.TOKEN_START;
	return this;
}

TokensStartState.prototype = Object.create(DecisionState.prototype);
TokensStartState.prototype.constructor = TokensStartState;

exports.ATNState = ATNState;
exports.BasicState = BasicState;
exports.DecisionState = DecisionState;
exports.BlockStartState = BlockStartState;
exports.BlockEndState = BlockEndState;
exports.LoopEndState = LoopEndState;
exports.RuleStartState = RuleStartState;
exports.RuleStopState = RuleStopState;
exports.TokensStartState = TokensStartState;
exports.PlusLoopbackState = PlusLoopbackState;
exports.StarLoopbackState = StarLoopbackState;
exports.StarLoopEntryState = StarLoopEntryState;
exports.PlusBlockStartState = PlusBlockStartState;
exports.StarBlockStartState = StarBlockStartState;
exports.BasicBlockStartState = BasicBlockStartState;
<commit_msg>refactored ATNState.js to es 6 classes use module.exports use jsdoc<commit_after>/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD 3-clause license that
 * can be found in the LICENSE.txt file in the project root.
 */

const INITIAL_NUM_TRANSITIONS = 4;

/**
 * The following images show the relation of states and
 * {@link ATNState//transitions} for various grammar constructs.
 *
 *

 *
 *
Solid edges marked with an &//0949; indicate a required
 * {@link EpsilonTransition}.

 *
 *
Dashed edges indicate locations where any transition derived from
 * {@link Transition} might appear.

 *
 *
Dashed nodes are place holders for either a sequence of linked
 * {@link BasicState} states or the inclusion of a block representing a nested
 * construct in one of the forms below.

 *
 *
Nodes showing multiple outgoing alternatives with a {@code ...} support
 * any number of alternatives (one or more). Nodes without the {@code ...} only
 * support the exact number of alternatives shown in the diagram.

 *
 *

 *
 *
Basic Blocks

 *
 *
Rule

 *
 * <embed src="images/Rule.svg" type="image/svg+xml"/>
 *
 *
Block of 1 or more alternatives

 *
 * <embed src="images/Block.svg" type="image/svg+xml"/>
 *
 *
Greedy Loops

 *
 *
Greedy Closure: {@code (...)*}

 *
 * <embed src="images/ClosureGreedy.svg" type="image/svg+xml"/>
 *
 *
Greedy Positive Closure: {@code (...)+}

 *
 * <embed src="images/PositiveClosureGreedy.svg" type="image/svg+xml"/>
 *
 *
Greedy Optional: {@code (...)?}

 *
 * <embed src="images/OptionalGreedy.svg" type="image/svg+xml"/>
 *
 *
Non-Greedy Loops

 *
 *
Non-Greedy Closure: {@code (...)*?}

 *
 * <embed src="images/ClosureNonGreedy.svg" type="image/svg+xml"/>
 *
 *
Non-Greedy Positive Closure: {@code (...)+?}

 *
 * <embed src="images/PositiveClosureNonGreedy.svg" type="image/svg+xml"/>
 *
 *
Non-Greedy Optional: {@code (...)??}

 *
 * <embed src="images/OptionalNonGreedy.svg" type="image/svg+xml"/>
 */
class ATNState {
    constructor() {
        // Which ATN are we in?
        this.atn = null;
        this.stateNumber = ATNState.INVALID_STATE_NUMBER;
        this.stateType = null;
        this.ruleIndex = 0; // at runtime, we don't have Rule objects
        this.epsilonOnlyTransitions = false;
        // Track the transitions emanating from this ATN state.
        this.transitions = [];
        // Used to cache lookahead during parsing, not used during construction
        this.nextTokenWithinRule = null;
    }

    toString() {
        return this.stateNumber;
    }

    equals(other) {
        if (other instanceof ATNState) {
            return this.stateNumber===other.stateNumber;
        } else {
            return false;
        }
    }

    isNonGreedyExitState() {
        return false;
    }

    addTransition(trans, index) {
        if(index===undefined) {
            index = -1;
        }
        if (this.transitions.length===0) {
            this.epsilonOnlyTransitions = trans.isEpsilon;
        } else if(this.epsilonOnlyTransitions !== trans.isEpsilon) {
            this.epsilonOnlyTransitions = false;
        }
        if (index===-1) {
            this.transitions.push(trans);
        } else {
            this.transitions.splice(index, 1, trans);
        }
    }
}

// constants for serialization
ATNState.INVALID_TYPE = 0;
ATNState.BASIC = 1;
ATNState.RULE_START = 2;
ATNState.BLOCK_START = 3;
ATNState.PLUS_BLOCK_START = 4;
ATNState.STAR_BLOCK_START = 5;
ATNState.TOKEN_START = 6;
ATNState.RULE_STOP = 7;
ATNState.BLOCK_END = 8;
ATNState.STAR_LOOP_BACK = 9;
ATNState.STAR_LOOP_ENTRY = 10;
ATNState.PLUS_LOOP_BACK = 11;
ATNState.LOOP_END = 12;

ATNState.serializationNames = [
            "INVALID",
            "BASIC",
            "RULE_START",
            "BLOCK_START",
            "PLUS_BLOCK_START",
            "STAR_BLOCK_START",
            "TOKEN_START",
            "RULE_STOP",
            "BLOCK_END",
            "STAR_LOOP_BACK",
            "STAR_LOOP_ENTRY",
            "PLUS_LOOP_BACK",
            "LOOP_END" ];

ATNState.INVALID_STATE_NUMBER = -1;

class BasicState extends ATNState {
    constructor() {
        super();
        this.stateType = ATNState.BASIC;
    }
}

class DecisionState extends ATNState {
    constructor() {
        super();
        this.decision = -1;
        this.nonGreedy = false;
        return this;
    }
}

/**
 *  The start of a regular {@code (...)} block
 */
class BlockStartState extends DecisionState {
    constructor() {
        super();
        this.endState = null;
        return this;
    }
}

class BasicBlockStartState extends BlockStartState {
    constructor() {
        super();
        this.stateType = ATNState.BLOCK_START;
        return this;
    }
}

/**
 * Terminal node of a simple {@code (a|b|c)} block
 */
class BlockEndState extends ATNState {
    constructor() {
        super();
        this.stateType = ATNState.BLOCK_END;
        this.startState = null;
        return this;
    }
}

/**
 * The last node in the ATN for a rule, unless that rule is the start symbol.
 * In that case, there is one transition to EOF. Later, we might encode
 * references to all calls to this rule to compute FOLLOW sets for
 * error handling
 */
class RuleStopState extends ATNState {
    constructor() {
        super();
        this.stateType = ATNState.RULE_STOP;
        return this;
    }
}

class RuleStartState extends ATNState {
    constructor() {
        super();
        this.stateType = ATNState.RULE_START;
        this.stopState = null;
        this.isPrecedenceRule = false;
        return this;
    }
}

/**
 * Decision state for {@code A+} and {@code (A|B)+}.  It has two transitions:
 * one to the loop back to start of the block and one to exit.
 */
class PlusLoopbackState extends DecisionState {
    constructor() {
        super();
        this.stateType = ATNState.PLUS_LOOP_BACK;
        return this;
    }
}

/**
 * Start of {@code (A|B|...)+} loop. Technically a decision state, but
 * we don't use for code generation; somebody might need it, so I'm defining
 * it for completeness. In reality, the {@link PlusLoopbackState} node is the
 * real decision-making note for {@code A+}
 */
class PlusBlockStartState extends BlockStartState {
    constructor() {
        super();
        this.stateType = ATNState.PLUS_BLOCK_START;
        this.loopBackState = null;
        return this;
    }
}

/**
 * The block that begins a closure loop
 */
class StarBlockStartState extends BlockStartState {
    constructor() {
        super();
        this.stateType = ATNState.STAR_BLOCK_START;
        return this;
    }
}

class StarLoopbackState extends ATNState {
    constructor() {
        super();
        this.stateType = ATNState.STAR_LOOP_BACK;
        return this;
    }
}

class StarLoopEntryState extends DecisionState {
    constructor() {
        super();
        this.stateType = ATNState.STAR_LOOP_ENTRY;
        this.loopBackState = null;
        // Indicates whether this state can benefit from a precedence DFA during SLL decision making.
        this.isPrecedenceDecision = null;
        return this;
    }
}

/**
 * Mark the end of a * or + loop
 */
class LoopEndState extends ATNState {
    constructor() {
        super();
        this.stateType = ATNState.LOOP_END;
        this.loopBackState = null;
        return this;
    }
}

/**
 * The Tokens rule start state linking to each lexer rule start state
 */
class TokensStartState extends DecisionState {
    constructor() {
        super();
        this.stateType = ATNState.TOKEN_START;
        return this;
    }
}

module.exports = {
    ATNState,
    BasicState,
    DecisionState,
    BlockStartState,
    BlockEndState,
    LoopEndState,
    RuleStartState,
    RuleStopState,
    TokensStartState,
    PlusLoopbackState,
    StarLoopbackState,
    StarLoopEntryState,
    PlusBlockStartState,
    StarBlockStartState,
    BasicBlockStartState
}
<|endoftext|>


<commit_before>/*
	enhance.audit: this example file uses the enhance.js api to:
		 * determine whether a browser is qualified for enhancements
		 * define available CSS and JS assets
		 * test features and device conditions and environment to determine which files to load
		 * load those files via a single concatenated call
*/
(function( win ){

	// re-reference ejs var locally
	var ejs = win.ejs,
		docElem = win.document.documentElement;

	// Add your qualifications for major browser experience divisions here.
	// For example, you might choose to only enhance browsers that support document.querySelectorAll (IE8+, etc).
	// Use case will vary, but basic browsers: last stop here!
	if( !( "querySelectorAll" in win.document ) ){
		return;
	}

	// Add "enhanced" class to HTML element
	docElem.className += " enhanced";

	// Configure css and js paths, if desirable.
	ejs.basepath.js = "_js/";
	ejs.basepath.css = "_css/";

	// Define potential JS files for dynamic loading
	ejs.files.js = {
		domlib : "_lib/wrap.custom.js",
		ajaxinc : "_lib/ajaxinclude.wrap.js",
		general	: "generalenhancements.js",
		touch	: "touch.js",
		widescreen	: "widescreen.js"
	};

	// Define potential CSS files for dynamic loading
	ejs.files.css = {
		sample		: "sample1.css"
	};

	// Start queueing files for load.
	// Pass js or css paths one at a time to ejs.addFile

	// Add general js enhancements to all qualified browsers
	ejs.addFile( ejs.files.js.domlib );
	ejs.addFile( ejs.files.js.ajaxinc );
	ejs.addFile( ejs.files.js.general );

	// if touch events are supported, add touch file
	if( "ontouchend" in win.document ){
		ejs.addFile( ejs.files.js.touch );
	}

	// if screen is wider than 500px, add widescreen file
	if( screen.width > 500 ){
		ejs.addFile( ejs.files.js.widescreen );
	}

	// add a CSS file if the body has a class of "tmpl-home"
	// (beware: don't rely on loading CSS this way for styles that need to ejsly at page load or you'll get a FOUC)

	// Note: since we're using hasClass to check if the body element has a class or not, we need to wrap all remaining logic in a call to ejs.isDefined
	ejs.bodyReady( function(){

		if( ejs.hasClass( win.document.body, "tmpl-home" ) ){
			ejs.addFile( ejs.files.css.sample );
		}

		// Load the files, enhance page
		ejs.enhance();

	});

}( window ));<commit_msg>comment change<commit_after>/*
	enhance.config: this example file uses the enhance.js api to:
		 * determine whether a browser is qualified for enhancements
		 * define available CSS and JS assets
		 * test features and device conditions and environment to determine which files to load
		 * load those files via a single concatenated call
*/
(function( win ){

	// re-reference ejs var locally
	var ejs = win.ejs,
		docElem = win.document.documentElement;

	// Add your qualifications for major browser experience divisions here.
	// For example, you might choose to only enhance browsers that support document.querySelectorAll (IE8+, etc).
	// Use case will vary, but basic browsers: last stop here!
	if( !( "querySelectorAll" in win.document ) ){
		return;
	}

	// Add "enhanced" class to HTML element
	docElem.className += " enhanced";

	// Configure css and js paths, if desirable.
	ejs.basepath.js = "_js/";
	ejs.basepath.css = "_css/";

	// Define potential JS files for dynamic loading
	ejs.files.js = {
		domlib : "_lib/wrap.custom.js",
		ajaxinc : "_lib/ajaxinclude.wrap.js",
		general	: "generalenhancements.js",
		touch	: "touch.js",
		widescreen	: "widescreen.js"
	};

	// Define potential CSS files for dynamic loading
	ejs.files.css = {
		sample		: "sample1.css"
	};

	// Start queueing files for load.
	// Pass js or css paths one at a time to ejs.addFile

	// Add general js enhancements to all qualified browsers
	ejs.addFile( ejs.files.js.domlib );
	ejs.addFile( ejs.files.js.ajaxinc );
	ejs.addFile( ejs.files.js.general );

	// if touch events are supported, add touch file
	if( "ontouchend" in win.document ){
		ejs.addFile( ejs.files.js.touch );
	}

	// if screen is wider than 500px, add widescreen file
	if( screen.width > 500 ){
		ejs.addFile( ejs.files.js.widescreen );
	}

	// add a CSS file if the body has a class of "tmpl-home"
	// (beware: don't rely on loading CSS this way for styles that need to ejsly at page load or you'll get a FOUC)

	// Note: since we're using hasClass to check if the body element has a class or not, we need to wrap all remaining logic in a call to ejs.isDefined
	ejs.bodyReady( function(){

		if( ejs.hasClass( win.document.body, "tmpl-home" ) ){
			ejs.addFile( ejs.files.css.sample );
		}

		// Load the files, enhance page
		ejs.enhance();

	});

}( window ));<|endoftext|>


<commit_before>/* Python 'slice' object */

function slice(start, stop, step) {
    return new _slice(start, stop, step);
}

function _slice(start, stop, step) {
    this.__init__(start, stop, step);
}

_slice.__name__ = 'slice';
_slice.prototype.__class__ = _slice;
_slice.prototype.MARK = "slice";

_slice.prototype.__init__ = function(start, stop, step) {
    if (!defined(stop) && !defined(step))
    {
        stop = start;
        start = null;
    }
    if (!start && start != 0) start = null;
    if (!defined(stop)) stop = null;
    if (!defined(step)) step = null;
    this.start = start;
    this.stop = stop;
    this.step = step;
};

_slice.prototype.__str__ = function() {
    return str("slice(" + this.start + ", " + this.stop + ", " + this.step + ")");
};

_slice.prototype.indices = function(n) {
    var start = this.start;
    if (start == null)
        start = 0;
    if (start > n)
        start = n;
    if (start < 0)
        start = n+start;
    var stop = this.stop;
    if (stop > n)
        stop = n;
    if (stop == null)
        stop = n;
    if (stop < 0)
        stop = n+stop;
    var step = this.step;
    if (step == null)
        step = 1;
    return tuple([start, stop, step]);
};

<commit_msg> * Converted slice type to use __inherit and new class framework<commit_after>/* Python 'slice' object */

var slice = __inherit(object);

slice.__name__ = 'slice';
slice.prototype.__class__ = slice;
slice.prototype.MARK = "slice";

slice.prototype.__init__ = function(start, stop, step) {
    if (!defined(stop) && !defined(step))
    {
        stop = start;
        start = null;
    }
    if (!start && start != 0) start = null;
    if (!defined(stop)) stop = null;
    if (!defined(step)) step = null;
    this.start = start;
    this.stop = stop;
    this.step = step;
};

slice.prototype.__str__ = function() {
    return str("slice(" + this.start + ", " + this.stop + ", " + this.step + ")");
};

slice.prototype.indices = function(n) {
    var start = this.start;
    if (start == null)
        start = 0;
    if (start > n)
        start = n;
    if (start < 0)
        start = n+start;
    var stop = this.stop;
    if (stop > n)
        stop = n;
    if (stop == null)
        stop = n;
    if (stop < 0)
        stop = n+stop;
    var step = this.step;
    if (step == null)
        step = 1;
    return tuple.__call__([start, stop, step]);
};

<|endoftext|>


<commit_before>//META{"name":"menuDraggable"}*//
var menuDraggable = function () {};

menuDraggable.prototype.convert = function () {

	var selected = null, // Object of the element to be moved
		selection_array = [],
		x_pos = 0, y_pos = 0, // Stores x & y coordinates of the mouse pointer
		x_elem = 0, y_elem = 0, // Stores top, left values (edge) of the element
		left_margin = 20,
		right_margin = 20,
		top_margin = 20,
		bottom_margin = 20;

	if(bdPluginStorage.get("menuDraggable", "menuDraggableData") == null) bdPluginStorage.set("menuDraggable", "menuDraggableData", []);
	if(bdPluginStorage.get("menuDraggable", "menuDraggableData") != []) selection_array = bdPluginStorage.get("menuDraggable", "menuDraggableData");

	document.onmouseup = DetectedClick;

	function DetectedClick() {
		if (document.getElementById("draggable-grip") != null) _destroy();
		setTimeout(function(){
			var newP = document.createElement("div");
			newP.style.cssText = "width:800px; height:20px; cursor:move; position:fixed";
			newP.id = "draggable-grip";
			var parent_Node = document.getElementsByClassName('modal-inner')[0];
			if (parent_Node == null) return;
			if (document.getElementById("draggable-grip") == null) parent_Node.appendChild(newP);
			parent_Node.style.position = "fixed";

			if (containsElement(selection_array, parent_Node.firstChild.className) == false) selection_array.push({name: parent_Node.firstChild.className, pos: {left_pos: null, top_pos: null}});

			var i = findElement(selection_array, parent_Node.firstChild.className);
			if (i != null) {
				//console.log("Found element!");
				if (selection_array[i].pos.left_pos != null) {
					parent_Node.style.left = selection_array[i].pos.left_pos + 'px';
					parent_Node.style.top = selection_array[i].pos.top_pos + 'px';

					if (parseInt(parent_Node.style.left.replace('px', null)) < left_margin) parent_Node.style.left = left_margin + 'px';
					if (parseInt(parent_Node.style.top.replace('px', null)) < top_margin) parent_Node.style.top = top_margin + 'px';
					if (parseInt(parent_Node.style.left.replace('px', null)) > (window.innerWidth - parent_Node.offsetWidth - right_margin)) parent_Node.style.left = window.innerWidth - parent_Node.offsetWidth - right_margin  + 'px';
					if (parseInt(parent_Node.style.top.replace('px', null)) > (window.innerHeight - parent_Node.offsetHeight - bottom_margin)) parent_Node.style.top = window.innerHeight - parent_Node.offsetHeight - bottom_margin + 'px';
				}
			}
			//console.log(selection_array);
			bdPluginStorage.set("menuDraggable", "menuDraggableData", selection_array);

			parent_Node = null;
			enabledrag();
		}, 130);
	};

	function containsElement(list, objName) {
		var i;
		for (i = 0; i < list.length; i++) {
			if (list[i].name == objName)
				return true;
		}
		return false;
	}

	function findElement(list, objName) {
		var i;
		for (i = 0; i < list.length; i++) {
			if (list[i].name == objName)
				return i;
		}
		return null;
	}

	// Will be called when user starts dragging an element
	function _drag_init(elem) {
		// Store the object of the element which needs to be moved into a temporary variable
		selected = elem;

		x_elem = x_pos - selected.offsetLeft;
		y_elem = y_pos - selected.offsetTop;
	}

	// Will be called when user dragging an element
	function _move_elem(e) {
		x_pos = document.all ? window.event.clientX : e.pageX;
		y_pos = document.all ? window.event.clientY : e.pageY;
		if (selected !== null) {
			selected.style.left = (x_pos - x_elem) + 'px';
			selected.style.top = (y_pos - y_elem) + 'px';
		}
	}

	// Destroy the object when we are done
	function _destroy() {
		if (selected != null) {
			if (parseInt(selected.style.left.replace('px', null)) < left_margin) selected.style.left = left_margin + 'px';
			if (parseInt(selected.style.top.replace('px', null)) < top_margin) selected.style.top = top_margin + 'px';
			if (parseInt(selected.style.left.replace('px', null)) > (window.innerWidth - selected.offsetWidth - right_margin)) selected.style.left = window.innerWidth - selected.offsetWidth - right_margin  + 'px';
			if (parseInt(selected.style.top.replace('px', null)) > (window.innerHeight - selected.offsetHeight - bottom_margin)) selected.style.top = window.innerHeight - selected.offsetHeight - bottom_margin + 'px';

			var i = findElement(selection_array, selected.firstChild.className);
			selection_array[i].pos.left_pos = (parseInt(selected.style.left.replace('px', null)));
			selection_array[i].pos.top_pos = (parseInt(selected.style.top.replace('px', null)));
		}

		selected = null;
		document.onmouseup = DetectedClick;
	}

	// Bind the functions...
	function enabledrag() {
		document.getElementById('draggable-grip').onmousedown = function (e) {
			_drag_init(this.parentElement);
			return false;
		};
		document.onmousemove = _move_elem;
		document.onmouseup = _destroy;
	}
};

menuDraggable.prototype.start = function () {
    this.convert();
};

menuDraggable.prototype.load = function () {};

menuDraggable.prototype.unload = function () {};

menuDraggable.prototype.stop = function () {
	console.log("Stopped MenuDraggables Plugin!");
	bdPluginStorage.set("menuDraggable", "menuDraggableData", []);
	//ghetto removal of functions :^)
	menuDraggable.prototype.convert = function() {};
	document.onmouseup = function() {};
	/*
	DetectedClick() {};
	containsElement() {};
	findElement() {};
	_drag_init() {};
	_destroy() {};
	enabledrag() {};
	*/
};

menuDraggable.prototype.getSettingsPanel = function () {
    return "";
};

menuDraggable.prototype.getName = function () {
    return "Draggable Menus";
};
menuDraggable.prototype.getDescription = function () {
    return "Lets you drag most popup menus around on the screen, as if they were windows. Saves in between sessions, to reset windows, disable the plugin, then close and reopen discord.";
};
menuDraggable.prototype.getVersion = function () {
    return "1.0";
};
menuDraggable.prototype.getAuthor = function () {
    return "Bunnies McGee";
};
<commit_msg>biggest daddy update<commit_after>//META{"name":"menuDraggable"}*//
var menuDraggable = function () {};

menuDraggable.prototype.convert = function () {

	var selected = null, // Object of the element to be moved
		selection_array = [],
		x_pos = 0, y_pos = 0, // Stores x & y coordinates of the mouse pointer
		x_elem = 0, y_elem = 0, // Stores top, left values (edge) of the element
		left_margin = 20,
		right_margin = 20,
		top_margin = 20,
		bottom_margin = 20;

	if(bdPluginStorage.get("menuDraggable", "menuDraggableData") == null) bdPluginStorage.set("menuDraggable", "menuDraggableData", []);
	if(bdPluginStorage.get("menuDraggable", "menuDraggableData") != []) selection_array = bdPluginStorage.get("menuDraggable", "menuDraggableData");

	document.onmouseup = DetectedClick;

	function DetectedClick() {
		if (document.getElementById("draggable-grip") != null) _destroy();
		setTimeout(function(){
			var parent_Node = document.getElementsByClassName('modal-inner')[0];
			if (parent_Node == null)
				//parent_Node = document.getElementsByClassName('popout popout-right no-arrow')[0];
				//if (parent_Node == null)
				//	return;

			var newP = document.createElement("div");
			newP.style.cssText = "width:" + parent_Node.offsetWidth +"px; height:20px; cursor:move; position:fixed";
			newP.id = "draggable-grip";

			if (document.getElementById("draggable-grip") == null) parent_Node.appendChild(newP);
			parent_Node.style.position = "fixed";

			console.log(parent_Node.firstChild.className);
			if (containsElement(selection_array, parent_Node.firstChild.className) == false)
				if (parent_Node.firstChild.className != "modal-image") selection_array.push({name: parent_Node.firstChild.className, pos: {left_pos: null, top_pos: null}});

			var i = findElement(selection_array, parent_Node.firstChild.className);
			if (i != null) {
				//console.log("Found element!");
				if (selection_array[i].pos.left_pos != null) {
					if (parent_Node.firstChild.className != "modal-image") {
						parent_Node.style.left = selection_array[i].pos.left_pos + 'px';
						parent_Node.style.top = selection_array[i].pos.top_pos + 'px';

						if (parseInt(parent_Node.style.left.replace('px', null)) < left_margin) parent_Node.style.left = left_margin + 'px';
						if (parseInt(parent_Node.style.top.replace('px', null)) < top_margin) parent_Node.style.top = top_margin + 'px';
						if (parseInt(parent_Node.style.left.replace('px', null)) > (window.innerWidth - parent_Node.offsetWidth - right_margin)) parent_Node.style.left = window.innerWidth - parent_Node.offsetWidth - right_margin  + 'px';
						if (parseInt(parent_Node.style.top.replace('px', null)) > (window.innerHeight - parent_Node.offsetHeight - bottom_margin)) parent_Node.style.top = window.innerHeight - parent_Node.offsetHeight - bottom_margin + 'px';
					}
				}
			}
			//console.log(selection_array);
			bdPluginStorage.set("menuDraggable", "menuDraggableData", selection_array);

			parent_Node = null;
			enabledrag();
		}, 130);
	};

	function containsElement(list, objName) {
		var i;
		for (i = 0; i < list.length; i++) {
			if (list[i].name == objName)
				return true;
		}
		return false;
	}

	function findElement(list, objName) {
		var i;
		for (i = 0; i < list.length; i++) {
			if (list[i].name == objName)
				return i;
		}
		return null;
	}

	// Will be called when user starts dragging an element
	function _drag_init(elem) {
		// Store the object of the element which needs to be moved into a temporary variable
		selected = elem;

		x_elem = x_pos - selected.offsetLeft;
		y_elem = y_pos - selected.offsetTop;
	}

	// Will be called when user dragging an element
	function _move_elem(e) {
		x_pos = document.all ? window.event.clientX : e.pageX;
		y_pos = document.all ? window.event.clientY : e.pageY;
		if (selected !== null) {
			selected.style.left = (x_pos - x_elem) + 'px';
			selected.style.top = (y_pos - y_elem) + 'px';
		}
	}

	// Destroy the object when we are done
	function _destroy() {
		if (selected != null) {
			if (parseInt(selected.style.left.replace('px', null)) < left_margin) selected.style.left = left_margin + 'px';
			if (parseInt(selected.style.top.replace('px', null)) < top_margin) selected.style.top = top_margin + 'px';
			if (parseInt(selected.style.left.replace('px', null)) > (window.innerWidth - selected.offsetWidth - right_margin)) selected.style.left = window.innerWidth - selected.offsetWidth - right_margin  + 'px';
			if (parseInt(selected.style.top.replace('px', null)) > (window.innerHeight - selected.offsetHeight - bottom_margin)) selected.style.top = window.innerHeight - selected.offsetHeight - bottom_margin + 'px';

			var i = findElement(selection_array, selected.firstChild.className);
			selection_array[i].pos.left_pos = (parseInt(selected.style.left.replace('px', null)));
			selection_array[i].pos.top_pos = (parseInt(selected.style.top.replace('px', null)));
		}

		selected = null;
		document.onmouseup = DetectedClick;
	}

	// Bind the functions...
	function enabledrag() {
		document.getElementById('draggable-grip').onmousedown = function (e) {
			_drag_init(this.parentElement);
			return false;
		};
		document.onmousemove = _move_elem;
		document.onmouseup = _destroy;
	}
};

menuDraggable.prototype.start = function () {
    this.convert();
};

menuDraggable.prototype.load = function () {};

menuDraggable.prototype.unload = function () {};

menuDraggable.prototype.stop = function () {
	console.log("Stopped MenuDraggables Plugin!");
	bdPluginStorage.set("menuDraggable", "menuDraggableData", []);
	//ghetto removal of functions :^)
	menuDraggable.prototype.convert = function() {};
	document.onmouseup = function() {};
	/*
	DetectedClick() {};
	containsElement() {};
	findElement() {};
	_drag_init() {};
	_destroy() {};
	enabledrag() {};
	*/
};

menuDraggable.prototype.getSettingsPanel = function () {
	var html = '

Emoji Size Options
<input type="range" id="smallRangeSlider" value="22" onchange="updateSmallEmoji()">

<input type="range" id="bigRangeSlider" value="32" onchange="updateBigEmoji()">

';
    return html;
};

menuDraggable.prototype.getName = function () {
    return "Draggable Menus";
};
menuDraggable.prototype.getDescription = function () {
    return "Lets you drag most popup menus around on the screen, as if they were windows. Saves in between sessions, to reset windows, disable the plugin, then close and reopen discord.";
};
menuDraggable.prototype.getVersion = function () {
    return "1.0";
};
menuDraggable.prototype.getAuthor = function () {
    return "Bunnies McGee";
};
<|endoftext|>


<commit_before>/*
---

name: ScrollView

description: Provides a view that scrolls when its content is larger than the
             view area.

license: MIT-style license.

authors:
	- Jean-Philippe Dery (jeanphilippe.dery@gmail.com)

requires:
	- View

provides:
	- ScrollView

...
*/

/**
 * @see    http://moobilejs.com/doc/latest/View/ScrollView
 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @edited 0.2.0
 * @since  0.1.0
 */
Moobile.ScrollView = new Class({

	Extends: Moobile.View,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouch: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouchTime: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouchStartX: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouchStartY: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouchDuration: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouchDirectionX: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouchDirectionY: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_scroller: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_offset: null,

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#options
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @edited 0.2.0
	 * @since  0.1.0
	 */
	options: {
		scroller: ['IScroll.Android', 'Native', 'IScroll'],
		momentum: true,
		scrollX: false,
		scrollY: true,
		snapToPage: false,
		snapToPageAt: 20,
		snapToPageSizeX: null,
		snapToPageSizeY: null,
		snapToPageDuration: 150,
		snapToPageDelay: 150,
		initialScrollX: 0,
		initialScrollY: 0
	},

	/**
	 * @overridden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	willBuild: function() {
		this.parent();
		this.element.addClass('scroll-view');
	},

	/**
	 * @overridden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @edited 0.2.0
	 * @since  0.1.0
	 */
	didBuild: function() {

		this.parent();

		if (this.options.snapToPage) this.options.momentum = false;

		var options = {
			momentum: this.options.momentum,
			scrollX: this.options.scrollX,
			scrollY: this.options.scrollY,
		};

		this.contentElement.addEvent('touchcancel', this.bound('_onTouchCancel'));
		this.contentElement.addEvent('touchstart', this.bound('_onTouchStart'));
		this.contentElement.addEvent('touchend', this.bound('_onTouchEnd'));

		this._scroller = Moobile.Scroller.create(this.contentElement, this.contentWrapperElement, this.options.scroller, options);
		this._scroller.addEvent('scroll', this.bound('_onScroll'));

		var name = this._scroller.getName();
		if (name) {
			this.element.addClass(name + '-engine');
		}
	},

	/**
	 * @overridden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @edited 0.2.0
	 * @since  0.1.0
	 */
	didBecomeReady: function() {
		this.parent();
		this._scroller.refresh();
		this._scroller.scrollTo(
			this.options.initialScrollX,
			this.options.initialScrollY
		);
	},

	/**
	 * @overridden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @edited 0.2.0
	 * @since  0.1.0
	 */
	destroy: function() {
		this.contentElement.removeEvent('touchcancel', this.bound('_onTouchCancel'));
		this.contentElement.removeEvent('touchstart', this.bound('_onTouchStart'));
		this.contentElement.removeEvent('touchend', this.bound('_onTouchEnd'));
		this._scroller.removeEvent('scroll', this.bound('_onScroll'));
		this._scroller.destroy();
		this._scroller = null;
		this.parent();
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#setContentSize
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	setContentSize: function(x, y) {
		this.contentElement.setStyle('width', x);
		this.contentElement.setStyle('height', y);
		return this;
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#getContentSize
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getContentSize: function() {
		return this.contentElement.getScrollSize();
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#scrollTo
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	scrollTo: function(x, y, time) {
		this._scroller.scrollTo(x, y, time);
		return this;
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#scrollToElement
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	scrollToElement: function(element, time) {
		this._scroller.scrollToElement(element, time);
		return this;
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#scrollToPage
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @edited 0.2.0
	 * @since  0.1.0
	 */
	scrollToPage: function(pageX, pageY, time) {

		pageX = pageX || 0;
		pageY = pageY || 0;

		if (pageX < 0) pageX = 0;
		if (pageY < 0) pageY = 0;

		var frame = this.getSize();
		var total = this.getScrollSize();

		var xmax = total.x - frame.x;
		var ymax = total.y - frame.y;
		var x = (this.options.snapToPageSizeX || frame.x) * pageX;
		var y = (this.options.snapToPageSizeY || frame.y) * pageY;
		if (x > xmax) x = xmax;
		if (y > ymax) y = ymax;

		this.scrollTo(x, y, time);

		return this;
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#getScroll
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getScroll: function() {
		return this._scroller.getScroll();
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#getScrollSize
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getScrollSize: function() {
		return this._scroller.getScrollSize();
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#getScroller
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getScroller: function() {
		return this._scroller;
	},

	/**
	 * @overridden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	willHide: function() {
		this.parent();
		this._offset = this._scroller.getScroll();
	},

	/**
	 * @overridden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	didShow: function() {
		this.parent();
		this._scroller.refresh();
		this._scroller.scrollTo(this._offset.x, this._offset.y);
	},

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_snapToPage: function() {

		var frame = this.getSize();
		var pageSizeX = this.options.snapToPageSizeX || frame.x;
		var pageSizeY = this.options.snapToPageSizeY || frame.y;

		var scroll = this.getScroll();
		var pageX = Math.floor(scroll.x / pageSizeX);
		var pageY = Math.floor(scroll.y / pageSizeY);
		var moveX = (scroll.x / pageSizeX - pageX) * 100;
		var moveY = (scroll.y / pageSizeY - pageY) * 100;
		if (this._activeTouchDirectionX === 'left') moveX = 100 - moveX;
		if (this._activeTouchDirectionY === 'top')  moveY = 100 - moveY;

		// in this case it's still the page we started from
		if (this._activeTouchDirectionX === 'left') pageX += 1;
		if (this._activeTouchDirectionY === 'top')  pageY += 1;

		var snapToPageAt = this.options.snapToPageAt;
		var snapToPageDelay = this.options.snapToPageDelay;

		if (moveX > snapToPageAt || this._activeTouchDuration < snapToPageDelay) {
			switch (this._activeTouchDirectionX) {
				case 'rg': pageX += 1; break;
				case 'lf': pageX -= 1; break;
			}
		}

		if (moveY > snapToPageAt || this._activeTouchDuration < snapToPageDelay) {
			switch (this._activeTouchDirectionY) {
				case 'tp': pageY -= 1; break;
				case 'bt': pageY += 1; break;
			}
		}

		return this.scrollToPage(pageX, pageY, this.options.snapToPageDuration);
	},

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_onTouchCancel: function() {
		this._activeTouch = null;
		this._activeTouchTime = null;
		this._activeTouchStartX = null;
		this._activeTouchStartY = null;
		this._activeTouchDirectionX = null;
		this._activeTouchDirectionY = null;
	},

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_onTouchStart: function(e) {

		var touch = e.changedTouches[0];

		if (this._activeTouch === null) {
			this._activeTouch = touch;
			this._activeTouchTime = Date.now();
			this._activeTouchStartX = touch.pageX;
			this._activeTouchStartY = touch.pageY;
		}
	},

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_onTouchEnd: function(e) {

		var touch = e.changedTouches[0];

		if (this._activeTouch.identifier === touch.identifier) {
			this._activeTouchDuration = Date.now() - this._activeTouchTime;
			this._activeTouchDirectionX = this._activeTouchStartX < touch.pageX ? 'lf' : 'rg';
			this._activeTouchDirectionY = this._activeTouchStartY < touch.pageY ? 'tp' : 'bt';

			if (this.options.snapToPage) {
				if (this._activeTouchStartX !== touch.pageX ||
					this._activeTouchStartY !== touch.pageY) {
					this._snapToPage();
				}
			}

			this._activeTouch = null;
			this._activeTouchTime = null;
			this._activeTouchDuration = null;
			this._activeTouchDirectionX = null;
			this._activeTouchDirectionY = null;
		}
	},

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	_onScroll: function() {
		this.fireEvent('scroll');
	}

});

//------------------------------------------------------------------------------
// Roles
//------------------------------------------------------------------------------

Moobile.Component.defineRole('scroll-view', null, function(element) {
	this.addChildComponent(Moobile.Component.create(Moobile.ScrollView, element, 'data-scroll-view'));
});
<commit_msg>Added scrolltopage event<commit_after>/*
---

name: ScrollView

description: Provides a view that scrolls when its content is larger than the
             view area.

license: MIT-style license.

authors:
	- Jean-Philippe Dery (jeanphilippe.dery@gmail.com)

requires:
	- View

provides:
	- ScrollView

...
*/

/**
 * @see    http://moobilejs.com/doc/latest/View/ScrollView
 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @edited 0.2.0
 * @since  0.1.0
 */
Moobile.ScrollView = new Class({

	Extends: Moobile.View,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouch: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouchTime: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouchStartX: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouchStartY: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouchDuration: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouchDirectionX: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_activeTouchDirectionY: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_scroller: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_offset: null,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_snapping: false,

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_snappingTo: null,

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#options
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @edited 0.2.0
	 * @since  0.1.0
	 */
	options: {
		scroller: ['IScroll.Android', 'Native', 'IScroll'],
		momentum: true,
		scrollX: false,
		scrollY: true,
		snapToPage: false,
		snapToPageAt: 20,
		snapToPageSizeX: null,
		snapToPageSizeY: null,
		snapToPageDuration: 150,
		snapToPageDelay: 150,
		initialScrollX: 0,
		initialScrollY: 0
	},

	/**
	 * @overridden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	willBuild: function() {
		this.parent();
		this.element.addClass('scroll-view');
	},

	/**
	 * @overridden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @edited 0.2.0
	 * @since  0.1.0
	 */
	didBuild: function() {

		this.parent();

		if (this.options.snapToPage) this.options.momentum = false;

		var options = {
			momentum: this.options.momentum,
			scrollX: this.options.scrollX,
			scrollY: this.options.scrollY,
		};

		this.contentElement.addEvent('touchcancel', this.bound('_onTouchCancel'));
		this.contentElement.addEvent('touchstart', this.bound('_onTouchStart'));
		this.contentElement.addEvent('touchend', this.bound('_onTouchEnd'));

		this._scroller = Moobile.Scroller.create(this.contentElement, this.contentWrapperElement, this.options.scroller, options);
		this._scroller.addEvent('scroll', this.bound('_onScroll'));

		var name = this._scroller.getName();
		if (name) {
			this.element.addClass(name + '-engine');
		}
	},

	/**
	 * @overridden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @edited 0.2.0
	 * @since  0.1.0
	 */
	didBecomeReady: function() {
		this.parent();
		this._scroller.refresh();
		this._scroller.scrollTo(
			this.options.initialScrollX,
			this.options.initialScrollY
		);
	},

	/**
	 * @overridden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @edited 0.2.0
	 * @since  0.1.0
	 */
	destroy: function() {
		this.contentElement.removeEvent('touchcancel', this.bound('_onTouchCancel'));
		this.contentElement.removeEvent('touchstart', this.bound('_onTouchStart'));
		this.contentElement.removeEvent('touchend', this.bound('_onTouchEnd'));
		this._scroller.removeEvent('scroll', this.bound('_onScroll'));
		this._scroller.destroy();
		this._scroller = null;
		this.parent();
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#setContentSize
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	setContentSize: function(x, y) {
		this.contentElement.setStyle('width', x);
		this.contentElement.setStyle('height', y);
		return this;
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#getContentSize
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getContentSize: function() {
		return this.contentElement.getScrollSize();
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#scrollTo
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	scrollTo: function(x, y, time) {
		this._scroller.scrollTo(x, y, time);
		return this;
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#scrollToElement
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	scrollToElement: function(element, time) {
		this._scroller.scrollToElement(element, time);
		return this;
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#scrollToPage
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @edited 0.2.0
	 * @since  0.1.0
	 */
	scrollToPage: function(pageX, pageY, time) {

		pageX = pageX || 0;
		pageY = pageY || 0;

		if (pageX < 0) pageX = 0;
		if (pageY < 0) pageY = 0;

		var frame = this.getSize();
		var total = this.getScrollSize();

		var xmax = total.x - frame.x;
		var ymax = total.y - frame.y;
		var x = (this.options.snapToPageSizeX || frame.x) * pageX;
		var y = (this.options.snapToPageSizeY || frame.y) * pageY;
		if (x > xmax) x = xmax;
		if (y > ymax) y = ymax;

		this.scrollTo(x, y, time);

		this.fireEvent('scrolltopage', [pageX, pageY], time);

		return this;
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#getScroll
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getScroll: function() {
		return this._scroller.getScroll();
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#getPage
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	getPage: function() {

		var frame = this.getSize();
		var pageSizeX = this.options.snapToPageSizeX || frame.x;
		var pageSizeY = this.options.snapToPageSizeY || frame.y;

		var scroll = this.getScroll();
		var pageX = Math.floor(scroll.x / pageSizeX);
		var pageY = Math.floor(scroll.y / pageSizeY);

		if (this._activeTouch) {
			if (this._activeTouchDirectionX === 'left') pageX += 1
			if (this._activeTouchDirectionY === 'top')  pageY += 1;
		}

		return {
			x: pageX,
			y: pageY
		};
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#getScrollSize
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getScrollSize: function() {
		return this._scroller.getScrollSize();
	},

	/**
	 * @see    http://moobilejs.com/doc/latest/View/ScrollView#getScroller
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getScroller: function() {
		return this._scroller;
	},

	/**
	 * @overridden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	willHide: function() {
		this.parent();
		this._offset = this._scroller.getScroll();
	},

	/**
	 * @overridden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	didShow: function() {
		this.parent();
		this._scroller.refresh();
		this._scroller.scrollTo(this._offset.x, this._offset.y);
	},

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_snapToPage: function() {

		var frame = this.getSize();
		var pageSizeX = this.options.snapToPageSizeX || frame.x;
		var pageSizeY = this.options.snapToPageSizeY || frame.y;

		var scroll = this.getScroll();
		var pageX = Math.floor(scroll.x / pageSizeX);
		var pageY = Math.floor(scroll.y / pageSizeY);
		var moveX = (scroll.x / pageSizeX - pageX) * 100;
		var moveY = (scroll.y / pageSizeY - pageY) * 100;

		if (this._activeTouchDirectionX === 'left') {
			moveX = 100 - moveX;
			pageX += 1
		}

		if (this._activeTouchDirectionY === 'top') {
			moveY = 100 - moveY;
			pageY += 1;
		}

		var currentPageX = pageX;
		var currentPageY = pageY;

		var snapToPageAt = this.options.snapToPageAt;
		var snapToPageDelay = this.options.snapToPageDelay;
		var snapToPageDuration = this.options.snapToPageDuration

		if (moveX > snapToPageAt || this._activeTouchDuration < snapToPageDelay) {
			switch (this._activeTouchDirectionX) {
				case 'rg': pageX += 1; break;
				case 'lf': pageX -= 1; break;
			}
		}

		if (moveY > snapToPageAt || this._activeTouchDuration < snapToPageDelay) {
			switch (this._activeTouchDirectionY) {
				case 'tp': pageY -= 1; break;
				case 'bt': pageY += 1; break;
			}
		}

		if (pageX !== currentPageX ||
			pageY !== currentPageY) {
			this.scrollToPage(pageX, pageY, snapToPageDuration)
		}

		return this;
	},

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_onTouchCancel: function() {
		this._activeTouch = null;
		this._activeTouchTime = null;
		this._activeTouchStartX = null;
		this._activeTouchStartY = null;
		this._activeTouchDirectionX = null;
		this._activeTouchDirectionY = null;
	},

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_onTouchStart: function(e) {

		var touch = e.changedTouches[0];

		if (this._activeTouch === null) {
			this._activeTouch = touch;
			this._activeTouchTime = Date.now();
			this._activeTouchStartX = touch.pageX;
			this._activeTouchStartY = touch.pageY;
		}
	},

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.2.0
	 */
	_onTouchEnd: function(e) {

		var touch = e.changedTouches[0];

		if (this._activeTouch.identifier === touch.identifier) {
			this._activeTouchDuration = Date.now() - this._activeTouchTime;
			this._activeTouchDirectionX = this._activeTouchStartX < touch.pageX ? 'lf' : 'rg';
			this._activeTouchDirectionY = this._activeTouchStartY < touch.pageY ? 'tp' : 'bt';

			if (this.options.snapToPage) {
				if (this._activeTouchStartX !== touch.pageX ||
					this._activeTouchStartY !== touch.pageY) {
					this._snapToPage();
				}
			}

			this._activeTouch = null;
			this._activeTouchTime = null;
			this._activeTouchDuration = null;
			this._activeTouchDirectionX = null;
			this._activeTouchDirectionY = null;
		}
	},

	/**
	 * @hidden
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	_onScroll: function() {
		this.fireEvent('scroll');
	}

});

//------------------------------------------------------------------------------
// Roles
//------------------------------------------------------------------------------

Moobile.Component.defineRole('scroll-view', null, function(element) {
	this.addChildComponent(Moobile.Component.create(Moobile.ScrollView, element, 'data-scroll-view'));
});
<|endoftext|>


<commit_before>/**
 * Menu Button
 */
Garnish.MenuBtn = Garnish.Base.extend({

	$btn: null,
	menu: null,
	showingMenu: false,
	disabled: true,

	/**
	 * Constructor
	 */
	init: function(btn, settings)
	{
		this.$btn = $(btn);

		// Is this already a menu button?
		if (this.$btn.data('menubtn'))
		{
			// Grab the old MenuBtn's menu container
			var $menu = this.$btn.data('menubtn').menu.$container;

			Garnish.log('Double-instantiating a menu button on an element');
			this.$btn.data('menubtn').destroy();
		}
		else
		{
			var $menu = this.$btn.next('.menu').detach();
		}

		this.$btn.data('menubtn', this);

		this.setSettings(settings, Garnish.MenuBtn.defaults);

		this.menu = new Garnish.Menu($menu, {
			attachToElement: this.$btn,
			onOptionSelect: $.proxy(this, 'onOptionSelect')
		});

		this.menu.on('hide', $.proxy(this, 'onMenuHide'));

		this.addListener(this.$btn, 'mousedown', 'onMouseDown');
		this.enable();
	},

	onMouseDown: function(ev)
	{
		if (ev.which != Garnish.PRIMARY_CLICK || ev.metaKey)
		{
			return;
		}

		ev.preventDefault();

		if (this.showingMenu)
		{
			this.hideMenu();
		}
		else
		{
			this.showMenu();
		}
	},

	showMenu: function()
	{
		if (this.disabled)
		{
			return;
		}

		this.menu.show();
		this.$btn.addClass('active');
		this.showingMenu = true;

		setTimeout($.proxy(function() {
			this.addListener(Garnish.$doc, 'mousedown', 'onMouseDown');
		}, this), 1);

		if (!Garnish.isMobileBrowser())
		{
			this.addListener(Garnish.$win, 'resize', 'hideMenu');
		}
	},

	hideMenu: function()
	{
		this.menu.hide();
	},

	onMenuHide: function()
	{
		this.$btn.removeClass('active');
		this.showingMenu = false;

		this.removeListener(Garnish.$doc, 'mousedown');

		if (!Garnish.isMobileBrowser())
		{
			this.removeListener(Garnish.$doc, 'resize');
		}
	},

	onOptionSelect: function(option)
	{
		this.settings.onOptionSelect(option);
	},

	enable: function ()
	{
		this.disabled = false;
	},

	disable: function ()
	{
		this.disabled = true;
	}
},
{
	defaults: {
		onOptionSelect: $.noop
	}
});
<commit_msg>Stop listening to window resize when hiding the menu<commit_after>/**
 * Menu Button
 */
Garnish.MenuBtn = Garnish.Base.extend({

	$btn: null,
	menu: null,
	showingMenu: false,
	disabled: true,

	/**
	 * Constructor
	 */
	init: function(btn, settings)
	{
		this.$btn = $(btn);

		// Is this already a menu button?
		if (this.$btn.data('menubtn'))
		{
			// Grab the old MenuBtn's menu container
			var $menu = this.$btn.data('menubtn').menu.$container;

			Garnish.log('Double-instantiating a menu button on an element');
			this.$btn.data('menubtn').destroy();
		}
		else
		{
			var $menu = this.$btn.next('.menu').detach();
		}

		this.$btn.data('menubtn', this);

		this.setSettings(settings, Garnish.MenuBtn.defaults);

		this.menu = new Garnish.Menu($menu, {
			attachToElement: this.$btn,
			onOptionSelect: $.proxy(this, 'onOptionSelect')
		});

		this.menu.on('hide', $.proxy(this, 'onMenuHide'));

		this.addListener(this.$btn, 'mousedown', 'onMouseDown');
		this.enable();
	},

	onMouseDown: function(ev)
	{
		if (ev.which != Garnish.PRIMARY_CLICK || ev.metaKey)
		{
			return;
		}

		ev.preventDefault();

		if (this.showingMenu)
		{
			this.hideMenu();
		}
		else
		{
			this.showMenu();
		}
	},

	showMenu: function()
	{
		if (this.disabled)
		{
			return;
		}

		this.menu.show();
		this.$btn.addClass('active');
		this.showingMenu = true;

		setTimeout($.proxy(function() {
			this.addListener(Garnish.$doc, 'mousedown', 'onMouseDown');
		}, this), 1);

		if (!Garnish.isMobileBrowser())
		{
			this.addListener(Garnish.$win, 'resize', 'hideMenu');
		}
	},

	hideMenu: function()
	{
		if (!Garnish.isMobileBrowser())
		{
			this.removeListener(Garnish.$win, 'resize');
		}

		this.menu.hide();
	},

	onMenuHide: function()
	{
		this.$btn.removeClass('active');
		this.showingMenu = false;

		this.removeListener(Garnish.$doc, 'mousedown');

		if (!Garnish.isMobileBrowser())
		{
			this.removeListener(Garnish.$doc, 'resize');
		}
	},

	onOptionSelect: function(option)
	{
		this.settings.onOptionSelect(option);
	},

	enable: function ()
	{
		this.disabled = false;
	},

	disable: function ()
	{
		this.disabled = true;
	}
},
{
	defaults: {
		onOptionSelect: $.noop
	}
});
<|endoftext|>