<commit_before>/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * Tabzilla global navigation for Mozilla projects
 *
 * This code is licensed under the Mozilla Public License 1.1.
 *
 * Event handling portions adapted from the YUI Event component used under
 * the following license:
 *
 *   Copyright © 2012 Yahoo! Inc. All rights reserved.
 *
 *   Redistribution and use of this software 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 Yahoo! Inc. nor the names of YUI's contributors may
 *     be used to endorse or promote products derived from this software
 *     without specific prior written permission of Yahoo! Inc.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "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 THE COPYRIGHT OWNER OR
 *   CONTRIBUTORS 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.
 *
 * Portions adapted from the jQuery Easing plugin written by Robert Penner and
 * used under the following license:
 *
 *   Copyright 2001 Robert Penner
 *   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 author nor the names of contributors may be
 *     used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "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 THE COPYRIGHT OWNER OR
 *   CONTRIBUTORS 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.
 *
 *
 * @copyright 2012 silverorange Inc.
 * @license   http://www.mozilla.org/MPL/MPL-1.1.html Mozilla Public License 1.1
 * @author    Michael Gauthier <mike@silverorange.com>
 * @author    Steven Garrity <steven@silverorange.com>
 */

function Tabzilla()
{
    if (typeof jQuery != 'undefined' && jQuery) {
        jQuery(document).ready(Tabzilla.init);
    } else {
        Tabzilla.run();
    }
}

Tabzilla.READY_POLL_INTERVAL = 40;
Tabzilla.ARIA_ANNOUNCEMENTS = {
    CLICK_TO_OPEN : "Click to open up a section with interesting mozilla links",
    OPENED: "Link section open, use the tab key to move your focus into it. Press escape key to close"
}
Tabzilla.readyInterval = null;
Tabzilla.jQueryCDNSrc =
    '//www.mozilla.org/media/js/libs/jquery-1.7.1.min.js';

Tabzilla.hasCSSTransitions = (function() {
    var div = document.createElement('div');
    div.innerHTML = '

';

    var hasTransitions = (
           (div.firstChild.style.webkitTransition !== undefined)
        || (div.firstChild.style.MozTransition !== undefined)
        || (div.firstChild.style.msTransition !== undefined)
        || (div.firstChild.style.OTransition !== undefined)
    );

    delete div;

    return hasTransitions;
})();

/**
 * Sets up the DOMReady event for Tabzilla
 *
 * Adapted from the YUI Event component. Defined in Tabzilla so we do not
 * depend on YUI or jQuery. The YUI DOMReady implementation is based on work
 * Dean Edwards, John Resig, Matthias Miller and Diego Perini.
 */
Tabzilla.run = function()
{
    var webkit = 0, isIE = false, ua = navigator.userAgent;
    var m = ua.match(/AppleWebKit\/([^\s]*)/);

    if (m && m[1]) {
        webkit = parseInt(m[1], 10);
    } else {
        m = ua.match(/Opera[\s\/]([^\s]*)/);
        if (!m || !m[1]) {
            m = ua.match(/MSIE\s([^;]*)/);
            if (m && m[1]) {
                isIE = true;
            }
        }
    }

    // Internet Explorer: use the readyState of a defered script.
    // This isolates what appears to be a safe moment to manipulate
    // the DOM prior to when the document's readyState suggests
    // it is safe to do so.
    if (isIE) {
        if (self !== self.top) {
            document.onreadystatechange = function() {
                if (document.readyState == 'complete') {
                    document.onreadystatechange = null;
                    Tabzilla.ready();
                }
            };
        } else {
            var n = document.createElement('p');
            Tabzilla.readyInterval = setInterval(function() {
                try {
                    // throws an error if doc is not ready
                    n.doScroll('left');
                    clearInterval(Tabzilla.readyInterval);
                    Tabzilla.readyInterval = null;
                    Tabzilla.ready();
                    n = null;
                } catch (ex) {
                }
            }, Tabzilla.READY_POLL_INTERVAL);
        }

    // The document's readyState in Safari currently will
    // change to loaded/complete before images are loaded.
    } else if (webkit && webkit < 525) {
        Tabzilla.readyInterval = setInterval(function() {
            var rs = document.readyState;
            if ('loaded' == rs || 'complete' == rs) {
                clearInterval(Tabzilla.readyInterval);
                Tabzilla.readyInterval = null;
                Tabzilla.ready();
            }
        }, Tabzilla.READY_POLL_INTERVAL);

    // FireFox and Opera: These browsers provide a event for this
    // moment.  The latest WebKit releases now support this event.
    } else {
        Tabzilla.addEventListener(document, 'DOMContentLoaded', Tabzilla.ready);
    }
};

Tabzilla.ready = function()
{
    if (!Tabzilla.DOMReady) {
        Tabzilla.DOMReady = true;

        var onLoad = function() {
            Tabzilla.init();
            Tabzilla.removeEventListener(
                document,
                'DOMContentLoaded',
                Tabzilla.ready
            );
        };

        // if we don't have jQuery, dynamically load jQuery from CDN
        if (typeof jQuery == 'undefined') {
            var script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = Tabzilla.jQueryCDNSrc;
            document.getElementsByTagName('body')[0].appendChild(script);

            if (script.readyState) {
                // IE
                script.onreadystatechange = function() {
                    if (   script.readyState == 'loaded'
                        || script.readyState == 'complete'
                    ) {
                        onLoad();
                    }
                };
            } else {
                // Others
                script.onload = onLoad;
            }
        } else {
            onLoad();
        }
    }
};

Tabzilla.init = function()
{
    if (!Tabzilla.hasCSSTransitions) {
        // add easing functions
        jQuery.extend(jQuery.easing, {
            'easeInOut':  function (x, t, b, c, d) {
                if (( t /= d / 2) < 1) {
                    return c / 2 * t * t + b;
                }
                return -c / 2 * ((--t) * (t - 2) - 1) + b;
            }
        });
    }

    Tabzilla.link  = document.getElementById('tabzilla');
    Tabzilla.panel = Tabzilla.buildPanel();

    // add panel as first element of body element
    var body = document.getElementsByTagName('body')[0];
    body.insertBefore(Tabzilla.panel, body.firstChild);

    // set up event listeners for link
    Tabzilla.addEventListener(Tabzilla.link, 'click', function(e) {
        Tabzilla.preventDefault(e);
        Tabzilla.toggle();
    });

    Tabzilla.$announce = jQuery("#aria-announce");

    Tabzilla.$panel = jQuery(Tabzilla.panel);
    Tabzilla.$link  = jQuery(Tabzilla.link);

    Tabzilla.$panel.addClass('tabzilla-closed');
    Tabzilla.$link.addClass('tabzilla-closed');
    Tabzilla.$panel.removeClass('tabzilla-opened');
    Tabzilla.$link.removeClass('tabzilla-opened');

    Tabzilla.$link.attr("role","button");

    Tabzilla.opened = false;

    Tabzilla.$link.focus(function() {
        if (!Tabzilla.opened){
            Tabzilla.$announce.html(Tabzilla.ARIA_ANNOUNCEMENTS.CLICK_TO_OPEN);
        }
    });
    Tabzilla.$link.blur(function() {
        Tabzilla.$announce.html("");
    });
    jQuery(document).keydown(function(e) {
        if (e.which === 27 && Tabzilla.opened) {
            Tabzilla.toggle();
            Tabzilla.$link.focus();
        }
    });
    Tabzilla.$link.keypress(function(e) {
        if (e.which === 32) {
         	Tabzilla.toggle();
			Tabzilla.preventDefault(e);
        }
    });
    Tabzilla.$panel.keypress(function(e) {
        if (e.which === 13) {
         	Tabzilla.toggle();
			Tabzilla.$link.focus();
        }
    });
};

Tabzilla.buildPanel = function()
{
    var panel = document.createElement('div');
    panel.id = 'tabzilla-panel';
    panel.innerHTML = Tabzilla.content;
    return panel;
};

Tabzilla.addEventListener = function(el, ev, handler)
{
    if (typeof el.attachEvent != 'undefined') {
        el.attachEvent('on' + ev, handler);
    } else {
        el.addEventListener(ev, handler, false);
    }
};

Tabzilla.removeEventListener = function(el, ev, handler)
{
    if (typeof el.detachEvent != 'undefined') {
        el.detachEvent('on' + ev, handler);
    } else {
        el.removeEventListener(ev, handler, false);
    }
};

Tabzilla.toggle = function()
{
    if (Tabzilla.opened) {
        Tabzilla.close();
    } else {
        Tabzilla.open();
    }
};

Tabzilla.open = function()
{
    if (Tabzilla.opened) {
        return;
    }

    if (Tabzilla.hasCSSTransitions) {
        Tabzilla.$panel.addClass('tabzilla-opened');
        Tabzilla.$link.addClass('tabzilla-opened');
        Tabzilla.$panel.removeClass('tabzilla-closed');
        Tabzilla.$link.removeClass('tabzilla-closed');
    } else {
        // jQuery animation fallback
        jQuery(Tabzilla.panel).animate({ height: 200 }, 200, 'easeInOut').toggleClass("open");;
    }

    Tabzilla.$panel.attr("tabindex","-1").focus();
    Tabzilla.$announce.html(Tabzilla.ARIA_ANNOUNCEMENTS.OPENED);

    Tabzilla.opened = true;
};

Tabzilla.close = function()
{
    if (!Tabzilla.opened) {
        return;
    }

    if (Tabzilla.hasCSSTransitions) {
        Tabzilla.$panel.removeClass('tabzilla-opened');
        Tabzilla.$link.removeClass('tabzilla-opened');
        Tabzilla.$panel.addClass('tabzilla-closed');
        Tabzilla.$link.addClass('tabzilla-closed');
    } else {
        // jQuery animation fallback
        jQuery(Tabzilla.panel).animate({ height: 0 }, 200, 'easeInOut').toggleClass("open");

    }

    Tabzilla.opened = false;
};

Tabzilla.preventDefault = function(ev)
{
    if (ev.preventDefault) {
        ev.preventDefault();
    } else {
        ev.returnValue = false;
    }
};

Tabzilla.content =
    '
'
    + '
'
    + '
'
    + '    '
    + '      Visit the Den for tips, tricks and other tidbits to help you get the most out of your Firefox browsing experience.'
    + '
'
    + '
'
    + '
'
    + '
'
    + '

Mozilla
'
    + '
'
    + '
Mission
'
    + '
About
'
    + '
Projects
'
    + '
Support
'
    + '
Developer Network
'
    + '
'
    + '
'
    + '

Products
'
    + '
'
    + '
Firefox
'
    + '
Thunderbird
'
    + '
'
    + '
'
    + '

Innovations
'
    + '
'
    + '
WebFWD
'
    + '
Labs
'
    + '
'
    + '
'
    + '

Get Involved
'
    + '
'
    + '
Volunteer
'
    + '
Careers
'
    + '
Find us
'
    + '
Join us
'
    + '
'
    + '
'
    + '
'
    + '        Website Directory'
    + '        <form title="Search Mozilla sites" action="http://www.google.com/cse">'
    + '          <input type="hidden" value="002443141534113389537:ysdmevkkknw" name="cx">'
    + '          <input type="hidden" value="FORID:0" name="cof">'
    + '          Search'
    + '          <input type="search" placeholder="Search" id="q" name="q">'
    + '        </form>'
    + '
'
    + '
'
    + '
'
    + '
'
    + '

';

Tabzilla();
<commit_msg>even more indentation fixes<commit_after>/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * Tabzilla global navigation for Mozilla projects
 *
 * This code is licensed under the Mozilla Public License 1.1.
 *
 * Event handling portions adapted from the YUI Event component used under
 * the following license:
 *
 *   Copyright © 2012 Yahoo! Inc. All rights reserved.
 *
 *   Redistribution and use of this software 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 Yahoo! Inc. nor the names of YUI's contributors may
 *     be used to endorse or promote products derived from this software
 *     without specific prior written permission of Yahoo! Inc.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "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 THE COPYRIGHT OWNER OR
 *   CONTRIBUTORS 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.
 *
 * Portions adapted from the jQuery Easing plugin written by Robert Penner and
 * used under the following license:
 *
 *   Copyright 2001 Robert Penner
 *   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 author nor the names of contributors may be
 *     used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "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 THE COPYRIGHT OWNER OR
 *   CONTRIBUTORS 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.
 *
 *
 * @copyright 2012 silverorange Inc.
 * @license   http://www.mozilla.org/MPL/MPL-1.1.html Mozilla Public License 1.1
 * @author    Michael Gauthier <mike@silverorange.com>
 * @author    Steven Garrity <steven@silverorange.com>
 */

function Tabzilla()
{
    if (typeof jQuery != 'undefined' && jQuery) {
        jQuery(document).ready(Tabzilla.init);
    } else {
        Tabzilla.run();
    }
}

Tabzilla.READY_POLL_INTERVAL = 40;
Tabzilla.ARIA_ANNOUNCEMENTS = {
    CLICK_TO_OPEN : "Click to open up a section with interesting mozilla links",
    OPENED: "Link section open, use the tab key to move your focus into it. Press escape key to close"
}
Tabzilla.readyInterval = null;
Tabzilla.jQueryCDNSrc =
    '//www.mozilla.org/media/js/libs/jquery-1.7.1.min.js';

Tabzilla.hasCSSTransitions = (function() {
    var div = document.createElement('div');
    div.innerHTML = '

';

    var hasTransitions = (
           (div.firstChild.style.webkitTransition !== undefined)
        || (div.firstChild.style.MozTransition !== undefined)
        || (div.firstChild.style.msTransition !== undefined)
        || (div.firstChild.style.OTransition !== undefined)
    );

    delete div;

    return hasTransitions;
})();

/**
 * Sets up the DOMReady event for Tabzilla
 *
 * Adapted from the YUI Event component. Defined in Tabzilla so we do not
 * depend on YUI or jQuery. The YUI DOMReady implementation is based on work
 * Dean Edwards, John Resig, Matthias Miller and Diego Perini.
 */
Tabzilla.run = function()
{
    var webkit = 0, isIE = false, ua = navigator.userAgent;
    var m = ua.match(/AppleWebKit\/([^\s]*)/);

    if (m && m[1]) {
        webkit = parseInt(m[1], 10);
    } else {
        m = ua.match(/Opera[\s\/]([^\s]*)/);
        if (!m || !m[1]) {
            m = ua.match(/MSIE\s([^;]*)/);
            if (m && m[1]) {
                isIE = true;
            }
        }
    }

    // Internet Explorer: use the readyState of a defered script.
    // This isolates what appears to be a safe moment to manipulate
    // the DOM prior to when the document's readyState suggests
    // it is safe to do so.
    if (isIE) {
        if (self !== self.top) {
            document.onreadystatechange = function() {
                if (document.readyState == 'complete') {
                    document.onreadystatechange = null;
                    Tabzilla.ready();
                }
            };
        } else {
            var n = document.createElement('p');
            Tabzilla.readyInterval = setInterval(function() {
                try {
                    // throws an error if doc is not ready
                    n.doScroll('left');
                    clearInterval(Tabzilla.readyInterval);
                    Tabzilla.readyInterval = null;
                    Tabzilla.ready();
                    n = null;
                } catch (ex) {
                }
            }, Tabzilla.READY_POLL_INTERVAL);
        }

    // The document's readyState in Safari currently will
    // change to loaded/complete before images are loaded.
    } else if (webkit && webkit < 525) {
        Tabzilla.readyInterval = setInterval(function() {
            var rs = document.readyState;
            if ('loaded' == rs || 'complete' == rs) {
                clearInterval(Tabzilla.readyInterval);
                Tabzilla.readyInterval = null;
                Tabzilla.ready();
            }
        }, Tabzilla.READY_POLL_INTERVAL);

    // FireFox and Opera: These browsers provide a event for this
    // moment.  The latest WebKit releases now support this event.
    } else {
        Tabzilla.addEventListener(document, 'DOMContentLoaded', Tabzilla.ready);
    }
};

Tabzilla.ready = function()
{
    if (!Tabzilla.DOMReady) {
        Tabzilla.DOMReady = true;

        var onLoad = function() {
            Tabzilla.init();
            Tabzilla.removeEventListener(
                document,
                'DOMContentLoaded',
                Tabzilla.ready
            );
        };

        // if we don't have jQuery, dynamically load jQuery from CDN
        if (typeof jQuery == 'undefined') {
            var script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = Tabzilla.jQueryCDNSrc;
            document.getElementsByTagName('body')[0].appendChild(script);

            if (script.readyState) {
                // IE
                script.onreadystatechange = function() {
                    if (   script.readyState == 'loaded'
                        || script.readyState == 'complete'
                    ) {
                        onLoad();
                    }
                };
            } else {
                // Others
                script.onload = onLoad;
            }
        } else {
            onLoad();
        }
    }
};

Tabzilla.init = function()
{
    if (!Tabzilla.hasCSSTransitions) {
        // add easing functions
        jQuery.extend(jQuery.easing, {
            'easeInOut':  function (x, t, b, c, d) {
                if (( t /= d / 2) < 1) {
                    return c / 2 * t * t + b;
                }
                return -c / 2 * ((--t) * (t - 2) - 1) + b;
            }
        });
    }

    Tabzilla.link  = document.getElementById('tabzilla');
    Tabzilla.panel = Tabzilla.buildPanel();

    // add panel as first element of body element
    var body = document.getElementsByTagName('body')[0];
    body.insertBefore(Tabzilla.panel, body.firstChild);

    // set up event listeners for link
    Tabzilla.addEventListener(Tabzilla.link, 'click', function(e) {
        Tabzilla.preventDefault(e);
        Tabzilla.toggle();
    });

    Tabzilla.$announce = jQuery("#aria-announce");

    Tabzilla.$panel = jQuery(Tabzilla.panel);
    Tabzilla.$link  = jQuery(Tabzilla.link);

    Tabzilla.$panel.addClass('tabzilla-closed');
    Tabzilla.$link.addClass('tabzilla-closed');
    Tabzilla.$panel.removeClass('tabzilla-opened');
    Tabzilla.$link.removeClass('tabzilla-opened');

    Tabzilla.$link.attr("role","button");

    Tabzilla.opened = false;

    Tabzilla.$link.focus(function() {
        if (!Tabzilla.opened){
            Tabzilla.$announce.html(Tabzilla.ARIA_ANNOUNCEMENTS.CLICK_TO_OPEN);
        }
    });
    Tabzilla.$link.blur(function() {
        Tabzilla.$announce.html("");
    });
    jQuery(document).keydown(function(e) {
        if (e.which === 27 && Tabzilla.opened) {
            Tabzilla.toggle();
            Tabzilla.$link.focus();
        }
    });
    Tabzilla.$link.keypress(function(e) {
        if (e.which === 32) {
         	Tabzilla.toggle();
			Tabzilla.preventDefault(e);
        }
    });
    Tabzilla.$panel.keypress(function(e) {
        if (e.which === 13) {
            Tabzilla.toggle();
            Tabzilla.$link.focus();
        }
    });
};

Tabzilla.buildPanel = function()
{
    var panel = document.createElement('div');
    panel.id = 'tabzilla-panel';
    panel.innerHTML = Tabzilla.content;
    return panel;
};

Tabzilla.addEventListener = function(el, ev, handler)
{
    if (typeof el.attachEvent != 'undefined') {
        el.attachEvent('on' + ev, handler);
    } else {
        el.addEventListener(ev, handler, false);
    }
};

Tabzilla.removeEventListener = function(el, ev, handler)
{
    if (typeof el.detachEvent != 'undefined') {
        el.detachEvent('on' + ev, handler);
    } else {
        el.removeEventListener(ev, handler, false);
    }
};

Tabzilla.toggle = function()
{
    if (Tabzilla.opened) {
        Tabzilla.close();
    } else {
        Tabzilla.open();
    }
};

Tabzilla.open = function()
{
    if (Tabzilla.opened) {
        return;
    }

    if (Tabzilla.hasCSSTransitions) {
        Tabzilla.$panel.addClass('tabzilla-opened');
        Tabzilla.$link.addClass('tabzilla-opened');
        Tabzilla.$panel.removeClass('tabzilla-closed');
        Tabzilla.$link.removeClass('tabzilla-closed');
    } else {
        // jQuery animation fallback
        jQuery(Tabzilla.panel).animate({ height: 200 }, 200, 'easeInOut').toggleClass("open");;
    }

    Tabzilla.$panel.attr("tabindex","-1").focus();
    Tabzilla.$announce.html(Tabzilla.ARIA_ANNOUNCEMENTS.OPENED);

    Tabzilla.opened = true;
};

Tabzilla.close = function()
{
    if (!Tabzilla.opened) {
        return;
    }

    if (Tabzilla.hasCSSTransitions) {
        Tabzilla.$panel.removeClass('tabzilla-opened');
        Tabzilla.$link.removeClass('tabzilla-opened');
        Tabzilla.$panel.addClass('tabzilla-closed');
        Tabzilla.$link.addClass('tabzilla-closed');
    } else {
        // jQuery animation fallback
        jQuery(Tabzilla.panel).animate({ height: 0 }, 200, 'easeInOut').toggleClass("open");

    }

    Tabzilla.opened = false;
};

Tabzilla.preventDefault = function(ev)
{
    if (ev.preventDefault) {
        ev.preventDefault();
    } else {
        ev.returnValue = false;
    }
};

Tabzilla.content =
    '
'
    + '
'
    + '
'
    + '    '
    + '      Visit the Den for tips, tricks and other tidbits to help you get the most out of your Firefox browsing experience.'
    + '
'
    + '
'
    + '
'
    + '
'
    + '

Mozilla
'
    + '
'
    + '
Mission
'
    + '
About
'
    + '
Projects
'
    + '
Support
'
    + '
Developer Network
'
    + '
'
    + '
'
    + '

Products
'
    + '
'
    + '
Firefox
'
    + '
Thunderbird
'
    + '
'
    + '
'
    + '

Innovations
'
    + '
'
    + '
WebFWD
'
    + '
Labs
'
    + '
'
    + '
'
    + '

Get Involved
'
    + '
'
    + '
Volunteer
'
    + '
Careers
'
    + '
Find us
'
    + '
Join us
'
    + '
'
    + '
'
    + '
'
    + '        Website Directory'
    + '        <form title="Search Mozilla sites" action="http://www.google.com/cse">'
    + '          <input type="hidden" value="002443141534113389537:ysdmevkkknw" name="cx">'
    + '          <input type="hidden" value="FORID:0" name="cof">'
    + '          Search'
    + '          <input type="search" placeholder="Search" id="q" name="q">'
    + '        </form>'
    + '
'
    + '
'
    + '
'
    + '
'
    + '

';

Tabzilla();
<|endoftext|>


<commit_before>import React from 'react';
import * as TypesService from '../../../../../../../../services/types-service';
import ComboBox from '../../../../../../../../components/common/ComboBox/ComboBox';
import style from './DefaultValue.css';

const booleanSingleVariantSuggestions = [{label: 'true', value: true}, {label: 'false', value: false}];
const DefaultValueInput = ({value, valueType, onChange}) => {
  if (valueType === TypesService.types.boolean.name) {
    return (
      <ComboBox
        options={booleanSingleVariantSuggestions}
        selected={booleanSingleVariantSuggestions.filter(x => x.value === value)}
        placeholder="Enter value here"
        showValueInOptions={false}
        clearButton
        onChange={e => onChange(e && e.value)}
      />
    )
  }
  return (
    <input
      onChange={e => onChange(e.target.value)}
      value={value}
      placeholder="Enter value here"
      className={style['values-input']}
    />
  )
};

const DefaultValue = (props) => {
  return (

      Default Value:
      <DefaultValueInput {...props}/>

  );
};

export default DefaultValue;
<commit_msg>fix default value component - input value cannot be undefined<commit_after>import React from 'react';
import * as TypesService from '../../../../../../../../services/types-service';
import ComboBox from '../../../../../../../../components/common/ComboBox/ComboBox';
import style from './DefaultValue.css';

const booleanSingleVariantSuggestions = [{label: 'true', value: true}, {label: 'false', value: false}];
const DefaultValueInput = ({value, valueType, onChange}) => {
  if (valueType === TypesService.types.boolean.name) {
    return (
      <ComboBox
        options={booleanSingleVariantSuggestions}
        selected={booleanSingleVariantSuggestions.filter(x => x.value === value)}
        placeholder="Enter value here"
        showValueInOptions={false}
        clearButton
        onChange={e => onChange(e && e.value)}
      />
    )
  }
  return (
    <input
      onChange={e => onChange(e.target.value)}
      value={value || ''}
      placeholder="Enter value here"
      className={style['values-input']}
    />
  )
};

const DefaultValue = (props) => {
  return (

      Default Value:
      <DefaultValueInput {...props}/>

  );
};

export default DefaultValue;
<|endoftext|>


<commit_before>/**
 * Object to be appended in the priority queue.
 *
 * @class
 * @param 	mixed 	v
 * @param	integer	p
 * @author	Augusto Pascutti
 */
var QueueItem = function(v, p) {
	this.value    = v;
	this.priority = p;
};

/**
 * Priority queue class.
 *
 * @class
 * @param 	Function[optional] 	c	Compare function to be used
 * @param	integer[optional]	m	Max number of elements to hold
 * @author	Augusto Pascutti
 */
var PriorityQueue = function(c, m) { this.init(c, m) };
PriorityQueue.prototype = {
	_queue: [],
	_compare: undefined,
	_size: 0,

	/**
	 * Priority queue class constructor.
	 *
	 * @class
	 * @param 	Function[optional] 	compare_function	Compare function to be used
	 * @param	integer[optional]	maximum_size		Max number of elements to hold
	 */
	init: function(compare_function, maximum_size) {
		this._compare = compare_function || undefined;
		this._size    = maximum_size || 0 ;
		this.reset();
	},

	/**
	 * Pushes something to the priority queue.
	 *
	 * @param 	mixed 		value
	 * @param 	integer		priority
	 * @return 	void
	 */
	push: function(value, priority) {
		this._queue.push(new QueueItem(value, priority));
		this._queue.sort(this.compare());
		this._maitain();
	},

	/**
	 * Removes the most important item and return its value.
	 *
	 * @return mixed
	 */
	pop: function() {
		item = this._queue.shift();
		this._maitain();
		return (item) ? item.value : undefined;
	},

	/**
	 * Returns most important item value from this queue, without removing it.
	 *
	 * @return mixed
	 */
	top: function() {
		item = this._queue[0];
		return (item) ? item.value : undefined;
	},

	/**
	 * Removes the less important item and return its value.
	 *
	 * @return mixed
	 */
	shift: function() {
		item = this._queue.pop();
		this._maitain();
		return (item) ? item.value : undefined;
	},

	/**
	 * Returns the less important item value, without removing it.
	 *
	 * @return mixed
	 */
	bottom: function() {
		idx  = this.length-1;
		item = this._queue[idx];
		return (item) ? item.value : undefined;
	},

	/**
	 * Returns the ordered queue as an Array.
	 *
	 * @return Array
	 */
	getArray: function() {
	    return this._queue || new Array();
	},

	/**
	 * Resets the queue.
	 *
	 * @return void
	 */
	reset: function() {
		this._queue = [];
		this._maitain();
	},

	/**
	 * Returns the compare function.
	 * If no compare function is set, defines a default one.
	 *
	 * @return Function
	 */
	compare: function() {
		if (!this._compare) {
			this._compare = function(a,b) {
				return b.priority - a.priority;
			};
		}
		return this._compare;
	},

	/**
	 * Defines a fixed size to the queue.
	 * Zero for no limit and any other number to set it as the highest number
	 * of items allowed in this queue.
	 *
	 * @param 	integer		i
	 * @return 	void
	 */
	size: function(i) {
		this._size = i;
	},

	/**
	 * Keeps the size of the queue by removing the less important item of it and length
	 * information atribute.
     *
	 * @return void
	 */
	_maitain: function() {
	    this.length = this._queue.length;
		if ( this._size == 0 ) return;
		while (this._size < this.length) {
			this.shift();
		}
	},
};

module.exports = PriorityQueue;<commit_msg>un-nodejs-ify PriorityQueue<commit_after>/**
 * Object to be appended in the priority queue.
 *
 * @class
 * @param 	mixed 	v
 * @param	integer	p
 * @author	Augusto Pascutti
 */
var QueueItem = function(v, p) {
	this.value    = v;
	this.priority = p;
};

/**
 * Priority queue class.
 *
 * @class
 * @param 	Function[optional] 	c	Compare function to be used
 * @param	integer[optional]	m	Max number of elements to hold
 * @author	Augusto Pascutti
 */
var PriorityQueue = function(c, m) { this.init(c, m) };
PriorityQueue.prototype = {
	_queue: [],
	_compare: undefined,
	_size: 0,

	/**
	 * Priority queue class constructor.
	 *
	 * @class
	 * @param 	Function[optional] 	compare_function	Compare function to be used
	 * @param	integer[optional]	maximum_size		Max number of elements to hold
	 */
	init: function(compare_function, maximum_size) {
		this._compare = compare_function || undefined;
		this._size    = maximum_size || 0 ;
		this.reset();
	},

	/**
	 * Pushes something to the priority queue.
	 *
	 * @param 	mixed 		value
	 * @param 	integer		priority
	 * @return 	void
	 */
	push: function(value, priority) {
		this._queue.push(new QueueItem(value, priority));
		this._queue.sort(this.compare());
		this._maitain();
	},

	/**
	 * Removes the most important item and return its value.
	 *
	 * @return mixed
	 */
	pop: function() {
		item = this._queue.shift();
		this._maitain();
		return (item) ? item.value : undefined;
	},

	/**
	 * Returns most important item value from this queue, without removing it.
	 *
	 * @return mixed
	 */
	top: function() {
		item = this._queue[0];
		return (item) ? item.value : undefined;
	},

	/**
	 * Removes the less important item and return its value.
	 *
	 * @return mixed
	 */
	shift: function() {
		item = this._queue.pop();
		this._maitain();
		return (item) ? item.value : undefined;
	},

	/**
	 * Returns the less important item value, without removing it.
	 *
	 * @return mixed
	 */
	bottom: function() {
		idx  = this.length-1;
		item = this._queue[idx];
		return (item) ? item.value : undefined;
	},

	/**
	 * Returns the ordered queue as an Array.
	 *
	 * @return Array
	 */
	getArray: function() {
	    return this._queue || new Array();
	},

	/**
	 * Resets the queue.
	 *
	 * @return void
	 */
	reset: function() {
		this._queue = [];
		this._maitain();
	},

	/**
	 * Returns the compare function.
	 * If no compare function is set, defines a default one.
	 *
	 * @return Function
	 */
	compare: function() {
		if (!this._compare) {
			this._compare = function(a,b) {
				return b.priority - a.priority;
			};
		}
		return this._compare;
	},

	/**
	 * Defines a fixed size to the queue.
	 * Zero for no limit and any other number to set it as the highest number
	 * of items allowed in this queue.
	 *
	 * @param 	integer		i
	 * @return 	void
	 */
	size: function(i) {
		this._size = i;
	},

	/**
	 * Keeps the size of the queue by removing the less important item of it and length
	 * information atribute.
     *
	 * @return void
	 */
	_maitain: function() {
	    this.length = this._queue.length;
		if ( this._size == 0 ) return;
		while (this._size < this.length) {
			this.shift();
		}
	},
};
<|endoftext|>


<commit_before>'use strict';

Ember.SimpleAuth.Stores.Ephemeral = Ember.SimpleAuth.Stores.Base.extend({
  init: function() {
    this.clear();
  },

  persist: function(properties) {
    for (var property in properties) {
      this._data[property] = properties[property];
    }
  },

  restore: function() {
    return Ember.$.extend({}, this._data);
  },

  clear: function() {
    delete this._data;
    this._data = {};
  }
});
<commit_msg>code cleanup<commit_after>'use strict';

Ember.SimpleAuth.Stores.Ephemeral = Ember.SimpleAuth.Stores.Base.extend({
  init: function() {
    this.clear();
  },

  persist: function(properties) {
    this._data = Ember.$.extend(properties, this._data);
  },

  restore: function() {
    return Ember.$.extend({}, this._data);
  },

  clear: function() {
    delete this._data;
    this._data = {};
  }
});
<|endoftext|>


<commit_before>'use strict';

const chai   = require('chai');
const expect = chai.expect;

const Board  = require('../lib/board');
const Block  = require('../lib/block');

describe('Pac-man', function(){

  context('that collides with a wall', function(){

    beforeEach(function() {
      this.board  = new Board();
      this.pacman = new Block(10, 10, 'pacman', this.board, 'alive');
      this.wall   = new Block(10, 10, 'wall', this.board);

    });

    it('will not be able to move', function(){

      expect(this.pacman.x).to.eql(10);
      expect(this.pacman.y).to.eql(10);

      this.pacman.move(1, 0);

      expect(this.pacman.x).to.eql(10);
      expect(this.pacman.y).to.eql(10);

      this.pacman.move(0, 1);

      expect(this.pacman.x).to.eql(10);
      expect(this.pacman.y).to.eql(10);

      this.pacman.move(-1, 0);

      expect(this.pacman.x).to.eql(10);
      expect(this.pacman.y).to.eql(10);

      this.pacman.move(0, -1);

      expect(this.pacman.x).to.eql(10);
      expect(this.pacman.y).to.eql(10);

    });

  });

  context('that collides with a ghost', function(){

    beforeEach(function() {
      this.board  = new Board();
      this.pacman = new Block(10, 10, 'pacman', this.board, 'alive');
      this.ghost  = new Block(11, 10, 'ghost', this.board, 'offense');

    });

    context('should die', function(){

      it('when it moves to the right', function(){

        expect(this.pacman.state).to.eql('alive');
        expect(this.ghost.state).to.eql('offense');

        this.pacman.move(1, 0);

        expect(this.pacman.state).to.eql('dead');
      });

    });

  });

});

<commit_msg>add collision test<commit_after>'use strict';

const chai   = require('chai');
const expect = chai.expect;

const Board  = require('../lib/board');
const Block  = require('../lib/block');

describe('Pac-man', function(){

  context('should not be able to move', function(){

    beforeEach(function() {
      this.board  = new Board();
      this.pacman = new Block(20, 20, 'pacman', this.board, 'alive');
    });

    context('into a wall', function(){

      it('to the left', function(){
        this.block = new Block(19, 20, 'wall', this.board);

        this.pacman.move(-1, 0);

        expect(this.pacman.x).to.eql(20);
        expect(this.pacman.y).to.eql(20);

      });

      it('to the right', function(){
        this.block = new Block(21, 20, 'wall', this.board);

        this.pacman.move(1, 0);

        expect(this.pacman.x).to.eql(20);
        expect(this.pacman.y).to.eql(20);

      });

      it('upward', function(){
        this.block = new Block(20, 21, 'wall', this.board);

        this.pacman.move(0, 1);

        expect(this.pacman.x).to.eql(20);
        expect(this.pacman.y).to.eql(20);

      });

      it('downward', function(){
        this.block = new Block(20, 19, 'wall', this.board);

        this.pacman.move(0, -1);

        expect(this.pacman.x).to.eql(20);
        expect(this.pacman.y).to.eql(20);

      });

    });

  });

  context('that collides with a ghost', function(){

    beforeEach(function() {
      this.board  = new Board();
      this.pacman = new Block(10, 10, 'pacman', this.board, 'alive');

    });

    context('should die', function(){

      it('when it moves to the right', function(){
        this.ghost  = new Block(11, 10, 'ghost', this.board, 'offense');
        expect(this.pacman.state).to.eql('alive');
        expect(this.ghost.state).to.eql('offense');

        this.pacman.move(1, 0);

        expect(this.pacman.state).to.eql('dead');
      });

      it('when it moves to the left', function(){

        this.ghost  = new Block(9, 10, 'ghost', this.board, 'offense');

        expect(this.pacman.state).to.eql('alive');
        expect(this.ghost.state).to.eql('offense');

        this.pacman.move(-1, 0);

        expect(this.pacman.state).to.eql('dead');
      });

      it('when it moves up', function(){

        this.ghost  = new Block(10, 9, 'ghost', this.board, 'offense');

        expect(this.pacman.state).to.eql('alive');
        expect(this.ghost.state).to.eql('offense');

        this.pacman.move(0, -1);

        expect(this.pacman.state).to.eql('dead');
      });

      it('when it moves down', function(){

        this.ghost  = new Block(10, 11, 'ghost', this.board, 'offense');

        expect(this.pacman.state).to.eql('alive');
        expect(this.ghost.state).to.eql('offense');

        this.pacman.move(0, 1);

        expect(this.pacman.state).to.eql('dead');
      });

    });
  });

  context('that collides with a pellet', function(){

    beforeEach(function() {
      this.board  = new Board(300, 300);
      this.pacman = new Block(10, 10, 'pacman', this.board, 'alive');

    });

    context('should eat the pellet and score points', function(){

      it('when moving left', function(){
        this.pellet = new Block(9, 10, 'pellet', this.board, 'unEaten');

        this.pacman.move(-1, 0);

        // expect(this.pellet.state).to.eql('eaten');
        expect(this.pacman.board.score).to.eql(1);

      });

      it('when moving right', function(){
        this.pellet = new Block(11, 10, 'pellet', this.board, 'unEaten');

        expect(this.board.score).to.eql(0);
        expect(this.pellet.state).to.eql('unEaten');

        this.pacman.move(1, 0);

        // expect(this.pellet.state).to.eql('eaten');
        expect(this.board.score).to.eql(1);
      });

      it('when moving up', function(){
        this.pellet = new Block(10, 9, 'pellet', this.board, 'unEaten');

        expect(this.board.score).to.eql(0);
        expect(this.pellet.state).to.eql('unEaten');

        this.pacman.move(0, -1);

        // expect(this.pellet.state).to.eql('eaten');
        expect(this.board.score).to.eql(1);
      });

      it('when moving down', function(){
        this.pellet = new Block(10, 11, 'pellet', this.board, 'unEaten');

        expect(this.board.score).to.eql(0);
        expect(this.pellet.state).to.eql('unEaten');

        this.pacman.move(0, 1);

        // expect(this.pellet.state).to.eql('eaten');
        expect(this.board.score).to.eql(1);
      });

    });

  });

});

<|endoftext|>


<commit_before>
var stop_reading = false; //when the system is creating the blocks in Scratch, the reading of the codes stops
var detectedBarcodeMarkers = {};  //detected codes and its attributes
var codes_detected = []; // an array of the detected codes numbers
var last_codes_detected = []; // an array of the last detected codes numbers
var old_ids = []; // ids of the blocks that was created in Scratch last time
var par_list = []; // parameters of funtions (like numbers, strings...)

// numbers of the blocks in Scratch
var greenFlag_block = 75;
var meow_block = 39;
var drum_block = 43;
var play_pith = 41;
var wait = 83;
var repeat = 84;
var end_repeat = 84.5;
var green_flag_button = 0.5;

// codes and blocks or parameters in Scratch
var code_to_block = {0:greenFlag_block, 1:meow_block, 2:drum_block, 3:play_pith,
  4: wait, 5: repeat, 6: end_repeat, 7: "1", 8: "meow", 9: "10", 63: green_flag_button};

// code 4 -> green flag button TO TEST!
var green_flag_count = 0;

// play the info of the new blocks in the script
check_new_blocks = function(list, last_list) {
  if (last_list.length < list.length) {
    for (var i = 0; i <last_list.length; i++) {
      if (list[i] != last_list[i]) {
        //console.log("play sound ", list[i]);
        return;
      }
    }
    //console.log("play sound ", list[list.length-1]);
    //play the sound of the last block in list
  }
}

// check if the block regognized has already been added to the codes_detected list
checkBlock = function(list, item) {
  var obj;
  for (i = 0; i<list.length; i++) {
    if (list[i][0] == item[0] && list[i][1] == item[1]) {
      return 1;
    }
  }
  return -1; // the block is not in the list
}

invert_list_order = function(list) {
  var list_inverted = [];
  list_inverted.push(list[0]);
  for (var i = list.length - 1; i > 0; i--) {
    list_inverted.push(list[i]);
  }
  return list_inverted;
}

getPosition = function(pos) {
  var pos_2 = [];
  pos_2[0] = parseFloat(pos[0].toFixed(2));
  pos_2[1] = parseFloat(pos[1].toFixed(2));
  return pos_2;
}

// method to create the blocks in Scratch
createBlocksInScratch = function() {
  var flag_end_repeat = false;  //flag to put the next block outside the repeat
  var last_repeat_id = ""; // block id of the last repeat found
	// loop to delete the blocks that are in Scratch Script
  if (old_ids.length != 0){
    var delete_blocks = new window.Blockly.Events.fromJson({type: Blockly.Events.DELETE,
      ids: old_ids}, workspace);
    delete_blocks.run(true);
    old_ids = [];
  }
  console.log("teste last_codes_detected ", last_codes_detected);
  var codes = invert_list_order(last_codes_detected);
	var last_id = ""; // save the last block id created in Scratch so it can be used to connect to the next one

  //codes = [0, 1, 5, 1, 2, 6, 2];
  console.log("teste codes ", codes);
	// loop to create the blocks and connect them
	for (var i = 0; i < codes.length; i++) {
    if (code_to_block[codes[i]] == 84.5) {
      flag_end_repeat = true; // the next block will be outside the repeat
    }
    else if (code_to_block[codes[i]] != 0.5){
  		var toolbox = document.getElementById('toolbox');
  		var blocks = toolbox.getElementsByTagName('block');
  		var blockXML = blocks[code_to_block[codes[i]]];
  		var block = window.Blockly.Xml.domToBlock(blockXML, workspace);
  		block.initSvg();
      old_ids.push(block.id);

      //var changeEvent = new window.Blockly.Events.fromJson({type: Blockly.Events.CHANGE, blockId: block.id,
               //element: "field", name: "NUM", newValue: par_list.pop()}, workspace);
      //changeEvent.run(true); // Event to change parameter

  		if (last_id.localeCompare("") != 0) {
  			child_id = block.id;
  			parent_id = last_id;
        if (code_to_block[codes[i-1]] != null && code_to_block[codes[i-1]] == 84) { // if last block was a loop
          last_repeat_id = last_id; // save id
          var moveEvent = new window.Blockly.Events.fromJson({type: Blockly.Events.MOVE, blockId: child_id,
                   newParentId: parent_id, newInputName: "SUBSTACK"}, workspace);
          moveEvent.run(true); // Event to connect the blocks

        }
        else {
          if (flag_end_repeat) {
            flag_end_repeat = false;
            parent_id = last_repeat_id;
          }
          var moveEvent = new window.Blockly.Events.fromJson({type: Blockly.Events.MOVE, blockId: child_id,
                   newParentId: parent_id}, workspace);
          moveEvent.run(true); // Event to connect the blocks
        }

  		}
  		last_id = block.id;
    }
	}
  old_codes = codes_detected;
  stop_reading = false;
}
transformPosition = function(position) {

	return  new_position;
}

window.ARThreeOnLoad = function() {

	ARController.getUserMediaThreeScene({maxARVideoSize: 320, cameraParam: 'Data/camera_para-iPhone 5 rear 640x480 1.0m.dat',
	onSuccess: function(arScene, arController, arCamera) {

		arController.setPatternDetectionMode(artoolkit.AR_MATRIX_CODE_DETECTION);

		document.body.className = arController.orientation;

		var renderer = new THREE.WebGLRenderer({antialias: true});
		if (arController.orientation === 'portrait') {
			//var w = (window.innerWidth / arController.videoHeight) * arController.videoWidth;
			//var h = window.innerWidth;
			//renderer.setSize(w, h);
			//renderer.domElement.style.paddingBottom = (w-h) + 'px';
		} else {
			if (/Android|mobile|iPad|iPhone/i.test(navigator.userAgent)) {
				//renderer.setSize(window.innerWidth, (window.innerWidth / arController.videoWidth) * arController.videoHeight);
			} else {
				renderer.setSize(arController.videoWidth, arController.videoHeight);
				document.body.className += ' desktop';
			}
		}
		renderer.domElement.style.visibility = "hidden";
		renderer.setSize(0, 0);

		document.body.insertBefore(renderer.domElement, document.body.firstChild);

		arController.addEventListener('getMarker', function(ev) { // event that a marker was recognized

      // ckeck green flag "button" - some seconds hiding the block and the code run
      if (last_codes_detected.length > 0) {
        if (last_codes_detected.indexOf(63) == -1) {
          if (green_flag_count < 400) {
            green_flag_count ++;
          }
        }
        else {
          green_flag_count = 0;
        }
        if (green_flag_count == 200) {
          stop_reading = true;
          createBlocksInScratch();
          green_flag_count ++;
        }

        if (green_flag_count == 700) {
          console.log("stop_reading ", stop_reading);
          window.vm.greenFlag();  // green flag "pressed"
          green_flag_count ++;
        }
      }

			var barcodeId = ev.data.marker.idMatrix;
			if (barcodeId !== -1 && stop_reading == false) {
				var transform = ev.data.matrix;
				if (!detectedBarcodeMarkers[barcodeId]) {
          // if the code is not for a parameter, put it in the codes_detected list
          if (barcodeId < 7 || barcodeId == 63) {
  					detectedBarcodeMarkers[barcodeId] = {
  						visible: true,
  						pos: [],
  						matrix: new Float32Array(16)
  					}
  					detectedBarcodeMarkers[barcodeId].visible = true;
            detectedBarcodeMarkers[barcodeId].pos.push(getPosition(ev.data.marker.pos));
  					//detectedBarcodeMarkers[barcodeId].pos.push(ev.data.marker.pos);
  					//console.log("saw a barcode marker with id", barcodeId);
  					//console.log("position : ", detectedBarcodeMarkers[barcodeId].pos[0]);
  					detectedBarcodeMarkers[barcodeId].matrix.set(transform);
  					codes_detected.push(barcodeId);
          }

          else {
            //set the parameter to the method (pos of parameter is (x + w, y) the position of the method (x, y))
            // probably the parameter order in the list is the parameter order to put in the function list TEST IT!
            //var pos_test = getPosition(ev.data.marker.pos);
            par_list.push(code_to_block[barcodeId]);

          }
				}
        else {
          if (checkBlock(detectedBarcodeMarkers[barcodeId].pos, getPosition(ev.data.marker.pos)) == -1) {
          //if the barcode detected was not added to the list, add it
              detectedBarcodeMarkers[barcodeId].pos.push(getPosition(ev.data.marker.pos));
              //console.log("again saw a barcode marker with id", barcodeId);
    					//console.log("position : ", detectedBarcodeMarkers[barcodeId].pos[1]);
              detectedBarcodeMarkers[barcodeId].visible = true;
              detectedBarcodeMarkers[barcodeId].matrix.set(transform);
    					codes_detected.push(barcodeId);
          }
        }
        // restart the list with the first block: the green flag
				if (detectedBarcodeMarkers[0] && barcodeId == 0) {
					detectedBarcodeMarkers = {};
          //check_new_blocks(codes_detected, last_codes_detected);
          last_codes_detected = codes_detected;
					codes_detected = [];
          par_list = [];
					detectedBarcodeMarkers[barcodeId] = {
						visible: true,
						pos: [],
						matrix: new Float32Array(16)
					}
					detectedBarcodeMarkers[barcodeId].visible = true;
          detectedBarcodeMarkers[barcodeId].pos.push(getPosition(ev.data.marker.pos));
					//detectedBarcodeMarkers[barcodeId].pos.push(ev.data.marker.pos);
					detectedBarcodeMarkers[barcodeId].matrix.set(transform);
					codes_detected.push(barcodeId);
				}

			}
		});
		var tick = function() {
			arScene.process();
			arScene.renderOn(renderer);
			requestAnimationFrame(tick);
		};
		tick();
	}});
	delete window.ARThreeOnLoad;
};

if (window.ARController && ARController.getUserMediaThreeScene) {
	ARThreeOnLoad();
}
<commit_msg>hange in the way parameters in block are set - part 1<commit_after>
var stop_reading = false; //when the system is creating the blocks in Scratch, the reading of the codes stops
var detectedBarcodeMarkers = {};  //detected codes and its attributes
var codes_detected = []; // an array of the detected codes numbers
var last_codes_detected = []; // an array of the last detected codes numbers
var old_ids = []; // ids of the blocks that was created in Scratch last time
var par_list = []; // parameters of funtions (like numbers, strings...)

// numbers of the blocks in Scratch
var greenFlag_block = 75;
var meow_block = 39;
var drum_block = 43;
var play_pith = 41;
var wait = 83;
var repeat = 84;
var end_repeat = 84.5;
var green_flag_button = 0.5;

// codes and blocks or parameters in Scratch
var code_to_block = {0:greenFlag_block, 1:meow_block, 2:drum_block, 3:play_pith,
  4: wait, 5: repeat, 6: end_repeat, 7: "1", 8: "meow", 9: "10", 63: green_flag_button};

// code 4 -> green flag button TO TEST!
var green_flag_count = 0;

// play the info of the new blocks in the script
check_new_blocks = function(list, last_list) {
  if (last_list.length < list.length) {
    for (var i = 0; i <last_list.length; i++) {
      if (list[i] != last_list[i]) {
        //console.log("play sound ", list[i]);
        return;
      }
    }
    //console.log("play sound ", list[list.length-1]);
    //play the sound of the last block in list
  }
}

// check if the block regognized has already been added to the codes_detected list
checkBlock = function(list, item) {
  var obj;
  for (i = 0; i<list.length; i++) {
    if (list[i][0] == item[0] && list[i][1] == item[1]) {
      return 1;
    }
  }
  return -1; // the block is not in the list
}

invert_list_order = function(list) {
  var list_inverted = [];
  list_inverted.push(list[0]);
  for (var i = list.length - 1; i > 0; i--) {
    list_inverted.push(list[i]);
  }
  return list_inverted;
}

getPosition = function(pos) {
  var pos_2 = [];
  pos_2[0] = parseFloat(pos[0].toFixed(2));
  pos_2[1] = parseFloat(pos[1].toFixed(2));
  return pos_2;
}

// method to create the blocks in Scratch
createBlocksInScratch = function() {
  var flag_end_repeat = false;  //flag to put the next block outside the repeat
  var last_repeat_id = ""; // block id of the last repeat found
	// loop to delete the blocks that are in Scratch Script
  if (old_ids.length != 0){
    var delete_blocks = new window.Blockly.Events.fromJson({type: Blockly.Events.DELETE,
      ids: old_ids}, workspace);
    delete_blocks.run(true);
    old_ids = [];
  }
  console.log("teste last_codes_detected ", last_codes_detected);
  var codes = invert_list_order(last_codes_detected);
	var last_id = ""; // save the last block id created in Scratch so it can be used to connect to the next one

  codes = [0, 4];
  par_list = [9];
  console.log("teste codes ", codes);
	// loop to create the blocks and connect them
	for (var i = 0; i < codes.length; i++) {
    if (code_to_block[codes[i]] == 84.5) {
      flag_end_repeat = true; // the next block will be outside the repeat
    }
    else if (code_to_block[codes[i]] != 0.5){
  		var toolbox = document.getElementById('toolbox');
  		var blocks = toolbox.getElementsByTagName('block');
  		var blockXML = blocks[code_to_block[codes[i]]];
  		var block = window.Blockly.Xml.domToBlock(blockXML, workspace);
      //if (code_to_block[codes[i]] == 83) {
        //console.log("lalal ", Object.getOwnPropertyNames(block));
        //console.log(block.childBlocks_);
        //console.log(block.childBlocks_[0]);
      //}
  		block.initSvg();
      old_ids.push(block.id);
      console.log ("id:", block.id)
      if (code_to_block[codes[i]] == 83) {
        var changeEvent = new window.Blockly.Events.fromJson({type: Blockly.Events.CHANGE, blockId: block.childBlocks_[0].id,
                element: "field", name: "NUM", newValue: code_to_block[par_list.pop()]}, workspace);
        changeEvent.run(true); // Event to change parameter
      }

  		if (last_id.localeCompare("") != 0) {
  			child_id = block.id;
  			parent_id = last_id;
        if (code_to_block[codes[i-1]] != null && code_to_block[codes[i-1]] == 84) { // if last block was a loop
          last_repeat_id = last_id; // save id
          var moveEvent = new window.Blockly.Events.fromJson({type: Blockly.Events.MOVE, blockId: child_id,
                   newParentId: parent_id, newInputName: "SUBSTACK"}, workspace);
          moveEvent.run(true); // Event to connect the blocks

        }
        else {
          if (flag_end_repeat) {
            flag_end_repeat = false;
            parent_id = last_repeat_id;
          }
          var moveEvent = new window.Blockly.Events.fromJson({type: Blockly.Events.MOVE, blockId: child_id,
                   newParentId: parent_id}, workspace);
          moveEvent.run(true); // Event to connect the blocks
        }

  		}
  		last_id = block.id;
    }
	}
  old_codes = codes_detected;
  stop_reading = false;
}
transformPosition = function(position) {

	return  new_position;
}

window.ARThreeOnLoad = function() {

	ARController.getUserMediaThreeScene({maxARVideoSize: 320, cameraParam: 'Data/camera_para-iPhone 5 rear 640x480 1.0m.dat',
	onSuccess: function(arScene, arController, arCamera) {

		arController.setPatternDetectionMode(artoolkit.AR_MATRIX_CODE_DETECTION);

		document.body.className = arController.orientation;

		var renderer = new THREE.WebGLRenderer({antialias: true});
		if (arController.orientation === 'portrait') {
			//var w = (window.innerWidth / arController.videoHeight) * arController.videoWidth;
			//var h = window.innerWidth;
			//renderer.setSize(w, h);
			//renderer.domElement.style.paddingBottom = (w-h) + 'px';
		} else {
			if (/Android|mobile|iPad|iPhone/i.test(navigator.userAgent)) {
				//renderer.setSize(window.innerWidth, (window.innerWidth / arController.videoWidth) * arController.videoHeight);
			} else {
				renderer.setSize(arController.videoWidth, arController.videoHeight);
				document.body.className += ' desktop';
			}
		}
		renderer.domElement.style.visibility = "hidden";
		renderer.setSize(0, 0);

		document.body.insertBefore(renderer.domElement, document.body.firstChild);

		arController.addEventListener('getMarker', function(ev) { // event that a marker was recognized

      // ckeck green flag "button" - some seconds hiding the block and the code run
      if (last_codes_detected.length > 0) {
        if (last_codes_detected.indexOf(63) == -1) {
          if (green_flag_count < 400) {
            green_flag_count ++;
          }
        }
        else {
          green_flag_count = 0;
        }
        if (green_flag_count == 200) {
          stop_reading = true;
          createBlocksInScratch();
          green_flag_count ++;
        }

        if (green_flag_count == 700) {
          console.log("stop_reading ", stop_reading);
          window.vm.greenFlag();  // green flag "pressed"
          green_flag_count ++;
        }
      }

			var barcodeId = ev.data.marker.idMatrix;
			if (barcodeId !== -1 && stop_reading == false) {
				var transform = ev.data.matrix;
				if (!detectedBarcodeMarkers[barcodeId]) {
          detectedBarcodeMarkers[barcodeId] = {
            visible: true,
            pos: [],
            matrix: new Float32Array(16)
          }
          // if the code is not for a parameter, put it in the codes_detected list
          if (barcodeId < 7 || barcodeId == 63) {
  					detectedBarcodeMarkers[barcodeId].visible = true;
            detectedBarcodeMarkers[barcodeId].pos.push(getPosition(ev.data.marker.pos));
  					//detectedBarcodeMarkers[barcodeId].pos.push(ev.data.marker.pos);
  					//console.log("saw a barcode marker with id", barcodeId);
  					//console.log("position : ", detectedBarcodeMarkers[barcodeId].pos[0]);
  					detectedBarcodeMarkers[barcodeId].matrix.set(transform);
  					codes_detected.push(barcodeId);
          }

          else {
            //set the parameter to the method (pos of parameter is (x + w, y) the position of the method (x, y))
            // probably the parameter order in the list is the parameter order to put in the function list TEST IT!
            //var pos_test = getPosition(ev.data.marker.pos);
            detectedBarcodeMarkers[barcodeId].visible = true;
            detectedBarcodeMarkers[barcodeId].pos.push(getPosition(ev.data.marker.pos));
            par_list.push(code_to_block[barcodeId]);

          }
				}
        else {
          if (checkBlock(detectedBarcodeMarkers[barcodeId].pos, getPosition(ev.data.marker.pos)) == -1) {
          //if the barcode detected was not added to the list, add it
            if (barcodeId < 7 || barcodeId == 63) {
              // if the code is not for a parameter, put it in the codes_detected list
              detectedBarcodeMarkers[barcodeId].pos.push(getPosition(ev.data.marker.pos));
              //console.log("again saw a barcode marker with id", barcodeId);
    					//console.log("position : ", detectedBarcodeMarkers[barcodeId].pos[1]);
              detectedBarcodeMarkers[barcodeId].visible = true;
              detectedBarcodeMarkers[barcodeId].matrix.set(transform);
    					codes_detected.push(barcodeId);
            }
            else {
              detectedBarcodeMarkers[barcodeId].visible = true;
              detectedBarcodeMarkers[barcodeId].pos.push(getPosition(ev.data.marker.pos));
              par_list.push(code_to_block[barcodeId]);
            }
          }
        }
        // restart the list with the first block: the green flag
				if (detectedBarcodeMarkers[0] && barcodeId == 0) {
					detectedBarcodeMarkers = {};
          //check_new_blocks(codes_detected, last_codes_detected);
          last_codes_detected = codes_detected;
					codes_detected = [];
          par_list = [];
					detectedBarcodeMarkers[barcodeId] = {
						visible: true,
						pos: [],
						matrix: new Float32Array(16)
					}
					detectedBarcodeMarkers[barcodeId].visible = true;
          detectedBarcodeMarkers[barcodeId].pos.push(getPosition(ev.data.marker.pos));
					//detectedBarcodeMarkers[barcodeId].pos.push(ev.data.marker.pos);
					detectedBarcodeMarkers[barcodeId].matrix.set(transform);
					codes_detected.push(barcodeId);
				}

			}
		});
		var tick = function() {
			arScene.process();
			arScene.renderOn(renderer);
			requestAnimationFrame(tick);
		};
		tick();
	}});
	delete window.ARThreeOnLoad;
};

if (window.ARController && ARController.getUserMediaThreeScene) {
	ARThreeOnLoad();
}
<|endoftext|>


<commit_before>var ngEventProvider = function (grid, $scope, domUtilityService, $timeout) {
    var self = this;
    // The init method gets called during the ng-grid directive execution.
    self.colToMove = undefined;
    self.groupToMove = undefined;
    self.assignEvents = function() {
        // Here we set the onmousedown event handler to the header container.
        if (grid.config.jqueryUIDraggable && !grid.config.enablePinning) {
            grid.$groupPanel.droppable({
                addClasses: false,
                drop: function(event) {
                    self.onGroupDrop(event);
                }
            });
        } else {
            grid.$groupPanel.on('mousedown', self.onGroupMouseDown).on('dragover', self.dragOver).on('drop', self.onGroupDrop);
            grid.$headerScroller.on('mousedown', self.onHeaderMouseDown).on('dragover', self.dragOver);
            if (grid.config.enableColumnReordering) {
                grid.$headerScroller.on('drop', self.onHeaderDrop);
            }
        }
        $scope.$watch('renderedColumns', function() {
            $timeout(self.setDraggables);
        });
    };
    self.dragStart = function(evt){
      //FireFox requires there to be dataTransfer if you want to drag and drop.
      evt.dataTransfer.setData('text', ''); //cannot be empty string
    };
    self.dragOver = function(evt) {
        evt.preventDefault();
    };
    //For JQueryUI
    self.setDraggables = function() {
        if (!grid.config.jqueryUIDraggable) {
            //Fix for FireFox. Instead of using jQuery on('dragstart', function) on find, we have to use addEventListeners for each column.
            var columns = grid.$root.find('.ngHeaderSortColumn'); //have to iterate if using addEventListener
            angular.forEach(columns, function(col){
                if(col.className && col.className.indexOf("ngHeaderSortColumn") !== -1){
                    col.setAttribute('draggable', 'true');
                    //jQuery 'on' function doesn't have  dataTransfer as part of event in handler unless added to event props, which is not recommended
                    //See more here: http://api.jquery.com/category/events/event-object/
                    if (col.addEventListener) { //IE8 doesn't have drag drop or event listeners
                        col.addEventListener('dragstart', self.dragStart);
                    }
                }
            });
            if (navigator.userAgent.indexOf("MSIE") !== -1){
                //call native IE dragDrop() to start dragging
                grid.$root.find('.ngHeaderSortColumn').bind('selectstart', function () {
                    this.dragDrop();
                    return false;
                });
            }
        } else {
            grid.$root.find('.ngHeaderSortColumn').draggable({
                helper: 'clone',
                appendTo: 'body',
                stack: 'div',
                addClasses: false,
                start: function(event) {
                    self.onHeaderMouseDown(event);
                }
            }).droppable({
                drop: function(event) {
                    self.onHeaderDrop(event);
                }
            });
        }
    };
    self.onGroupMouseDown = function(event) {
        var groupItem = $(event.target);
        // Get the scope from the header container
        if (groupItem[0].className !== 'ngRemoveGroup') {
            var groupItemScope = angular.element(groupItem).scope();
            if (groupItemScope) {
                // set draggable events
                if (!grid.config.jqueryUIDraggable) {
                    groupItem.attr('draggable', 'true');
                    if(this.addEventListener){//IE8 doesn't have drag drop or event listeners
                        this.addEventListener('dragstart', self.dragStart);
                    }
                    if (navigator.userAgent.indexOf("MSIE") !== -1){
                        //call native IE dragDrop() to start dragging
                        groupItem.bind('selectstart', function () {
                            this.dragDrop();
                            return false;
                        });
                    }
                }
                // Save the column for later.
                self.groupToMove = { header: groupItem, groupName: groupItemScope.group, index: groupItemScope.$index };
            }
        } else {
            self.groupToMove = undefined;
        }
    };
    self.onGroupDrop = function(event) {
        event.stopPropagation();
        // clear out the colToMove object
        var groupContainer;
        var groupScope;
        if (self.groupToMove) {
            // Get the closest header to where we dropped
            groupContainer = $(event.target).closest('.ngGroupElement'); // Get the scope from the header.
            if (groupContainer.context.className === 'ngGroupPanel') {
                $scope.configGroups.splice(self.groupToMove.index, 1);
                $scope.configGroups.push(self.groupToMove.groupName);
            } else {
                groupScope = angular.element(groupContainer).scope();
                if (groupScope) {
                    // If we have the same column, do nothing.
                    if (self.groupToMove.index !== groupScope.$index) {
                        // Splice the columns
                        $scope.configGroups.splice(self.groupToMove.index, 1);
                        $scope.configGroups.splice(groupScope.$index, 0, self.groupToMove.groupName);
                    }
                }
            }
            self.groupToMove = undefined;
            grid.fixGroupIndexes();
        } else if (self.colToMove) {
            if ($scope.configGroups.indexOf(self.colToMove.col) === -1) {
                groupContainer = $(event.target).closest('.ngGroupElement'); // Get the scope from the header.
                if (groupContainer.context.className === 'ngGroupPanel' || groupContainer.context.className === 'ngGroupPanelDescription ng-binding') {
                    $scope.groupBy(self.colToMove.col);
                } else {
                    groupScope = angular.element(groupContainer).scope();
                    if (groupScope) {
                        // Splice the columns
                        $scope.removeGroup(groupScope.$index);
                    }
                }
            }
            self.colToMove = undefined;
        }
        if (!$scope.$$phase) {
            $scope.$apply();
        }
    };
    //Header functions
    self.onHeaderMouseDown = function(event) {
        // Get the closest header container from where we clicked.
        var headerContainer = $(event.target).closest('.ngHeaderSortColumn');
        // Get the scope from the header container
        var headerScope = angular.element(headerContainer).scope();
        if (headerScope) {
            // Save the column for later.
            self.colToMove = { header: headerContainer, col: headerScope.col };
        }
    };
    self.onHeaderDrop = function(event) {
        if (!self.colToMove || self.colToMove.col.pinned) {
            return;
        }
        // Get the closest header to where we dropped
        var headerContainer = $(event.target).closest('.ngHeaderSortColumn');
        // Get the scope from the header.
        var headerScope = angular.element(headerContainer).scope();
        if (headerScope) {
            // If we have the same column or the target column is pinned, do nothing.
            if (self.colToMove.col === headerScope.col || headerScope.col.pinned) {
                return;
            }
            // Splice the columns
            $scope.columns.splice(self.colToMove.col.index, 1);
            $scope.columns.splice(headerScope.col.index, 0, self.colToMove.col);
            grid.fixColumnIndexes();
            // clear out the colToMove object
            self.colToMove = undefined;
            domUtilityService.digest($scope);
        }
    };

    self.assignGridEventHandlers = function() {
        //Chrome and firefox both need a tab index so the grid can recieve focus.
        //need to give the grid a tabindex if it doesn't already have one so
        //we'll just give it a tab index of the corresponding gridcache index
        //that way we'll get the same result every time it is run.
        //configurable within the options.
        if (grid.config.tabIndex === -1) {
            grid.$viewport.attr('tabIndex', domUtilityService.numberOfGrids);
            domUtilityService.numberOfGrids++;
        } else {
            grid.$viewport.attr('tabIndex', grid.config.tabIndex);
        }
        // resize on window resize
        var windowThrottle;
        $(window).resize(function(){
            clearTimeout(windowThrottle);
            windowThrottle = setTimeout(function() {
                //in function for IE8 compatibility
                domUtilityService.RebuildGrid($scope,grid);
            }, 100);
        });
        // resize on parent resize as well.
        var parentThrottle;
        $(grid.$root.parent()).on('resize', function() {
            clearTimeout(parentThrottle);
            parentThrottle = setTimeout(function() {
                //in function for IE8 compatibility
                domUtilityService.RebuildGrid($scope,grid);
            }, 100);
        });
    };
    // In this example we want to assign grid events.
    self.assignGridEventHandlers();
    self.assignEvents();
};
<commit_msg>Avoid various memory leaks - Removing window resize handlers on destruction - Stop watching from parent scope on destroy so we can hopefully let go of the grid - Added cleanup to avoid leaking grid through additions to the options object<commit_after>var ngEventProvider = function (grid, $scope, domUtilityService, $timeout) {
    var self = this;
    // The init method gets called during the ng-grid directive execution.
    self.colToMove = undefined;
    self.groupToMove = undefined;
    self.assignEvents = function() {
        // Here we set the onmousedown event handler to the header container.
        if (grid.config.jqueryUIDraggable && !grid.config.enablePinning) {
            grid.$groupPanel.droppable({
                addClasses: false,
                drop: function(event) {
                    self.onGroupDrop(event);
                }
            });
        } else {
            grid.$groupPanel.on('mousedown', self.onGroupMouseDown).on('dragover', self.dragOver).on('drop', self.onGroupDrop);
            grid.$headerScroller.on('mousedown', self.onHeaderMouseDown).on('dragover', self.dragOver);
            if (grid.config.enableColumnReordering) {
                grid.$headerScroller.on('drop', self.onHeaderDrop);
            }
        }
        $scope.$watch('renderedColumns', function() {
            $timeout(self.setDraggables);
        });
    };
    self.dragStart = function(evt){
      //FireFox requires there to be dataTransfer if you want to drag and drop.
      evt.dataTransfer.setData('text', ''); //cannot be empty string
    };
    self.dragOver = function(evt) {
        evt.preventDefault();
    };
    //For JQueryUI
    self.setDraggables = function() {
        if (!grid.config.jqueryUIDraggable) {
            //Fix for FireFox. Instead of using jQuery on('dragstart', function) on find, we have to use addEventListeners for each column.
            var columns = grid.$root.find('.ngHeaderSortColumn'); //have to iterate if using addEventListener
            angular.forEach(columns, function(col){
                if(col.className && col.className.indexOf("ngHeaderSortColumn") !== -1){
                    col.setAttribute('draggable', 'true');
                    //jQuery 'on' function doesn't have  dataTransfer as part of event in handler unless added to event props, which is not recommended
                    //See more here: http://api.jquery.com/category/events/event-object/
                    if (col.addEventListener) { //IE8 doesn't have drag drop or event listeners
                        col.addEventListener('dragstart', self.dragStart);
                    }
                }
            });
            if (navigator.userAgent.indexOf("MSIE") !== -1){
                //call native IE dragDrop() to start dragging
                grid.$root.find('.ngHeaderSortColumn').bind('selectstart', function () {
                    this.dragDrop();
                    return false;
                });
            }
        } else {
            grid.$root.find('.ngHeaderSortColumn').draggable({
                helper: 'clone',
                appendTo: 'body',
                stack: 'div',
                addClasses: false,
                start: function(event) {
                    self.onHeaderMouseDown(event);
                }
            }).droppable({
                drop: function(event) {
                    self.onHeaderDrop(event);
                }
            });
        }
    };
    self.onGroupMouseDown = function(event) {
        var groupItem = $(event.target);
        // Get the scope from the header container
        if (groupItem[0].className !== 'ngRemoveGroup') {
            var groupItemScope = angular.element(groupItem).scope();
            if (groupItemScope) {
                // set draggable events
                if (!grid.config.jqueryUIDraggable) {
                    groupItem.attr('draggable', 'true');
                    if(this.addEventListener){//IE8 doesn't have drag drop or event listeners
                        this.addEventListener('dragstart', self.dragStart);
                    }
                    if (navigator.userAgent.indexOf("MSIE") !== -1){
                        //call native IE dragDrop() to start dragging
                        groupItem.bind('selectstart', function () {
                            this.dragDrop();
                            return false;
                        });
                    }
                }
                // Save the column for later.
                self.groupToMove = { header: groupItem, groupName: groupItemScope.group, index: groupItemScope.$index };
            }
        } else {
            self.groupToMove = undefined;
        }
    };
    self.onGroupDrop = function(event) {
        event.stopPropagation();
        // clear out the colToMove object
        var groupContainer;
        var groupScope;
        if (self.groupToMove) {
            // Get the closest header to where we dropped
            groupContainer = $(event.target).closest('.ngGroupElement'); // Get the scope from the header.
            if (groupContainer.context.className === 'ngGroupPanel') {
                $scope.configGroups.splice(self.groupToMove.index, 1);
                $scope.configGroups.push(self.groupToMove.groupName);
            } else {
                groupScope = angular.element(groupContainer).scope();
                if (groupScope) {
                    // If we have the same column, do nothing.
                    if (self.groupToMove.index !== groupScope.$index) {
                        // Splice the columns
                        $scope.configGroups.splice(self.groupToMove.index, 1);
                        $scope.configGroups.splice(groupScope.$index, 0, self.groupToMove.groupName);
                    }
                }
            }
            self.groupToMove = undefined;
            grid.fixGroupIndexes();
        } else if (self.colToMove) {
            if ($scope.configGroups.indexOf(self.colToMove.col) === -1) {
                groupContainer = $(event.target).closest('.ngGroupElement'); // Get the scope from the header.
                if (groupContainer.context.className === 'ngGroupPanel' || groupContainer.context.className === 'ngGroupPanelDescription ng-binding') {
                    $scope.groupBy(self.colToMove.col);
                } else {
                    groupScope = angular.element(groupContainer).scope();
                    if (groupScope) {
                        // Splice the columns
                        $scope.removeGroup(groupScope.$index);
                    }
                }
            }
            self.colToMove = undefined;
        }
        if (!$scope.$$phase) {
            $scope.$apply();
        }
    };
    //Header functions
    self.onHeaderMouseDown = function(event) {
        // Get the closest header container from where we clicked.
        var headerContainer = $(event.target).closest('.ngHeaderSortColumn');
        // Get the scope from the header container
        var headerScope = angular.element(headerContainer).scope();
        if (headerScope) {
            // Save the column for later.
            self.colToMove = { header: headerContainer, col: headerScope.col };
        }
    };
    self.onHeaderDrop = function(event) {
        if (!self.colToMove || self.colToMove.col.pinned) {
            return;
        }
        // Get the closest header to where we dropped
        var headerContainer = $(event.target).closest('.ngHeaderSortColumn');
        // Get the scope from the header.
        var headerScope = angular.element(headerContainer).scope();
        if (headerScope) {
            // If we have the same column or the target column is pinned, do nothing.
            if (self.colToMove.col === headerScope.col || headerScope.col.pinned) {
                return;
            }
            // Splice the columns
            $scope.columns.splice(self.colToMove.col.index, 1);
            $scope.columns.splice(headerScope.col.index, 0, self.colToMove.col);
            grid.fixColumnIndexes();
            // clear out the colToMove object
            self.colToMove = undefined;
            domUtilityService.digest($scope);
        }
    };

    self.assignGridEventHandlers = function() {
        //Chrome and firefox both need a tab index so the grid can recieve focus.
        //need to give the grid a tabindex if it doesn't already have one so
        //we'll just give it a tab index of the corresponding gridcache index
        //that way we'll get the same result every time it is run.
        //configurable within the options.
        if (grid.config.tabIndex === -1) {
            grid.$viewport.attr('tabIndex', domUtilityService.numberOfGrids);
            domUtilityService.numberOfGrids++;
        } else {
            grid.$viewport.attr('tabIndex', grid.config.tabIndex);
        }
        // resize on window resize
        var windowThrottle;
        var windowResize = function(){
            clearTimeout(windowThrottle);
            windowThrottle = setTimeout(function() {
                //in function for IE8 compatibility
                domUtilityService.RebuildGrid($scope,grid);
            }, 100);
        };
        $(window).on('resize.nggrid', windowResize);
        // resize on parent resize as well.
        var parentThrottle;
        var parentResize = function() {
            clearTimeout(parentThrottle);
            parentThrottle = setTimeout(function() {
                //in function for IE8 compatibility
                domUtilityService.RebuildGrid($scope,grid);
            }, 100);
        };
        $(grid.$root.parent()).on('resize.nggrid', parentResize);

        $scope.$on('$destroy', function(){
            $(window).off('resize.nggrid', windowResize);
            $(grid.$root.parent()).off('resize.nggrid', parentResize);
        });
    };
    // In this example we want to assign grid events.
    self.assignGridEventHandlers();
    self.assignEvents();
};
<|endoftext|>


<commit_before>'use strict'; // eslint-disable-line

const stream = require('stream');
const MongoClient = require('mongodb').MongoClient;
const { Timestamp } = require('bson');

let lastEndID = undefined;

const ops = {
    i: 'put',
    u: 'put',
    d: 'delete',
};

class ListRecordStream extends stream.Transform {
    constructor(logger) {
        super({ objectMode: true });
        this.logger = logger;
        this.hasStarted = false;
        this.start = undefined;
        this.end = undefined;
        this.lastUniqID = undefined;
        // this.unpublishedListing is true once we pass the oplog that has the
        // start seq timestamp and uniqID 'h'
        this.unpublishedListing = undefined;
    }

    _transform(itemObj, encoding, callback) {
        if (!itemObj) {
            this.push(null);
            this.emit('info', {
                start: this.start,
                end: this.end,
                uniqID: this.lastUniqID,
            });
            return callback();
        }

        // always update to most recent uniqID
        this.lastUniqID = itemObj.h.toString();

        if (this.end === undefined || itemObj.ts.toNumber() > this.end) {
            this.end = itemObj.ts.toNumber();
        }

        // only push to stream unpublished objects
        if (!this.unpublishedListing) {
            if (lastEndID === itemObj.h.toString()) {
                this.unpublishedListing = true;
            }
            return callback();
        }

        if (!this.hasStarted) {
            this.hasStarted = true;
            this.start = itemObj.ts.toNumber();
            this.emit('info', {
                start: this.start,
                end: this.end,
                uniqId: this.lastUniqID,
            });
        }

        // don't push oplogs that have already been sent
        if (!this.unpublishedListing) {
            return callback();
        }

        const dbName = itemObj.ns.split('.');
        const streamObject = {
            timestamp: new Date(itemObj.ts.high_ * 1000),
            db: dbName[1],
            entries: [
                {
                    type: ops[itemObj.op],
                    key: itemObj.o._id,
                    value: JSON.stringify(itemObj.o.value),
                },
            ],
        };
        return callback(null, streamObject);
    }

    _flush(callback) {
        this.emit('info', {
            start: this.start,
            end: this.end,
            uniqID: this.lastUniqID,
        });
        this.push(null);
        callback();
    }
}

/**
 * @class
 * @classdesc Class to consume mongo oplog
 */
class LogConsumer {

    /**
     * @constructor
     *
     * @param {object} mongoConfig - object with the mongo configuration
     * @param {string} logger - logger
     */
    constructor(mongoConfig, logger) {
        const { replicaSetHosts } = mongoConfig;
        // 'local' is the database where MongoDB has oplogs.rs capped collection
        this.database = 'local';
        this.mongoUrl = `mongodb://${replicaSetHosts}/local`;
        this.logger = logger;
    }

    /**
     * Connect to MongoClient using Mongo node module to access database and
     * database oplogs (operation logs)
     *
     * @param {function} done - callback function, called with an error object
     * or null and an object as 2nd parameter
     * @return {undefined}
    */
    connectMongo(done) {
        MongoClient.connect(this.mongoUrl, { replicaSet: 'rs0' },
        (err, client) => {
            if (err) {
                this.logger.error('Unable to connect to MongoDB',
                { error: err });
                return done(err);
            }
            this.logger.info('connected to mongodb');
            this.client = client;
            this.db = client.db(this.database, {
                ignoreUndefined: true,
            });
            return done();
        });
    }
    /**
     * Read a series of log records from mongo
     *
     * @param {Object} [params] - params object
     * @param {String} [params.startSeq] - fetch starting from this
     *   sequence number
     * @param {Number} [params.limit] - maximum number of log records
     *   to return
     * @param {function} cb - callback function, called with an error
     *   object or null and an object as 2nd parameter
     *
     * @return {undefined}
     */
    readRecords(params, cb) {
        const recordStream = new ListRecordStream(this.logger);
        const limit = params.limit || 10000;
        const startIDandSeq = params.startSeq.toString().split('|');
        const startSeq = parseInt(startIDandSeq[0], 10) || 0;
        lastEndID = startIDandSeq[1];

        this.coll = this.db.collection('oplog.rs');
        return this.coll.find({
            ns: /^(?!.*metadata.*(?:__)).*metadata\.\w+.*/,
            ts: { $gte: Timestamp.fromNumber(startSeq) },
        }, {
            limit,
            tailable: false,
            awaitData: false,
            noCursorTimeout: true,
            OplogReplay: true,
            numberOfRetries: Number.MAX_VALUE,
        }, (err, res) => {
            const stream = res.stream();
            stream.on('data', data => {
                recordStream.write(data);
            });
            stream.on('end', () => {
                recordStream.write(undefined);
            });
            recordStream.once('info', info => {
                recordStream.removeAllListeners('error');
                cb(null, { info, log: recordStream });
            });
            return undefined;
        });
    }
}

module.exports = LogConsumer;
<commit_msg>Fix mongodb log consumer initial values<commit_after>'use strict'; // eslint-disable-line

const stream = require('stream');
const MongoClient = require('mongodb').MongoClient;
const { Timestamp } = require('bson');

let lastEndID = null;

const ops = {
    i: 'put',
    u: 'put',
    d: 'delete',
};

class ListRecordStream extends stream.Transform {
    constructor(logger) {
        super({ objectMode: true });
        this.logger = logger;
        this.hasStarted = false;
        this.start = null;
        this.end = null;
        this.lastUniqID = null;
        // this.unpublishedListing is true once we pass the oplog that has the
        // start seq timestamp and uniqID 'h'
        this.unpublishedListing = null;
    }

    _transform(itemObj, encoding, callback) {
        if (!itemObj) {
            this.push(null);
            this.emit('info', {
                start: this.start,
                end: this.end,
                uniqID: this.lastUniqID,
            });
            return callback();
        }

        // always update to most recent uniqID
        this.lastUniqID = itemObj.h.toString();

        if (this.end === null || itemObj.ts.toNumber() > this.end) {
            this.end = itemObj.ts.toNumber();
        }

        // only push to stream unpublished objects
        if (!this.unpublishedListing) {
            if (lastEndID === itemObj.h.toString()) {
                this.unpublishedListing = true;
            }
            return callback();
        }

        if (!this.hasStarted) {
            this.hasStarted = true;
            this.start = itemObj.ts.toNumber();
            this.emit('info', {
                start: this.start,
                end: this.end,
                uniqId: this.lastUniqID,
            });
        }

        // don't push oplogs that have already been sent
        if (!this.unpublishedListing) {
            return callback();
        }

        const dbName = itemObj.ns.split('.');
        const streamObject = {
            timestamp: new Date(itemObj.ts.high_ * 1000),
            db: dbName[1],
            entries: [
                {
                    type: ops[itemObj.op],
                    key: itemObj.o._id,
                    value: JSON.stringify(itemObj.o.value),
                },
            ],
        };
        return callback(null, streamObject);
    }

    _flush(callback) {
        this.emit('info', {
            start: this.start,
            end: this.end,
            uniqID: this.lastUniqID,
        });
        this.push(null);
        callback();
    }
}

/**
 * @class
 * @classdesc Class to consume mongo oplog
 */
class LogConsumer {

    /**
     * @constructor
     *
     * @param {object} mongoConfig - object with the mongo configuration
     * @param {string} logger - logger
     */
    constructor(mongoConfig, logger) {
        const { replicaSetHosts } = mongoConfig;
        // 'local' is the database where MongoDB has oplogs.rs capped collection
        this.database = 'local';
        this.mongoUrl = `mongodb://${replicaSetHosts}/local`;
        this.logger = logger;
    }

    /**
     * Connect to MongoClient using Mongo node module to access database and
     * database oplogs (operation logs)
     *
     * @param {function} done - callback function, called with an error object
     * or null and an object as 2nd parameter
     * @return {undefined}
    */
    connectMongo(done) {
        MongoClient.connect(this.mongoUrl, { replicaSet: 'rs0' },
        (err, client) => {
            if (err) {
                this.logger.error('Unable to connect to MongoDB',
                { error: err });
                return done(err);
            }
            this.logger.info('connected to mongodb');
            this.client = client;
            this.db = client.db(this.database, {
                ignoreUndefined: true,
            });
            return done();
        });
    }
    /**
     * Read a series of log records from mongo
     *
     * @param {Object} [params] - params object
     * @param {String} [params.startSeq] - fetch starting from this
     *   sequence number
     * @param {Number} [params.limit] - maximum number of log records
     *   to return
     * @param {function} cb - callback function, called with an error
     *   object or null and an object as 2nd parameter
     *
     * @return {undefined}
     */
    readRecords(params, cb) {
        const recordStream = new ListRecordStream(this.logger);
        const limit = params.limit || 10000;
        const startIDandSeq = params.startSeq.toString().split('|');
        const startSeq = parseInt(startIDandSeq[0], 10) || 0;
        lastEndID = startIDandSeq[1];

        this.coll = this.db.collection('oplog.rs');
        return this.coll.find({
            ns: /^(?!.*metadata.*(?:__)).*metadata\.\w+.*/,
            ts: { $gte: Timestamp.fromNumber(startSeq) },
        }, {
            limit,
            tailable: false,
            awaitData: false,
            noCursorTimeout: true,
            OplogReplay: true,
            numberOfRetries: Number.MAX_VALUE,
        }, (err, res) => {
            const stream = res.stream();
            stream.on('data', data => {
                recordStream.write(data);
            });
            stream.on('end', () => {
                recordStream.write(undefined);
            });
            recordStream.once('info', info => {
                recordStream.removeAllListeners('error');
                cb(null, { info, log: recordStream });
            });
            return undefined;
        });
    }
}

module.exports = LogConsumer;
<|endoftext|>