<commit_before>// Copyright (c) 2009, Scott Ferguson
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the software nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

function HotKeyManager() {
    /*****************
     N - Next Post
     P/M - Previous Post
     J - Next Page
     K/H - Previous Page
     O - Reanchor thread
     Q - Quick Quote current post
     E - Quick Edit current post
     R - Quick Reply current thread
    *******************/
    this.bindHotKeys();

    jQuery(document).data("enableSALRHotkeys", true);
    jQuery(document).bind("enableSALRHotkeys", this.enableHotKeys);
    jQuery(document).bind("disableSALRHotkeys", this.disableHotKeys);

    this.thread_post_size = jQuery('div#thread > table.post').size();
    this.current_post = findFirstUnreadPost();
    this.first_keypress = true;
}

HotKeyManager.prototype.bindHotKeys = function() {
    var that = this;

    jQuery(document).keypress(function(event) {
        var quick_reply_block = false;

        if(!jQuery(document).data("enableSALRHotkeys"))
            return;

        /*if (findCurrentPage() == 'showthread.php') {
            if (quickReply.isExpanded() || quickReply.isVisible()) {
                quick_reply_block = true;
            }
        }

        if (!quick_reply_block) {*/
            switch(event.keyCode) {
                case 110:
                    // Next post
                    that.nextPost();
                    break;
                case 112:
                case 109:
                    // Previous post
                    that.previousPost();
                    break;
                case 106:
                    // Next page
                    that.nextPage();
                    break;
                case 107:
                case 104:
                    // Previous page
                    that.previousPage();
                    break;
                case 111:
                    // Re-anchor thread
                    that.anchorThread();
                    break;
                case 49: /* 1 */
                    // Jump to first post on the page
                    that.firstPost();
                    break;
                case 48: /* 0 */
                    // Jump to last post on the page
                    that.lastPost();
                    break;
                case 115: /* s */
                    //that.toggleSignatures();
                    break;
                case 113:
                    // Quick quote current post
                    that.quoteCurrentPost();
                    event.preventDefault();
                    break;
                case 101:
                    // Quick edit current post
                    break;
                case 114:
                    // TODO: Conditionalize on quick reply being enabled
                    that.displayQuickReply();
                    event.preventDefault();
                    break;
                case 27:
                    // TODO: Conditionalize on quick reply being enabled
                    that.hideQuickReply();
                    break;
            }
        //}
    });
};

HotKeyManager.prototype.enableHotKeys = function() {
    jQuery(document).data("enableSALRHotkeys", true);
};

HotKeyManager.prototype.disableHotKeys = function() {
    jQuery(document).data("enableSALRHotkeys", false);
};

HotKeyManager.prototype.nextPage = function() {
    this.pageCount = countPages();

    switch(findCurrentPage()) {
        case 'forumdisplay.php':
        case 'showthread.php':
            this.rootPageType = (findCurrentPage() == 'forumdisplay.php') ? 'forumid' : 'threadid';
            this.basePageID = findForumID();
            this.currentPage = Number(jQuery('span.curpage').html());
            if (this.currentPage <= 0)
                this.currentPage = 1;
            if (this.currentPage < this.pageCount)
                jumpToPage(buildUrl(this.rootPageType, this.basePageID, this.currentPage + 1));
            break;
    }
};

HotKeyManager.prototype.previousPage = function() {
    this.pageCount = countPages();

    switch(findCurrentPage()) {
        case 'forumdisplay.php':
        case 'showthread.php':
            this.rootPageType = (findCurrentPage() == 'forumdisplay.php') ? 'forumid' : 'threadid';
            this.basePageID = findForumID();
            this.currentPage = Number(jQuery('span.curpage').html());
            if (this.currentPage <= 0)
                this.currentPage = 1;
            if (this.currentPage > 1)
                jumpToPage(buildUrl(this.rootPageType, this.basePageID, this.currentPage - 1));
            break;
    }
};

HotKeyManager.prototype.nextPost = function() {
    if (findCurrentPage() != 'showthread.php') {
        return;
    }

    if (this.first_keypress) {
        this.first_keypress = false;
    } else if (this.current_post < (this.thread_post_size - 1)) {
        this.current_post++;
    }

    var post = jQuery('div#thread > table.post');
    var previous_post = post.eq(this.current_post - 1);
    var current_post = post.eq(this.current_post);
    previous_post.removeClass('selected-post');
    current_post.addClass('selected-post');

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.previousPost = function() {
    if (findCurrentPage() != 'showthread.php') {
        return;
    }

    this.first_keypress = false;
    if (this.current_post > 0) {
        this.current_post--;
    }

    var post = jQuery('div#thread > table.post');
    var previous_post = post.eq(this.current_post + 1);
    var current_post = post.eq(this.current_post);
    previous_post.removeClass('selected-post');
    current_post.addClass('selected-post');

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.firstPost = function() {
    if (findCurrentPage() != 'showthread.php') {
        return;
    }

    this.first_keypress = false;
    var post = jQuery('div#thread > table.post');
    var current_post = post.eq(this.current_post);

    if (this.current_post > 0) {
        var previous_post = post.eq(this.current_post);

        this.current_post=0;
        current_post = post.eq(this.current_post);

        previous_post.removeClass('selected-post');
        current_post.addClass('selected-post');
    }

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.lastPost = function() {
    if (findCurrentPage() != 'showthread.php') {
        return;
    }

    this.first_keypress = false;
    var post = jQuery('div#thread > table.post');
    var current_post = post.eq(this.current_post);

    if (this.current_post < this.thread_post_size-1) {
        var previous_post = post.eq(this.current_post);

        this.current_post=this.thread_post_size-1;
        current_post = post.eq(this.current_post);

        previous_post.removeClass('selected-post');
        current_post.addClass('selected-post');
    }

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.anchorThread = function() {
    if (findCurrentPage() != 'showthread.php') {
        return;
    } else if (this.current_post == -1) {
        return;
    }

    this.first_keypress = false;
    var post = jQuery('div#thread > table.post').eq(this.current_post);
    post.addClass('selected-post');

    jQuery(window).scrollTop(post.offset().top);
};

HotKeyManager.prototype.toggleSignatures = function() {
    jQuery('p.signature').each(function() {
        if (jQuery(this).css('display') == 'none')
            jQuery(this).css('display','');
        else
            jQuery(this).css('display','none');
    });
};

HotKeyManager.prototype.quoteCurrentPost = function() {
    if (this.current_post == -1) {
        return;
    }

    var current_post = jQuery('div#thread > table.post').eq(this.current_post);
    var username = jQuery('tr > td.userinfo > dl > dt.author', current_post).html();
    // Query for the quote
    var quote = jQuery('tr > td.postbody', current_post).clone();

    quickReply.appendQuote(username, quote);
    quickReply.show();
};

HotKeyManager.prototype.displayQuickReply = function() {
    if (findCurrentPage() == 'showthread.php') {
        quickReply.show();
    }
};

HotKeyManager.prototype.hideQuickReply = function() {
    if (findCurrentPage() == 'showthread.php') {
        quickReply.hide();
    }
};
<commit_msg>Prevent hotkeys from swallowing keypresses on other pages<commit_after>// Copyright (c) 2009, Scott Ferguson
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the software nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

function HotKeyManager() {
    /*****************
     N - Next Post
     P/M - Previous Post
     J - Next Page
     K/H - Previous Page
     O - Reanchor thread
     Q - Quick Quote current post
     E - Quick Edit current post
     R - Quick Reply current thread
    *******************/
    this.bindHotKeys();

    jQuery(document).data("enableSALRHotkeys", true);
    jQuery(document).bind("enableSALRHotkeys", this.enableHotKeys);
    jQuery(document).bind("disableSALRHotkeys", this.disableHotKeys);

    this.thread_post_size = jQuery('div#thread > table.post').size();
    this.current_post = findFirstUnreadPost();
    this.first_keypress = true;
}

HotKeyManager.prototype.bindHotKeys = function() {
    var that = this;

    jQuery(document).keypress(function(event) {
        var quick_reply_block = false;

        if(!jQuery(document).data("enableSALRHotkeys"))
            return;

        /*if (findCurrentPage() == 'showthread.php') {
            if (quickReply.isExpanded() || quickReply.isVisible()) {
                quick_reply_block = true;
            }
        }

        if (!quick_reply_block) {*/
            switch(event.keyCode) {
                case 110:
                    // Next post
                    that.nextPost();
                    break;
                case 112:
                case 109:
                    // Previous post
                    that.previousPost();
                    break;
                case 106:
                    // Next page
                    that.nextPage();
                    break;
                case 107:
                case 104:
                    // Previous page
                    that.previousPage();
                    break;
                case 111:
                    // Re-anchor thread
                    that.anchorThread();
                    break;
                case 49: /* 1 */
                    // Jump to first post on the page
                    that.firstPost();
                    break;
                case 48: /* 0 */
                    // Jump to last post on the page
                    that.lastPost();
                    break;
                case 115: /* s */
                    //that.toggleSignatures();
                    break;
                case 113:
                    // Quick quote current post
                    if (findCurrentPage() == 'showthread.php') {
                        that.quoteCurrentPost();
                        event.preventDefault();
                    }
                    break;
                case 101:
                    // Quick edit current post
                    break;
                case 114:
                    // TODO: Conditionalize on quick reply being enabled
                    if (findCurrentPage() == 'showthread.php') {
                        that.displayQuickReply();
                        event.preventDefault();
                    }
                    break;
                case 27:
                    // TODO: Conditionalize on quick reply being enabled
                    that.hideQuickReply();
                    break;
            }
        //}
    });
};

HotKeyManager.prototype.enableHotKeys = function() {
    jQuery(document).data("enableSALRHotkeys", true);
};

HotKeyManager.prototype.disableHotKeys = function() {
    jQuery(document).data("enableSALRHotkeys", false);
};

HotKeyManager.prototype.nextPage = function() {
    this.pageCount = countPages();

    switch(findCurrentPage()) {
        case 'forumdisplay.php':
        case 'showthread.php':
            this.rootPageType = (findCurrentPage() == 'forumdisplay.php') ? 'forumid' : 'threadid';
            this.basePageID = findForumID();
            this.currentPage = Number(jQuery('span.curpage').html());
            if (this.currentPage <= 0)
                this.currentPage = 1;
            if (this.currentPage < this.pageCount)
                jumpToPage(buildUrl(this.rootPageType, this.basePageID, this.currentPage + 1));
            break;
    }
};

HotKeyManager.prototype.previousPage = function() {
    this.pageCount = countPages();

    switch(findCurrentPage()) {
        case 'forumdisplay.php':
        case 'showthread.php':
            this.rootPageType = (findCurrentPage() == 'forumdisplay.php') ? 'forumid' : 'threadid';
            this.basePageID = findForumID();
            this.currentPage = Number(jQuery('span.curpage').html());
            if (this.currentPage <= 0)
                this.currentPage = 1;
            if (this.currentPage > 1)
                jumpToPage(buildUrl(this.rootPageType, this.basePageID, this.currentPage - 1));
            break;
    }
};

HotKeyManager.prototype.nextPost = function() {
    if (findCurrentPage() != 'showthread.php') {
        return;
    }

    if (this.first_keypress) {
        this.first_keypress = false;
    } else if (this.current_post < (this.thread_post_size - 1)) {
        this.current_post++;
    }

    var post = jQuery('div#thread > table.post');
    var previous_post = post.eq(this.current_post - 1);
    var current_post = post.eq(this.current_post);
    previous_post.removeClass('selected-post');
    current_post.addClass('selected-post');

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.previousPost = function() {
    if (findCurrentPage() != 'showthread.php') {
        return;
    }

    this.first_keypress = false;
    if (this.current_post > 0) {
        this.current_post--;
    }

    var post = jQuery('div#thread > table.post');
    var previous_post = post.eq(this.current_post + 1);
    var current_post = post.eq(this.current_post);
    previous_post.removeClass('selected-post');
    current_post.addClass('selected-post');

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.firstPost = function() {
    if (findCurrentPage() != 'showthread.php') {
        return;
    }

    this.first_keypress = false;
    var post = jQuery('div#thread > table.post');
    var current_post = post.eq(this.current_post);

    if (this.current_post > 0) {
        var previous_post = post.eq(this.current_post);

        this.current_post=0;
        current_post = post.eq(this.current_post);

        previous_post.removeClass('selected-post');
        current_post.addClass('selected-post');
    }

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.lastPost = function() {
    if (findCurrentPage() != 'showthread.php') {
        return;
    }

    this.first_keypress = false;
    var post = jQuery('div#thread > table.post');
    var current_post = post.eq(this.current_post);

    if (this.current_post < this.thread_post_size-1) {
        var previous_post = post.eq(this.current_post);

        this.current_post=this.thread_post_size-1;
        current_post = post.eq(this.current_post);

        previous_post.removeClass('selected-post');
        current_post.addClass('selected-post');
    }

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.anchorThread = function() {
    if (findCurrentPage() != 'showthread.php') {
        return;
    } else if (this.current_post == -1) {
        return;
    }

    this.first_keypress = false;
    var post = jQuery('div#thread > table.post').eq(this.current_post);
    post.addClass('selected-post');

    jQuery(window).scrollTop(post.offset().top);
};

HotKeyManager.prototype.toggleSignatures = function() {
    jQuery('p.signature').each(function() {
        if (jQuery(this).css('display') == 'none')
            jQuery(this).css('display','');
        else
            jQuery(this).css('display','none');
    });
};

HotKeyManager.prototype.quoteCurrentPost = function() {
    if (this.current_post == -1) {
        return;
    }

    var current_post = jQuery('div#thread > table.post').eq(this.current_post);
    var username = jQuery('tr > td.userinfo > dl > dt.author', current_post).html();
    // Query for the quote
    var quote = jQuery('tr > td.postbody', current_post).clone();

    quickReply.appendQuote(username, quote);
    quickReply.show();
};

HotKeyManager.prototype.displayQuickReply = function() {
    if (findCurrentPage() == 'showthread.php') {
        quickReply.show();
    }
};

HotKeyManager.prototype.hideQuickReply = function() {
    if (findCurrentPage() == 'showthread.php') {
        quickReply.hide();
    }
};
<|endoftext|>


<commit_before>// Copyright (c) 2009, Scott Ferguson
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the software nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

function HotKeyManager() {
    /*****************
     N - Next Post
     P/M - Previous Post
     J - Next Page
     K/H - Previous Page
     O - Reanchor thread
     Q - Quick Quote current post
     E - Quick Edit current post
     R - Quick Reply current thread
    *******************/
    this.bindHotKeys();

    this.thread_post_size = jQuery('div#thread > table.post').size();
    this.current_post = this.findFirstUnreadPost();
}

HotKeyManager.prototype.bindHotKeys = function() {
    var that = this;

    jQuery(document).keypress(function(event) {
        var quick_reply_block = false;

        if (findCurrentPage() == 'showthread.php') {
            if (quickReply.isExpanded() || quickReply.isVisible()) {
                quick_reply_block = true;
            }
        }

        if (!quick_reply_block) {
            switch(event.keyCode) {
                case 110:
                    // Next post
                    that.nextPost();
                    break;
                case 112:
                case 109:
                    // Previous post
                    that.previousPost();
                    break;
                case 106:
                    // Next page
                    that.nextPage();
                    break;
                case 107:
                case 104:
                    // Previous page
                    that.previousPage();
                    break;
                case 111:
                    // Re-anchor thread
                    that.anchorThread();
                    break;
                case 49: /* 1 */
                    // Jump to first post on the page
                    that.firstPost();
                    break;
                case 48: /* 0 */
                    // Jump to last post on the page
                    that.lastPost();
                    break;
                case 113:
                    // Quick quote current post
                    that.quoteCurrentPost();
                    break;
                case 101:
                    // Quick edit current post
                    break;
                case 114:
                    // TODO: Conditionalize on quick reply being enabled
                    that.displayQuickReply();
                    break;
                case 27:
                    // TODO: Conditionalize on quick reply being enabled
                    that.hideQuickReply();
                    break;
            }
        }
    });
};

HotKeyManager.prototype.findFirstUnreadPost = function() {
    var index = 0;
    var count = 0;

    var anchor_post = window.location.href.split('#pti')[1];
    if (!isNaN(anchor_post)) {
        return anchor_post-1;
    }

    // Check if the user has the "Show an icon next to each post indicating if
    // it has been seen or not" option enabled for the forums
    var use_setseen = (jQuery('td.postdate > a[href*=action=setseen]').length > 0);

    jQuery('div#thread > table.post').each(function() {
        if (use_setseen) {
            // User has setseen icons enabled
            var posticon_img = jQuery('img[src*=posticon]', this);

            count++;
            if (posticon_img.attr('src') == 'http://fi.somethingawful.com/style/posticon-seen.gif') {
                index = count;
            }
        } else {
            // User has read post coloring enabled
            var post = jQuery('tr', this);

            count++;
            if (post.hasClass('seen1') || post.hasClass('seen2')) {
                index = count;
            }
        }
    });

    if (index == this.thread_post_size) // All posts read
        return index-1;

    return index;
};

HotKeyManager.prototype.nextPage = function() {
    this.pageCount = countPages();

    switch(findCurrentPage()) {
        case 'forumdisplay.php':
        case 'showthread.php':
            this.rootPageType = (findCurrentPage() == 'forumdisplay.php') ? 'forumid' : 'threadid';
            this.basePageID = findForumID();
            this.currentPage = Number(jQuery('span.curpage').html());
            if (this.currentPage <= 0)
                this.currentPage = 1;
            if (this.currentPage < this.pageCount)
                jumpToPage(this.rootPageType, this.basePageID, this.currentPage + 1);
            break;
    }
};

HotKeyManager.prototype.previousPage = function() {
    this.pageCount = countPages();

    switch(findCurrentPage()) {
        case 'forumdisplay.php':
        case 'showthread.php':
            this.rootPageType = (findCurrentPage() == 'forumdisplay.php') ? 'forumid' : 'threadid';
            this.basePageID = findForumID();
            this.currentPage = Number(jQuery('span.curpage').html());
            if (this.currentPage <= 0)
                this.currentPage = 1;
            if (this.currentPage > 1)
                jumpToPage(this.rootPageType, this.basePageID, this.currentPage - 1);
            break;
    }
};

HotKeyManager.prototype.nextPost = function() {
    if (!findCurrentPage() == 'showthread.php') {
        return;
    }

    if (this.current_post < (this.thread_post_size - 1)) {
        this.current_post++;
    }

    var post = jQuery('div#thread > table.post');
    var previous_post = post.eq(this.current_post - 1);
    var current_post = post.eq(this.current_post);
    previous_post.css('border', '1px solid #c1c1c1');
    current_post.css('border', '2px dashed #aaa');

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.previousPost = function() {
    if (!findCurrentPage() == 'showthread.php') {
        return;
    }

    if (this.current_post > 0) {
        this.current_post--;
    }

    var post = jQuery('div#thread > table.post');
    var previous_post = post.eq(this.current_post + 1);
    var current_post = post.eq(this.current_post);
    previous_post.css('border', '1px solid #c1c1c1');
    current_post.css('border', '2px dashed #aaa');

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.firstPost = function() {
    if (!findCurrentPage() == 'showthread.php') {
        return;
    }

    var post = jQuery('div#thread > table.post');
    var current_post = post.eq(this.current_post);

    if (this.current_post > 0) {
        var previous_post = post.eq(this.current_post);
        previous_post.css('border', '1px solid #c1c1c1');

        this.current_post=0;
        current_post = post.eq(this.current_post);
        current_post.css('border', '2px dashed #aaa');
    }

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.lastPost = function() {
    if (!findCurrentPage() == 'showthread.php') {
        return;
    }

    var post = jQuery('div#thread > table.post');
    var current_post = post.eq(this.current_post);

    if (this.current_post < this.thread_post_size-1) {
        var previous_post = post.eq(this.current_post);
        previous_post.css('border', '1px solid #c1c1c1');

        this.current_post=this.thread_post_size-1;
        current_post = post.eq(this.current_post);
        current_post.css('border', '2px dashed #aaa');
    }

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.anchorThread = function() {
    if (!findCurrentPage() == 'showthread.php') {
        return;
    } else if (this.current_post == -1) {
        return;
    }

    var post = jQuery('div#thread > table.post').eq(this.current_post);
    post.css('border', '2px dashed #aaa');

    jQuery(window).scrollTop(post.offset().top);
};

HotKeyManager.prototype.quoteCurrentPost = function() {
    if (this.current_post == -1) {
        return;
    }

    var current_post = jQuery('div#thread > table.post').eq(this.current_post);
    var username = jQuery('tr > td.userinfo > dl > dt.author', current_post).html();
    // Query for the quote
    var quote = jQuery('tr > td.postbody', current_post).clone();

    quickReply.appendQuote(username, quote);
    quickReply.show();
};

HotKeyManager.prototype.displayQuickReply = function() {
    if (findCurrentPage() == 'showthread.php') {
        quickReply.show();
    }
};

HotKeyManager.prototype.hideQuickReply = function() {
    if (findCurrentPage() == 'showthread.php') {
        quickReply.hide();
    }
};
<commit_msg>If the post anchor is set in the url, start keyboard nav with that postid<commit_after>// Copyright (c) 2009, Scott Ferguson
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the software nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

function HotKeyManager() {
    /*****************
     N - Next Post
     P/M - Previous Post
     J - Next Page
     K/H - Previous Page
     O - Reanchor thread
     Q - Quick Quote current post
     E - Quick Edit current post
     R - Quick Reply current thread
    *******************/
    this.bindHotKeys();

    this.thread_post_size = jQuery('div#thread > table.post').size();
    this.current_post = this.findFirstUnreadPost();
}

HotKeyManager.prototype.bindHotKeys = function() {
    var that = this;

    jQuery(document).keypress(function(event) {
        var quick_reply_block = false;

        if (findCurrentPage() == 'showthread.php') {
            if (quickReply.isExpanded() || quickReply.isVisible()) {
                quick_reply_block = true;
            }
        }

        if (!quick_reply_block) {
            switch(event.keyCode) {
                case 110:
                    // Next post
                    that.nextPost();
                    break;
                case 112:
                case 109:
                    // Previous post
                    that.previousPost();
                    break;
                case 106:
                    // Next page
                    that.nextPage();
                    break;
                case 107:
                case 104:
                    // Previous page
                    that.previousPage();
                    break;
                case 111:
                    // Re-anchor thread
                    that.anchorThread();
                    break;
                case 49: /* 1 */
                    // Jump to first post on the page
                    that.firstPost();
                    break;
                case 48: /* 0 */
                    // Jump to last post on the page
                    that.lastPost();
                    break;
                case 113:
                    // Quick quote current post
                    that.quoteCurrentPost();
                    break;
                case 101:
                    // Quick edit current post
                    break;
                case 114:
                    // TODO: Conditionalize on quick reply being enabled
                    that.displayQuickReply();
                    break;
                case 27:
                    // TODO: Conditionalize on quick reply being enabled
                    that.hideQuickReply();
                    break;
            }
        }
    });
};

HotKeyManager.prototype.findFirstUnreadPost = function() {
    var index = 0;
    var count = 0;

    // Get post number on page from anchor in URL
    var anchor_post = window.location.href.split('#pti')[1];
    if (!isNaN(anchor_post)) {
        return anchor_post-1;
    }

    // Get postid from anchor in URL
    anchor_post = window.location.href.split('#post')[1];
    if (!isNaN(anchor_post)) {
        var pti = jQuery('table#post'+anchor_post+' tr:first').attr('id').split(
        if (!isNaN(pti))
            return pti-1;
    }

    // Check if the user has the "Show an icon next to each post indicating if
    // it has been seen or not" option enabled for the forums
    var use_setseen = (jQuery('td.postdate > a[href*=action=setseen]').length > 0);

    jQuery('div#thread > table.post').each(function() {
        if (use_setseen) {
            // User has setseen icons enabled
            var posticon_img = jQuery('img[src*=posticon]', this);

            count++;
            if (posticon_img.attr('src') == 'http://fi.somethingawful.com/style/posticon-seen.gif') {
                index = count;
            }
        } else {
            // User has read post coloring enabled
            var post = jQuery('tr', this);

            count++;
            if (post.hasClass('seen1') || post.hasClass('seen2')) {
                index = count;
            }
        }
    });

    if (index == this.thread_post_size) // All posts read
        return index-1;

    return index;
};

HotKeyManager.prototype.nextPage = function() {
    this.pageCount = countPages();

    switch(findCurrentPage()) {
        case 'forumdisplay.php':
        case 'showthread.php':
            this.rootPageType = (findCurrentPage() == 'forumdisplay.php') ? 'forumid' : 'threadid';
            this.basePageID = findForumID();
            this.currentPage = Number(jQuery('span.curpage').html());
            if (this.currentPage <= 0)
                this.currentPage = 1;
            if (this.currentPage < this.pageCount)
                jumpToPage(this.rootPageType, this.basePageID, this.currentPage + 1);
            break;
    }
};

HotKeyManager.prototype.previousPage = function() {
    this.pageCount = countPages();

    switch(findCurrentPage()) {
        case 'forumdisplay.php':
        case 'showthread.php':
            this.rootPageType = (findCurrentPage() == 'forumdisplay.php') ? 'forumid' : 'threadid';
            this.basePageID = findForumID();
            this.currentPage = Number(jQuery('span.curpage').html());
            if (this.currentPage <= 0)
                this.currentPage = 1;
            if (this.currentPage > 1)
                jumpToPage(this.rootPageType, this.basePageID, this.currentPage - 1);
            break;
    }
};

HotKeyManager.prototype.nextPost = function() {
    if (!findCurrentPage() == 'showthread.php') {
        return;
    }

    if (this.current_post < (this.thread_post_size - 1)) {
        this.current_post++;
    }

    var post = jQuery('div#thread > table.post');
    var previous_post = post.eq(this.current_post - 1);
    var current_post = post.eq(this.current_post);
    previous_post.css('border', '1px solid #c1c1c1');
    current_post.css('border', '2px dashed #aaa');

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.previousPost = function() {
    if (!findCurrentPage() == 'showthread.php') {
        return;
    }

    if (this.current_post > 0) {
        this.current_post--;
    }

    var post = jQuery('div#thread > table.post');
    var previous_post = post.eq(this.current_post + 1);
    var current_post = post.eq(this.current_post);
    previous_post.css('border', '1px solid #c1c1c1');
    current_post.css('border', '2px dashed #aaa');

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.firstPost = function() {
    if (!findCurrentPage() == 'showthread.php') {
        return;
    }

    var post = jQuery('div#thread > table.post');
    var current_post = post.eq(this.current_post);

    if (this.current_post > 0) {
        var previous_post = post.eq(this.current_post);
        previous_post.css('border', '1px solid #c1c1c1');

        this.current_post=0;
        current_post = post.eq(this.current_post);
        current_post.css('border', '2px dashed #aaa');
    }

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.lastPost = function() {
    if (!findCurrentPage() == 'showthread.php') {
        return;
    }

    var post = jQuery('div#thread > table.post');
    var current_post = post.eq(this.current_post);

    if (this.current_post < this.thread_post_size-1) {
        var previous_post = post.eq(this.current_post);
        previous_post.css('border', '1px solid #c1c1c1');

        this.current_post=this.thread_post_size-1;
        current_post = post.eq(this.current_post);
        current_post.css('border', '2px dashed #aaa');
    }

    jQuery(window).scrollTop(current_post.offset().top);
};

HotKeyManager.prototype.anchorThread = function() {
    if (!findCurrentPage() == 'showthread.php') {
        return;
    } else if (this.current_post == -1) {
        return;
    }

    var post = jQuery('div#thread > table.post').eq(this.current_post);
    post.css('border', '2px dashed #aaa');

    jQuery(window).scrollTop(post.offset().top);
};

HotKeyManager.prototype.quoteCurrentPost = function() {
    if (this.current_post == -1) {
        return;
    }

    var current_post = jQuery('div#thread > table.post').eq(this.current_post);
    var username = jQuery('tr > td.userinfo > dl > dt.author', current_post).html();
    // Query for the quote
    var quote = jQuery('tr > td.postbody', current_post).clone();

    quickReply.appendQuote(username, quote);
    quickReply.show();
};

HotKeyManager.prototype.displayQuickReply = function() {
    if (findCurrentPage() == 'showthread.php') {
        quickReply.show();
    }
};

HotKeyManager.prototype.hideQuickReply = function() {
    if (findCurrentPage() == 'showthread.php') {
        quickReply.hide();
    }
};
<|endoftext|>


<commit_before>iD.geocoder = function() {

    var map;

    function geocoder(selection) {
        function keydown() {
            if (d3.event.keyCode !== 13) return;
            d3.event.preventDefault();
            d3.json('http://api.tiles.mapbox.com/v3/mapbox/geocode/' +
                encodeURIComponent(this.value) + '.json', function(err, resp) {
                hide();
                map.center([resp.results[0][0].lon, resp.results[0][0].lat]);
            });
        }

        function clickoutside(selection) {
            selection
                .on('click.geocoder-inside', function() {
                    return d3.event.stopPropagation();
                });
            d3.select('body').on('click.geocoder-outside', hide);
        }

        function show() { setVisible(true); }
        function hide() { setVisible(false); }
        function toggle() { setVisible(gcForm.classed('hide')); }

        function setVisible(show) {
            button.classed('active', show);
            gcForm.classed('hide', !show);
            var input_node = d3.select('.map-overlay input').node();
            if (show) input_node.focus();
            else input_node.blur();
        }

        var button = selection.append('button')
            .attr('class','narrow')
            .html('')
            .on('click', toggle);

        var gcForm = selection.append('form');

        gcForm.attr('class','content map-overlay hide')
            .append('input')
                .attr({ type: 'text', placeholder: 'find a place' })
                .on('keydown', keydown);

        selection.call(clickoutside);
    }

    geocoder.map = function(_) {
        if (!arguments.length) return map;
        map = _;
        return geocoder;
    };

    return geocoder;
};
<commit_msg>Use proper geocoding endpoint. Fixes #315<commit_after>iD.geocoder = function() {

    var map;

    function geocoder(selection) {
        function keydown() {
            if (d3.event.keyCode !== 13) return;
            d3.event.preventDefault();
            d3.json('http://api.tiles.mapbox.com/v3/openstreetmap.map-hn253zqn/geocode/' +
                encodeURIComponent(this.value) + '.json', function(err, resp) {
                if (err) return hide();
                hide();
                map.center([resp.results[0][0].lon, resp.results[0][0].lat]);
            });
        }

        function clickoutside(selection) {
            selection
                .on('click.geocoder-inside', function() {
                    return d3.event.stopPropagation();
                });
            d3.select('body').on('click.geocoder-outside', hide);
        }

        function show() { setVisible(true); }
        function hide() { setVisible(false); }
        function toggle() { setVisible(gcForm.classed('hide')); }

        function setVisible(show) {
            button.classed('active', show);
            gcForm.classed('hide', !show);
            var input_node = d3.select('.map-overlay input').node();
            if (show) input_node.focus();
            else input_node.blur();
        }

        var button = selection.append('button')
            .attr('class','narrow')
            .html('')
            .on('click', toggle);

        var gcForm = selection.append('form');

        gcForm.attr('class','content map-overlay hide')
            .append('input')
                .attr({ type: 'text', placeholder: 'find a place' })
                .on('keydown', keydown);

        selection.call(clickoutside);
    }

    geocoder.map = function(_) {
        if (!arguments.length) return map;
        map = _;
        return geocoder;
    };

    return geocoder;
};
<|endoftext|>


<commit_before>iD.ui.ScenarioViewer = function(context) {
    var dispatch = d3.dispatch('choose'),
        state = 'select',
        base,
        id,
        preset,
        reference,
        selectedScenario;

    /**
     * Add Simulate Button
     */
    function scenarioViewer(selection) {
        var entity = context.entity(id),
            tags = _.clone(entity.tags);

        console.log("scenarioViewerscenarioViewer");
        console.log(context);

        selection.selectAll('.simulateContainer').remove();

        var body = selection.selectAll('.inspector-body')
            .data([0]);

        if (entity.tags.type === 'scenario') {
            var outer = prepend('div', body)
                .attr('class', 'inspector-inner simulateContainer')

            outer.insert('button', ':first-child')
                .attr('class', 'simulateButton')
                .html('Simulate')
                .on('click', function() {
                    var pane = d3.select('div.entity-editor-pane');
                    var parameters = prompt(
                        'Specify arguments with which to run the "' +
                        entity.tags.name + '" scenario.',
                        'key-1=value-1&key-2=value-2');
                    if (parameters != null) {
                        console.log(entity);
                        d3.xhr('/simulate?scenario=' + entity.id + '&' +
                                parameters)
                            .on('load', function(xhr) {
                                window.open('/simulation/' + xhr.response)
                            })
                            .send('PUT');
                    };
                });
        }
    }

    /**
     * Init Selectbox
     */
    scenarioViewer.initScenarioSelection = function(parent) {
        var outer = prepend('div', parent)
            .attr('class', 'foobar inspector-inner fillL cf')
            .append('span');
        outer.html('');
        var input = outer.append('input')
            .attr('class', 'value combobox-input')
            .attr('type', 'text')
            .attr('placeholder', 'Getting scenarios');
        outer.append('div').attr('class', 'combobox-caret');

        d3.xhr('/scenario')
            .on('load', function(xhr) {
                if (xhr.response === '') {
                    input.attr('placeholder', 'Select a scenario');
                } else {
                    var e = JSON.parse(xhr.response);
                    prepend('span', outer).text('Selected scenario: ' + e.value);
                    selectedScenario = 'r' + e.title;

                    input.attr('placeholder', 'Change selected scenario');
                    outer.append('a')
                        .attr('class', 'value combobox-input')
                        .attr('href', '#')
                        .text(e.value)
                        .on('click', function() {
                            context.enter(iD.modes.Select(context, ['r' + e.title]))
                        });
                    drawColors();
                };
            })
            .send('GET');

        d3.xhr('/scenarios')
            .on('load', function(xhr) {
                var data = JSON.parse(xhr.response);
                input.call(d3.combobox()
                    .data(data)
                    .minItems(1)
                    .on('accept', function(e) {
                        d3.xhr('/scenario/' + e.title)
                            .on('load', function(_) {
                                if (e.title === null) {
                                    window.location.reload(true);
                                    return;
                                };
                                try {
                                    context.enter(iD.modes.Select(context, ['r' + e.title]));
                                    window.location.reload(true);
                                } catch (err) {
                                    window.location.reload(true);
                                }
                            })
                            .send('PUT')
                    }));
                input.attr('defaultValue', data[0]);
            })
            .send('GET');

        return input;
    }

    function drawColors() {
        try {
            var scenarioEntity = context.entity(selectedScenario);
        } catch (e) {
            console.log(e);
            window.setTimeout(drawColors, 500);
            return;
        }

        var entitys = {
            node: [],
            ways: [],
            relation: []
        };

        if (entitysLoaded()) {
            for (var i = 0; i < scenarioEntity.members.length; i++) {
                switch (scenarioEntity.members[i].type) {
                    case "node":
                        entitys.node.push(context.entity(scenarioEntity.members[i].id));
                        break;
                    case "way":
                        entitys.ways.push(context.entity(scenarioEntity.members[i].id));
                        break;
                    case "relation":
                        entitys.relation.push(context.entity(scenarioEntity.members[i].id));
                        break;
                }
            }
        } else {
            window.setTimeout(drawColors, 400);
            return;
        }

        function entitysLoaded() {
            for (var i = 0; i < scenarioEntity.members.length; i++) {
                if (!context.hasEntity(scenarioEntity.members[i].id)) {
                    return false;
                } else {
                    console.log("has " + scenarioEntity.members[i].id);
                }
            }
            return true;
        }

        var maxWay = getMaxTagValue(entitys.ways);
        var maxRelation = getMaxTagValue(entitys.relation);

        console.log(maxWay);
        console.log(maxRelation);

        for (var i = 0; i < entitys.relation.length; i++) {
            var color = getColor(entitys.relation[i].tags.value / maxRelation);
            console.log(color);
            d3.select()
                .attr("style", "fill: " + color);

            console.log("set: " + color + " to " + entitys.relation[i].id);

            createClass(".area-fill .w" + entitys.relation[i].id.substr(1, entitys.relation[i].id.length), "fill: " + color);
        }
    }

    scenarioViewer.state = function(_) {
        if (!arguments.length) return state;
        state = _;
        return scenarioViewer;
    };

    scenarioViewer.entityID = function(_) {
        if (!arguments.length) return id;
        id = _;
        base = context.graph();
        scenarioViewer.preset(context.presets().match(context.entity(id), base));
        return scenarioViewer;
    };

    scenarioViewer.preset = function(_) {
        if (!arguments.length) return preset;
        if (_ !== preset) {
            preset = _;
            reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context)
                .showing(false);
        }
        return scenarioViewer;
    };

    /**
     * [getMaxTagValue description]
     * Returns Max Value of array entries of tags.value
     * @param  {[entity]} array [description]
     * @return {[number]}       [description]
     */
    function getMaxTagValue(array) {
        var maxValue = 0;
        for (var i = 0; i < array.length; i++) {
            try {
                if (maxValue < array[i].tags.value)
                    maxValue = array[i].tags.value;
            } catch (e) {

            }
        }
        return maxValue;
    }

    /**
     * [getColor description]
     * Returns Color based on percentage value (0.0 - 1)
     *
     */
    function getColor(value) {
        var hue = ((1 - value) * 120).toString(10);
        return ["hsl(", hue, ",100%,50%)"].join("");
    }

    /**
     * [createClass description]
     * Sets addional CSS Class in Head Style Tag
     * @param  {[string]} name  [description]
     * selector
     * Selector will be higher prio with map and layer class added before name selector param
     * @param  {[string]} rules [description]
     * css rule
     */
    function createClass(name, rules) {
        var style = document.createElement('style');
        style.type = 'text/css';
        document.getElementsByTagName('head')[0].appendChild(style);
        if (!(style.sheet || {}).insertRule)
            (style.styleSheet || style.sheet).addRule('#map .layer ' + name, rules);
        else
            style.sheet.insertRule('#map .layer ' + name + "{" + rules + "}", 0);
    }

    function prepend(element, parentEle) {
        return parentEle.insert(element, ':first-child');
    };

    return d3.rebind(scenarioViewer, dispatch, 'on');
};<commit_msg>Ways stroke based on value<commit_after>iD.ui.ScenarioViewer = function(context) {
    var dispatch = d3.dispatch('choose'),
        state = 'select',
        base,
        id,
        preset,
        reference,
        selectedScenario;

    /**
     * Add Simulate Button
     */
    function scenarioViewer(selection) {
        var entity = context.entity(id),
            tags = _.clone(entity.tags);

        selection.selectAll('.simulateContainer').remove();

        var body = selection.selectAll('.inspector-body')
            .data([0]);

        if (entity.tags.type === 'scenario') {
            var outer = prepend('div', body)
                .attr('class', 'inspector-inner simulateContainer')

            outer.insert('button', ':first-child')
                .attr('class', 'simulateButton')
                .html('Simulate')
                .on('click', function() {
                    var pane = d3.select('div.entity-editor-pane');
                    var parameters = prompt(
                        'Specify arguments with which to run the "' +
                        entity.tags.name + '" scenario.',
                        'key-1=value-1&key-2=value-2');
                    if (parameters != null) {
                        console.log(entity);
                        d3.xhr('/simulate?scenario=' + entity.id + '&' +
                                parameters)
                            .on('load', function(xhr) {
                                window.open('/simulation/' + xhr.response)
                            })
                            .send('PUT');
                    };
                });
        }
    }

    /**
     * Init Selectbox
     */
    scenarioViewer.initScenarioSelection = function(parent) {
        var outer = prepend('div', parent)
            .attr('class', 'foobar inspector-inner fillL cf')
            .append('span');
        outer.html('');
        var input = outer.append('input')
            .attr('class', 'value combobox-input')
            .attr('type', 'text')
            .attr('placeholder', 'Getting scenarios');
        outer.append('div').attr('class', 'combobox-caret');

        d3.xhr('/scenario')
            .on('load', function(xhr) {
                if (xhr.response === '') {
                    input.attr('placeholder', 'Select a scenario');
                } else {
                    var e = JSON.parse(xhr.response);
                    prepend('span', outer).text('Selected scenario: ' + e.value);
                    selectedScenario = 'r' + e.title;

                    input.attr('placeholder', 'Change selected scenario');
                    outer.append('a')
                        .attr('class', 'value combobox-input')
                        .attr('href', '#')
                        .text(e.value)
                        .on('click', function() {
                            context.enter(iD.modes.Select(context, ['r' + e.title]))
                        });
                    drawColors();
                };
            })
            .send('GET');

        d3.xhr('/scenarios')
            .on('load', function(xhr) {
                var data = JSON.parse(xhr.response);
                input.call(d3.combobox()
                    .data(data)
                    .minItems(1)
                    .on('accept', function(e) {
                        d3.xhr('/scenario/' + e.title)
                            .on('load', function(_) {
                                if (e.title === null) {
                                    window.location.reload(true);
                                    return;
                                };
                                try {
                                    context.enter(iD.modes.Select(context, ['r' + e.title]));
                                    window.location.reload(true);
                                } catch (err) {
                                    window.location.reload(true);
                                }
                            })
                            .send('PUT')
                    }));
                input.attr('defaultValue', data[0]);
            })
            .send('GET');

        return input;
    }

    function drawColors() {
        try {
            var scenarioEntity = context.entity(selectedScenario);
        } catch (e) {
            console.log(e);
            window.setTimeout(drawColors, 500);
            return;
        }

        var entitys = {
            node: [],
            ways: [],
            relation: []
        };

        if (entitysLoaded()) {
            for (var i = 0; i < scenarioEntity.members.length; i++) {
                switch (scenarioEntity.members[i].type) {
                    case "node":
                        entitys.node.push(context.entity(scenarioEntity.members[i].id));
                        break;
                    case "way":
                        var way = context.entity(scenarioEntity.members[i].id);
                        if(way.tags.type != 'hub_area')
                            entitys.ways.push(way);
                        break;
                    case "relation":
                        entitys.relation.push(context.entity(scenarioEntity.members[i].id));
                        break;
                }
            }
        } else {
            window.setTimeout(drawColors, 400);
            return;
        }

        console.log(scenarioEntity);
        console.log(entitys.ways);
        console.log(entitys.relation);
        var maxNode = getMaxTagValue(entitys.node);
        var maxWay = getMaxTagValue(entitys.ways);
        var maxRelation = getMaxTagValue(entitys.relation);
        console.log(maxWay);
        console.log(maxRelation);

        // Draw Node
        for (var i = 0; i < entitys.node.length; i++) {
            var color = getColor(entitys.node[i].tags.value / maxNode);

            createClass(".layer-hit .stroke ." + entitys.node[i].id, "fill: " + color);
        }

        // Draw Relation
        for (var i = 0; i < entitys.relation.length; i++) {
            var color = getColor(entitys.relation[i].tags.value / maxRelation);

            createClass(".area-fill .w" + entitys.relation[i].id.substr(1, entitys.relation[i].id.length), "fill: " + color);
        }

        // Draw Ways
        for (var i = 0; i < entitys.ways.length; i++) {
            var color = getColor(entitys.ways[i].tags.value / maxWay);
            console.log(color);

            createClass(".layer-lines .line-stroke ." + entitys.ways[i].id, "stroke: " + color);
        }

        function entitysLoaded() {
            for (var i = 0; i < scenarioEntity.members.length; i++) {
                if (!context.hasEntity(scenarioEntity.members[i].id)) {
                    return false;
                } else {
                    console.log("has " + scenarioEntity.members[i].id);
                }
            }
            return true;
        }
    }

    scenarioViewer.state = function(_) {
        if (!arguments.length) return state;
        state = _;
        return scenarioViewer;
    };

    scenarioViewer.entityID = function(_) {
        if (!arguments.length) return id;
        id = _;
        base = context.graph();
        scenarioViewer.preset(context.presets().match(context.entity(id), base));
        return scenarioViewer;
    };

    scenarioViewer.preset = function(_) {
        if (!arguments.length) return preset;
        if (_ !== preset) {
            preset = _;
            reference = iD.ui.TagReference(preset.reference(context.geometry(id)), context)
                .showing(false);
        }
        return scenarioViewer;
    };

    /**
     * [getMaxTagValue description]
     * Returns Max Value of array entries of tags.value
     * @param  {[entity]} array [description]
     * @return {[number]}       [description]
     */
    function getMaxTagValue(array) {
        var maxValue = 0;
        for (var i = 0; i < array.length; i++) {
            try {
                if (maxValue < array[i].tags.value)
                    maxValue = array[i].tags.value;
            } catch (e) {

            }
        }
        return maxValue;
    }

    /**
     * [getColor description]
     * Returns Color based on percentage value (0.0 - 1)
     *
     */
    function getColor(value) {
        var hue = ((1 - value) * 120).toString(10);
        return ["hsl(", hue, ",100%,50%)"].join("");
    }

    /**
     * [createClass description]
     * Sets addional CSS Class in Head Style Tag
     * @param  {[string]} name  [description]
     * selector
     * Selector will be higher prio with map and layer class added before name selector param
     * @param  {[string]} rules [description]
     * css rule
     */
    function createClass(name, rules) {
        var style = document.createElement('style');
        style.type = 'text/css';
        document.getElementsByTagName('head')[0].appendChild(style);
        if (!(style.sheet || {}).insertRule)
            (style.styleSheet || style.sheet).addRule('#map .layer ' + name, rules);
        else
            style.sheet.insertRule('#map .layer ' + name + "{" + rules + "}", 0);
    }

    function prepend(element, parentEle) {
        return parentEle.insert(element, ':first-child');
    };

    return d3.rebind(scenarioViewer, dispatch, 'on');
};<|endoftext|>


<commit_before>$(window).scroll(function() {
    // get current scroll top in px, add window height and
    // check if this is greater than the document height minus 300
    if ($(window).scrollTop() + $(window).height() >= $(document).height() - 300) {
        loadMoreContent();
	masonry_gallery();
    }
});

function loadMoreContent() {
    // there should be a button, which leads to next posts
    var next = $("a.next").first();
    var paginationnav = $("div.paginationnav").first();
    next.each(function(key, value) {
        var url = $(value).attr('href');
        // remove paginationnav, so we can't load multiple times
        next.remove();
        // adding a temp. dom object to load the next page
        var list = $("

");
        // call load method and get the div.post-list node
        list.load(url + " div.post-list", function(response, status, xhr) {
            // check if we doesn't get any error
            if ( status != "error" ) {
                // copy all childrens of our temp container to the real container
                // note: jQuery load will copy the div.post-list node as well,
                // so use the childrens and move them
                var container = $("div.post-list");
                list.children("div.post-list").children().each(
                    function(key, value){
                        container.append(value);
                    }
                );
            }
        });
	paginationnav.load(url + " div.paginationnav");
    });

}

/* ********* WINDOW LOAD ********** */
$(window).load(function() {
    masonry_gallery()
    beforeafter()
    var paginationnav = $("div.paginationnav").first();
    paginationnav.hide();
});

function masonry_gallery($)
{
    var $masonry_gallery = jQuery("div.gallery.masonry-gallery.post-list").first();
    if ( $masonry_gallery.length > 0 ) {
	    $masonry_gallery.each( function(index, element) {
		    var $masonry_items = $(element).find('.gallery-item');

		    // set masonry layout
		    $(element).isotope({
			    masonry: { columnWidth: $(element).find('.gallery-item')[0] },
			    itemSelector: '.gallery-item'
		    });
		    $(element).isotope('layout');

		    // filtering
		    jQuery('#gallery-filter li a').on('click', function(){
			    jQuery('#gallery-filter li a').removeClass('active');
			    jQuery(this).addClass('active');
			    var selector = jQuery(this).attr('data-filter');
			    $masonry_gallery.isotope({ filter: selector });
			    return false;
		    });

		    // changing layout
		    jQuery('#grid-changer li a').on('click', function(){
			    jQuery('#grid-changer li a').removeClass('active');
			    jQuery(this).toggleClass('active');

			    $masonry_items.removeClass('col-3');
			    $masonry_items.removeClass('col-4');
			    $masonry_items.removeClass('col-5');
			    $masonry_items.toggleClass(jQuery(this).closest('li').attr('class'));
			    $masonry_gallery.isotope('layout');
		    });

	    });
    }
}

function beforeafter($){
    var $before_after = jQuery('.before-after.gallery');
    if ( $before_after.length > 0 ) {
	    $before_after.imageReveal({
		    barWidth: 4,
		    touchBarWidth: 50,
		    startPosition: 0.5,
		    width: jQuery('.before-after img').width(),
		    height:  jQuery('.before-after img').height()
	    });
    }
}

<commit_msg>Script modif<commit_after>$(window).scroll(function() {
    // get current scroll top in px, add window height and
    // check if this is greater than the document height minus 300
    if ($(window).scrollTop() + $(window).height() >= $(document).height() - 300) {
        loadMoreContent(jQuery);
	masonry_gallery(jQuery);
    }
});

function loadMoreContent() {
    // there should be a button, which leads to next posts
    var next = $("a.next").first();
    var paginationnav = $("div.paginationnav").first();
    next.each(function(key, value) {
        var url = $(value).attr('href');
        // remove paginationnav, so we can't load multiple times
        next.remove();
        // adding a temp. dom object to load the next page
        var list = $("

");
        // call load method and get the div.post-list node
        list.load(url + " div.post-list", function(response, status, xhr) {
            // check if we doesn't get any error
            if ( status != "error" ) {
                // copy all childrens of our temp container to the real container
                // note: jQuery load will copy the div.post-list node as well,
                // so use the childrens and move them
                var container = $("div.post-list");
                list.children("div.post-list").children().each(
                    function(key, value){
                        container.append(value);
                    }
                );
            }
        });
	paginationnav.load(url + " div.paginationnav");
    });

}

/* ********* WINDOW LOAD ********** */
$(window).load(function() {
    masonry_gallery(jQuery)
    beforeafter(jQuery)
    var paginationnav = $("div.paginationnav").first();
    paginationnav.hide();
});

function masonry_gallery($)
{
    var $masonry_gallery = jQuery("div.gallery.masonry-gallery.post-list").first();
    if ( $masonry_gallery.length > 0 ) {
	    $masonry_gallery.each( function(index, element) {
		    var $masonry_items = $(element).find('.gallery-item');

		    // set masonry layout
		    $(element).isotope({
			    masonry: { columnWidth: $(element).find('.gallery-item')[0] },
			    itemSelector: '.gallery-item'
		    });
		    $(element).isotope('layout');

		    // filtering
		    jQuery('#gallery-filter li a').on('click', function(){
			    jQuery('#gallery-filter li a').removeClass('active');
			    jQuery(this).addClass('active');
			    var selector = jQuery(this).attr('data-filter');
			    $masonry_gallery.isotope({ filter: selector });
			    return false;
		    });

		    // changing layout
		    jQuery('#grid-changer li a').on('click', function(){
			    jQuery('#grid-changer li a').removeClass('active');
			    jQuery(this).toggleClass('active');

			    $masonry_items.removeClass('col-3');
			    $masonry_items.removeClass('col-4');
			    $masonry_items.removeClass('col-5');
			    $masonry_items.toggleClass(jQuery(this).closest('li').attr('class'));
			    $masonry_gallery.isotope('layout');
		    });

	    });
    }
}

function beforeafter($){
    var $before_after = jQuery('.before-after.gallery');
    if ( $before_after.length > 0 ) {
	    $before_after.imageReveal({
		    barWidth: 4,
		    touchBarWidth: 50,
		    startPosition: 0.5,
		    width: jQuery('.before-after img').width(),
		    height:  jQuery('.before-after img').height()
	    });
    }
}

<|endoftext|>


<commit_before>import React from 'react';
import {connectToStores} from 'fluxible-addons-react';
import {NavLink, navigateAction} from 'fluxible-router';
import Error from '../Error/Error';
import AddDeckStore from '../../stores/AddDeckStore';
import addDeckShowWrongFields from '../../actions/addDeck/addDeckShowWrongFields';
import addDeckSaveDeck from '../../actions/addDeck/addDeckSaveDeck';
import addDeckDestruct from '../../actions/addDeck/addDeckDestruct';
let ReactDOM = require('react-dom');
let classNames = require('classnames');

class AddDeck extends React.Component {
    constructor(props) {
        super(props);
        this.redirectID = 0;
        this.percentage = 0;
        this.filename = '';
    }
    componentDidMount(){
    }
    componentDidUpdate(){
    }

    handleUpload(x) {
        console.log('handleUpload: ', x);
    }
    handleAddDeck(x) {
        console.log('handleAddDeck');

        //validate input
        const title = this.refs.input_title.value;
        const language = this.refs.select_languages.value;
        const description = this.refs.textarea_description.value;
        const theme = this.refs.select_themes.value;
        const licence = this.refs.select_licences.value;
        const tags = this.refs.input_tags.value.split(', ');
        const acceptedConditions = this.refs.checkbox_conditions.checked;
        console.log(title, language, description, theme, licence, tags, acceptedConditions);

        //check empty or not selected
        let everythingIsFine = true;
        let wrongFields = {};
        if (title === null || title === undefined || title === '') {
            wrongFields.title = true;
            everythingIsFine = false;
        }
        else {
            wrongFields.title = false;
        }
        if (language === null || language === undefined || language.length !== 5) {
            wrongFields.language = true;
            everythingIsFine = false;
        }
        else {
            wrongFields.language = false;
        }
        if (licence === null || licence === undefined || licence.length < 2) {
            wrongFields.licence = true;
            everythingIsFine = false;
        }
        else {
            wrongFields.licence = false;
        }
        if (acceptedConditions === false) {
            wrongFields.conditions = true;
            everythingIsFine = false;
        }
        else {
            wrongFields.conditions = false;
        }

        //call action to update view
        this.context.executeAction(addDeckShowWrongFields, wrongFields);

        //if everything is fine then create the deck
        if (everythingIsFine) {
            this.correctMetadata(title, language, description, theme, licence, tags, acceptedConditions);
        }
    }
    correctMetadata(title, language, description, theme, licence, tags, acceptedConditions) {
        this.context.executeAction(addDeckSaveDeck, {
            title: title,
            language: language,
            description: description,
            theme: theme,
            licence: licence,
            tags: tags
        });
    }
    handleCancel(x) {
        console.log('handleCancel: ', x);
        //TODO: check if there already inputs which should be stored?

        this.context.executeAction(navigateAction, {
            url: '/'
        });
    }
    handleRedirect(){
        this.context.executeAction(navigateAction, {
            url: '/deck/' + this.redirectID
        });
        return false;
    }
    /*
    use it like:
      this.percentage++;
      this.updateProgressBar();
    */
    updateProgressBar() {
        $('#progressbar_addDeck_upload').progress({
            percent: this.percentage,
            text: {
                active  : 'Uploading: {percent}%',
                success : 'Slides uploaded!'
            }
        });
    }

    render() {
        if (this.props.AddDeckStore.redirectID !== 0) {
            setTimeout( () => {
                this.redirectID = this.props.AddDeckStore.redirectID;
                this.handleRedirect();
                this.context.executeAction(addDeckDestruct, {});
            }, 1000);
        }

        let fieldClass_title = classNames({
            'required': true,
            'field': true,
            'error': this.props.AddDeckStore.wrongFields.title
        });
        let fieldClass_language = classNames({
            'required': true,
            'field': true,
            'error': this.props.AddDeckStore.wrongFields.language
        });
        let fieldClass_licence = classNames({
            'required': true,
            'field': true,
            'error': this.props.AddDeckStore.wrongFields.licence
        });
        let fieldClass_conditions = classNames({
            'required': true,
            'inline': true,
            'field': true,
            'error': this.props.AddDeckStore.wrongFields.conditions
        });
        let languageOptions = <select className="ui search dropdown" aria-labelledby="language" aria-required="true" ref="select_languages">
            <option>
                Select Language
            </option>
            <option value="en_EN" >
                English
            </option>
        </select>;
        let themeOptions = <select className="ui search dropdown" aria-labelledby="theme" ref="select_themes">
          <option value="" >Select Theme</option>
          <option value="DefaultTheme" >Default</option>
        </select>;
        let licenceOptions = <select className="ui search dropdown" aria-labelledby="license" ref="select_licences">
          <option value="" >Select License</option>
          <option value="CC0" >CC0</option>
          <option value="CC BY" >CC BY</option>
          <option value="CC BY-SA" >CC BY-SA</option>
        </select>;

        let errorView ='';
        if (this.props.AddDeckStore.error !== null)
            errorView = <Error error={this.props.AddDeckStore.error} />;
        else
            errorView ='';

        let hint_title = this.props.AddDeckStore.wrongFields.title ? 'The title is a must have.' : undefined;
        let hint_language = this.props.AddDeckStore.wrongFields.language ? 'The language is a must have.' : undefined;
        let hint_licence = this.props.AddDeckStore.wrongFields.licence ? 'The licence is a must have.' : undefined;

        return (

Add deck

                                  Upload file

                              {this.filename}

                  <form className="ui form upload">

                                  Title

                              <input type="text" name="deck-title" placeholder="Title" aria-required="true" ref="input_title" />

                                  Language

                              {languageOptions}

                          Description
                          <textarea rows="4" aria-labelledby="deck-description" ref="textarea_description" ></textarea>

                              Choose deck theme
                                  {themeOptions}

                              License
                                  {licenceOptions}

                          <input type="text" name="tags" placeholder="Add Tags" ref="input_tags" />

                              <input type="checkbox" tabIndex="0" aria-labelledby="terms" aria-required="true" ref="checkbox_conditions" />

                                  I agree to the terms and conditions

                  </form>

                          Add deck

                          Cancel

          {errorView}

        );
    }
}

AddDeck.contextTypes = {
    executeAction: React.PropTypes.func.isRequired
};
AddDeck = connectToStores(AddDeck, [AddDeckStore], (context, props) => {
    return {
        AddDeckStore: context.getStore(AddDeckStore).getState()
    };
});
export default AddDeck;
<commit_msg>[SWIK-371] Added link for terms and conditions<commit_after>import React from 'react';
import {connectToStores} from 'fluxible-addons-react';
import {NavLink, navigateAction} from 'fluxible-router';
import Error from '../Error/Error';
import AddDeckStore from '../../stores/AddDeckStore';
import addDeckShowWrongFields from '../../actions/addDeck/addDeckShowWrongFields';
import addDeckSaveDeck from '../../actions/addDeck/addDeckSaveDeck';
import addDeckDestruct from '../../actions/addDeck/addDeckDestruct';
let ReactDOM = require('react-dom');
let classNames = require('classnames');

//TODO: update link to terms of use;

class AddDeck extends React.Component {
    constructor(props) {
        super(props);
        this.redirectID = 0;
        this.percentage = 0;
        this.filename = '';
    }
    componentDidMount(){
    }
    componentDidUpdate(){
    }

    handleUpload(x) {
        console.log('handleUpload: ', x);
    }
    handleAddDeck(x) {
        console.log('handleAddDeck');

        //validate input
        const title = this.refs.input_title.value;
        const language = this.refs.select_languages.value;
        const description = this.refs.textarea_description.value;
        const theme = this.refs.select_themes.value;
        const licence = this.refs.select_licences.value;
        const tags = this.refs.input_tags.value.split(', ');
        const acceptedConditions = this.refs.checkbox_conditions.checked;
        console.log(title, language, description, theme, licence, tags, acceptedConditions);

        //check empty or not selected
        let everythingIsFine = true;
        let wrongFields = {};
        if (title === null || title === undefined || title === '') {
            wrongFields.title = true;
            everythingIsFine = false;
        }
        else {
            wrongFields.title = false;
        }
        if (language === null || language === undefined || language.length !== 5) {
            wrongFields.language = true;
            everythingIsFine = false;
        }
        else {
            wrongFields.language = false;
        }
        if (licence === null || licence === undefined || licence.length < 2) {
            wrongFields.licence = true;
            everythingIsFine = false;
        }
        else {
            wrongFields.licence = false;
        }
        if (acceptedConditions === false) {
            wrongFields.conditions = true;
            everythingIsFine = false;
        }
        else {
            wrongFields.conditions = false;
        }

        //call action to update view
        this.context.executeAction(addDeckShowWrongFields, wrongFields);

        //if everything is fine then create the deck
        if (everythingIsFine) {
            this.correctMetadata(title, language, description, theme, licence, tags, acceptedConditions);
        }
    }
    correctMetadata(title, language, description, theme, licence, tags, acceptedConditions) {
        this.context.executeAction(addDeckSaveDeck, {
            title: title,
            language: language,
            description: description,
            theme: theme,
            licence: licence,
            tags: tags
        });
    }
    handleCancel(x) {
        console.log('handleCancel: ', x);
        //TODO: check if there already inputs which should be stored?

        this.context.executeAction(navigateAction, {
            url: '/'
        });
    }
    handleRedirect(){
        this.context.executeAction(navigateAction, {
            url: '/deck/' + this.redirectID
        });
        return false;
    }
    /*
    use it like:
      this.percentage++;
      this.updateProgressBar();
    */
    updateProgressBar() {
        $('#progressbar_addDeck_upload').progress({
            percent: this.percentage,
            text: {
                active  : 'Uploading: {percent}%',
                success : 'Slides uploaded!'
            }
        });
    }

    render() {
        if (this.props.AddDeckStore.redirectID !== 0) {
            setTimeout( () => {
                this.redirectID = this.props.AddDeckStore.redirectID;
                this.handleRedirect();
                this.context.executeAction(addDeckDestruct, {});
            }, 1000);
        }

        let fieldClass_title = classNames({
            'required': true,
            'field': true,
            'error': this.props.AddDeckStore.wrongFields.title
        });
        let fieldClass_language = classNames({
            'required': true,
            'field': true,
            'error': this.props.AddDeckStore.wrongFields.language
        });
        let fieldClass_licence = classNames({
            'required': true,
            'field': true,
            'error': this.props.AddDeckStore.wrongFields.licence
        });
        let fieldClass_conditions = classNames({
            'required': true,
            'inline': true,
            'field': true,
            'error': this.props.AddDeckStore.wrongFields.conditions
        });
        let languageOptions = <select className="ui search dropdown" aria-labelledby="language" aria-required="true" ref="select_languages">
            <option>
                Select Language
            </option>
            <option value="en_EN" >
                English
            </option>
        </select>;
        let themeOptions = <select className="ui search dropdown" aria-labelledby="theme" ref="select_themes">
          <option value="" >Select Theme</option>
          <option value="DefaultTheme" >Default</option>
        </select>;
        let licenceOptions = <select className="ui search dropdown" aria-labelledby="license" ref="select_licences">
          <option value="" >Select License</option>
          <option value="CC0" >CC0</option>
          <option value="CC BY" >CC BY</option>
          <option value="CC BY-SA" >CC BY-SA</option>
        </select>;

        let errorView ='';
        if (this.props.AddDeckStore.error !== null)
            errorView = <Error error={this.props.AddDeckStore.error} />;
        else
            errorView ='';

        let hint_title = this.props.AddDeckStore.wrongFields.title ? 'The title is a must have.' : undefined;
        let hint_language = this.props.AddDeckStore.wrongFields.language ? 'The language is a must have.' : undefined;
        let hint_licence = this.props.AddDeckStore.wrongFields.licence ? 'The licence is a must have.' : undefined;

        return (

Add deck

                                  Upload file

                              {this.filename}

                  <form className="ui form upload">

                                  Title

                              <input type="text" name="deck-title" placeholder="Title" aria-required="true" ref="input_title" />

                                  Language

                              {languageOptions}

                          Description
                          <textarea rows="4" aria-labelledby="deck-description" ref="textarea_description" ></textarea>

                              Choose deck theme
                                  {themeOptions}

                              License
                                  {licenceOptions}

                          <input type="text" name="tags" placeholder="Add Tags" ref="input_tags" />

                              <input type="checkbox" tabIndex="0" aria-labelledby="terms" aria-required="true" ref="checkbox_conditions" />

                                  I agree to the terms and conditions

                  </form>

                          Add deck

                          Cancel

          {errorView}

        );
    }
}

AddDeck.contextTypes = {
    executeAction: React.PropTypes.func.isRequired
};
AddDeck = connectToStores(AddDeck, [AddDeckStore], (context, props) => {
    return {
        AddDeckStore: context.getStore(AddDeckStore).getState()
    };
});
export default AddDeck;
<|endoftext|>


<commit_before>import { useState, useEffect, useRef } from "react";

export const TableOfContents = ({ staticPosition, noh3s }) => {
  const { nestedHeadings } = useHeadingsData(noh3s);
  const [activeId, setActiveId] = useState();
  useIntersectionObserver(setActiveId);
  return (

      <Headings headings={nestedHeadings} activeId={activeId} />

  );
};

const getNestedHeadings = (headingElements) => {
  const nestedHeadings = [];

  headingElements.forEach((heading, index) => {
    const { innerText: title, id } = heading;

    if (heading.nodeName === "H2") {
      nestedHeadings.push({ id, title, items: [] });
    } else if (heading.nodeName === "H3" && nestedHeadings.length > 0) {
      nestedHeadings[nestedHeadings.length - 1].items.push({
        id,
        title,
      });
    }
  });

  return nestedHeadings;
};

const useHeadingsData = (noh3s) => {
  const [nestedHeadings, setNestedHeadings] = useState([]);

  const query = noh3s ? "h2" : "h2, h3";
  useEffect(() => {
    const headingElements = Array.from(document.querySelectorAll(query));

    const newNestedHeadings = getNestedHeadings(headingElements);
    setNestedHeadings(newNestedHeadings);
  }, []);

  return { nestedHeadings };
};

const Headings = ({ headings, activeId }) => (

    {headings.map((heading, index) => (

        <a
          className={index === 0 ? "font-bold" : "font-medium text-sm"}
          href={`#${heading.id}`}
        >
          {heading.title}

        {heading.items.length > 0 && (

            {heading.items.map((child) => (

                <a href={`#${child.id}`}>{child.title}

            ))}

        )}

    ))}

);

const useIntersectionObserver = (setActiveId) => {
  const headingElementsRef = useRef({});
  useEffect(() => {
    const callback = (headings) => {
      headingElementsRef.current = headings.reduce((map, headingElement) => {
        map[headingElement.target.id] = headingElement;
        return map;
      }, headingElementsRef.current);

      const visibleHeadings = [];
      Object.keys(headingElementsRef.current).forEach((key) => {
        const headingElement = headingElementsRef.current[key];
        if (headingElement.isIntersecting) visibleHeadings.push(headingElement);
      });

      const getIndexFromId = (id) =>
        headingElements.findIndex((heading) => heading.id === id);

      if (visibleHeadings.length === 1) {
        setActiveId(visibleHeadings[0].target.id);
      } else if (visibleHeadings.length > 1) {
        const sortedVisibleHeadings = visibleHeadings.sort(
          (a, b) => getIndexFromId(a.target.id) > getIndexFromId(b.target.id)
        );
        setActiveId(sortedVisibleHeadings[0].target.id);
      }
    };

    const observer = new IntersectionObserver(callback, {
      rootMargin: "0px 0px -50% 0px",
    });

    const headingElements = Array.from(document.querySelectorAll("h2, h3"));

    headingElements.forEach((element) => observer.observe(element));

    return () => observer.disconnect();
  }, [setActiveId]);
};
<commit_msg>TableOfContents: hide sidebar if only one header<commit_after>import { useState, useEffect, useRef } from "react";
import classNames from "classnames";

export const TableOfContents = ({ staticPosition, noh3s }) => {
  const { nestedHeadings } = useHeadingsData(noh3s);
  const [activeId, setActiveId] = useState();
  useIntersectionObserver(setActiveId);
  return (

      <Headings headings={nestedHeadings} activeId={activeId} />

  );
};

const getNestedHeadings = (headingElements) => {
  const nestedHeadings = [];

  headingElements.forEach((heading, index) => {
    const { innerText: title, id } = heading;

    if (heading.nodeName === "H2") {
      nestedHeadings.push({ id, title, items: [] });
    } else if (heading.nodeName === "H3" && nestedHeadings.length > 0) {
      nestedHeadings[nestedHeadings.length - 1].items.push({
        id,
        title,
      });
    }
  });

  return nestedHeadings;
};

const useHeadingsData = (noh3s) => {
  const [nestedHeadings, setNestedHeadings] = useState([]);

  const query = noh3s ? "h2" : "h2, h3";
  useEffect(() => {
    const headingElements = Array.from(document.querySelectorAll(query));

    const newNestedHeadings = getNestedHeadings(headingElements);
    setNestedHeadings(newNestedHeadings);
  }, []);

  return { nestedHeadings };
};

const Headings = ({ headings, activeId }) => (

    {headings.map((heading, index) => (

        <a
          className={index === 0 ? "font-bold" : "font-medium text-sm"}
          href={`#${heading.id}`}
        >
          {heading.title}

        {heading.items.length > 0 && (

            {heading.items.map((child) => (

                <a href={`#${child.id}`}>{child.title}

            ))}

        )}

    ))}

);

const useIntersectionObserver = (setActiveId) => {
  const headingElementsRef = useRef({});
  useEffect(() => {
    const callback = (headings) => {
      headingElementsRef.current = headings.reduce((map, headingElement) => {
        map[headingElement.target.id] = headingElement;
        return map;
      }, headingElementsRef.current);

      const visibleHeadings = [];
      Object.keys(headingElementsRef.current).forEach((key) => {
        const headingElement = headingElementsRef.current[key];
        if (headingElement.isIntersecting) visibleHeadings.push(headingElement);
      });

      const getIndexFromId = (id) =>
        headingElements.findIndex((heading) => heading.id === id);

      if (visibleHeadings.length === 1) {
        setActiveId(visibleHeadings[0].target.id);
      } else if (visibleHeadings.length > 1) {
        const sortedVisibleHeadings = visibleHeadings.sort(
          (a, b) => getIndexFromId(a.target.id) > getIndexFromId(b.target.id)
        );
        setActiveId(sortedVisibleHeadings[0].target.id);
      }
    };

    const observer = new IntersectionObserver(callback, {
      rootMargin: "0px 0px -50% 0px",
    });

    const headingElements = Array.from(document.querySelectorAll("h2, h3"));

    headingElements.forEach((element) => observer.observe(element));

    return () => observer.disconnect();
  }, [setActiveId]);
};
<|endoftext|>